Masquer la saisie du mot de passe sur le terminal

Je veux masquer mon mot de passe en l'écrivant avec *. J'utilise linux GCC pour ce code. Je sais qu'une solution consiste à utiliser la fonction getch() comme ceci

#include <conio.h>   
int main()
{
    char c,password[10];
    int i;
    while( (c=getch())!= 'n');{
        password[i] = c;
        printf("*");
        i++;
    }
    return 1;
}

Mais le problème est que GCC n'inclut pas le fichier conio.h donc, {[2] } est inutile pour moi. Quelqu'un aurait-il une solution?

46
demandé sur fortunate_man 2011-07-28 13:16:15

12 réponses

Dans le monde Linux, le masquage n'est généralement pas fait avec des astérisques, normalement l'écho est simplement désactivé et le terminal affiche des blancs, par exemple si vous utilisez su ou connectez-vous à un terminal virtuel, etc.

Il existe une fonction de bibliothèque pour gérer l'obtention des mots de passe, elle ne masquera pas le mot de passe avec des astérisques mais désactivera l'écho du mot de passe au terminal. J'ai sorti cela d'un livre linux que j'ai. Je crois que sa partie de la norme posix

#include <unistd.h>
char *getpass(const char *prompt);

/*Returns pointer to statically allocated input password string
on success, or NULL on error*/

La fonction getpass() première désactive l'écho et tout le traitement de caractères spéciaux de terminal (tels que le caractère d'interruption, normalement Contrôle-C).

Il imprime ensuite la chaîne pointée par invite, et lit une ligne de input, renvoyant la chaîne d'entrée terminée par null avec la fin newline stripped, comme résultat de sa fonction.

Une recherche google pour getpass() a une référence à L'implémentation GNU (devrait être dans la plupart des distributions linux) et un exemple de code pour implémenter votre posséder si nécessaire

Http://www.gnu.org/s/hello/manual/libc/getpass.html

Leur exemple pour rouler le vôtre:

#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;
}

Si besoin est, vous pouvez l'utiliser comme base pour le modifier pour afficher des astérisques.

41
répondu matt 2016-11-07 13:51:00

La fonctionnalité de getch (qui est une fonction Windows non standard) peut être émulée avec ce code:

#include <termios.h>
#include <unistd.h>
int getch() {
    struct termios oldt, newt;
    int ch;
    tcgetattr(STDIN_FILENO, &oldt);
    newt = oldt;
    newt.c_lflag &= ~(ICANON | ECHO);
    tcsetattr(STDIN_FILENO, TCSANOW, &newt);
    ch = getchar();
    tcsetattr(STDIN_FILENO, TCSANOW, &oldt);
    return ch;
}

Notez que votre approche n'est pas parfaite - il est préférable d'utiliser quelque chose comme ncurses ou une autre bibliothèque de terminal pour gérer ces choses.

9
répondu Delan Azabani 2011-07-28 09:21:18

Sans getch pour s'appuyer et éviter l'obsolète getpass, l'approche recommandée est de désactiver L'écho du terminal via l'utilisation de termios. Après quelques recherches pour trouver une routine de mot de passe flexible en conserve, j'ai été surpris que très peu pour une utilisation autonome avec C. plutôt que de simplement recoder getch avec des options termios c_lflag, une approche légèrement plus généralisée ne prend que quelques ajouts. Au-delà du remplacement de getch, toute routine doit appliquer une longueur maximale spécifiée pour éviter le débordement, tronquer si l'utilisateur tente d'entrer au-delà du maximum et avertit si la troncature se produit d'une manière ou d'une autre.

Ci-dessous, les ajouts permettront la lecture à partir de n'importe quel flux d'entrée FILE *, en limitant la longueur à une longueur spécifiée, fournir une capacité d'édition minimale (retour arrière) lors de la prise d'entrée, permettre au masque de caractères d'être spécifié ou désactivé complètement, et enfin retourner la longueur du mot de passe entré. Un avertissement a été ajouté lorsque le mot de passe entré a été tronqué au maximum ou spécifié longueur.

Espérons que cela se révélera utile aux autres avec cette question à la recherche d'une solution similaire:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include <sys/time.h>
#include <termios.h>
#include <errno.h>   /* for errno */
#include <unistd.h>  /* for EINTR */

#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 = 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 programme simple montrant l'utilisation serait comme suit. Si vous utilisez un tableau statique de caractères pour contenir le mot de passe, assurez-vous qu'un pointeur est passé à la fonction.

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;
}

Exemple De Sortie

$ ./bin/getpasswd2

 Enter password: ******
 you entered   : 123456  (6 chars)

 Enter password:
 you entered   : abcdef  (6 chars)
8
répondu David C. Rankin 2015-09-06 08:51:16

Vous pouvez créer votre propre fonction getch() sous Linux de cette manière.

