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