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