C command-line password input

2019-01-14 22:54发布

问题:

I want to allow users to enter password using command-line interface. but I don't want to display this password on screen (or display "****").

How to do it in C? Thanks.

Update:

I'm working on Linux only. So I don't actually care about Win or other systems. I tried Lucas' solution and it worked fine. However, I still have another question:

  1. If this is a single process & single thread app, changing setting of termios affects different terminals?

  2. How about 1 process - multi threads, multi processes - multi threads?

Thanks very much.

回答1:

If your system provides it, getpass is an option:

#include <unistd.h>
/* ... */
char *password = getpass("Password: ");

This will not display anything as characters are typed.



回答2:

If you are using a UNIX environment something like this can turn off the ECHO of the command-line.

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

#define SIZE 100

void getPassword(char password[])
{
    static struct termios oldt, newt;
    int i = 0;
    int c;

    /*saving the old settings of STDIN_FILENO and copy settings for resetting*/
    tcgetattr( STDIN_FILENO, &oldt);
    newt = oldt;

    /*setting the approriate bit in the termios struct*/
    newt.c_lflag &= ~(ECHO);          

    /*setting the new bits*/
    tcsetattr( STDIN_FILENO, TCSANOW, &newt);

    /*reading the password from the console*/
    while ((c = getchar())!= '\n' && c != EOF && i < SIZE){
        password[i++] = c;
    }
    password[i] = '\0';

    /*resetting our old STDIN_FILENO*/ 
    tcsetattr( STDIN_FILENO, TCSANOW, &oldt);

}


int main(void)
{
    char password[SIZE];
    printf("please enter password\n");
    getPassword(password);

    printf("Do something with the password <<%s>>\n", password);
    return 0;
}


回答3:

The function getpass is now obsolete. Use termios.

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

void get_password(char *password)
{
    static struct termios old_terminal;
    static struct termios new_terminal;

    //get settings of the actual terminal
    tcgetattr(STDIN_FILENO, &old_terminal);

    // do not echo the characters
    new_terminal = old_terminal;
    new_terminal.c_lflag &= ~(ECHO);

    // set this as the new terminal options
    tcsetattr(STDIN_FILENO, TCSANOW, &new_terminal);

    // get the password
    // the user can add chars and delete if he puts it wrong
    // the input process is done when he hits the enter
    // the \n is stored, we replace it with \0
    if (fgets(password, BUFSIZ, stdin) == NULL)
        password[0] = '\0';
    else
        password[strlen(password)-1] = '\0';

    // go back to the old settings
    tcsetattr(STDIN_FILENO, TCSANOW, &old_terminal);
}

int main(void)
{
    char password[BUFSIZ];

    puts("Insert password:");
    get_password(password);
    puts(password);
}


回答4:

For C/commandline/linux see:

man getch
man noecho

see the coment in getch about noecho. I've never tried this myself.

In bash if you use read -s it does not echo on the screen:

> read -s x
<type something><enter>
> echo $x
<whatever you typed>


回答5:

To do this in a portable way you will need to use a standardized or de-facto standard library.

See man 3 termios and man 3 ncurses.

Here is a program that will work on Linux and other Unix systems...

#include <stdio.h>

int main(void) {
  int f = open("/dev/tty", 0);
  char s[100];

  system("stty -echo > /dev/tty");
  f >= 0 && read(f, s, sizeof s) > 0 &&  printf("password was %s", s);
  system("stty echo > /dev/tty");
  return 0;
}

A number of improvements are possible. Using termios would probably be better, and it would avoid the fork and possible security issues of system(). Using standard I/O to read the password would probably be better. (As written the typed newline would have to be deleted.) There is a getpass() function in Linux and some others however it is marked as "obsolete. Do not use it.". It might be a good idea to deal with SIGTSTP.

Overall, I might look for an existing solution that deals with all these little issues...



回答6:

If you have access to the curses library, you can use noecho. If you're using Microsoft's C compiler, you can use _getch. But afaik, both of these tie you to the console. If you need to read stdin regardless of whether it comes from the console or a file or a piped command, you need to tell the operating system how it should handle the console.



回答7:

     #include<conio.h>
     #include<iostream>
     using namespace std;
     int main(){
     char *pass = new char[20];
     cout<<"Password :";
     int i=0;   
     while( ( pass[i]=getch() ) != '\n' && pass[i] != '\r' && i<19 )
     {putchar('*'); i++;}
     pass[i]='\0';
     cout<<endl;
     if(strcmp("123456789",pass)==0 ) // do stuff
     return 0;}