Forum Programmation.c Masquer stdout puis réafficher.

Posté par  (site web personnel) .
Étiquettes : aucune
0
5
mar.
2007
Bonjour,

Dans un morceau de code, l'utilisateur doit taper un mot de passe, je ne voudrais pas qu'il s'affiche, pour ce faire, j'emploie ce code :

...
for (i = 0; i < 3; i++) {
printf("Password (ne s'affiche pas): ");
if((password = read_password()) == EOF)return FALSE;
if (strlen(password) == 1) {
printf("Erreur \n");
continue;
}

password[strlen(password) - 1] = '\0';

...
}

return FALSE;
}

/* Return EOF si problème */
char* read_password(){
int sortie;
char * password = (char *) malloc(sizeof(char) * TAILLE_MOT_PASSE);
if((sortie = dup(1)) == -1) return EOF;/*Dupliquer stdout */
if(close(1) == -1) return EOF;/* Fermer stdout */
if (!fgets(password, (TAILLE_MOT_PASSE + 1), stdin)) return EOF;/* Lire le mot de passe */
if(dup2(sortie,1) == -1) return EOF; /* On n'as pas su remettre stdout */
return password;
}


La for du dessus n'est pas de moi, je n'ai écrit que la fonction read_password().
Le problème : ça ne marche pas, il affiche le mot de passe et n'affiche pas :

