I am always confused about return a string literal or a string from a function.
Immutable, literal string
As I understand it, you are safe to return a string literal directly if the return type is declared const, to declare that the string is not intended to be altered. This means you needn't worry about the lifespan of the string / memory leaks.
Mutable, non-literal string
However, if you need a string that you can change in-place, you need to consider the lifespan of the string and the size of the memory allocation in which it is stored.
This becomes an issue, since you can no longer blithely return the same memory containing string for each invocation of the function, since a previous use could have altered the contents of that memory, and/or may still be in use. Hence a new piece of memory must be allocated to hold the string returned.
This is where the potential for a leak occurs, and where the choice needs to be made about where the allocation and de-allocation should occur. You could have the function itself allocate the memory and state in the documentation that this happens and stipulate therein that the caller has to free the memory when it is no longer required (preventing a leak). This means the function can simply return a char *.
The other option is to pass in some memory to the function that was allocated by the caller, and have the function place the string inside that memory. In this case, the caller both allocates and is responsible for freeing that memory.
Finally, I mentioned that the size of the memory and string need to be managed when using a mutable string. The allocation needs to be both large enough for the string initially set by the function and also for any changed that are made after the function, before the memory is freed. Failing to do this correctly can cause a buffer overflow by writing a string to long to fit in the memory initially allocated; this is extremely dangerous to the health and security of your program. It can cause bugs and security holes that are extremely hard to spot (since the source of the error - the overflow - can be far removed from the symptoms seen when the program fails).