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