Password (ne s'affiche pas):

Donc ça fait l'inverse de ce que je voulais.
Pouvez-vous m'aider ?
  • # curses.h

    Posté par  . Évalué à 2.

    SI tu peux utiliser des libs, essaye avec la fonction noecho() de curses.h.

    man noecho ou http://www.delafond.org/traducmanfr/X11/man3/curs_inopts.3x.(...)

    Sinon, je sais pas.
    • [^] # Re: curses.h

      Posté par  . Évalué à 2.

      un exemple d'utilisation : http://www.phim.unibe.ch/comp_doc/c_manual/C/EXAMPLES/passwo(...)
      /************************************************************************
       *
       * Purpose: To read and verify a password.
       * Compile: gcc password.c -lcurses
       * Notes:   To read text from the keyboard on a UNIX system without 
       *	    it being echoed to the screen you can use noecho() and 
       *	    getch() from curses.h In DOS you can use getch() in conio.h
       * Author:  M. J. Leslie
       * Date:    12-Mar-94
       *
       ************************************************************************/
      
      #include <curses.h>
      
      main()
      {
        int  i;
        char buffer[80];			/* work buffer			*/
      
        initscr();				/* initialize the screen	*/
      
        printw("Please enter a password => ");	/* update screen image		*/
        refresh();				/* Update screen with screen image */
        noecho();				/* Suppress echo to the screen	*/
      					/* Read characters until C/R	*/ 
        while((buffer[i] = getch()) != '\n') i++;
      
        printw("\nPassword is %s - press return to continue.",buffer);
        refresh();
        getch();
      
        endwin();				/* Shut down curses		*/
      }
      
  • # scanf ?

    Posté par  . Évalué à 2.

    oula, ca m'a l'aire bien compliqué ton code...
    j'ai pas tout regardé, mais un simple scanf() ne ferait'il pas l'affaire.

    un truc du style
    scanf("%s",&str);

    j'peux metromper (ca fait des année que j'ai pas fais du C) mais il me semble que scanf n'ecrit pas sur stdout ?..

    sinon, autres fonctions a voir aussi : fgets() et fgetc()
  • # pas la bonne méthode

    Posté par  . Évalué à 3.

    Tu ne peux pas car en fait stdin et stdout correspondent plus ou moins au même fichier, l'un étant ouvert en lecture, l'autre en écriture. Le fait de voir ce que tu écris dans le terminal, ca ne signifie en aucun cas que tout est envoyé sur stdout. Ce n'est en fait pas du tout le cas.

    De plus, printf équivaut à un fprintf sur stdout, celui-ci étant bufferisé. Donc pour être sûr qu'on voit le prompt qui demande le mot de passe, il faut flusher avant de demander le mot de passe.

    Enfin la méthode classique pour demander des mots de passe, quand on est sur un terminal, c'est d'utiliser les fonctions déclarées dans le fichier termios.h. Je la fais courte dans un petit programme d'exemple (sans malloc, toussa ...):



    #include <stdio.h>
    #include <unistd.h>
    #include <termios.h>

    char password[1000];

    char* passwd ()
    {
    int fail;
    char *p;
    struct termios termio;

    fail = tcgetattr (1, &termio);
    if (fail) {
    perror ("tcgetattr");
    return 0;
    }

    termio.c_lflag &= ~ECHO;
    fail = tcsetattr (1, TCSANOW, &termio);
    if (fail) {
    perror ("tcsetattr");
    return 0;
    }

    p = fgets (password, -1+sizeof password, stdin);
    termio.c_lflag |= ECHO;
    tcsetattr (1, TCSANOW, &termio);

    return p;
    }

    int main ()
    {
    *password = 0;
    fprintf (stdout, "Password (ne s'affiche pas): ");
    fflush (stdout);

    passwd ();
    if (!*password)
    return 1;

    password[-1+sizeof password] = 0;
    printf ("\npassword: %s", password);
    return 0;
    }
    • [^] # Re: pas la bonne méthode

      Posté par  (site web personnel) . Évalué à 1.

      Ben en tout cas, simple copier coller et ça segfault.
      • [^] # Re: pas la bonne méthode

        Posté par  . Évalué à 3.

        http://chezmoicamarche.org/
        plus exactement:

        $ cat > main.c
        (...) copier-coller du texte (...)
        ^D
        $
        $ gcc -W -Wall main.c
        $ ./a.out
        Password (ne s'affiche pas):
        password: plop
        $

        Donc il ne doit pas manquer grand chose. Je serais bien curieux de voir où ca foire, quand même. Si dès fois t'as l'occas de passer un coup de debuggeur ...
        Quoi qu'il en soit, man termios.
        • [^] # Re: pas la bonne méthode

          Posté par  . Évalué à 3.

          Ceci dit, je dis qu'il ne faut pas s'occuper de stdout et je le fait ...
          La fonction passwd doit utiliser stdin, et donc:

          char* passwd ()
          {
          int fail;
          char *p;
          struct termios termio;

          fail = tcgetattr (0, &termio);
          if (fail) {
          perror ("tcgetattr");
          return 0;
          }

          termio.c_lflag &= ~ECHO;
          fail = tcsetattr (0, TCSANOW, &termio);
          if (fail) {
          perror ("tcsetattr");
          return 0;
          }

          p = fgets (password, -1+sizeof password, stdin);
          termio.c_lflag |= ECHO;
          tcsetattr (0, TCSANOW, &termio);

          return p;
          }
        • [^] # Re: pas la bonne méthode

          Posté par  (site web personnel) . Évalué à 2.

          gg, j'ai du foiré quelque chose, effectivement ça marche.
          • [^] # Re: pas la bonne méthode

            Posté par  . Évalué à 2.

            Ça devient tout de suite plus clair quand on comprend qu'en principe l'écho est local, donc géré par le terminal lui-même et pas par le système auquel il est relié.

            Il faut donc demander au terminal de passer dans un mode particulier. En fonction de l'équipement, ça se fait soit avec des ioctl(), soit par l'émission de caractères spéciaux.

            Il existe donc l'interface standard de termios pour contrôler les paramètre d'un terminal. C'est ce qui est exploité notamment par la commande stty, depuis le shell ...
        • [^] # Re: pas la bonne méthode

          Posté par  . Évalué à 1.

          Le fait qu'il ne t'affiche pas le "Password" est normal, le buffer de printf n'est pas plein, pour qu'il s'affiche, il suffit de rajouter un simple retour à la ligne avec \n et ça fonctionnnera.

          printf("Password\n");


          J'ai galéré pas mal aussi avant de savoir ça...
  • # man getpass()

    Posté par  . Évalué à 3.

    Voilà.
    Sachant que dès que tu as fini de jouer avec ton mot de passe, il faut l'écraser avec des 0 du genre memset(pass, 0, sizeof(pass)), pour éviter qu'il soit (en clair ou non) en mémoire, parce qu'un coredump par exemple c'est vite arrivé...

Suivre le flux des commentaires

Note : les commentaires appartiennent à celles et ceux qui les ont postés. Nous n’en sommes pas responsables.