Подтвердить что ты не робот

Скрыть ввод пароля на терминале

Я хочу скрыть свой пароль при записи *. Я использую 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() для меня бесполезен. У кого-нибудь есть решение?

4b9b3361

Ответ 1

В мире 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).

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

Поиск gopass() имеет ссылку на реализацию 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;
}

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

Ответ 2

Функциональность 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 или другой библиотеки терминалов для обработки этих вещей.

Ответ 3

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

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

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

#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)

Ответ 4

Вы можете создать свою собственную функцию 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;
}

Ответ 5

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

#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()?

Ответ 6

Вы можете использовать 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

И c файл

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;
}

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

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

Ответ 7

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

syntex функции 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

Ответ 8

#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;
}

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

Ответ 9

К сожалению, в стандартной библиотеке C такой функции нет. Возможно, в сторонней библиотеке.

Один из вариантов - использовать escape-последовательности ANSI, чтобы установить цвет фона для цвета переднего плана в консоли, чтобы скрыть пароль. Попробуйте эту ссылку.

Ответ 10

При сканировании символов вы можете взять его в буфер. Также вам нужно написать код, если нажать Backspace, и правильно исправить вставленный пароль.

Вот код, который я написал с проклятиями. Скомпилировать с помощью gcc file.c -o pass_prog -lcurses

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

#define ENOUGH_SIZE 256

#define ECHO_ON 1
#define ECHO_OFF 0

#define BACK_SPACE 127

char *my_getpass (int echo_state);

int main (void)
{
  char *pass;

  initscr ();

  printw ("Enter Password: ");
  pass = my_getpass (ECHO_ON);

  printw ("\nEntered Password: %s", pass);
  refresh ();
  getch ();
  endwin ();
  return 0;
}


char *my_getpass (int echo_state)
{
  char *pass, c;
  int i=0;

  pass = malloc (sizeof (char) * ENOUGH_SIZE);
  if (pass == NULL)
  {
    perror ("Exit");
    exit (1);
  }

  cbreak ();
  noecho ();

  while ((c=getch()) != '\n')
  {
    if (c == BACK_SPACE)
    {
      /* Do not let the buffer underflow */
      if (i > 0)
      { 
        i--;
        if (echo_state == ECHO_ON)
               printw ("\b \b");
      }
    }
    else if (c == '\t')
      ; /* Ignore tabs */
    else
    {
      pass[i] = c;
      i = (i >= ENOUGH_SIZE) ? ENOUGH_SIZE - 1 : i+1;
      if (echo_state == ECHO_ON)
        printw ("*");
    }
  }
  echo ();
  nocbreak ();
  /* Terminate the password string with NUL */
  pass[i] = '\0';
  endwin ();
  return pass;
}

Ответ 11

В C вы можете использовать функцию getpasswd(), которая в значительной степени выполняет аналогичную функцию stty в оболочке, например:

#include <stdio.h>
#include <string.h>
#include <pwd.h>
#include <unistd.h>

int main()
{
    char acct[80], password[80];

    printf("Account: ");
    fgets(acct, 80, stdin);

    acct[strlen(acct)-1] = 0; /* remove carriage return */

    strncpy(password, getpass("Password: "), 80);
    printf("You entered acct %s and pass %s\n", acct, password);

    return 0;
}

Вот эквивалентная оболочка script, которая использует stty (которая меняет настройки вашего tty):

save_state=$(stty -g)
/bin/echo -n "Account: "
read acct
/bin/echo -n "Password: "
stty -echo
read password # this won’t echo
stty "$save_state"
echo ""
echo account = $acct and password = $password

Источник: Как я могу прочитать пароль, не повторяя его в C?

Ответ 12

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

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

то же самое произойдет в C в DOS с getch tho. который также с радостью вернет 0x08 для backspace (или 127 или что-то другое, что ваш os использует как backspace)

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

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