]>
Commit | Line | Data |
---|---|---|
b1322259 RS |
1 | /* |
2 | * Copyright 2001-2016 The OpenSSL Project Authors. All Rights Reserved. | |
b6d1e52d | 3 | * |
3c120f91 | 4 | * Licensed under the Apache License 2.0 (the "License"). You may not use |
b1322259 RS |
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 | |
b6d1e52d GT |
8 | */ |
9 | ||
b6d1e52d | 10 | #include "eng_int.h" |
b6d1e52d | 11 | |
0f113f3e MC |
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 | */ | |
b6d1e52d GT |
16 | static const char *int_no_description = ""; |
17 | ||
0f113f3e MC |
18 | /* |
19 | * These internal functions handle 'CMD'-related control commands when the | |
b6d1e52d | 20 | * ENGINE in question has asked us to take care of it (ie. the ENGINE did not |
0f113f3e MC |
21 | * set the ENGINE_FLAGS_MANUAL_CMD_CTRL flag. |
22 | */ | |
b6d1e52d GT |
23 | |
24 | static int int_ctrl_cmd_is_null(const ENGINE_CMD_DEFN *defn) | |
0f113f3e MC |
25 | { |
26 | if ((defn->cmd_num == 0) || (defn->cmd_name == NULL)) | |
27 | return 1; | |
28 | return 0; | |
29 | } | |
b6d1e52d GT |
30 | |
31 | static int int_ctrl_cmd_by_name(const ENGINE_CMD_DEFN *defn, const char *s) | |
0f113f3e MC |
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 | } | |
b6d1e52d GT |
43 | |
44 | static int int_ctrl_cmd_by_num(const ENGINE_CMD_DEFN *defn, unsigned int num) | |
0f113f3e MC |
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 | } | |
b6d1e52d | 60 | |
41a15c4f | 61 | static int int_ctrl_helper(ENGINE *e, int cmd, long i, void *p, |
0f113f3e MC |
62 | void (*f) (void)) |
63 | { | |
64 | int idx; | |
65 | char *s = (char *)p; | |
b5fe5dfb RS |
66 | const ENGINE_CMD_DEFN *cdp; |
67 | ||
0f113f3e MC |
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 | /* | |
0d4fb843 | 93 | * For the rest of the commands, the 'long' argument must specify a valid |
0f113f3e MC |
94 | * command number - so we need to conduct a search. |
95 | */ | |
b5fe5dfb RS |
96 | if ((e->cmd_defns == NULL) |
97 | || ((idx = int_ctrl_cmd_by_num(e->cmd_defns, (unsigned int)i)) < 0)) { | |
0f113f3e MC |
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 */ | |
b5fe5dfb | 102 | cdp = &e->cmd_defns[idx]; |
0f113f3e MC |
103 | switch (cmd) { |
104 | case ENGINE_CTRL_GET_NEXT_CMD_TYPE: | |
b5fe5dfb RS |
105 | cdp++; |
106 | return int_ctrl_cmd_is_null(cdp) ? 0 : cdp->cmd_num; | |
0f113f3e | 107 | case ENGINE_CTRL_GET_NAME_LEN_FROM_CMD: |
b5fe5dfb | 108 | return strlen(cdp->cmd_name); |
0f113f3e | 109 | case ENGINE_CTRL_GET_NAME_FROM_CMD: |
b5fe5dfb | 110 | return strlen(strcpy(s, cdp->cmd_name)); |
0f113f3e | 111 | case ENGINE_CTRL_GET_DESC_LEN_FROM_CMD: |
b5fe5dfb RS |
112 | return strlen(cdp->cmd_desc == NULL ? int_no_description |
113 | : cdp->cmd_desc); | |
0f113f3e | 114 | case ENGINE_CTRL_GET_DESC_FROM_CMD: |
b5fe5dfb RS |
115 | return strlen(strcpy(s, cdp->cmd_desc == NULL ? int_no_description |
116 | : cdp->cmd_desc)); | |
0f113f3e | 117 | case ENGINE_CTRL_GET_CMD_FLAGS: |
b5fe5dfb | 118 | return cdp->cmd_flags; |
0f113f3e MC |
119 | } |
120 | /* Shouldn't really be here ... */ | |
121 | ENGINEerr(ENGINE_F_INT_CTRL_HELPER, ENGINE_R_INTERNAL_LIST_ERROR); | |
122 | return -1; | |
123 | } | |
b6d1e52d | 124 | |
0f113f3e MC |
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 | } | |
40e068d5 | 132 | CRYPTO_THREAD_write_lock(global_engine_lock); |
0f113f3e | 133 | ref_exists = ((e->struct_ref > 0) ? 1 : 0); |
40e068d5 | 134 | CRYPTO_THREAD_unlock(global_engine_lock); |
0f113f3e MC |
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 | } | |
b6d1e52d GT |
176 | |
177 | int ENGINE_cmd_is_executable(ENGINE *e, int cmd) | |
0f113f3e MC |
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 | } | |
b6d1e52d GT |
192 | |
193 | int ENGINE_ctrl_cmd(ENGINE *e, const char *cmd_name, | |
0f113f3e MC |
194 | long i, void *p, void (*f) (void), int cmd_optional) |
195 | { | |
196 | int num; | |
b6d1e52d | 197 | |
700b8145 | 198 | if (e == NULL || cmd_name == NULL) { |
0f113f3e | 199 | ENGINEerr(ENGINE_F_ENGINE_CTRL_CMD, ERR_R_PASSED_NULL_PARAMETER); |
b6d1e52d | 200 | return 0; |
0f113f3e | 201 | } |
700b8145 F |
202 | if (e->ctrl == NULL |
203 | || (num = ENGINE_ctrl(e, ENGINE_CTRL_GET_CMD_FROM_NAME, | |
204 | 0, (void *)cmd_name, NULL)) <= 0) { | |
0f113f3e MC |
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; | |
b6d1e52d | 216 | } |
0f113f3e MC |
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 | } | |
b6d1e52d GT |
228 | |
229 | int ENGINE_ctrl_cmd_string(ENGINE *e, const char *cmd_name, const char *arg, | |
0f113f3e MC |
230 | int cmd_optional) |
231 | { | |
232 | int num, flags; | |
233 | long l; | |
234 | char *ptr; | |
b4bb825f | 235 | |
700b8145 F |
236 | if (e == NULL || cmd_name == NULL) { |
237 | ENGINEerr(ENGINE_F_ENGINE_CTRL_CMD_STRING, ERR_R_PASSED_NULL_PARAMETER); | |
0f113f3e MC |
238 | return 0; |
239 | } | |
700b8145 | 240 | if (e->ctrl == NULL |
b4bb825f F |
241 | || (num = ENGINE_ctrl(e, ENGINE_CTRL_GET_CMD_FROM_NAME, |
242 | 0, (void *)cmd_name, NULL)) <= 0) { | |
0f113f3e MC |
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 | } | |
b4bb825f F |
263 | |
264 | flags = ENGINE_ctrl(e, ENGINE_CTRL_GET_CMD_FLAGS, num, NULL, NULL); | |
265 | if (flags < 0) { | |
0f113f3e MC |
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 | } |