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
krb5_prompter_posix(krb5_context context,void * data,const char * name,const char * banner,int num_prompts,krb5_prompt prompts[])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 
intrfunc(int sig)124 static krb5_sigtype intrfunc(int sig)
125 {
126     got_int = 1;
127 }
128 
129 static void
catch_signals(osiginfo * osigint)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
restore_signals(osiginfo * osigint)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
setup_tty(FILE * fp,int hidden,struct termios * saveparm,osiginfo * osigint)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
restore_tty(FILE * fp,struct termios * saveparm,osiginfo * osigint)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
krb5_prompter_posix(krb5_context context,void * data,const char * name,const char * banner,int num_prompts,krb5_prompt prompts[])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
krb5_prompter_posix(krb5_context context,void * data,const char * name,const char * banner,int num_prompts,krb5_prompt prompts[])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
krb5int_set_prompt_types(krb5_context context,krb5_prompt_type * types)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
krb5_get_prompt_types(krb5_context context)324 krb5_get_prompt_types(krb5_context context)
325 {
326     return context->prompt_types;
327 }
328