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
15typedef struct sigaction osiginfo;
16#else
17typedef struct krb5_sigtype (*osiginfo)();
18#endif
19
20static void	catch_signals(osiginfo *);
21static void	restore_signals(osiginfo *);
22static krb5_sigtype	intrfunc(int sig);
23
24static krb5_error_code	setup_tty(FILE*, int, struct termios *, osiginfo *);
25static krb5_error_code	restore_tty(FILE*, struct termios *, osiginfo *);
26
27static volatile int got_int;	/* should be sig_atomic_t */
28
29krb5_error_code KRB5_CALLCONV
30krb5_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    }
115cleanup:
116    if (fp != NULL)
117	fclose(fp);
118    else if (fd >= 0)
119	close(fd);
120
121    return errcode;
122}
123
124static krb5_sigtype intrfunc(int sig)
125{
126    got_int = 1;
127}
128
129static void
130catch_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
144static void
145restore_signals(osiginfo *osigint)
146{
147#ifdef POSIX_SIGNALS
148    sigaction(SIGINT, osigint, NULL);
149#else
150    signal(SIGINT, *osigint);
151#endif
152}
153
154static krb5_error_code
155setup_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
187static krb5_error_code
188restore_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
211krb5_error_code KRB5_CALLCONV
212krb5_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
303krb5_error_code KRB5_CALLCONV
304krb5_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
316void
317krb5int_set_prompt_types(krb5_context context, krb5_prompt_type *types)
318{
319    context->prompt_types = types;
320}
321
322krb5_prompt_type*
323KRB5_CALLCONV
324krb5_get_prompt_types(krb5_context context)
325{
326    return context->prompt_types;
327}
328