Senza getch
fare affidamento ed evitare l'obsoleto getpass
, l'approccio consigliato è disabilitare il terminale ECHO tramite termios
uso. Dopo alcune ricerche per trovare una routine di password flessibile predefinita, sono rimasto sorpreso dal fatto che pochissimi per l'uso autonomo con C. Piuttosto che semplicemente ricodificare getch
con termios c_lflag
opzioni, un approccio leggermente più generalizzato richiede solo alcune aggiunte. Oltre a sostituire getch
qualsiasi routine dovrebbe imporre una lunghezza massima specificata per impedire l'overflow, troncare se l'utente tenta di entrare oltre il massimo e avvisare se il troncamento si verifica in qualche modo.
Di seguito, le aggiunte consentiranno la lettura da qualsiasi FILE *
flusso di input, limitando la lunghezza a una lunghezza specificata, fornisce una capacità di modifica minima (backspace) durante l'acquisizione dell'input, consente di specificare o disabilitare completamente la maschera dei caratteri e infine restituisce la lunghezza della password immessa. È stato aggiunto un avviso quando la password immessa è stata troncata alla lunghezza massima o specificata.
Si spera che si dimostri utile ad altri con questa domanda alla ricerca di una soluzione simile:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <termios.h>
#define MAXPW 32
/* read a string from fp into pw masking keypress with mask char.
getpasswd will read upto sz - 1 chars into pw, null-terminating
the resulting string. On success, the number of characters in
pw are returned, -1 otherwise.
*/
ssize_t getpasswd (char **pw, size_t sz, int mask, FILE *fp)
{
if (!pw || !sz || !fp) return -1; /* validate input */
#ifdef MAXPW
if (sz > MAXPW) sz = MAXPW;
#endif
if (*pw == NULL) { /* reallocate if no address */
void *tmp = realloc (*pw, sz * sizeof **pw);
if (!tmp)
return -1;
memset (tmp, 0, sz); /* initialize memory to 0 */
*pw = (char*) tmp;
}
size_t idx = 0; /* index, number of chars in read */
int c = 0;
struct termios old_kbd_mode; /* orig keyboard settings */
struct termios new_kbd_mode;
if (tcgetattr (0, &old_kbd_mode)) { /* save orig settings */
fprintf (stderr, "%s() error: tcgetattr failed.\n", __func__);
return -1;
} /* copy old to new */
memcpy (&new_kbd_mode, &old_kbd_mode, sizeof(struct termios));
new_kbd_mode.c_lflag &= ~(ICANON | ECHO); /* new kbd flags */
new_kbd_mode.c_cc[VTIME] = 0;
new_kbd_mode.c_cc[VMIN] = 1;
if (tcsetattr (0, TCSANOW, &new_kbd_mode)) {
fprintf (stderr, "%s() error: tcsetattr failed.\n", __func__);
return -1;
}
/* read chars from fp, mask if valid char specified */
while (((c = fgetc (fp)) != '\n' && c != EOF && idx < sz - 1) ||
(idx == sz - 1 && c == 127))
{
if (c != 127) {
if (31 < mask && mask < 127) /* valid ascii char */
fputc (mask, stdout);
(*pw)[idx++] = c;
}
else if (idx > 0) { /* handle backspace (del) */
if (31 < mask && mask < 127) {
fputc (0x8, stdout);
fputc (' ', stdout);
fputc (0x8, stdout);
}
(*pw)[--idx] = 0;
}
}
(*pw)[idx] = 0; /* null-terminate */
/* reset original keyboard */
if (tcsetattr (0, TCSANOW, &old_kbd_mode)) {
fprintf (stderr, "%s() error: tcsetattr failed.\n", __func__);
return -1;
}
if (idx == sz - 1 && c != '\n') /* warn if pw truncated */
fprintf (stderr, " (%s() warning: truncated at %zu chars.)\n",
__func__, sz - 1);
return idx; /* number of chars in passwd */
}
Un semplice programma che mostra l'uso sarebbe il seguente. Se utilizzi un array statico di caratteri per contenere la password, assicurati solo che venga passato un puntatore alla funzione.
int main (void ) {
char pw[MAXPW] = {0};
char *p = pw;
FILE *fp = stdin;
ssize_t nchr = 0;
printf ( "\n Enter password: ");
nchr = getpasswd (&p, MAXPW, '*', fp);
printf ("\n you entered : %s (%zu chars)\n", p, nchr);
printf ( "\n Enter password: ");
nchr = getpasswd (&p, MAXPW, 0, fp);
printf ("\n you entered : %s (%zu chars)\n\n", p, nchr);
return 0;
}
Risultato di esempio
$ ./bin/getpasswd2
Enter password: ******
you entered : 123456 (6 chars)
Enter password:
you entered : abcdef (6 chars)
Nel mondo Linux, il mascheramento di solito non viene eseguito con asterischi, normalmente l'eco viene semplicemente disattivato e il terminale visualizza spazi vuoti, ad es. se usi su
o accedere a un terminale virtuale ecc.
C'è una funzione di libreria per gestire l'ottenimento delle password, non maschererà la password con asterischi ma disabiliterà l'eco della password al terminale. L'ho estratto da un libro su Linux che ho. Credo che faccia parte dello standard posix
#include <unistd.h> char *getpass(const char *prompt); /*Returns pointer to statically allocated input password string on success, or NULL on error*/
La funzione getpass() prima disabilita l'eco e tutta l'elaborazione dei caratteri speciali del terminale (come il carattere di interruzione, normalmente Control-C).
Quindi stampa la stringa puntata dal prompt e legge una riga di input, restituendo la stringa di input con terminazione null con il newline finale rimosso, come risultato della funzione.
Una ricerca su Google per getpass() ha un riferimento all'implementazione GNU (dovrebbe essere presente nella maggior parte delle distribuzioni Linux) e alcuni esempi di codice per implementare la tua, se necessario
http://www.gnu.org/s/hello/manual/libc/getpass.html
Il loro esempio per rotolare il tuo:
#include <termios.h>
#include <stdio.h>
ssize_t
my_getpass (char **lineptr, size_t *n, FILE *stream)
{
struct termios old, new;
int nread;
/* Turn echoing off and fail if we can't. */
if (tcgetattr (fileno (stream), &old) != 0)
return -1;
new = old;
new.c_lflag &= ~ECHO;
if (tcsetattr (fileno (stream), TCSAFLUSH, &new) != 0)
return -1;
/* Read the password. */
nread = getline (lineptr, n, stream);
/* Restore terminal. */
(void) tcsetattr (fileno (stream), TCSAFLUSH, &old);
return nread;
}
Se necessario, puoi usarlo come base per modificarlo per visualizzare gli asterischi.