/* * Copyright (c) 2022 Albert Schwarzkopf * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include #include #include #include #include #include #include #include #include #define PWBUF_SIZE 256 #define MODE_ECHO 0 #define MODE_STARS 1 struct termios saved_termios; uint8_t *pwbuf = NULL; size_t pwbufsize = 0; size_t pwindex = 0; int current_mode = MODE_ECHO; void enter_raw_mode() { struct termios raw = saved_termios; raw.c_lflag &= ~(ECHO | ICANON); if(tcsetattr(STDIN_FILENO, TCSAFLUSH, &raw) != 0) { fprintf(stderr, "Failed tcsetattr\n"); exit(EXIT_FAILURE); } } void setup_console() { if(tcgetattr(STDIN_FILENO, &saved_termios) != 0) { fprintf(stderr, "Failed tcgetattr\n"); exit(EXIT_FAILURE); } enter_raw_mode(); } void restore_console() { if(tcsetattr(STDIN_FILENO, TCSAFLUSH, &saved_termios) != 0) { fprintf(stderr, "Failed tcsetattr\n"); exit(EXIT_FAILURE); } } void allocate_pw_buf() { pwbuf = calloc(PWBUF_SIZE, sizeof(uint8_t)); if(pwbuf == NULL) { fprintf(stderr, "Failed to allocate memory\n"); exit(EXIT_FAILURE); } int lock = mlock(pwbuf, PWBUF_SIZE); if(lock != 0) { fprintf(stderr, "Failed to mlock buffer\n"); exit(EXIT_FAILURE); } pwbufsize = PWBUF_SIZE; } void grow_pw_buf() { size_t newsize = pwbufsize + PWBUF_SIZE; uint8_t *newbuf = calloc(newsize, sizeof(uint8_t)); if(newbuf == NULL) { fprintf(stderr, "Failed to allocate memory\n"); exit(EXIT_FAILURE); } int lock = mlock(newbuf, newsize); if(lock != 0) { fprintf(stderr, "Failed to mlock buffer\n"); exit(EXIT_FAILURE); } memcpy(newbuf, pwbuf, pwbufsize); explicit_bzero(pwbuf, pwbufsize); pwbuf = newbuf; pwbufsize = newsize; } void exit_handler() { explicit_bzero(pwbuf, pwbufsize); explicit_bzero(&pwbufsize, sizeof(pwbufsize)); explicit_bzero(&pwindex, sizeof(pwbufsize)); restore_console(); } void clear_term_line() { const char *clearcmd = "\33[2K\r"; write(STDERR_FILENO, clearcmd, strlen(clearcmd)); } void print_password() { clear_term_line(); char *prompt = "Password: "; write(STDERR_FILENO, prompt, strlen(prompt)); if(current_mode == MODE_ECHO) { write(STDERR_FILENO, pwbuf, pwindex); } if(current_mode == MODE_STARS) { for(size_t i = 0; i < pwindex; i++) { uint8_t n = pwbuf[i]; /* Skip utf-8 byte 2-4, we won't print an asterisk for those*/ if((n >>6) == 0b10) { continue; } char mask = '*'; write(STDERR_FILENO, &mask, 1); } } fsync(STDERR_FILENO); } void switch_mode() { current_mode = ( current_mode + 1 ) % 2; } int main() { if(atexit(exit_handler) != 0) { fprintf(stderr, "Failed to register exit handler\n"); exit(EXIT_FAILURE); } if(prctl(PR_SET_DUMPABLE, 0) != 0) { fprintf(stderr, "Failed to make process not dumpable\n"); exit(EXIT_FAILURE); } setup_console(); allocate_pw_buf(); print_password(); uint8_t c; while (read(STDIN_FILENO, &c, 1) == 1) { if(iscntrl(c)) { if(c == '\t') { switch_mode(); print_password(); } if(c == 27) /* Escape sequence */ { int remaining = 0; int ret = ioctl (STDIN_FILENO,FIONREAD,&remaining); if(ret != 0) { fprintf(stderr, "ioctl() failed\n"); exit(EXIT_FAILURE); } char tmp; /* Just consume them, we don't care */ while(remaining--) { read(STDIN_FILENO, &tmp, 1); } } if(c == 18) /* Control + R, device control 2. or "reveal" for us :-) */ { if(pwindex > 0) { char c = '\b'; write(STDERR_FILENO, &c, 1); write(STDERR_FILENO, &pwbuf[pwindex-1], 1); fsync(STDERR_FILENO); } } if(c == 127) /* Backspace */ { if(pwindex > 0) { --pwindex; } print_password(); continue; } if(c == '\n') { break; } continue; } if(pwindex == pwbufsize) { grow_pw_buf(); } pwbuf[pwindex] = c; ++pwindex; print_password(); } clear_term_line(); fsync(STDERR_FILENO); write(STDOUT_FILENO, pwbuf, pwindex); fsync(STDOUT_FILENO); return 0; }