13

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.

user397232
  • 1,750
  • 6
  • 21
  • 30
  • 7
    http://www.linux.com/learn/docs/man/3177-getpass3 – sambowry Nov 23 '09 at 22:37
  • Which console and OS are you on? In most cases the user input will be echoed back on the command-line. This can be changed but will be platform dependent. – Lucas Nov 23 '09 at 22:37
  • 2
    duplicate: http://stackoverflow.com/questions/1413445/read-a-password-from-stdcin – Jonathan Graehl Nov 23 '09 at 22:37
  • It will be different on Windows, Linux, mobile phone, microwave oven or the elevator interface. For which one(s) do you want to write your code? – pmg Nov 23 '09 at 22:37
  • Not really an exact duplicate, as the other question explicitly allows C++ constructs and libraries. – Stefan Kendall Nov 23 '09 at 22:39
  • Be aware that classically, getpass() only supported 8 characters of password input. More modern versions lift that limit (on Solaris, to 256 characters), and you're more likely to encounter modern than ancient. However, length could be a problem on versions of Unix (as opposed to Linux or the BSD derivatives and MacOS X - which supports 128 bytes). – Jonathan Leffler Nov 24 '09 at 04:42

7 Answers7

18

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.

alanc
  • 4,102
  • 21
  • 24
Left For Archive
  • 2,626
  • 1
  • 18
  • 19
  • This function is now [obsolete](https://man7.org/linux/man-pages/man3/getpass.3.html). The man page now recommends manually disabling ECHO using termios() – adrian Dec 20 '21 at 22:27
10

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;
}
Lucas
  • 13,679
  • 13
  • 62
  • 94
6

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);
}
Henrique Gouveia
  • 131
  • 1
  • 10
2

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>
stefanB
  • 77,323
  • 27
  • 116
  • 141
  • Upvoted for the RTFM philosophy and cross platform hint. – conradkleinespel Jul 31 '14 at 20:09
  • Unsafe. The password is then stored in an environment variable and can be read by other processes. Another basic requirement is to zero-out the password ASAP, and this approach doesn't fulfill this requirement. – Nino Filiu Jun 24 '19 at 08:47
2

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...

DigitalRoss
  • 143,651
  • 25
  • 248
  • 329
1

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.

Tim Sparkles
  • 771
  • 6
  • 21
0
     #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;}