1/*
2 * Copyright 1995-2020 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 <string.h>
11#include <openssl/err.h>
12#include <openssl/ui.h>
13#include "apps_ui.h"
14
15static UI_METHOD *ui_method = NULL;
16static const UI_METHOD *ui_base_method = NULL;
17
18static int ui_open(UI *ui)
19{
20    int (*opener)(UI *ui) = UI_method_get_opener(ui_base_method);
21
22    if (opener != NULL)
23        return opener(ui);
24    return 1;
25}
26
27static int ui_read(UI *ui, UI_STRING *uis)
28{
29    int (*reader)(UI *ui, UI_STRING *uis) = NULL;
30
31    if (UI_get_input_flags(uis) & UI_INPUT_FLAG_DEFAULT_PWD
32        && UI_get0_user_data(ui)) {
33        switch (UI_get_string_type(uis)) {
34        case UIT_PROMPT:
35        case UIT_VERIFY:
36            {
37                const char *password =
38                    ((PW_CB_DATA *)UI_get0_user_data(ui))->password;
39
40                if (password != NULL) {
41                    UI_set_result(ui, uis, password);
42                    return 1;
43                }
44            }
45            break;
46        case UIT_NONE:
47        case UIT_BOOLEAN:
48        case UIT_INFO:
49        case UIT_ERROR:
50            break;
51        }
52    }
53
54    reader = UI_method_get_reader(ui_base_method);
55    if (reader != NULL)
56        return reader(ui, uis);
57    /* Default to the empty password if we've got nothing better */
58    UI_set_result(ui, uis, "");
59    return 1;
60}
61
62static int ui_write(UI *ui, UI_STRING *uis)
63{
64    int (*writer)(UI *ui, UI_STRING *uis) = NULL;
65
66    if (UI_get_input_flags(uis) & UI_INPUT_FLAG_DEFAULT_PWD
67        && UI_get0_user_data(ui)) {
68        switch (UI_get_string_type(uis)) {
69        case UIT_PROMPT:
70        case UIT_VERIFY:
71            {
72                const char *password =
73                    ((PW_CB_DATA *)UI_get0_user_data(ui))->password;
74
75                if (password != NULL)
76                    return 1;
77            }
78            break;
79        case UIT_NONE:
80        case UIT_BOOLEAN:
81        case UIT_INFO:
82        case UIT_ERROR:
83            break;
84        }
85    }
86
87    writer = UI_method_get_writer(ui_base_method);
88    if (writer != NULL)
89        return writer(ui, uis);
90    return 1;
91}
92
93static int ui_close(UI *ui)
94{
95    int (*closer)(UI *ui) = UI_method_get_closer(ui_base_method);
96
97    if (closer != NULL)
98        return closer(ui);
99    return 1;
100}
101
102/* object_name defaults to prompt_info from ui user data if present */
103static char *ui_prompt_construct(UI *ui, const char *phrase_desc,
104                                 const char *object_name)
105{
106    PW_CB_DATA *cb_data = (PW_CB_DATA *)UI_get0_user_data(ui);
107
108    if (phrase_desc == NULL)
109        phrase_desc = "pass phrase";
110    if (object_name == NULL && cb_data != NULL)
111        object_name = cb_data->prompt_info;
112    return UI_construct_prompt(NULL, phrase_desc, object_name);
113}
114
115int set_base_ui_method(const UI_METHOD *ui_meth)
116{
117    if (ui_meth == NULL)
118        ui_meth = UI_null();
119    ui_base_method = ui_meth;
120    return 1;
121}
122
123int setup_ui_method(void)
124{
125    ui_base_method = UI_null();
126#ifndef OPENSSL_NO_UI_CONSOLE
127    ui_base_method = UI_OpenSSL();
128#endif
129    ui_method = UI_create_method("OpenSSL application user interface");
130    return ui_method != NULL
131        && 0 == UI_method_set_opener(ui_method, ui_open)
132        && 0 == UI_method_set_reader(ui_method, ui_read)
133        && 0 == UI_method_set_writer(ui_method, ui_write)
134        && 0 == UI_method_set_closer(ui_method, ui_close)
135        && 0 == UI_method_set_prompt_constructor(ui_method,
136                                                 ui_prompt_construct);
137}
138
139void destroy_ui_method(void)
140{
141    if (ui_method != NULL) {
142        UI_destroy_method(ui_method);
143        ui_method = NULL;
144    }
145}
146
147const UI_METHOD *get_ui_method(void)
148{
149    return ui_method;
150}
151
152static void *ui_malloc(int sz, const char *what)
153{
154    void *vp = OPENSSL_malloc(sz);
155
156    if (vp == NULL) {
157        BIO_printf(bio_err, "Could not allocate %d bytes for %s\n", sz, what);
158        ERR_print_errors(bio_err);
159        exit(1);
160    }
161    return vp;
162}
163
164int password_callback(char *buf, int bufsiz, int verify, PW_CB_DATA *cb_data)
165{
166    int res = 0;
167    UI *ui;
168    int ok = 0;
169    char *buff = NULL;
170    int ui_flags = 0;
171    const char *prompt_info = NULL;
172    char *prompt;
173
174    if ((ui = UI_new_method(ui_method)) == NULL)
175        return 0;
176
177    if (cb_data != NULL && cb_data->prompt_info != NULL)
178        prompt_info = cb_data->prompt_info;
179    prompt = UI_construct_prompt(ui, "pass phrase", prompt_info);
180    if (prompt == NULL) {
181        BIO_printf(bio_err, "Out of memory\n");
182        UI_free(ui);
183        return 0;
184    }
185
186    ui_flags |= UI_INPUT_FLAG_DEFAULT_PWD;
187    UI_ctrl(ui, UI_CTRL_PRINT_ERRORS, 1, 0, 0);
188
189    /* We know that there is no previous user data to return to us */
190    (void)UI_add_user_data(ui, cb_data);
191
192    ok = UI_add_input_string(ui, prompt, ui_flags, buf,
193                             PW_MIN_LENGTH, bufsiz - 1);
194
195    if (ok >= 0 && verify) {
196        buff = ui_malloc(bufsiz, "password buffer");
197        ok = UI_add_verify_string(ui, prompt, ui_flags, buff,
198                                  PW_MIN_LENGTH, bufsiz - 1, buf);
199    }
200    if (ok >= 0)
201        do {
202            ok = UI_process(ui);
203        } while (ok < 0 && UI_ctrl(ui, UI_CTRL_IS_REDOABLE, 0, 0, 0));
204
205    OPENSSL_clear_free(buff, (unsigned int)bufsiz);
206
207    if (ok >= 0)
208        res = strlen(buf);
209    if (ok == -1) {
210        BIO_printf(bio_err, "User interface error\n");
211        ERR_print_errors(bio_err);
212        OPENSSL_cleanse(buf, (unsigned int)bufsiz);
213        res = 0;
214    }
215    if (ok == -2) {
216        BIO_printf(bio_err, "aborted!\n");
217        OPENSSL_cleanse(buf, (unsigned int)bufsiz);
218        res = 0;
219    }
220    UI_free(ui);
221    OPENSSL_free(prompt);
222    return res;
223}
224