Скрыть ввод пароля на терминале - PullRequest
50 голосов
/ 28 июля 2011

Я хочу замаскировать свой пароль при написании его с помощью *. Я использую Linux GCC для этого кода. Я знаю, что одним из решений является использование getch() функции, подобной этой

#include <conio.h>   
int main()
{
    char c,password[10];
    int i;
    while( (c=getch())!= '\n');{
        password[i] = c;
        printf("*");
        i++;
    }
    return 1;
}

но проблема в том, что GCC не включает файл conio.h, поэтому getch() для меня бесполезен. У кого-нибудь есть решение?

Ответы [ 13 ]

46 голосов
/ 29 июля 2011

В мире Linux маскирование обычно не выполняется со звездочками, обычно эхо просто отключается, и терминал отображает пропуски, например. если вы используете su или входите в виртуальный терминал и т. д.

Существует функция библиотеки для обработки получения паролей, она не будет маскировать пароль звездочками, но отключит отображение пароля на терминале. Я вытащил это из книги о Linux, которая у меня есть. Я считаю, что это часть стандарта posix

#include <unistd.h>
char *getpass(const char *prompt);

/*Returns pointer to statically allocated input password string
on success, or NULL on error*/

Функция getpass () сначала отключает эхо и всю обработку терминальные специальные символы (например, символ прерывания, обычно Control-C).

Затем печатает строку, указанную в приглашении, и читает строку input, возвращая завершенную нулем строку ввода с завершающим символ новой строки, как результат функции.

В поиске Google getpass () есть ссылка на реализацию GNU (должна быть в большинстве дистрибутивов Linux) и пример кода для реализации собственного, если это необходимо,

http://www.gnu.org/s/hello/manual/libc/getpass.html

Их пример прокрутки собственного:

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

ssize_t
my_getpass (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;

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

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

    return nread;
}

При необходимости вы можете использовать это в качестве основы, чтобы изменить его для отображения звездочек.

9 голосов
/ 06 сентября 2015

Без getch, на который можно положиться и избегать устаревшего getpass, рекомендуемый подход состоит в отключении терминала ECHO при использовании termios.После нескольких поисков, чтобы найти стандартную процедуру гибкого пароля, я был удивлен, что очень мало для автономного использования с C. Вместо простого перекодирования getch с опциями termios c_lflag, немного более обобщенный подход требует всего лишь нескольких дополнений.Помимо замены getch любая подпрограмма должна обеспечивать указанную максимальную длину, чтобы предотвратить переполнение, усекать, если пользователь пытается войти за пределы максимума, и предупреждать, если усечение происходит каким-либо образом.

Ниже, дополнения позволят прочитатьиз любого FILE * входного потока, ограничивая длину до указанной длины, предоставляя минимальную возможность редактирования (возврата) при получении ввода, позволяя полностью или полностью отключить маску символов и, наконец, вернуть длину введенного пароля.Предупреждение было добавлено, когда введенный пароль был усечен до максимальной или указанной длины.

Надеемся, что этот вопрос окажется полезным для других, ищущих аналогичное решение:

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

#include <sys/time.h>
#include <termios.h>
#include <errno.h>   /* for errno */
#include <unistd.h>  /* for EINTR */

#define MAXPW 32

/* read a string from fp into pw masking keypress with mask char.
getpasswd will read upto sz - 1 chars into pw, null-terminating
the resulting string. On success, the number of characters in
pw are returned, -1 otherwise.
*/
ssize_t getpasswd (char **pw, size_t sz, int mask, FILE *fp)
{
    if (!pw || !sz || !fp) return -1;       /* validate input   */
#ifdef MAXPW
    if (sz > MAXPW) sz = MAXPW;
#endif

    if (*pw == NULL) {              /* reallocate if no address */
        void *tmp = realloc (*pw, sz * sizeof **pw);
        if (!tmp)
            return -1;
        memset (tmp, 0, sz);    /* initialize memory to 0   */
        *pw = tmp;
    }

    size_t idx = 0;         /* index, number of chars in read   */
    int c = 0;

    struct termios old_kbd_mode;    /* orig keyboard settings   */
    struct termios new_kbd_mode;

    if (tcgetattr (0, &old_kbd_mode)) { /* save orig settings   */
        fprintf (stderr, "%s() error: tcgetattr failed.\n", __func__);
        return -1;
    }   /* copy old to new */
    memcpy (&new_kbd_mode, &old_kbd_mode, sizeof(struct termios));

    new_kbd_mode.c_lflag &= ~(ICANON | ECHO);  /* new kbd flags */
    new_kbd_mode.c_cc[VTIME] = 0;
    new_kbd_mode.c_cc[VMIN] = 1;
    if (tcsetattr (0, TCSANOW, &new_kbd_mode)) {
        fprintf (stderr, "%s() error: tcsetattr failed.\n", __func__);
        return -1;
    }

    /* read chars from fp, mask if valid char specified */
    while (((c = fgetc (fp)) != '\n' && c != EOF && idx < sz - 1) ||
            (idx == sz - 1 && c == 127))
    {
        if (c != 127) {
            if (31 < mask && mask < 127)    /* valid ascii char */
                fputc (mask, stdout);
            (*pw)[idx++] = c;
        }
        else if (idx > 0) {         /* handle backspace (del)   */
            if (31 < mask && mask < 127) {
                fputc (0x8, stdout);
                fputc (' ', stdout);
                fputc (0x8, stdout);
            }
            (*pw)[--idx] = 0;
        }
    }
    (*pw)[idx] = 0; /* null-terminate   */

    /* reset original keyboard  */
    if (tcsetattr (0, TCSANOW, &old_kbd_mode)) {
        fprintf (stderr, "%s() error: tcsetattr failed.\n", __func__);
        return -1;
    }

    if (idx == sz - 1 && c != '\n') /* warn if pw truncated */
        fprintf (stderr, " (%s() warning: truncated at %zu chars.)\n",
                __func__, sz - 1);

    return idx; /* number of chars in passwd    */
}

