1 #include "k5-int.h" 2 #if !defined(_WIN32) || (defined(_WIN32) && defined(__CYGWIN32__)) 3 #include <stdio.h> 4 #include <errno.h> 5 #include <signal.h> 6 #include <limits.h> 7 /* Is vxworks broken w.r.t. termios? --tlyu */ 8 #ifdef __vxworks 9 #define ECHO_PASSWORD 10 #endif 11 12 #include <termios.h> 13 14 #ifdef POSIX_SIGNALS 15 typedef struct sigaction osiginfo; 16 #else 17 typedef struct krb5_sigtype (*osiginfo)(); 18 #endif 19 20 static void catch_signals(osiginfo *); 21 static void restore_signals(osiginfo *); 22 static krb5_sigtype intrfunc(int sig); 23 24 static krb5_error_code setup_tty(FILE*, int, struct termios *, osiginfo *); 25 static krb5_error_code restore_tty(FILE*, struct termios *, osiginfo *); 26 27 static volatile int got_int; /* should be sig_atomic_t */ 28 29 krb5_error_code KRB5_CALLCONV 30 krb5_prompter_posix( 31 krb5_context context, 32 void *data, 33 const char *name, 34 const char *banner, 35 int num_prompts, 36 krb5_prompt prompts[]) 37 { 38 int fd, i, scratchchar; 39 FILE *fp; 40 char *retp; 41 krb5_error_code errcode; 42 struct termios saveparm; 43 osiginfo osigint; 44 45 errcode = KRB5_LIBOS_CANTREADPWD; 46 47 if (name) { 48 fputs(name, stdout); 49 fputs("\n", stdout); 50 } 51 if (banner) { 52 fputs(banner, stdout); 53 fputs("\n", stdout); 54 } 55 56 /* 57 * Get a non-buffered stream on stdin. 58 */ 59 fp = NULL; 60 fd = dup(STDIN_FILENO); 61 if (fd < 0) 62 return KRB5_LIBOS_CANTREADPWD; 63 fp = fdopen(fd, "r"); 64 if (fp == NULL) 65 goto cleanup; 66 if (setvbuf(fp, NULL, _IONBF, 0)) 67 goto cleanup; 68 69 for (i = 0; i < num_prompts; i++) { 70 errcode = KRB5_LIBOS_CANTREADPWD; 71 /* fgets() takes int, but krb5_data.length is unsigned. */ 72 if (prompts[i].reply->length > INT_MAX) 73 goto cleanup; 74 75 errcode = setup_tty(fp, prompts[i].hidden, &saveparm, &osigint); 76 if (errcode) 77 break; 78 79 /* put out the prompt */ 80 (void)fputs(prompts[i].prompt, stdout); 81 (void)fputs(": ", stdout); 82 (void)fflush(stdout); 83 (void)memset(prompts[i].reply->data, 0, prompts[i].reply->length); 84 85 got_int = 0; 86 retp = fgets(prompts[i].reply->data, (int)prompts[i].reply->length, 87 fp); 88 if (prompts[i].hidden) 89 putchar('\n'); 90 if (retp == NULL) { 91 if (got_int) 92 errcode = KRB5_LIBOS_PWDINTR; 93 else 94 errcode = KRB5_LIBOS_CANTREADPWD; 95 restore_tty(fp, &saveparm, &osigint); 96 break; 97 } 98 99 /* replace newline with null */ 100 retp = strchr(prompts[i].reply->data, '\n'); 101 if (retp != NULL) 102 *retp = '\0'; 103 else { 104 /* flush rest of input line */ 105 do { 106 scratchchar = getc(fp); 107 } while (scratchchar != EOF && scratchchar != '\n'); 108 } 109 110 errcode = restore_tty(fp, &saveparm, &osigint); 111 if (errcode) 112 break; 113 prompts[i].reply->length = strlen(prompts[i].reply->data); 114 } 115 cleanup: 116 if (fp != NULL) 117 fclose(fp); 118 else if (fd >= 0) 119 close(fd); 120 121 return errcode; 122 } 123 124 static krb5_sigtype intrfunc(int sig) 125 { 126 got_int = 1; 127 } 128 129 static void 130 catch_signals(osiginfo *osigint) 131 { 132 #ifdef POSIX_SIGNALS 133 struct sigaction sa; 134 135 sigemptyset(&sa.sa_mask); 136 sa.sa_flags = 0; 137 sa.sa_handler = intrfunc; 138 sigaction(SIGINT, &sa, osigint); 139 #else 140 *osigint = signal(SIGINT, intrfunc); 141 #endif 142 } 143 144 static void 145 restore_signals(osiginfo *osigint) 146 { 147 #ifdef POSIX_SIGNALS 148 sigaction(SIGINT, osigint, NULL); 149 #else 150 signal(SIGINT, *osigint); 151 #endif 152 } 153 154 static krb5_error_code 155 setup_tty(FILE *fp, int hidden, struct termios *saveparm, osiginfo *osigint) 156 { 157 krb5_error_code ret; 158 int fd; 159 struct termios tparm; 160 161 ret = KRB5_LIBOS_CANTREADPWD; 162 catch_signals(osigint); 163 fd = fileno(fp); 164 do { 165 if (!isatty(fd)) { 166 ret = 0; 167 break; 168 } 169 if (tcgetattr(fd, &tparm) < 0) 170 break; 171 *saveparm = tparm; 172 #ifndef ECHO_PASSWORD 173 if (hidden) 174 tparm.c_lflag &= ~(ECHO|ECHONL); 175 #endif 176 tparm.c_lflag |= ISIG|ICANON; 177 if (tcsetattr(STDIN_FILENO, TCSANOW, &tparm) < 0) 178 break; 179 ret = 0; 180 } while (0); 181 /* If we're losing, restore signal handlers. */ 182 if (ret) 183 restore_signals(osigint); 184 return ret; 185 } 186 187 static krb5_error_code 188 restore_tty(FILE* fp, struct termios *saveparm, osiginfo *osigint) 189 { 190 int ret, fd; 191 192 ret = 0; 193 fd = fileno(fp); 194 if (isatty(fd)) { 195 ret = tcsetattr(fd, TCSANOW, saveparm); 196 if (ret < 0) 197 ret = KRB5_LIBOS_CANTREADPWD; 198 else 199 ret = 0; 200 } 201 restore_signals(osigint); 202 return ret; 203 } 204 205 #else /* non-Cygwin Windows, or Mac */ 206 207 #if defined(_WIN32) 208 209 #include <io.h> 210 211 krb5_error_code KRB5_CALLCONV 212 krb5_prompter_posix(krb5_context context, 213 void *data, 214 const char *name, 215 const char *banner, 216 int num_prompts, 217 krb5_prompt prompts[]) 218 { 219 HANDLE handle; 220 DWORD old_mode, new_mode; 221 char *ptr; 222 int scratchchar; 223 krb5_error_code errcode = 0; 224 int i; 225 226 handle = GetStdHandle(STD_INPUT_HANDLE); 227 if (handle == INVALID_HANDLE_VALUE) 228 return ENOTTY; 229 if (!GetConsoleMode(handle, &old_mode)) 230 return ENOTTY; 231 232 new_mode = old_mode; 233 new_mode |= ( ENABLE_LINE_INPUT | ENABLE_PROCESSED_INPUT ); 234 new_mode &= ~( ENABLE_ECHO_INPUT ); 235 236 if (!SetConsoleMode(handle, new_mode)) 237 return ENOTTY; 238 239 if (!SetConsoleMode(handle, old_mode)) 240 return ENOTTY; 241 242 if (name) { 243 fputs(name, stdout); 244 fputs("\n", stdout); 245 } 246 247 if (banner) { 248 fputs(banner, stdout); 249 fputs("\n", stdout); 250 } 251 252 for (i = 0; i < num_prompts; i++) { 253 if (prompts[i].hidden) { 254 if (!SetConsoleMode(handle, new_mode)) { 255 errcode = ENOTTY; 256 goto cleanup; 257 } 258 } 259 260 fputs(prompts[i].prompt,stdout); 261 fputs(": ", stdout); 262 fflush(stdout); 263 memset(prompts[i].reply->data, 0, prompts[i].reply->length); 264 265 if (fgets(prompts[i].reply->data, prompts[i].reply->length, stdin) 266 == NULL) { 267 if (prompts[i].hidden) 268 putchar('\n'); 269 errcode = KRB5_LIBOS_CANTREADPWD; 270 goto cleanup; 271 } 272 if (prompts[i].hidden) 273 putchar('\n'); 274 /* fgets always null-terminates the returned string */ 275 276 /* replace newline with null */ 277 if ((ptr = strchr(prompts[i].reply->data, '\n'))) 278 *ptr = '\0'; 279 else /* flush rest of input line */ 280 do { 281 scratchchar = getchar(); 282 } while (scratchchar != EOF && scratchchar != '\n'); 283 284 prompts[i].reply->length = strlen(prompts[i].reply->data); 285 286 if (!SetConsoleMode(handle, old_mode)) { 287 errcode = ENOTTY; 288 goto cleanup; 289 } 290 } 291 292 cleanup: 293 if (errcode) { 294 for (i = 0; i < num_prompts; i++) { 295 memset(prompts[i].reply->data, 0, prompts[i].reply->length); 296 } 297 } 298 return errcode; 299 } 300 301 #else /* !_WIN32 */ 302 303 krb5_error_code KRB5_CALLCONV 304 krb5_prompter_posix(krb5_context context, 305 void *data, 306 const char *name, 307 const char *banner, 308 int num_prompts, 309 krb5_prompt prompts[]) 310 { 311 return(EINVAL); 312 } 313 #endif /* !_WIN32 */ 314 #endif /* Windows or Mac */ 315 316 void 317 krb5int_set_prompt_types(krb5_context context, krb5_prompt_type *types) 318 { 319 context->prompt_types = types; 320 } 321 322 krb5_prompt_type* 323 KRB5_CALLCONV 324 krb5_get_prompt_types(krb5_context context) 325 { 326 return context->prompt_types; 327 } 328