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