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