xref: /freebsd/crypto/openssl/crypto/engine/eng_ctrl.c (revision f25b8c9fb4f58cf61adb47d7570abe7caa6d385d)
1 /*
2  * Copyright 2001-2023 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 /* We need to use some engine deprecated APIs */
11 #define OPENSSL_SUPPRESS_DEPRECATED
12 
13 #include "eng_local.h"
14 
15 /*
16  * When querying a ENGINE-specific control command's 'description', this
17  * string is used if the ENGINE_CMD_DEFN has cmd_desc set to NULL.
18  */
19 static const char *int_no_description = "";
20 
21 /*
22  * These internal functions handle 'CMD'-related control commands when the
23  * ENGINE in question has asked us to take care of it (ie. the ENGINE did not
24  * set the ENGINE_FLAGS_MANUAL_CMD_CTRL flag.
25  */
26 
27 static int int_ctrl_cmd_is_null(const ENGINE_CMD_DEFN *defn)
28 {
29     if ((defn->cmd_num == 0) || (defn->cmd_name == NULL))
30         return 1;
31     return 0;
32 }
33 
34 static int int_ctrl_cmd_by_name(const ENGINE_CMD_DEFN *defn, const char *s)
35 {
36     int idx = 0;
37     while (!int_ctrl_cmd_is_null(defn) && (strcmp(defn->cmd_name, s) != 0)) {
38         idx++;
39         defn++;
40     }
41     if (int_ctrl_cmd_is_null(defn))
42         /* The given name wasn't found */
43         return -1;
44     return idx;
45 }
46 
47 static int int_ctrl_cmd_by_num(const ENGINE_CMD_DEFN *defn, unsigned int num)
48 {
49     int idx = 0;
50     /*
51      * NB: It is stipulated that 'cmd_defn' lists are ordered by cmd_num. So
52      * our searches don't need to take any longer than necessary.
53      */
54     while (!int_ctrl_cmd_is_null(defn) && (defn->cmd_num < num)) {
55         idx++;
56         defn++;
57     }
58     if (defn->cmd_num == num)
59         return idx;
60     /* The given cmd_num wasn't found */
61     return -1;
62 }
63 
64 static int int_ctrl_helper(ENGINE *e, int cmd, long i, void *p,
65     void (*f)(void))
66 {
67     int idx;
68     char *s = (char *)p;
69     const ENGINE_CMD_DEFN *cdp;
70 
71     /* Take care of the easy one first (eg. it requires no searches) */
72     if (cmd == ENGINE_CTRL_GET_FIRST_CMD_TYPE) {
73         if ((e->cmd_defns == NULL) || int_ctrl_cmd_is_null(e->cmd_defns))
74             return 0;
75         return e->cmd_defns->cmd_num;
76     }
77     /* One or two commands require that "p" be a valid string buffer */
78     if ((cmd == ENGINE_CTRL_GET_CMD_FROM_NAME) || (cmd == ENGINE_CTRL_GET_NAME_FROM_CMD) || (cmd == ENGINE_CTRL_GET_DESC_FROM_CMD)) {
79         if (s == NULL) {
80             ERR_raise(ERR_LIB_ENGINE, ERR_R_PASSED_NULL_PARAMETER);
81             return -1;
82         }
83     }
84     /* Now handle cmd_name -> cmd_num conversion */
85     if (cmd == ENGINE_CTRL_GET_CMD_FROM_NAME) {
86         if ((e->cmd_defns == NULL)
87             || ((idx = int_ctrl_cmd_by_name(e->cmd_defns, s)) < 0)) {
88             ERR_raise(ERR_LIB_ENGINE, ENGINE_R_INVALID_CMD_NAME);
89             return -1;
90         }
91         return e->cmd_defns[idx].cmd_num;
92     }
93     /*
94      * For the rest of the commands, the 'long' argument must specify a valid
95      * command number - so we need to conduct a search.
96      */
97     if ((e->cmd_defns == NULL)
98         || ((idx = int_ctrl_cmd_by_num(e->cmd_defns, (unsigned int)i)) < 0)) {
99         ERR_raise(ERR_LIB_ENGINE, ENGINE_R_INVALID_CMD_NUMBER);
100         return -1;
101     }
102     /* Now the logic splits depending on command type */
103     cdp = &e->cmd_defns[idx];
104     switch (cmd) {
105     case ENGINE_CTRL_GET_NEXT_CMD_TYPE:
106         cdp++;
107         return int_ctrl_cmd_is_null(cdp) ? 0 : cdp->cmd_num;
108     case ENGINE_CTRL_GET_NAME_LEN_FROM_CMD:
109         return strlen(cdp->cmd_name);
110     case ENGINE_CTRL_GET_NAME_FROM_CMD:
111         return strlen(strcpy(s, cdp->cmd_name));
112     case ENGINE_CTRL_GET_DESC_LEN_FROM_CMD:
113         return strlen(cdp->cmd_desc == NULL ? int_no_description
114                                             : cdp->cmd_desc);
115     case ENGINE_CTRL_GET_DESC_FROM_CMD:
116         return strlen(strcpy(s, cdp->cmd_desc == NULL ? int_no_description : cdp->cmd_desc));
117     case ENGINE_CTRL_GET_CMD_FLAGS:
118         return cdp->cmd_flags;
119     }
120     /* Shouldn't really be here ... */
121     ERR_raise(ERR_LIB_ENGINE, ENGINE_R_INTERNAL_LIST_ERROR);
122     return -1;
123 }
124 
125 int ENGINE_ctrl(ENGINE *e, int cmd, long i, void *p, void (*f)(void))
126 {
127     int ctrl_exists;
128 
129     if (e == NULL) {
130         ERR_raise(ERR_LIB_ENGINE, ERR_R_PASSED_NULL_PARAMETER);
131         return 0;
132     }
133 
134     ctrl_exists = ((e->ctrl == NULL) ? 0 : 1);
135 
136     /*
137      * Intercept any "root-level" commands before trying to hand them on to
138      * ctrl() handlers.
139      */
140     switch (cmd) {
141     case ENGINE_CTRL_HAS_CTRL_FUNCTION:
142         return ctrl_exists;
143     case ENGINE_CTRL_GET_FIRST_CMD_TYPE:
144     case ENGINE_CTRL_GET_NEXT_CMD_TYPE:
145     case ENGINE_CTRL_GET_CMD_FROM_NAME:
146     case ENGINE_CTRL_GET_NAME_LEN_FROM_CMD:
147     case ENGINE_CTRL_GET_NAME_FROM_CMD:
148     case ENGINE_CTRL_GET_DESC_LEN_FROM_CMD:
149     case ENGINE_CTRL_GET_DESC_FROM_CMD:
150     case ENGINE_CTRL_GET_CMD_FLAGS:
151         if (ctrl_exists && !(e->flags & ENGINE_FLAGS_MANUAL_CMD_CTRL))
152             return int_ctrl_helper(e, cmd, i, p, f);
153         if (!ctrl_exists) {
154             ERR_raise(ERR_LIB_ENGINE, ENGINE_R_NO_CONTROL_FUNCTION);
155             /*
156              * For these cmd-related functions, failure is indicated by a -1
157              * return value (because 0 is used as a valid return in some
158              * places).
159              */
160             return -1;
161         }
162     default:
163         break;
164     }
165     /* Anything else requires a ctrl() handler to exist. */
166     if (!ctrl_exists) {
167         ERR_raise(ERR_LIB_ENGINE, ENGINE_R_NO_CONTROL_FUNCTION);
168         return 0;
169     }
170     return e->ctrl(e, cmd, i, p, f);
171 }
172 
173 int ENGINE_cmd_is_executable(ENGINE *e, int cmd)
174 {
175     int flags;
176     if ((flags = ENGINE_ctrl(e, ENGINE_CTRL_GET_CMD_FLAGS, cmd, NULL, NULL)) < 0) {
177         ERR_raise(ERR_LIB_ENGINE, ENGINE_R_INVALID_CMD_NUMBER);
178         return 0;
179     }
180     if (!(flags & ENGINE_CMD_FLAG_NO_INPUT) && !(flags & ENGINE_CMD_FLAG_NUMERIC) && !(flags & ENGINE_CMD_FLAG_STRING))
181         return 0;
182     return 1;
183 }
184 
185 int ENGINE_ctrl_cmd(ENGINE *e, const char *cmd_name,
186     long i, void *p, void (*f)(void), int cmd_optional)
187 {
188     int num;
189 
190     if (e == NULL || cmd_name == NULL) {
191         ERR_raise(ERR_LIB_ENGINE, ERR_R_PASSED_NULL_PARAMETER);
192         return 0;
193     }
194     if (e->ctrl == NULL
195         || (num = ENGINE_ctrl(e, ENGINE_CTRL_GET_CMD_FROM_NAME,
196                 0, (void *)cmd_name, NULL))
197             <= 0) {
198         /*
199          * If the command didn't *have* to be supported, we fake success.
200          * This allows certain settings to be specified for multiple ENGINEs
201          * and only require a change of ENGINE id (without having to
202          * selectively apply settings). Eg. changing from a hardware device
203          * back to the regular software ENGINE without editing the config
204          * file, etc.
205          */
206         if (cmd_optional) {
207             ERR_clear_error();
208             return 1;
209         }
210         ERR_raise(ERR_LIB_ENGINE, ENGINE_R_INVALID_CMD_NAME);
211         return 0;
212     }
213     /*
214      * Force the result of the control command to 0 or 1, for the reasons
215      * mentioned before.
216      */
217     if (ENGINE_ctrl(e, num, i, p, f) > 0)
218         return 1;
219     return 0;
220 }
221 
222 int ENGINE_ctrl_cmd_string(ENGINE *e, const char *cmd_name, const char *arg,
223     int cmd_optional)
224 {
225     int num, flags;
226     long l;
227     char *ptr;
228 
229     if (e == NULL || cmd_name == NULL) {
230         ERR_raise(ERR_LIB_ENGINE, ERR_R_PASSED_NULL_PARAMETER);
231         return 0;
232     }
233     if (e->ctrl == NULL
234         || (num = ENGINE_ctrl(e, ENGINE_CTRL_GET_CMD_FROM_NAME,
235                 0, (void *)cmd_name, NULL))
236             <= 0) {
237         /*
238          * If the command didn't *have* to be supported, we fake success.
239          * This allows certain settings to be specified for multiple ENGINEs
240          * and only require a change of ENGINE id (without having to
241          * selectively apply settings). Eg. changing from a hardware device
242          * back to the regular software ENGINE without editing the config
243          * file, etc.
244          */
245         if (cmd_optional) {
246             ERR_clear_error();
247             return 1;
248         }
249         ERR_raise(ERR_LIB_ENGINE, ENGINE_R_INVALID_CMD_NAME);
250         return 0;
251     }
252     if (!ENGINE_cmd_is_executable(e, num)) {
253         ERR_raise(ERR_LIB_ENGINE, ENGINE_R_CMD_NOT_EXECUTABLE);
254         return 0;
255     }
256 
257     flags = ENGINE_ctrl(e, ENGINE_CTRL_GET_CMD_FLAGS, num, NULL, NULL);
258     if (flags < 0) {
259         /*
260          * Shouldn't happen, given that ENGINE_cmd_is_executable() returned
261          * success.
262          */
263         ERR_raise(ERR_LIB_ENGINE, ENGINE_R_INTERNAL_LIST_ERROR);
264         return 0;
265     }
266     /*
267      * If the command takes no input, there must be no input. And vice versa.
268      */
269     if (flags & ENGINE_CMD_FLAG_NO_INPUT) {
270         if (arg != NULL) {
271             ERR_raise(ERR_LIB_ENGINE, ENGINE_R_COMMAND_TAKES_NO_INPUT);
272             return 0;
273         }
274         /*
275          * We deliberately force the result of ENGINE_ctrl() to 0 or 1 rather
276          * than returning it as "return data". This is to ensure usage of
277          * these commands is consistent across applications and that certain
278          * applications don't understand it one way, and others another.
279          */
280         if (ENGINE_ctrl(e, num, 0, (void *)arg, NULL) > 0)
281             return 1;
282         return 0;
283     }
284     /* So, we require input */
285     if (arg == NULL) {
286         ERR_raise(ERR_LIB_ENGINE, ENGINE_R_COMMAND_TAKES_INPUT);
287         return 0;
288     }
289     /* If it takes string input, that's easy */
290     if (flags & ENGINE_CMD_FLAG_STRING) {
291         /* Same explanation as above */
292         if (ENGINE_ctrl(e, num, 0, (void *)arg, NULL) > 0)
293             return 1;
294         return 0;
295     }
296     /*
297      * If it doesn't take numeric either, then it is unsupported for use in a
298      * config-setting situation, which is what this function is for. This
299      * should never happen though, because ENGINE_cmd_is_executable() was
300      * used.
301      */
302     if (!(flags & ENGINE_CMD_FLAG_NUMERIC)) {
303         ERR_raise(ERR_LIB_ENGINE, ENGINE_R_INTERNAL_LIST_ERROR);
304         return 0;
305     }
306     l = strtol(arg, &ptr, 10);
307     if ((arg == ptr) || (*ptr != '\0')) {
308         ERR_raise(ERR_LIB_ENGINE, ENGINE_R_ARGUMENT_IS_NOT_A_NUMBER);
309         return 0;
310     }
311     /*
312      * Force the result of the control command to 0 or 1, for the reasons
313      * mentioned before.
314      */
315     if (ENGINE_ctrl(e, num, l, NULL, NULL) > 0)
316         return 1;
317     return 0;
318 }
319