int getch() {
    struct termios oldtc, newtc;
    int ch;
    tcgetattr(STDIN_FILENO, &oldtc);
    newtc = oldtc;
    newtc.c_lflag &= ~(ICANON | ECHO);
    tcsetattr(STDIN_FILENO, TCSANOW, &newtc);
    ch=getchar();
    tcsetattr(STDIN_FILENO, TCSANOW, &oldtc);
    return ch;
}

Code de démonstration:

int main(int argc, char **argv) {
    int ch;
    printf("Press x to exit.\n\n");
    for (;;) {
        ch = getch();
        printf("ch = %c (%d)\n", ch, ch);
        if(ch == 'x')
              break;
    }
    return 0;
}
6
répondu Susam Pal 2011-07-28 09:53:26

Votre méthode est correcte, mais vous devrez désactiver l'écho du terminal lorsque le mot de passe est entré:

#include <sgtty.h>

void echo_off()
{
    struct sgttyb state;
    (void)ioctl(0, (int)TIOCGETP, (char *)&state);
    state.sg_flags &= ~ECHO;
    (void)ioctl(0, (int)TIOCSETP, (char *)&state);
}

void echo_on()
{
    struct sgttyb state;
    (void)ioctl(0, (int)TIOCGETP, (char *)&state);
    state.sg_flags |= ECHO;
    (void)ioctl(0, (int)TIOCSETP, (char *)&state);
}

Au Lieu de getch(), pourquoi ne pas simplement utiliser getc() à la place?

3
répondu trojanfoe 2016-11-09 14:05:20

Merci à vous tous dont l'AIDE et le soutien pour résoudre mon problème. Je trouve un meilleur moyen de cacher mon mot de passe sous linux qui me convient le mieux. Pour utiliser getpass() fonction. Il suffit d'inclure "unistd.h" fichier.

Syntex de la fonction getpass:

Char * getpass (const char *message)

Paramètres: invite: pointeur de chaîne à imprimer tout en demandant le mot de passe

Valeur De Retour: pointeur de chaîne de mot de passe

Exemple:

#include <stdio.h>
#include <unistd.h>   
int main()
{
    char *password; // password string pointer
    password = getpass("Enter Password: "); // get a password
    printf("%s\n",password); // this is just for conformation
                             // that password stored successfully
    return 1;
}

Sortie:

Entrez Le Mot De Passe:

Heet

2
répondu Hit's 2011-07-29 11:58:49

Vous pouvez utiliser ncurses.h s'il n'est pas nécessaire d'être portable sur Windows pour cela, mais voici une sorte de version plus "portable":

S'il n'est pas nécessaire d'être portable, dirigez-vous vers une solution ncurses

Portablegetch.h

/*portablegetch.h*/
#ifndef PGETCH
#define PGETCH
#ifdef __unix__
#include <termios.h>
#include <unistd.h>

static struct termios n_term;
static struct termios o_term;

static int
cbreak(int fd) 
{
   if((tcgetattr(fd, &o_term)) == -1)
      return -1;
   n_term = o_term;
   n_term.c_lflag = n_term.c_lflag & ~(ECHO|ICANON);
   n_term.c_cc[VMIN] = 1;
   n_term.c_cc[VTIME]= 0;
   if((tcsetattr(fd, TCSAFLUSH, &n_term)) == -1)
      return -1;
   return 1;
}

int 
getch() 
{
   int cinput;

   if(cbreak(STDIN_FILENO) == -1) {
      fprintf(stderr, "cbreak failure, exiting \n");
      exit(EXIT_FAILURE);
   }
   cinput = getchar();
   tcsetattr(STDIN_FILENO, TCSANOW, &o_term);

   return cinput;
}

#elif _MSC_VER  || __WIN32__ || __MS_DOS__
  #include <conio.h>
#endif
#endif

Et le fichier c

Peu importe.c

#include <stdio.h>
#include <stdlib.h>
#include "portablegetch.h"

int 
main(int argc, char **argv) 
{
  int input;

  printf("Please Enter your Password:\t");

  while(( input=getch() ) != '\n')
        printf("*");
  printf("\n");

  return EXIT_SUCCESS;
}

Cela devrait correspondre à votre problème.

J'espère que ça aide.

2
répondu Daniel Leschkowski 2011-09-27 22:02:57
#include <termios.h>
#include <stdio.h>

static struct termios old, new;

void initTermios(int echo) {
    tcgetattr(0, &old);
    new = old;
    new.c_lflag &= ~ICANON;
    new.c_lflag &= echo ? ECHO : ~ECHO;
    tcsetattr(0, TCSANOW, &new);
}

void resetTermios(void) {
    tcsetattr(0, TCSANOW, &old);
}

char getch_(int echo) {
    char ch;
    initTermios(echo);
    ch = getchar();
    resetTermios();
    return ch;
}

char getch(void) {
    return getch_(0);
}

int main(void) {
    char c;
    printf("(getch example) please type a letter...");
    c = getch();
    printf("\nYou typed: %c\n", c);
    return 0;
}

