Getting a password in C without using getpass (3)?
Asked Answered
A

6

25

I could use getpass() to get a password. However, the man page says:

This function is obsolete. Do not use it.

What is the current way to get a password from the user's terminal without echoing it, in a POSIX-compliant way? [Originally I said "portably", but my intention was to avoid using an obsolete function.]

Afflatus answered 28/7, 2009 at 20:5 Comment(3)
There is no portable way - this depends heavily on your platform.Gracye
@Jerry, it isn't worth it... even though it is LEGACY, it is the most portable way to go about it.Wigley
@MichaelAaronSafyan I agree. It's a strange decision to make getpass() obsolete and even removed from POSIX, without providing an alternative (like NetBSD's getpass_r()). Now everyone makes his/her own version, ridden with security/usability/portability bugs.Fecund
L
18

this should work on linux/macosx, a windows version should use Get/Set ConsoleMode

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

int
main(int argc, char **argv)
{
    struct termios oflags, nflags;
    char password[64];

    /* disabling echo */
    tcgetattr(fileno(stdin), &oflags);
    nflags = oflags;
    nflags.c_lflag &= ~ECHO;
    nflags.c_lflag |= ECHONL;

    if (tcsetattr(fileno(stdin), TCSANOW, &nflags) != 0) {
        perror("tcsetattr");
        return EXIT_FAILURE;
    }

    printf("password: ");
    fgets(password, sizeof(password), stdin);
    password[strlen(password) - 1] = 0;
    printf("you typed '%s'\n", password);

    /* restore terminal */
    if (tcsetattr(fileno(stdin), TCSANOW, &oflags) != 0) {
        perror("tcsetattr");
        return EXIT_FAILURE;
    }

    return 0;
}
Lucienlucienne answered 28/7, 2009 at 20:59 Comment(6)
This seems the most straightforward way to me.Afflatus
You should use /dev/tty first before trying stdin, as if you're using a pipe, stdin would be the piped content, not the terminal input.Hesta
I wouldn't mind seeing char password[64] = {0}; or a memset before it is used if it is reentrantWino
password[strlen(password) - 1] = 0; - this is incredibly wrong.Murcia
@Murcia It is to remove the newline. fgets already ensures the string fits and is nul-terminated.Heartstrings
password[strlen(password) - 1] = 0; only removes the newline if it's there, else it wrongly removes the last character of the password. password[ strcspn( password, "\n" ) ] = '\0' works all the time.Erma
G
5

You could use the ncurses library to read from standard input without echoing the results to the screen. (Call noecho() before getting any input). The library has been around for ages and works on a wide variety of platforms (the windows version can be found here)

Griz answered 28/7, 2009 at 20:20 Comment(1)
I made a simple curses app (it's been a couple decaded since I used curses), and found that it cleared the screen when I did initscr(). There is probably some way around that, but it wasn't obvious from a quick perusal of the man pages.Afflatus
C
5

Even though this is a very old question that has already been answered, here's what I've been using (which is very similar to the accepted answer):

#include <termios.h>
#include <cstdio>

//
// The following is a slightly modifed version taken from:
// http://www.gnu.org/software/libc/manual/html_node/getpass.html
//
ssize_t my_getpass (char *prompt, 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;

    /* Display the prompt */
    if (prompt)
        printf("%s", prompt);

    /* Read the password. */
    nread = getline (lineptr, n, stream);

    /* Remove the carriage return */
    if (nread >= 1 && (*lineptr)[nread - 1] == '\n')
    {
        (*lineptr)[nread-1] = 0;
        nread--;
    }
    printf("\n");

    /* Restore terminal. */
    (void) tcsetattr (fileno (stream), TCSAFLUSH, &_old);

    return nread;
}

//
// Test harness - demonstrate calling my_getpass().
//
int main(int argc, char *argv[])
{
    size_t maxlen = 255;
    char pwd[maxlen];
    char *pPwd = pwd; // <-- haven't figured out how to avoid this.

    int count = my_getpass((char*)"Enter Password: ", &pPwd, &maxlen, stdin);

    printf("Size of password: %d\nPassword in plaintext: %s\n", count, pwd);

    return 0;
}
Cracy answered 12/6, 2015 at 10:58 Comment(1)
Me gusta. This will even let you pipe in your password from the command line; however, if you're gonna do that then you may not want to print out a password prompt.Wane
N
1

On windows, you can probably use the SetConsoleMode api, described here.

Narda answered 28/7, 2009 at 20:21 Comment(0)
D
1

According to the University of Milwaukee's documentation it is obsolete because:

The getpass() function is not threadsafe because it manipulates global signal state.

The getpass() function is scheduled to be withdrawn from a future version of the X/Open CAE Specification.

Dov answered 28/7, 2009 at 20:25 Comment(2)
It's been withdrawn from POSIX/Single UNIX (the successor to X/Open) since 2001.Ninetta
How is this an answer? This really should be a comment.Hotze
I
0

Another simple solution for Windows. Include "conio.h"

  for (;;) {
  int c = _getch();
  switch (c)
  {
  case '\r':
  case '\n':
  case EOF:
    _putch('\n');
    break;

  default:
    _putch('*'); //mask
    thePassword += char(c);
    continue;
  }
  break;
}
Imbibe answered 8/8, 2019 at 22:27 Comment(0)

© 2022 - 2025 — McMap. All rights reserved.