Add a general user interface API. This is designed to replace things
[openssl.git] / crypto / ui / ui_openssl.c
1 /* crypto/ui/ui_openssl.c -*- mode:C; c-file-style: "eay" -*- */
2 /* Written by Richard Levitte (levitte@stacken.kth.se) for the OpenSSL
3  * project 2000.
4  */
5 /* ====================================================================
6  * Copyright (c) 1998-2000 The OpenSSL Project.  All rights reserved.
7  *
8  * Redistribution and use in source and binary forms, with or without
9  * modification, are permitted provided that the following conditions
10  * are met:
11  *
12  * 1. Redistributions of source code must retain the above copyright
13  *    notice, this list of conditions and the following disclaimer. 
14  *
15  * 2. Redistributions in binary form must reproduce the above copyright
16  *    notice, this list of conditions and the following disclaimer in
17  *    the documentation and/or other materials provided with the
18  *    distribution.
19  *
20  * 3. All advertising materials mentioning features or use of this
21  *    software must display the following acknowledgment:
22  *    "This product includes software developed by the OpenSSL Project
23  *    for use in the OpenSSL Toolkit. (http://www.openssl.org/)"
24  *
25  * 4. The names "OpenSSL Toolkit" and "OpenSSL Project" must not be used to
26  *    endorse or promote products derived from this software without
27  *    prior written permission. For written permission, please contact
28  *    openssl-core@openssl.org.
29  *
30  * 5. Products derived from this software may not be called "OpenSSL"
31  *    nor may "OpenSSL" appear in their names without prior written
32  *    permission of the OpenSSL Project.
33  *
34  * 6. Redistributions of any form whatsoever must retain the following
35  *    acknowledgment:
36  *    "This product includes software developed by the OpenSSL Project
37  *    for use in the OpenSSL Toolkit (http://www.openssl.org/)"
38  *
39  * THIS SOFTWARE IS PROVIDED BY THE OpenSSL PROJECT ``AS IS'' AND ANY
40  * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
41  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
42  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE OpenSSL PROJECT OR
43  * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
44  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
45  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
46  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
47  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
48  * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
49  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
50  * OF THE POSSIBILITY OF SUCH DAMAGE.
51  * ====================================================================
52  *
53  * This product includes cryptographic software written by Eric Young
54  * (eay@cryptsoft.com).  This product includes software written by Tim
55  * Hudson (tjh@cryptsoft.com).
56  *
57  */
58
59 #if !defined(MSDOS) && !defined(VMS) && !defined(WIN32)
60 #include <openssl/opensslconf.h>
61 #ifdef OPENSSL_UNISTD
62 # include OPENSSL_UNISTD
63 #else
64 # include <unistd.h>
65 #endif
66 /* If unistd.h defines _POSIX_VERSION, we conclude that we
67  * are on a POSIX system and have sigaction and termios. */
68 #if defined(_POSIX_VERSION)
69
70 # define SIGACTION
71 # if !defined(TERMIOS) && !defined(TERMIO) && !defined(SGTTY)
72 # define TERMIOS
73 # endif
74
75 #endif
76 #endif
77
78 /* #define SIGACTION */ /* Define this if you have sigaction() */
79
80 #ifdef WIN16TTY
81 #undef WIN16
82 #undef _WINDOWS
83 #include <graph.h>
84 #endif
85
86 /* 06-Apr-92 Luke Brennan    Support for VMS */
87 #include "ui_locl.h"
88 #include "cryptlib.h"
89 #include <signal.h>
90 #include <stdio.h>
91 #include <string.h>
92 #include <setjmp.h>
93 #include <errno.h>
94
95 #ifdef VMS                      /* prototypes for sys$whatever */
96 #include <starlet.h>
97 #ifdef __DECC
98 #pragma message disable DOLLARID
99 #endif
100 #endif
101
102 #ifdef WIN_CONSOLE_BUG
103 #include <windows.h>
104 #include <wincon.h>
105 #endif
106
107
108 /* There are 5 types of terminal interface supported,
109  * TERMIO, TERMIOS, VMS, MSDOS and SGTTY
110  */
111
112 #if defined(__sgi) && !defined(TERMIOS)
113 #define TERMIOS
114 #undef  TERMIO
115 #undef  SGTTY
116 #endif
117
118 #if defined(linux) && !defined(TERMIO)
119 #undef  TERMIOS
120 #define TERMIO
121 #undef  SGTTY
122 #endif
123
124 #ifdef _LIBC
125 #undef  TERMIOS
126 #define TERMIO
127 #undef  SGTTY
128 #endif
129
130 #if !defined(TERMIO) && !defined(TERMIOS) && !defined(VMS) && !defined(MSDOS) && !defined(MAC_OS_pre_X) && !defined(MAC_OS_GUSI_SOURCE)
131 #undef  TERMIOS
132 #undef  TERMIO
133 #define SGTTY
134 #endif
135
136 #ifdef TERMIOS
137 #include <termios.h>
138 #define TTY_STRUCT              struct termios
139 #define TTY_FLAGS               c_lflag
140 #define TTY_get(tty,data)       tcgetattr(tty,data)
141 #define TTY_set(tty,data)       tcsetattr(tty,TCSANOW,data)
142 #endif
143
144 #ifdef TERMIO
145 #include <termio.h>
146 #define TTY_STRUCT              struct termio
147 #define TTY_FLAGS               c_lflag
148 #define TTY_get(tty,data)       ioctl(tty,TCGETA,data)
149 #define TTY_set(tty,data)       ioctl(tty,TCSETA,data)
150 #endif
151
152 #ifdef SGTTY
153 #include <sgtty.h>
154 #define TTY_STRUCT              struct sgttyb
155 #define TTY_FLAGS               sg_flags
156 #define TTY_get(tty,data)       ioctl(tty,TIOCGETP,data)
157 #define TTY_set(tty,data)       ioctl(tty,TIOCSETP,data)
158 #endif
159
160 #if !defined(_LIBC) && !defined(MSDOS) && !defined(VMS) && !defined(MAC_OS_pre_X)
161 #include <sys/ioctl.h>
162 #endif
163
164 #ifdef MSDOS
165 #include <conio.h>
166 #define fgets(a,b,c) noecho_fgets(a,b,c)
167 #endif
168
169 #ifdef VMS
170 #include <ssdef.h>
171 #include <iodef.h>
172 #include <ttdef.h>
173 #include <descrip.h>
174 struct IOSB {
175         short iosb$w_value;
176         short iosb$w_count;
177         long  iosb$l_info;
178         };
179 #endif
180
181 #if defined(MAC_OS_pre_X) || defined(MAC_OS_GUSI_SOURCE)
182 /*
183  * This one needs work. As a matter of fact the code is unoperational
184  * and this is only a trick to get it compiled.
185  *                                      <appro@fy.chalmers.se>
186  */
187 #define TTY_STRUCT int
188 #endif
189
190 #ifndef NX509_SIG
191 #define NX509_SIG 32
192 #endif
193
194
195 /* Define globals.  They are protected by a lock */
196 #ifdef SIGACTION
197 static struct sigaction savsig[NX509_SIG];
198 #else
199 static void (*savsig[NX509_SIG])(int );
200 #endif
201 static jmp_buf save;
202
203 #ifdef VMS
204 static struct IOSB iosb;
205 static $DESCRIPTOR(terminal,"TT");
206 static long tty_orig[3], tty_new[3];
207 static long status;
208 static unsigned short channel = 0;
209 #else
210 #ifndef MSDOS
211 static TTY_STRUCT tty_orig,tty_new;
212 #endif
213 #endif
214 static FILE *tty;
215 static int is_a_tty;
216
217 /* Declare static functions */
218 static void read_till_nl(FILE *);
219 static void recsig(int);
220 static void pushsig(void);
221 static void popsig(void);
222 #if defined(MSDOS) && !defined(WIN16)
223 static int noecho_fgets(char *buf, int size, FILE *tty);
224 #endif
225 static int read_string_inner(UI *ui, UI_STRING *uis, int echo);
226
227 static int read_string(UI *ui, UI_STRING *uis);
228
229 static int open_console(UI *ui);
230 static int echo_console(UI *ui);
231 static int noecho_console(UI *ui);
232 static int close_console(UI *ui);
233
234 static UI_METHOD ui_openssl =
235         {
236         "OpenSSL default user interface",
237         open_console,
238         read_string,
239         NULL,                   /* The reader function writes as well */
240         close_console,
241         };
242
243 /* The method with all the built-in thingies */
244 UI_METHOD *UI_OpenSSL(void)
245         {
246         return &ui_openssl;
247         }
248
249 static int read_string(UI *ui, UI_STRING *uis)
250         {
251         switch (UI_get_string_type(uis))
252                 {
253         case UI_VERIFY_NOECHO:
254                 fprintf(tty,"Verifying - %s",
255                         UI_get0_output_string(uis));
256                 fflush(tty);
257                 if (read_string_inner(ui, uis, 0) == 0)
258                         return 0;
259                 if (strcmp(UI_get0_result_string(uis),
260                         UI_get0_test_string(uis)) != 0)
261                         {
262                         fprintf(tty,"Verify failure\n");
263                         fflush(tty);
264                         return 0;
265                         }
266                 break;
267         case UI_VERIFY_ECHO:
268                 fprintf(tty,"Verifying - %s",
269                         UI_get0_output_string(uis));
270                 fflush(tty);
271                 if (read_string_inner(ui, uis, 1) == 0)
272                         return 0;
273                 if (strcmp(UI_get0_result_string(uis),
274                         UI_get0_test_string(uis)) != 0)
275                         {
276                         fprintf(tty,"Verify failure\n");
277                         fflush(tty);
278                         return 0;
279                         }
280                 break;
281         case UI_STRING_NOECHO:
282                 fputs(UI_get0_output_string(uis), tty);
283                 fflush(tty);
284                 return read_string_inner(ui, uis, 0);
285         case UI_STRING_ECHO:
286                 fputs(UI_get0_output_string(uis), tty);
287                 fflush(tty);
288                 return read_string_inner(ui, uis, 1);
289         default:
290                 fputs(UI_get0_output_string(uis), tty);
291                 fflush(tty);
292                 break;
293                 }
294         return 1;
295         }
296
297
298 /* Internal functions to read a string without echoing */
299 static void read_till_nl(FILE *in)
300         {
301 #define SIZE 4
302         char buf[SIZE+1];
303
304         do      {
305                 fgets(buf,SIZE,in);
306                 } while (strchr(buf,'\n') == NULL);
307         }
308
309 static int read_string_inner(UI *ui, UI_STRING *uis, int echo)
310         {
311         static int ps;
312         int ok;
313         char *result = OPENSSL_malloc(BUFSIZ);
314         int maxsize = BUFSIZ-1;
315
316 #ifndef WIN16
317         if (setjmp(save))
318                 {
319                 ok=0;
320                 goto error;
321                 }
322         ok=0;
323         ps=0;
324
325         pushsig();
326         ps=1;
327
328         if (!echo) noecho_console(ui);
329         ps=2;
330
331         while (!ok)
332                 {
333                 char *p;
334
335                 result[0]='\0';
336                 fgets(result,maxsize,tty);
337                 if (feof(tty)) goto error;
338                 if (ferror(tty)) goto error;
339                 if ((p=(char *)strchr(result,'\n')) != NULL)
340                         *p='\0';
341                 else    read_till_nl(tty);
342                 if (UI_set_result(uis, result) >= 0)
343                         ok=1;
344                 }
345
346 error:
347         if (!echo) fprintf(tty,"\n");
348         if (ps >= 2 && !echo)
349                 echo_console(ui);
350
351         if (ps >= 1)
352                 popsig();
353 #else
354         memset(result,0,BUFSIZ);
355         ok=1;
356 #endif
357
358         OPENSSL_free(result);
359         return ok;
360         }
361
362
363 /* Internal functions to open, handle and close a channel to the console.  */
364 static int open_console(UI *ui)
365         {
366         CRYPTO_w_lock(CRYPTO_LOCK_UI);
367         is_a_tty = 1;
368
369 #ifdef MSDOS
370         if ((tty=fopen("con","w+")) == NULL)
371                 tty=stdin;
372 #elif defined(MAC_OS_pre_X)
373         tty=stdin;
374 #else
375         if ((tty=fopen("/dev/tty","w+")) == NULL)
376                 tty=stdin;
377 #endif
378
379 #if defined(TTY_get) && !defined(VMS)
380         if (TTY_get(fileno(tty),&tty_orig) == -1)
381                 {
382 #ifdef ENOTTY
383                 if (errno == ENOTTY)
384                         is_a_tty=0;
385                 else
386 #endif
387 #ifdef EINVAL
388                 /* Ariel Glenn ariel@columbia.edu reports that solaris
389                  * can return EINVAL instead.  This should be ok */
390                 if (errno == EINVAL)
391                         is_a_tty=0;
392                 else
393 #endif
394                         return 0;
395                 }
396 #endif
397 #ifdef VMS
398         status = sys$assign(&terminal,&channel,0,0);
399         if (status != SS$_NORMAL)
400                 return 0;
401         status=sys$qiow(0,channel,IO$_SENSEMODE,&iosb,0,0,tty_orig,12,0,0,0,0);
402         if ((status != SS$_NORMAL) || (iosb.iosb$w_value != SS$_NORMAL))
403                 return 0;
404 #endif
405         return 1;
406         }
407
408 static int noecho_console(UI *ui)
409         {
410 #ifdef TTY_FLAGS
411         memcpy(&(tty_new),&(tty_orig),sizeof(tty_orig));
412         tty_new.TTY_FLAGS &= ~ECHO;
413 #endif
414
415 #if defined(TTY_set) && !defined(VMS)
416         if (is_a_tty && (TTY_set(fileno(tty),&tty_new) == -1))
417                 return 0;
418 #endif
419 #ifdef VMS
420         tty_new[0] = tty_orig[0];
421         tty_new[1] = tty_orig[1] | TT$M_NOECHO;
422         tty_new[2] = tty_orig[2];
423         status = sys$qiow(0,channel,IO$_SETMODE,&iosb,0,0,tty_new,12,0,0,0,0);
424         if ((status != SS$_NORMAL) || (iosb.iosb$w_value != SS$_NORMAL))
425                 return 0;
426 #endif
427         return 1;
428         }
429
430 static int echo_console(UI *ui)
431         {
432 #if defined(TTY_set) && !defined(VMS)
433         memcpy(&(tty_new),&(tty_orig),sizeof(tty_orig));
434         tty_new.TTY_FLAGS |= ECHO;
435 #endif
436
437 #if defined(TTY_set) && !defined(VMS)
438         if (is_a_tty && (TTY_set(fileno(tty),&tty_new) == -1))
439                 return 0;
440 #endif
441 #ifdef VMS
442         tty_new[0] = tty_orig[0];
443         tty_new[1] = tty_orig[1] & ~TT$M_NOECHO;
444         tty_new[2] = tty_orig[2];
445         status = sys$qiow(0,channel,IO$_SETMODE,&iosb,0,0,tty_new,12,0,0,0,0);
446         if ((status != SS$_NORMAL) || (iosb.iosb$w_value != SS$_NORMAL))
447                 return 0;
448 #endif
449         return 1;
450         }
451
452 static int close_console(UI *ui)
453         {
454         if (stdin != tty) fclose(tty);
455 #ifdef VMS
456         status = sys$dassgn(channel);
457 #endif
458         CRYPTO_w_unlock(CRYPTO_LOCK_UI);
459
460         return 1;
461         }
462
463
464 /* Internal functions to handle signals and act on them */
465 static void pushsig(void)
466         {
467         int i;
468 #ifdef SIGACTION
469         struct sigaction sa;
470
471         memset(&sa,0,sizeof sa);
472         sa.sa_handler=recsig;
473 #endif
474
475         for (i=1; i<NX509_SIG; i++)
476                 {
477 #ifdef SIGUSR1
478                 if (i == SIGUSR1)
479                         continue;
480 #endif
481 #ifdef SIGUSR2
482                 if (i == SIGUSR2)
483                         continue;
484 #endif
485 #ifdef SIGKILL
486                 if (i == SIGKILL) /* We can't make any action on that. */
487                         continue;
488 #endif
489 #ifdef SIGACTION
490                 sigaction(i,&sa,&savsig[i]);
491 #else
492                 savsig[i]=signal(i,recsig);
493 #endif
494                 }
495
496 #ifdef SIGWINCH
497         signal(SIGWINCH,SIG_DFL);
498 #endif
499         }
500
501 static void popsig(void)
502         {
503         int i;
504
505         for (i=1; i<NX509_SIG; i++)
506                 {
507 #ifdef SIGUSR1
508                 if (i == SIGUSR1)
509                         continue;
510 #endif
511 #ifdef SIGUSR2
512                 if (i == SIGUSR2)
513                         continue;
514 #endif
515 #ifdef SIGACTION
516                 sigaction(i,&savsig[i],NULL);
517 #else
518                 signal(i,savsig[i]);
519 #endif
520                 }
521         }
522
523 static void recsig(int i)
524         {
525         longjmp(save,1);
526 #ifdef LINT
527         i=i;
528 #endif
529         }
530
531
532 /* Internal functions specific for Windows */
533 #if defined(MSDOS) && !defined(WIN16)
534 static int noecho_fgets(char *buf, int size, FILE *tty)
535         {
536         int i;
537         char *p;
538
539         p=buf;
540         for (;;)
541                 {
542                 if (size == 0)
543                         {
544                         *p='\0';
545                         break;
546                         }
547                 size--;
548 #ifdef WIN16TTY
549                 i=_inchar();
550 #else
551                 i=getch();
552 #endif
553                 if (i == '\r') i='\n';
554                 *(p++)=i;
555                 if (i == '\n')
556                         {
557                         *p='\0';
558                         break;
559                         }
560                 }
561 #ifdef WIN_CONSOLE_BUG
562 /* Win95 has several evil console bugs: one of these is that the
563  * last character read using getch() is passed to the next read: this is
564  * usually a CR so this can be trouble. No STDIO fix seems to work but
565  * flushing the console appears to do the trick.
566  */
567                 {
568                         HANDLE inh;
569                         inh = GetStdHandle(STD_INPUT_HANDLE);
570                         FlushConsoleInputBuffer(inh);
571                 }
572 #endif
573         return(strlen(buf));
574         }
575 #endif