Copiez simplement ces extraits et utilisez-les. Espérons qu'il a aidé

2
répondu niko 2016-11-09 14:54:10

Malheureusement, dans la bibliothèque standard C, il n'y a pas une telle fonction hors de la boîte. Peut-être dans une bibliothèque tierce.

Une option consiste à utiliser des séquences d'échappement ANSI pour définir la couleur d'arrière-plan sur la couleur de premier plan dans la console pour masquer le mot de passe. Essayez ce lien .

0
répondu Constantinius 2011-07-28 09:23:16

En scannant les caractères, vous pouvez les prendre dans un tampon. Vous devez également écrire du code si vous appuyez sur backspace et corriger correctement le mot de passe inséré.

Voici un code que j'ai écrit une fois avec les malédictions. Compilez avec gcc file.c -o pass_prog -lcurses

#include <stdio.h>
#include <stdlib.h>
#include <curses.h>

#define ENOUGH_SIZE 256

#define ECHO_ON 1
#define ECHO_OFF 0

#define BACK_SPACE 127

char *my_getpass (int echo_state);

int main (void)
{
  char *pass;

  initscr ();

  printw ("Enter Password: ");
  pass = my_getpass (ECHO_ON);

  printw ("\nEntered Password: %s", pass);
  refresh ();
  getch ();
  endwin ();
  return 0;
}


char *my_getpass (int echo_state)
{
  char *pass, c;
  int i=0;

  pass = malloc (sizeof (char) * ENOUGH_SIZE);
  if (pass == NULL)
  {
    perror ("Exit");
    exit (1);
  }

  cbreak ();
  noecho ();

  while ((c=getch()) != '\n')
  {
    if (c == BACK_SPACE)
    {
      /* Do not let the buffer underflow */
      if (i > 0)
      { 
        i--;
        if (echo_state == ECHO_ON)
               printw ("\b \b");
      }
    }
    else if (c == '\t')
      ; /* Ignore tabs */
    else
    {
      pass[i] = c;
      i = (i >= ENOUGH_SIZE) ? ENOUGH_SIZE - 1 : i+1;
      if (echo_state == ECHO_ON)
        printw ("*");
    }
  }
  echo ();
  nocbreak ();
  /* Terminate the password string with NUL */
  pass[i] = '\0';
  endwin ();
  return pass;
}
0
répondu phoxis 2011-07-28 16:23:10

En C, vous pouvez utiliser la fonction getpasswd() qui fait à peu près la même chose que stty dans shell, exemple:

#include <stdio.h>
#include <string.h>
#include <pwd.h>
#include <unistd.h>

int main()
{
    char acct[80], password[80];

    printf(“Account: “);
    fgets(acct, 80, stdin);

    acct[strlen(acct)-1] = 0; /* remove carriage return */

    strncpy(password, getpass(“Password: “), 80);
    printf(“You entered acct %s and pass %s\n”, acct, password);

    return 0;
}

Voici un script shell équivalent qui utilise stty (qui modifie les paramètres de votre tty):

save_state=$(stty -g)
/bin/echo -n “Account: “
read acct
/bin/echo -n “Password: “
stty -echo
read password # this won’t echo
stty “$save_state”
echo “”
echo account = $acct and password = $password

Source: Comment puis-je lire un mot de passe sans le faire écho en C?

0
répondu kenorb 2016-11-09 14:04:08

Notez que L'ICANON termios lflag désactive le carriagereturn/linefeed de traitement, et le paramètre termios d'écho négatif désactive l'écho pour STDIN.

Lorsque vous utilisez ceci (avec ou sans que l'écho soit activé) pour lire un mot de passe et imprimer '*' pour les caractères entrés, il ne s'agit pas seulement de lire des caractères jusqu'à ce qu'un retour newline/chariot soit rencontré, vous devez également traiter le retour arrière dans votre routine de construction de chaînes (sinon les backspaces finissent ne provoque pas la suppression de caractères, comme ce serait le cas avec les différentes fonctions d'entrée basées sur une chaîne).

La même chose se produirait en C dans DOS avec getch tho. cela retournerait également 0x08 pour le retour arrière (ou 127 ou quel que soit votre système d'exploitation spécifique utilise comme Retour arrière)

Garder une trace de 'ne pas supprimer-avant - le début de la chaîne', remplacer la 'nouvelle fin de la chaîne' par 0 et déplacer le compteur de position actuel par un (sauf si vous êtes à la position 0) est jusqu'à le programmeur avec l'une de ces fonctions(même le getch sur dos c).

getpass() ne fait pas ce que l'Utilisateur a initialement demandé btw, il veut *(qui divulgue toujours la longueur du mot de passe aux personnes se tenant derrière lui et regardant son écran, ainsi que dans le scrollbuffer du terminal s'il ne le ferme pas après utilisation). mais SANS *est probablement une meilleure idée dans les "environnements non fermés".

-1
répondu HRH Sven Olaf von CyberBunker 2016-01-16 13:40:11