#include "k5-int.h" #if !defined(_WIN32) || (defined(_WIN32) && defined(__CYGWIN32__)) #include #include #include #include /* Is vxworks broken w.r.t. termios? --tlyu */ #ifdef __vxworks #define ECHO_PASSWORD #endif #include #ifdef POSIX_SIGNALS typedef struct sigaction osiginfo; #else typedef struct krb5_sigtype (*osiginfo)(); #endif static void catch_signals(osiginfo *); static void restore_signals(osiginfo *); static krb5_sigtype intrfunc(int sig); static krb5_error_code setup_tty(FILE*, int, struct termios *, osiginfo *); static krb5_error_code restore_tty(FILE*, struct termios *, osiginfo *); static volatile int got_int; /* should be sig_atomic_t */ krb5_error_code KRB5_CALLCONV krb5_prompter_posix( krb5_context context, void *data, const char *name, const char *banner, int num_prompts, krb5_prompt prompts[]) { int fd, i, scratchchar; FILE *fp; char *retp; krb5_error_code errcode; struct termios saveparm; osiginfo osigint; errcode = KRB5_LIBOS_CANTREADPWD; if (name) { fputs(name, stdout); fputs("\n", stdout); } if (banner) { fputs(banner, stdout); fputs("\n", stdout); } /* * Get a non-buffered stream on stdin. */ fp = NULL; fd = dup(STDIN_FILENO); if (fd < 0) return KRB5_LIBOS_CANTREADPWD; fp = fdopen(fd, "r"); if (fp == NULL) goto cleanup; if (setvbuf(fp, NULL, _IONBF, 0)) goto cleanup; for (i = 0; i < num_prompts; i++) { errcode = KRB5_LIBOS_CANTREADPWD; /* fgets() takes int, but krb5_data.length is unsigned. */ if (prompts[i].reply->length > INT_MAX) goto cleanup; errcode = setup_tty(fp, prompts[i].hidden, &saveparm, &osigint); if (errcode) break; /* put out the prompt */ (void)fputs(prompts[i].prompt, stdout); (void)fputs(": ", stdout); (void)fflush(stdout); (void)memset(prompts[i].reply->data, 0, prompts[i].reply->length); got_int = 0; retp = fgets(prompts[i].reply->data, (int)prompts[i].reply->length, fp); if (prompts[i].hidden) putchar('\n'); if (retp == NULL) { if (got_int) errcode = KRB5_LIBOS_PWDINTR; else errcode = KRB5_LIBOS_CANTREADPWD; restore_tty(fp, &saveparm, &osigint); break; } /* replace newline with null */ retp = strchr(prompts[i].reply->data, '\n'); if (retp != NULL) *retp = '\0'; else { /* flush rest of input line */ do { scratchchar = getc(fp); } while (scratchchar != EOF && scratchchar != '\n'); } errcode = restore_tty(fp, &saveparm, &osigint); if (errcode) break; prompts[i].reply->length = strlen(prompts[i].reply->data); } cleanup: if (fp != NULL) fclose(fp); else if (fd >= 0) close(fd); return errcode; } static krb5_sigtype intrfunc(int sig) { got_int = 1; } static void catch_signals(osiginfo *osigint) { #ifdef POSIX_SIGNALS struct sigaction sa; sigemptyset(&sa.sa_mask); sa.sa_flags = 0; sa.sa_handler = intrfunc; sigaction(SIGINT, &sa, osigint); #else *osigint = signal(SIGINT, intrfunc); #endif } static void restore_signals(osiginfo *osigint) { #ifdef POSIX_SIGNALS sigaction(SIGINT, osigint, NULL); #else signal(SIGINT, *osigint); #endif } static krb5_error_code setup_tty(FILE *fp, int hidden, struct termios *saveparm, osiginfo *osigint) { krb5_error_code ret; int fd; struct termios tparm; ret = KRB5_LIBOS_CANTREADPWD; catch_signals(osigint); fd = fileno(fp); do { if (!isatty(fd)) { ret = 0; break; } if (tcgetattr(fd, &tparm) < 0) break; *saveparm = tparm; #ifndef ECHO_PASSWORD if (hidden) tparm.c_lflag &= ~(ECHO|ECHONL); #endif tparm.c_lflag |= ISIG|ICANON; if (tcsetattr(STDIN_FILENO, TCSANOW, &tparm) < 0) break; ret = 0; } while (0); /* If we're losing, restore signal handlers. */ if (ret) restore_signals(osigint); return ret; } static krb5_error_code restore_tty(FILE* fp, struct termios *saveparm, osiginfo *osigint) { int ret, fd; ret = 0; fd = fileno(fp); if (isatty(fd)) { ret = tcsetattr(fd, TCSANOW, saveparm); if (ret < 0) ret = KRB5_LIBOS_CANTREADPWD; else ret = 0; } restore_signals(osigint); return ret; } #else /* non-Cygwin Windows, or Mac */ #if defined(_WIN32) #include krb5_error_code KRB5_CALLCONV krb5_prompter_posix(krb5_context context, void *data, const char *name, const char *banner, int num_prompts, krb5_prompt prompts[]) { HANDLE handle; DWORD old_mode, new_mode; char *ptr; int scratchchar; krb5_error_code errcode = 0; int i; handle = GetStdHandle(STD_INPUT_HANDLE); if (handle == INVALID_HANDLE_VALUE) return ENOTTY; if (!GetConsoleMode(handle, &old_mode)) return ENOTTY; new_mode = old_mode; new_mode |= ( ENABLE_LINE_INPUT | ENABLE_PROCESSED_INPUT ); new_mode &= ~( ENABLE_ECHO_INPUT ); if (!SetConsoleMode(handle, new_mode)) return ENOTTY; if (!SetConsoleMode(handle, old_mode)) return ENOTTY; if (name) { fputs(name, stdout); fputs("\n", stdout); } if (banner) { fputs(banner, stdout); fputs("\n", stdout); } for (i = 0; i < num_prompts; i++) { if (prompts[i].hidden) { if (!SetConsoleMode(handle, new_mode)) { errcode = ENOTTY; goto cleanup; } } fputs(prompts[i].prompt,stdout); fputs(": ", stdout); fflush(stdout); memset(prompts[i].reply->data, 0, prompts[i].reply->length); if (fgets(prompts[i].reply->data, prompts[i].reply->length, stdin) == NULL) { if (prompts[i].hidden) putchar('\n'); errcode = KRB5_LIBOS_CANTREADPWD; goto cleanup; } if (prompts[i].hidden) putchar('\n'); /* fgets always null-terminates the returned string */ /* replace newline with null */ if ((ptr = strchr(prompts[i].reply->data, '\n'))) *ptr = '\0'; else /* flush rest of input line */ do { scratchchar = getchar(); } while (scratchchar != EOF && scratchchar != '\n'); prompts[i].reply->length = strlen(prompts[i].reply->data); if (!SetConsoleMode(handle, old_mode)) { errcode = ENOTTY; goto cleanup; } } cleanup: if (errcode) { for (i = 0; i < num_prompts; i++) { memset(prompts[i].reply->data, 0, prompts[i].reply->length); } } return errcode; } #else /* !_WIN32 */ krb5_error_code KRB5_CALLCONV krb5_prompter_posix(krb5_context context, void *data, const char *name, const char *banner, int num_prompts, krb5_prompt prompts[]) { return(EINVAL); } #endif /* !_WIN32 */ #endif /* Windows or Mac */ void krb5int_set_prompt_types(krb5_context context, krb5_prompt_type *types) { context->prompt_types = types; } krb5_prompt_type* KRB5_CALLCONV krb5_get_prompt_types(krb5_context context) { return context->prompt_types; }