In generale gli string literal hanno la stessa lifetime degli array globali - in altre parole, è garantito che sopravvivano per tutta la durata del programma.

Per inciso, lì retStr dovrebbe restituire un const char *, dato che gli string literal non sono modificabili (a seconda dei casi, puoi ottenere un crash o comportamenti inspiegabili del programma).
a ok, e se invece fosse stato [...]
In entrambi i casi non c'è problema (salvo la precisazione del const di prima nel primo caso).
Nel primo caso assegni prima il puntatore al literal ad un altro puntatore e poi lo restituisci - perché dovrebbe essere un problema? Il punto non è la durata di vita dei puntatori (che vengono copiati), ma quella di ciò a cui puntano; qui "ciao" è un literal, quindi sopravvive per tutta la durata del programma, ergo puoi passarlo in giro come ti pare, tanto è memoria che non viene deallocata.

Nel secondo caso, restituisci una copia del puntatore che ti viene passato; vale un discorso analogo: se il puntatore puntava a qualcosa che esisteva prima della chiamata a funzione, non è che questo qualcosa è improvvisamente sparito... di nuovo, non conta il puntatore, ma ciò a cui punta.

Il caso problematico è questo:
codice:
char * retStr()
{
    char str[]="ciao";
    return str;
}
In questo caso sto restituendo un puntatore al primo elemento di un array locale a retStr, che cessa di esistere nel momento in cui la funzione termina, per cui il chiamante si ritrova restituito un puntatore a della roba che non esiste più, per cui è chiaro che in questo caso possono sorgere problemi.