ПростоеПрограмма показа использования будет выглядеть следующим образом.Если для хранения пароля используется статический массив символов, просто убедитесь, что указатель передан функции.

int main (void ) {

    char pw[MAXPW] = {0};
    char *p = pw;
    FILE *fp = stdin;
    ssize_t nchr = 0;

    printf ( "\n Enter password: ");
    nchr = getpasswd (&p, MAXPW, '*', fp);
    printf ("\n you entered   : %s  (%zu chars)\n", p, nchr);

    printf ( "\n Enter password: ");
    nchr = getpasswd (&p, MAXPW, 0, fp);
    printf ("\n you entered   : %s  (%zu chars)\n\n", p, nchr);

    return 0;
}

Пример вывода

$ ./bin/getpasswd2

 Enter password: ******
 you entered   : 123456  (6 chars)

 Enter password:
 you entered   : abcdef  (6 chars)
9 голосов
/ 28 июля 2011

Функциональность getch (которая является нестандартной функцией Windows) можно эмулировать с помощью этого кода:

#include <termios.h>
#include <unistd.h>
int getch() {
    struct termios oldt, newt;
    int ch;
    tcgetattr(STDIN_FILENO, &oldt);
    newt = oldt;
    newt.c_lflag &= ~(ICANON | ECHO);
    tcsetattr(STDIN_FILENO, TCSANOW, &newt);
    ch = getchar();
    tcsetattr(STDIN_FILENO, TCSANOW, &oldt);
    return ch;
}

Обратите внимание, что ваш подход не идеален - лучше использовать что-то вроде ncurses или другая библиотека терминалов для обработки этих вещей.

6 голосов
/ 28 июля 2011

Таким способом вы можете создать свою собственную функцию getch() в Linux.

int getch() {
    struct termios oldtc, newtc;
    int ch;
    tcgetattr(STDIN_FILENO, &oldtc);
    newtc = oldtc;
    newtc.c_lflag &= ~(ICANON | ECHO);
    tcsetattr(STDIN_FILENO, TCSANOW, &newtc);
    ch=getchar();
    tcsetattr(STDIN_FILENO, TCSANOW, &oldtc);
    return ch;
}

Демо-код:

int main(int argc, char **argv) {
    int ch;
    printf("Press x to exit.\n\n");
    for (;;) {
        ch = getch();
        printf("ch = %c (%d)\n", ch, ch);
        if(ch == 'x')
              break;
    }
    return 0;
}
3 голосов
/ 28 июля 2011

Ваш метод верный, однако вам нужно отключить эхо терминала во время ввода пароля:

#include <sgtty.h>

void echo_off()
{
    struct sgttyb state;
    (void)ioctl(0, (int)TIOCGETP, (char *)&state);
    state.sg_flags &= ~ECHO;
    (void)ioctl(0, (int)TIOCSETP, (char *)&state);
}

void echo_on()
{
    struct sgttyb state;
    (void)ioctl(0, (int)TIOCGETP, (char *)&state);
    state.sg_flags |= ECHO;
    (void)ioctl(0, (int)TIOCSETP, (char *)&state);
}

Вместо getch(), почему бы просто не использовать getc() вместо?

2 голосов
/ 28 сентября 2011
#include <termios.h>
#include <stdio.h>

static struct termios old, new;

void initTermios(int echo) {
    tcgetattr(0, &old);
    new = old;
    new.c_lflag &= ~ICANON;
    new.c_lflag &= echo ? ECHO : ~ECHO;
    tcsetattr(0, TCSANOW, &new);
}

void resetTermios(void) {
    tcsetattr(0, TCSANOW, &old);
}

char getch_(int echo) {
    char ch;
    initTermios(echo);
    ch = getchar();
    resetTermios();
    return ch;
}

