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
krb5_prompter_posix(krb5_context context,void * data,const char * name,const char * banner,int num_prompts,krb5_prompt prompts[])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
intrfunc(int sig)128 intrfunc(int sig)
129 {
130 got_int = 1;
131 }
132
133 static void
catch_signals(osiginfo * osigint)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
restore_signals(osiginfo * osigint)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
setup_tty(FILE * fp,int hidden,struct termios * saveparm,osiginfo * osigint)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
restore_tty(FILE * fp,struct termios * saveparm,osiginfo * osigint)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
krb5_prompter_posix(krb5_context context,void * data,const char * name,const char * banner,int num_prompts,krb5_prompt prompts[])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
krb5_prompter_posix(krb5_context context,void * data,const char * name,const char * banner,int num_prompts,krb5_prompt prompts[])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
k5_set_prompt_types(krb5_context context,krb5_prompt_type * types)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
krb5_get_prompt_types(krb5_context context)328 krb5_get_prompt_types(krb5_context context)
329 {
330 return context->prompt_types;
331 }
332