Получение пароля на C без использования getpass (3)?

Я мог бы использовать getpass(), чтобы получить пароль. Однако на странице руководства говорится:

Эта функция устарела. Не используйте его.

Каков текущий способ получить пароль с пользовательского терминала без его повторения в соответствии с POSIX? [Первоначально я сказал «переносимо», но я хотел избежать использования устаревшей функции.]


person Jerry Penner    schedule 28.07.2009    source источник
comment
Портативного способа нет — это сильно зависит от вашей платформы.   -  person    schedule 29.07.2009
comment
@ Джерри, оно того не стоит ... хотя это НАСЛЕДИЕ, это самый портативный способ сделать это.   -  person Michael Aaron Safyan    schedule 14.04.2010


Ответы (6)


это должно работать на linux/macosx, версия для Windows должна использовать Get /Установить режим консоли

#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;
}
person dfa    schedule 28.07.2009
comment
Это кажется мне самым простым способом. - person Jerry Penner; 29.07.2009
comment
Вы должны сначала использовать /dev/tty, прежде чем пытаться использовать стандартный ввод, как если бы вы использовали канал, стандартный ввод был бы передаваемым по конвейеру содержимым, а не вводом терминала. - person xryl669; 12.03.2013
comment
Я был бы не против увидеть char password[64] = {0}; или memset перед его использованием, если он реентерабельный - person Grady Player; 29.04.2014
comment
password[strlen(password) - 1] = 0; - это невероятно неправильно. - person ecatmur; 04.09.2015
comment
@ecatmur Это удаление новой строки. fgets уже гарантирует, что строка подходит и завершается нулем. - person stark; 02.08.2017
comment
password[strlen(password) - 1] = 0; удаляет только новую строку, если она есть, иначе она ошибочно удаляет последний символ пароля. password[ strcspn( password, "\n" ) ] = '\0' работает постоянно. - person Andrew Henle; 09.08.2019

Вы можете использовать библиотеку ncurses для чтения из стандартного ввода без повторения результатов. на экран. (Позвоните noecho(), прежде чем получить какие-либо данные). Библиотека существует уже много лет и работает на самых разных платформах (версию для Windows можно найти здесь )

person indy    schedule 28.07.2009
comment
Я сделал простое приложение curses (прошло несколько десятилетий с тех пор, как я использовал curses) и обнаружил, что оно очищает экран, когда я выполняю initscr(). Вероятно, есть какой-то способ обойти это, но это не было очевидно при быстром просмотре справочных страниц. - person Jerry Penner; 29.07.2009

Несмотря на то, что это очень старый вопрос, на который уже был дан ответ, вот что я использовал (что очень похоже на принятый ответ):

#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;
}
person Vanessa Deagan    schedule 12.06.2015
comment
Мне нравится. Это даже позволит вам ввести свой пароль из командной строки; однако, если вы собираетесь это сделать, возможно, вы не захотите распечатывать запрос пароля. - person JesseTG; 28.02.2016

В Windows вы, вероятно, можете использовать SetConsoleMode API, описанный здесь .

person Brian    schedule 28.07.2009

Согласно документации Университета Милуоки, он устарел. так как:

Функция getpass() не является потокобезопасной, поскольку она манипулирует глобальным состоянием сигнала.

Планируется, что функция getpass() будет исключена из будущей версии спецификации X/Open CAE.

person Kredns    schedule 28.07.2009
comment
Он был исключен из POSIX/Single UNIX (преемника X/Open) с 2001 года. - person Fred Foo; 19.11.2010
comment
Как это ответ? Это действительно должен быть комментарий. - person Mahmoud Al-Qudsi; 07.06.2013

Еще одно простое решение для Windows. Включить "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;
}
person Marius Matioc    schedule 08.08.2019