char getch(void) {
    return getch_(0);
}

int main(void) {
    char c;
    printf("(getch example) please type a letter...");
    c = getch();
    printf("\nYou typed: %c\n", c);
    return 0;
}

Просто скопируйте этот фрагмент и используйте его. Надеюсь, это помогло

2 голосов
/ 29 июля 2011

Спасибо всем, чья помощь и поддержка для решения моей проблемы.Я нахожу лучший способ скрыть свой пароль в Linux, который подходит мне лучше всего.Использовать функцию getpass ().Просто нужно включить файл "unistd.h".

синтаксис функции getpass:

char * getpass (const char * prompt)

Параметры: prompt: строковый указательпечатать при запросе пароля

Возвращаемое значение: строковый указатель пароля

Пример:

#include <stdio.h>
#include <unistd.h>   
int main()
{
    char *password; // password string pointer
    password = getpass("Enter Password: "); // get a password
    printf("%s\n",password); // this is just for conformation
                             // that password stored successfully
    return 1;
}

вывод:

Введите пароль:

Heet

2 голосов
/ 28 июля 2011

Вы можете использовать ncurses.h, если для этого нет необходимости переносить его на Windows, но вот какая-то более «переносимая» версия:

Если нет необходимости быть портативным, я укажу вам решение ncurses

portablegetch.h

/*portablegetch.h*/
#ifndef PGETCH
#define PGETCH
#ifdef __unix__
#include <termios.h>
#include <unistd.h>

static struct termios n_term;
static struct termios o_term;

static int
cbreak(int fd) 
{
   if((tcgetattr(fd, &o_term)) == -1)
      return -1;
   n_term = o_term;
   n_term.c_lflag = n_term.c_lflag & ~(ECHO|ICANON);
   n_term.c_cc[VMIN] = 1;
   n_term.c_cc[VTIME]= 0;
   if((tcsetattr(fd, TCSAFLUSH, &n_term)) == -1)
      return -1;
   return 1;
}

int 
getch() 
{
   int cinput;

   if(cbreak(STDIN_FILENO) == -1) {
      fprintf(stderr, "cbreak failure, exiting \n");
      exit(EXIT_FAILURE);
   }
   cinput = getchar();
   tcsetattr(STDIN_FILENO, TCSANOW, &o_term);

   return cinput;
}

#elif _MSC_VER  || __WIN32__ || __MS_DOS__
  #include <conio.h>
#endif
#endif

и с-файл

whatever.c

#include <stdio.h>
#include <stdlib.h>
#include "portablegetch.h"

int 
main(int argc, char **argv) 
{
  int input;

  printf("Please Enter your Password:\t");

  while(( input=getch() ) != '\n')
        printf("*");
  printf("\n");

  return EXIT_SUCCESS;
}

Это должно соответствовать вашей проблеме.

Надеюсь, это поможет.

0 голосов
/ 24 марта 2019

человек getpass

Эта функция устарела . Не используйте его. Если вы хотите прочитать ввод без включения эхолота терминала, см. описание флага ECHO в termios (3)

* * 1010

Если вы заметили, большинство современных дистрибутивов Linux не маскируют пароль со звездочками. Это раскрывает длину пароля, что никак не выгодно. Проще и лучше просто заставить курсор не двигаться, когда вводится пароль. Если по какой-либо причине вам требуется печатать * для каждого набранного символа, то вам придется захватывать каждое нажатие клавиши до того, как Enter будет ударил, и это всегда было проблематично.

0 голосов
/ 16 января 2016

обратите внимание, что ICANON termios lflag отключает обработку возврата каретки / перевода строки, а отрицательное значение ECHO termios отключает эхо для STDIN.

при использовании этого (с включенным или не включенным эхом) для считывания пароля и печати '*' для введенных символов, речь идет не только о чтении символов до тех пор, пока не встретится перевод строки / возврата каретки, вам также нужно обработать Backspace в вашей «подпрограмме построения строки» (иначе Backspace заканчиваются в реальной строке и не приводят к удалению символов из нее, как в случае с различными функциями ввода на основе строки).

то же самое произошло бы в C в DOS с getch tho. это также с радостью вернуло бы 0x08 для возврата (или 127 или что-то еще, что ваша конкретная ОС использует в качестве возврата)

отслеживание «не удалять-до-начала строки», замена «нового конца строки» на 0 и перемещение счетчика текущей позиции обратно на единицу (если вы не находитесь в позиции 0) до программист с любой из этих функций (даже с getch на dos C).

getpass() не делает то, что первоначально запрашивал пользователь, кстати, он хочет * s (который по-прежнему раскрывает длину пароля людям, стоящим за ним и смотрящим на его экран, а также в буфере прокрутки терминал, если он не закрывает его после использования). но без * это, вероятно, лучшая идея в «не закрытых средах».

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...