1/*
2 * Copyright 2001-2022 The OpenSSL Project Authors. All Rights Reserved.
3 *
4 * Licensed under the Apache License 2.0 (the "License").  You may not use
5 * this file except in compliance with the License.  You can obtain a copy
6 * in the file LICENSE in the source distribution or at
7 * https://www.openssl.org/source/license.html
8 */
9
10#include "e_os.h"
11#include <openssl/e_os2.h>
12#include <openssl/err.h>
13#include <openssl/ui.h>
14
15#ifndef OPENSSL_NO_UI_CONSOLE
16/*
17 * need for #define _POSIX_C_SOURCE arises whenever you pass -ansi to gcc
18 * [maybe others?], because it masks interfaces not discussed in standard,
19 * sigaction and fileno included. -pedantic would be more appropriate for the
20 * intended purposes, but we can't prevent users from adding -ansi.
21 */
22# if defined(OPENSSL_SYS_VXWORKS)
23#  include <sys/types.h>
24# endif
25
26# if !defined(_POSIX_C_SOURCE) && defined(OPENSSL_SYS_VMS)
27#  ifndef _POSIX_C_SOURCE
28#   define _POSIX_C_SOURCE 2
29#  endif
30# endif
31# include <signal.h>
32# include <stdio.h>
33# include <string.h>
34# include <errno.h>
35
36# if !defined(OPENSSL_SYS_MSDOS) && !defined(OPENSSL_SYS_VMS)
37#  include <unistd.h>
38/*
39 * If unistd.h defines _POSIX_VERSION, we conclude that we are on a POSIX
40 * system and have sigaction and termios.
41 */
42#  if defined(_POSIX_VERSION) && _POSIX_VERSION>=199309L
43
44#   define SIGACTION
45#   if !defined(TERMIOS) && !defined(TERMIO) && !defined(SGTTY)
46#    define TERMIOS
47#   endif
48
49#  endif
50# endif
51
52# include "ui_local.h"
53# include "internal/cryptlib.h"
54
55# ifdef OPENSSL_SYS_VMS          /* prototypes for sys$whatever */
56#  include <starlet.h>
57#  ifdef __DECC
58#   pragma message disable DOLLARID
59#  endif
60# endif
61
62# ifdef WIN_CONSOLE_BUG
63#  include <windows.h>
64#  ifndef OPENSSL_SYS_WINCE
65#   include <wincon.h>
66#  endif
67# endif
68
69/*
70 * There are 6 types of terminal interface supported, TERMIO, TERMIOS, VMS,
71 * MSDOS, WIN32 Console and SGTTY.
72 *
73 * If someone defines one of the macros TERMIO, TERMIOS or SGTTY, it will
74 * remain respected.  Otherwise, we default to TERMIOS except for a few
75 * systems that require something different.
76 *
77 * Note: we do not use SGTTY unless it's defined by the configuration.  We
78 * may eventually opt to remove its use entirely.
79 */
80
81# if !defined(TERMIOS) && !defined(TERMIO) && !defined(SGTTY)
82
83#  if defined(_LIBC)
84#   undef  TERMIOS
85#   define TERMIO
86#   undef  SGTTY
87/*
88 * We know that VMS, MSDOS, VXWORKS, use entirely other mechanisms.
89 */
90#  elif !defined(OPENSSL_SYS_VMS) \
91        && !defined(OPENSSL_SYS_MSDOS) \
92        && !defined(OPENSSL_SYS_VXWORKS)
93#   define TERMIOS
94#   undef  TERMIO
95#   undef  SGTTY
96#  endif
97
98# endif
99
100# if defined(OPENSSL_SYS_VXWORKS)
101#  undef TERMIOS
102#  undef TERMIO
103#  undef SGTTY
104# endif
105
106# ifdef TERMIOS
107#  include <termios.h>
108#  define TTY_STRUCT             struct termios
109#  define TTY_FLAGS              c_lflag
110#  define TTY_get(tty,data)      tcgetattr(tty,data)
111#  define TTY_set(tty,data)      tcsetattr(tty,TCSANOW,data)
112# endif
113
114# ifdef TERMIO
115#  include <termio.h>
116#  define TTY_STRUCT             struct termio
117#  define TTY_FLAGS              c_lflag
118#  define TTY_get(tty,data)      ioctl(tty,TCGETA,data)
119#  define TTY_set(tty,data)      ioctl(tty,TCSETA,data)
120# endif
121
122# ifdef SGTTY
123#  include <sgtty.h>
124#  define TTY_STRUCT             struct sgttyb
125#  define TTY_FLAGS              sg_flags
126#  define TTY_get(tty,data)      ioctl(tty,TIOCGETP,data)
127#  define TTY_set(tty,data)      ioctl(tty,TIOCSETP,data)
128# endif
129
130# if !defined(_LIBC) && !defined(OPENSSL_SYS_MSDOS) && !defined(OPENSSL_SYS_VMS) && ! (defined(OPENSSL_SYS_TANDEM) && defined(_SPT_MODEL_))
131#  include <sys/ioctl.h>
132# endif
133
134# ifdef OPENSSL_SYS_MSDOS
135#  include <conio.h>
136# endif
137
138# ifdef OPENSSL_SYS_VMS
139#  include <ssdef.h>
140#  include <iodef.h>
141#  include <ttdef.h>
142#  include <descrip.h>
143struct IOSB {
144    short iosb$w_value;
145    short iosb$w_count;
146    long iosb$l_info;
147};
148# endif
149
150# ifndef NX509_SIG
151#  define NX509_SIG 32
152# endif
153
154/* Define globals.  They are protected by a lock */
155# ifdef SIGACTION
156static struct sigaction savsig[NX509_SIG];
157# else
158static void (*savsig[NX509_SIG]) (int);
159# endif
160
161# ifdef OPENSSL_SYS_VMS
162static struct IOSB iosb;
163static $DESCRIPTOR(terminal, "TT");
164static long tty_orig[3], tty_new[3]; /* XXX Is there any guarantee that this
165                                      * will always suffice for the actual
166                                      * structures? */
167static long status;
168static unsigned short channel = 0;
169# elif defined(_WIN32) && !defined(_WIN32_WCE)
170static DWORD tty_orig, tty_new;
171# else
172#  if !defined(OPENSSL_SYS_MSDOS) || defined(__DJGPP__)
173static TTY_STRUCT tty_orig, tty_new;
174#  endif
175# endif
176static FILE *tty_in, *tty_out;
177static int is_a_tty;
178
179/* Declare static functions */
180# if !defined(OPENSSL_SYS_WINCE)
181static int read_till_nl(FILE *);
182static void recsig(int);
183static void pushsig(void);
184static void popsig(void);
185# endif
186# if defined(OPENSSL_SYS_MSDOS) && !defined(_WIN32)
187static int noecho_fgets(char *buf, int size, FILE *tty);
188# endif
189static int read_string_inner(UI *ui, UI_STRING *uis, int echo, int strip_nl);
190
191static int read_string(UI *ui, UI_STRING *uis);
192static int write_string(UI *ui, UI_STRING *uis);
193
194static int open_console(UI *ui);
195static int echo_console(UI *ui);
196static int noecho_console(UI *ui);
197static int close_console(UI *ui);
198
199/*
200 * The following function makes sure that info and error strings are printed
201 * before any prompt.
202 */
203static int write_string(UI *ui, UI_STRING *uis)
204{
205    switch (UI_get_string_type(uis)) {
206    case UIT_ERROR:
207    case UIT_INFO:
208        fputs(UI_get0_output_string(uis), tty_out);
209        fflush(tty_out);
210        break;
211    case UIT_NONE:
212    case UIT_PROMPT:
213    case UIT_VERIFY:
214    case UIT_BOOLEAN:
215        break;
216    }
217    return 1;
218}
219
220static int read_string(UI *ui, UI_STRING *uis)
221{
222    int ok = 0;
223
224    switch (UI_get_string_type(uis)) {
225    case UIT_BOOLEAN:
226        fputs(UI_get0_output_string(uis), tty_out);
227        fputs(UI_get0_action_string(uis), tty_out);
228        fflush(tty_out);
229        return read_string_inner(ui, uis,
230                                 UI_get_input_flags(uis) & UI_INPUT_FLAG_ECHO,
231                                 0);
232    case UIT_PROMPT:
233        fputs(UI_get0_output_string(uis), tty_out);
234        fflush(tty_out);
235        return read_string_inner(ui, uis,
236                                 UI_get_input_flags(uis) & UI_INPUT_FLAG_ECHO,
237                                 1);
238    case UIT_VERIFY:
239        fprintf(tty_out, "Verifying - %s", UI_get0_output_string(uis));
240        fflush(tty_out);
241        if ((ok = read_string_inner(ui, uis,
242                                    UI_get_input_flags(uis) &
243                                    UI_INPUT_FLAG_ECHO, 1)) <= 0)
244            return ok;
245        if (strcmp(UI_get0_result_string(uis), UI_get0_test_string(uis)) != 0) {
246            fprintf(tty_out, "Verify failure\n");
247            fflush(tty_out);
248            return 0;
249        }
250        break;
251    case UIT_NONE:
252    case UIT_INFO:
253    case UIT_ERROR:
254        break;
255    }
256    return 1;
257}
258
259# if !defined(OPENSSL_SYS_WINCE)
260/* Internal functions to read a string without echoing */
261static int read_till_nl(FILE *in)
262{
263#  define SIZE 4
264    char buf[SIZE + 1];
265
266    do {
267        if (!fgets(buf, SIZE, in))
268            return 0;
269    } while (strchr(buf, '\n') == NULL);
270    return 1;
271}
272
273static volatile sig_atomic_t intr_signal;
274# endif
275
276static int read_string_inner(UI *ui, UI_STRING *uis, int echo, int strip_nl)
277{
278    static int ps;
279    int ok;
280    char result[BUFSIZ];
281    int maxsize = BUFSIZ - 1;
282# if !defined(OPENSSL_SYS_WINCE)
283    char *p = NULL;
284    int echo_eol = !echo;
285
286    intr_signal = 0;
287    ok = 0;
288    ps = 0;
289
290    pushsig();
291    ps = 1;
292
293    if (!echo && !noecho_console(ui))
294        goto error;
295    ps = 2;
296
297    result[0] = '\0';
298#  if defined(_WIN32)
299    if (is_a_tty) {
300        DWORD numread;
301#   if defined(CP_UTF8)
302        if (GetEnvironmentVariableW(L"OPENSSL_WIN32_UTF8", NULL, 0) != 0) {
303            WCHAR wresult[BUFSIZ];
304
305            if (ReadConsoleW(GetStdHandle(STD_INPUT_HANDLE),
306                         wresult, maxsize, &numread, NULL)) {
307                if (numread >= 2 &&
308                    wresult[numread-2] == L'\r' &&
309                    wresult[numread-1] == L'\n') {
310                    wresult[numread-2] = L'\n';
311                    numread--;
312                }
313                wresult[numread] = '\0';
314                if (WideCharToMultiByte(CP_UTF8, 0, wresult, -1,
315                                        result, sizeof(result), NULL, 0) > 0)
316                    p = result;
317
318                OPENSSL_cleanse(wresult, sizeof(wresult));
319            }
320        } else
321#   endif
322        if (ReadConsoleA(GetStdHandle(STD_INPUT_HANDLE),
323                         result, maxsize, &numread, NULL)) {
324            if (numread >= 2 &&
325                result[numread-2] == '\r' && result[numread-1] == '\n') {
326                result[numread-2] = '\n';
327                numread--;
328            }
329            result[numread] = '\0';
330            p = result;
331        }
332    } else
333#  elif defined(OPENSSL_SYS_MSDOS)
334    if (!echo) {
335        noecho_fgets(result, maxsize, tty_in);
336        p = result;             /* FIXME: noecho_fgets doesn't return errors */
337    } else
338#  endif
339    p = fgets(result, maxsize, tty_in);
340    if (p == NULL)
341        goto error;
342    if (feof(tty_in))
343        goto error;
344    if (ferror(tty_in))
345        goto error;
346    if ((p = (char *)strchr(result, '\n')) != NULL) {
347        if (strip_nl)
348            *p = '\0';
349    } else if (!read_till_nl(tty_in))
350        goto error;
351    if (UI_set_result(ui, uis, result) >= 0)
352        ok = 1;
353
354 error:
355    if (intr_signal == SIGINT)
356        ok = -1;
357    if (echo_eol)
358        fprintf(tty_out, "\n");
359    if (ps >= 2 && !echo && !echo_console(ui))
360        ok = 0;
361
362    if (ps >= 1)
363        popsig();
364# else
365    ok = 1;
366# endif
367
368    OPENSSL_cleanse(result, BUFSIZ);
369    return ok;
370}
371
372/* Internal functions to open, handle and close a channel to the console.  */
373static int open_console(UI *ui)
374{
375    if (!CRYPTO_THREAD_write_lock(ui->lock))
376        return 0;
377    is_a_tty = 1;
378
379# if defined(OPENSSL_SYS_VXWORKS)
380    tty_in = stdin;
381    tty_out = stderr;
382# elif defined(_WIN32) && !defined(_WIN32_WCE)
383    if ((tty_out = fopen("conout$", "w")) == NULL)
384        tty_out = stderr;
385
386    if (GetConsoleMode(GetStdHandle(STD_INPUT_HANDLE), &tty_orig)) {
387        tty_in = stdin;
388    } else {
389        is_a_tty = 0;
390        if ((tty_in = fopen("conin$", "r")) == NULL)
391            tty_in = stdin;
392    }
393# else
394#  ifdef OPENSSL_SYS_MSDOS
395#   define DEV_TTY "con"
396#  else
397#   define DEV_TTY "/dev/tty"
398#  endif
399    if ((tty_in = fopen(DEV_TTY, "r")) == NULL)
400        tty_in = stdin;
401    if ((tty_out = fopen(DEV_TTY, "w")) == NULL)
402        tty_out = stderr;
403# endif
404
405# if defined(TTY_get) && !defined(OPENSSL_SYS_VMS)
406    if (TTY_get(fileno(tty_in), &tty_orig) == -1) {
407#  ifdef ENOTTY
408        if (errno == ENOTTY)
409            is_a_tty = 0;
410        else
411#  endif
412#  ifdef EINVAL
413            /*
414             * Ariel Glenn reports that solaris can return EINVAL instead.
415             * This should be ok
416             */
417        if (errno == EINVAL)
418            is_a_tty = 0;
419        else
420#  endif
421#  ifdef ENXIO
422            /*
423             * Solaris can return ENXIO.
424             * This should be ok
425             */
426        if (errno == ENXIO)
427            is_a_tty = 0;
428        else
429#  endif
430#  ifdef EIO
431            /*
432             * Linux can return EIO.
433             * This should be ok
434             */
435        if (errno == EIO)
436            is_a_tty = 0;
437        else
438#  endif
439#  ifdef EPERM
440            /*
441             * Linux can return EPERM (Operation not permitted),
442             * e.g. if a daemon executes openssl via fork()+execve()
443             * This should be ok
444             */
445        if (errno == EPERM)
446            is_a_tty = 0;
447        else
448#  endif
449#  ifdef ENODEV
450            /*
451             * MacOS X returns ENODEV (Operation not supported by device),
452             * which seems appropriate.
453             */
454        if (errno == ENODEV)
455                is_a_tty = 0;
456        else
457#  endif
458            {
459                ERR_raise_data(ERR_LIB_UI, UI_R_UNKNOWN_TTYGET_ERRNO_VALUE,
460                               "errno=%d", errno);
461                return 0;
462            }
463    }
464# endif
465# ifdef OPENSSL_SYS_VMS
466    status = sys$assign(&terminal, &channel, 0, 0);
467
468    /* if there isn't a TT device, something is very wrong */
469    if (status != SS$_NORMAL) {
470        ERR_raise_data(ERR_LIB_UI, UI_R_SYSASSIGN_ERROR,
471                       "status=%%X%08X", status);
472        return 0;
473    }
474
475    status = sys$qiow(0, channel, IO$_SENSEMODE, &iosb, 0, 0, tty_orig, 12,
476                      0, 0, 0, 0);
477
478    /* If IO$_SENSEMODE doesn't work, this is not a terminal device */
479    if ((status != SS$_NORMAL) || (iosb.iosb$w_value != SS$_NORMAL))
480        is_a_tty = 0;
481# endif
482    return 1;
483}
484
485static int noecho_console(UI *ui)
486{
487# ifdef TTY_FLAGS
488    memcpy(&(tty_new), &(tty_orig), sizeof(tty_orig));
489    tty_new.TTY_FLAGS &= ~ECHO;
490# endif
491
492# if defined(TTY_set) && !defined(OPENSSL_SYS_VMS)
493    if (is_a_tty && (TTY_set(fileno(tty_in), &tty_new) == -1))
494        return 0;
495# endif
496# ifdef OPENSSL_SYS_VMS
497    if (is_a_tty) {
498        tty_new[0] = tty_orig[0];
499        tty_new[1] = tty_orig[1] | TT$M_NOECHO;
500        tty_new[2] = tty_orig[2];
501        status = sys$qiow(0, channel, IO$_SETMODE, &iosb, 0, 0, tty_new, 12,
502                          0, 0, 0, 0);
503        if ((status != SS$_NORMAL) || (iosb.iosb$w_value != SS$_NORMAL)) {
504            ERR_raise_data(ERR_LIB_UI, UI_R_SYSQIOW_ERROR,
505                           "status=%%X%08X, iosb.iosb$w_value=%%X%08X",
506                           status, iosb.iosb$w_value);
507            return 0;
508        }
509    }
510# endif
511# if defined(_WIN32) && !defined(_WIN32_WCE)
512    if (is_a_tty) {
513        tty_new = tty_orig;
514        tty_new &= ~ENABLE_ECHO_INPUT;
515        SetConsoleMode(GetStdHandle(STD_INPUT_HANDLE), tty_new);
516    }
517# endif
518    return 1;
519}
520
521static int echo_console(UI *ui)
522{
523# if defined(TTY_set) && !defined(OPENSSL_SYS_VMS)
524    memcpy(&(tty_new), &(tty_orig), sizeof(tty_orig));
525    if (is_a_tty && (TTY_set(fileno(tty_in), &tty_new) == -1))
526        return 0;
527# endif
528# ifdef OPENSSL_SYS_VMS
529    if (is_a_tty) {
530        tty_new[0] = tty_orig[0];
531        tty_new[1] = tty_orig[1];
532        tty_new[2] = tty_orig[2];
533        status = sys$qiow(0, channel, IO$_SETMODE, &iosb, 0, 0, tty_new, 12,
534                          0, 0, 0, 0);
535        if ((status != SS$_NORMAL) || (iosb.iosb$w_value != SS$_NORMAL)) {
536            ERR_raise_data(ERR_LIB_UI, UI_R_SYSQIOW_ERROR,
537                           "status=%%X%08X, iosb.iosb$w_value=%%X%08X",
538                           status, iosb.iosb$w_value);
539            return 0;
540        }
541    }
542# endif
543# if defined(_WIN32) && !defined(_WIN32_WCE)
544    if (is_a_tty) {
545        tty_new = tty_orig;
546        SetConsoleMode(GetStdHandle(STD_INPUT_HANDLE), tty_new);
547    }
548# endif
549    return 1;
550}
551
552static int close_console(UI *ui)
553{
554    int ret = 1;
555
556    if (tty_in != stdin)
557        fclose(tty_in);
558    if (tty_out != stderr)
559        fclose(tty_out);
560# ifdef OPENSSL_SYS_VMS
561    status = sys$dassgn(channel);
562    if (status != SS$_NORMAL) {
563        ERR_raise_data(ERR_LIB_UI, UI_R_SYSDASSGN_ERROR,
564                       "status=%%X%08X", status);
565        ret = 0;
566    }
567# endif
568    CRYPTO_THREAD_unlock(ui->lock);
569
570    return ret;
571}
572
573# if !defined(OPENSSL_SYS_WINCE)
574/* Internal functions to handle signals and act on them */
575static void pushsig(void)
576{
577#  ifndef OPENSSL_SYS_WIN32
578    int i;
579#  endif
580#  ifdef SIGACTION
581    struct sigaction sa;
582
583    memset(&sa, 0, sizeof(sa));
584    sa.sa_handler = recsig;
585#  endif
586
587#  ifdef OPENSSL_SYS_WIN32
588    savsig[SIGABRT] = signal(SIGABRT, recsig);
589    savsig[SIGFPE] = signal(SIGFPE, recsig);
590    savsig[SIGILL] = signal(SIGILL, recsig);
591    savsig[SIGINT] = signal(SIGINT, recsig);
592    savsig[SIGSEGV] = signal(SIGSEGV, recsig);
593    savsig[SIGTERM] = signal(SIGTERM, recsig);
594#  else
595    for (i = 1; i < NX509_SIG; i++) {
596#   ifdef SIGUSR1
597        if (i == SIGUSR1)
598            continue;
599#   endif
600#   ifdef SIGUSR2
601        if (i == SIGUSR2)
602            continue;
603#   endif
604#   ifdef SIGKILL
605        if (i == SIGKILL)       /* We can't make any action on that. */
606            continue;
607#   endif
608#   ifdef SIGACTION
609        sigaction(i, &sa, &savsig[i]);
610#   else
611        savsig[i] = signal(i, recsig);
612#   endif
613    }
614#  endif
615
616#  ifdef SIGWINCH
617    signal(SIGWINCH, SIG_DFL);
618#  endif
619}
620
621static void popsig(void)
622{
623#  ifdef OPENSSL_SYS_WIN32
624    signal(SIGABRT, savsig[SIGABRT]);
625    signal(SIGFPE, savsig[SIGFPE]);
626    signal(SIGILL, savsig[SIGILL]);
627    signal(SIGINT, savsig[SIGINT]);
628    signal(SIGSEGV, savsig[SIGSEGV]);
629    signal(SIGTERM, savsig[SIGTERM]);
630#  else
631    int i;
632    for (i = 1; i < NX509_SIG; i++) {
633#   ifdef SIGUSR1
634        if (i == SIGUSR1)
635            continue;
636#   endif
637#   ifdef SIGUSR2
638        if (i == SIGUSR2)
639            continue;
640#   endif
641#   ifdef SIGACTION
642        sigaction(i, &savsig[i], NULL);
643#   else
644        signal(i, savsig[i]);
645#   endif
646    }
647#  endif
648}
649
650static void recsig(int i)
651{
652    intr_signal = i;
653}
654# endif
655
656/* Internal functions specific for Windows */
657# if defined(OPENSSL_SYS_MSDOS) && !defined(_WIN32)
658static int noecho_fgets(char *buf, int size, FILE *tty)
659{
660    int i;
661    char *p;
662
663    p = buf;
664    for (;;) {
665        if (size == 0) {
666            *p = '\0';
667            break;
668        }
669        size--;
670#  if defined(_WIN32)
671        i = _getch();
672#  else
673        i = getch();
674#  endif
675        if (i == '\r')
676            i = '\n';
677        *(p++) = i;
678        if (i == '\n') {
679            *p = '\0';
680            break;
681        }
682    }
683#  ifdef WIN_CONSOLE_BUG
684    /*
685     * Win95 has several evil console bugs: one of these is that the last
686     * character read using getch() is passed to the next read: this is
687     * usually a CR so this can be trouble. No STDIO fix seems to work but
688     * flushing the console appears to do the trick.
689     */
690    {
691        HANDLE inh;
692        inh = GetStdHandle(STD_INPUT_HANDLE);
693        FlushConsoleInputBuffer(inh);
694    }
695#  endif
696    return strlen(buf);
697}
698# endif
699
700static UI_METHOD ui_openssl = {
701    "OpenSSL default user interface",
702    open_console,
703    write_string,
704    NULL,                       /* No flusher is needed for command lines */
705    read_string,
706    close_console,
707    NULL
708};
709
710/* The method with all the built-in console thingies */
711UI_METHOD *UI_OpenSSL(void)
712{
713    return &ui_openssl;
714}
715
716static const UI_METHOD *default_UI_meth = &ui_openssl;
717
718#else
719
720static const UI_METHOD *default_UI_meth = NULL;
721
722#endif
723
724void UI_set_default_method(const UI_METHOD *meth)
725{
726    default_UI_meth = meth;
727}
728
729const UI_METHOD *UI_get_default_method(void)
730{
731    return default_UI_meth;
732}
733