1 /*
2 * Copyright 2000-2025 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 <openssl/opensslconf.h>
14
15 #include "apps.h"
16 #include "progs.h"
17 #include <stdio.h>
18 #include <stdlib.h>
19 #include <string.h>
20 #include <openssl/err.h>
21 #include <openssl/engine.h>
22 #include <openssl/ssl.h>
23 #include <openssl/store.h>
24
25 typedef enum OPTION_choice {
26 OPT_COMMON,
27 OPT_C,
28 OPT_T,
29 OPT_TT,
30 OPT_PRE,
31 OPT_POST,
32 OPT_V = 100,
33 OPT_VV,
34 OPT_VVV,
35 OPT_VVVV
36 } OPTION_CHOICE;
37
38 const OPTIONS engine_options[] = {
39 { OPT_HELP_STR, 1, '-', "Usage: %s [options] engine...\n" },
40
41 OPT_SECTION("General"),
42 { "help", OPT_HELP, '-', "Display this summary" },
43 { "t", OPT_T, '-', "Check that specified engine is available" },
44 { "pre", OPT_PRE, 's', "Run command against the ENGINE before loading it" },
45 { "post", OPT_POST, 's', "Run command against the ENGINE after loading it" },
46
47 OPT_SECTION("Output"),
48 { "v", OPT_V, '-', "List 'control commands' For each specified engine" },
49 { "vv", OPT_VV, '-', "Also display each command's description" },
50 { "vvv", OPT_VVV, '-', "Also add the input flags for each command" },
51 { "vvvv", OPT_VVVV, '-', "Also show internal input flags" },
52 { "c", OPT_C, '-', "List the capabilities of specified engine" },
53 { "tt", OPT_TT, '-', "Display error trace for unavailable engines" },
54 { OPT_MORE_STR, OPT_EOF, 1,
55 "Commands are like \"SO_PATH:/lib/libdriver.so\"" },
56
57 OPT_PARAMETERS(),
58 { "engine", 0, 0, "ID of engine(s) to load" },
59 { NULL }
60 };
61
append_buf(char ** buf,int * size,const char * s)62 static int append_buf(char **buf, int *size, const char *s)
63 {
64 const int expand = 256;
65 int len = strlen(s) + 1;
66 char *p = *buf;
67
68 if (p == NULL) {
69 *size = ((len + expand - 1) / expand) * expand;
70 p = *buf = app_malloc(*size, "engine buffer");
71 } else {
72 const int blen = strlen(p);
73
74 if (blen > 0)
75 len += 2 + blen;
76
77 if (len > *size) {
78 *size = ((len + expand - 1) / expand) * expand;
79 p = OPENSSL_realloc(p, *size);
80 if (p == NULL) {
81 OPENSSL_free(*buf);
82 *buf = NULL;
83 return 0;
84 }
85 *buf = p;
86 }
87
88 if (blen > 0) {
89 p += blen;
90 *p++ = ',';
91 *p++ = ' ';
92 }
93 }
94
95 strcpy(p, s);
96 return 1;
97 }
98
util_flags(BIO * out,unsigned int flags,const char * indent)99 static int util_flags(BIO *out, unsigned int flags, const char *indent)
100 {
101 int started = 0, err = 0;
102 /* Indent before displaying input flags */
103 BIO_printf(out, "%s%s(input flags): ", indent, indent);
104 if (flags == 0) {
105 BIO_printf(out, "<no flags>\n");
106 return 1;
107 }
108 /*
109 * If the object is internal, mark it in a way that shows instead of
110 * having it part of all the other flags, even if it really is.
111 */
112 if (flags & ENGINE_CMD_FLAG_INTERNAL) {
113 BIO_printf(out, "[Internal] ");
114 }
115
116 if (flags & ENGINE_CMD_FLAG_NUMERIC) {
117 BIO_printf(out, "NUMERIC");
118 started = 1;
119 }
120 /*
121 * Now we check that no combinations of the mutually exclusive NUMERIC,
122 * STRING, and NO_INPUT flags have been used. Future flags that can be
123 * OR'd together with these would need to added after these to preserve
124 * the testing logic.
125 */
126 if (flags & ENGINE_CMD_FLAG_STRING) {
127 if (started) {
128 BIO_printf(out, "|");
129 err = 1;
130 }
131 BIO_printf(out, "STRING");
132 started = 1;
133 }
134 if (flags & ENGINE_CMD_FLAG_NO_INPUT) {
135 if (started) {
136 BIO_printf(out, "|");
137 err = 1;
138 }
139 BIO_printf(out, "NO_INPUT");
140 started = 1;
141 }
142 /* Check for unknown flags */
143 flags = flags & ~ENGINE_CMD_FLAG_NUMERIC & ~ENGINE_CMD_FLAG_STRING & ~ENGINE_CMD_FLAG_NO_INPUT & ~ENGINE_CMD_FLAG_INTERNAL;
144 if (flags) {
145 if (started)
146 BIO_printf(out, "|");
147 BIO_printf(out, "<0x%04X>", flags);
148 }
149 if (err)
150 BIO_printf(out, " <illegal flags!>");
151 BIO_printf(out, "\n");
152 return 1;
153 }
154
util_verbose(ENGINE * e,int verbose,BIO * out,const char * indent)155 static int util_verbose(ENGINE *e, int verbose, BIO *out, const char *indent)
156 {
157 static const int line_wrap = 78;
158 int num;
159 int ret = 0;
160 char *name = NULL;
161 char *desc = NULL;
162 int flags;
163 int xpos = 0;
164 STACK_OF(OPENSSL_STRING) *cmds = NULL;
165 if (!ENGINE_ctrl(e, ENGINE_CTRL_HAS_CTRL_FUNCTION, 0, NULL, NULL) || ((num = ENGINE_ctrl(e, ENGINE_CTRL_GET_FIRST_CMD_TYPE, 0, NULL, NULL)) <= 0)) {
166 return 1;
167 }
168
169 cmds = sk_OPENSSL_STRING_new_null();
170 if (cmds == NULL)
171 goto err;
172
173 do {
174 int len;
175 /* Get the command input flags */
176 if ((flags = ENGINE_ctrl(e, ENGINE_CTRL_GET_CMD_FLAGS, num,
177 NULL, NULL))
178 < 0)
179 goto err;
180 if (!(flags & ENGINE_CMD_FLAG_INTERNAL) || verbose >= 4) {
181 /* Get the command name */
182 if ((len = ENGINE_ctrl(e, ENGINE_CTRL_GET_NAME_LEN_FROM_CMD, num,
183 NULL, NULL))
184 <= 0)
185 goto err;
186 name = app_malloc(len + 1, "name buffer");
187 if (ENGINE_ctrl(e, ENGINE_CTRL_GET_NAME_FROM_CMD, num, name,
188 NULL)
189 <= 0)
190 goto err;
191 /* Get the command description */
192 if ((len = ENGINE_ctrl(e, ENGINE_CTRL_GET_DESC_LEN_FROM_CMD, num,
193 NULL, NULL))
194 < 0)
195 goto err;
196 if (len > 0) {
197 desc = app_malloc(len + 1, "description buffer");
198 if (ENGINE_ctrl(e, ENGINE_CTRL_GET_DESC_FROM_CMD, num, desc,
199 NULL)
200 <= 0)
201 goto err;
202 }
203 /* Now decide on the output */
204 if (xpos == 0)
205 /* Do an indent */
206 xpos = BIO_puts(out, indent);
207 else
208 /* Otherwise prepend a ", " */
209 xpos += BIO_printf(out, ", ");
210 if (verbose == 1) {
211 /*
212 * We're just listing names, comma-delimited
213 */
214 if ((xpos > (int)strlen(indent)) && (xpos + (int)strlen(name) > line_wrap)) {
215 BIO_printf(out, "\n");
216 xpos = BIO_puts(out, indent);
217 }
218 xpos += BIO_printf(out, "%s", name);
219 } else {
220 /* We're listing names plus descriptions */
221 BIO_printf(out, "%s: %s\n", name,
222 (desc == NULL) ? "<no description>" : desc);
223 /* ... and sometimes input flags */
224 if ((verbose >= 3) && !util_flags(out, flags, indent))
225 goto err;
226 xpos = 0;
227 }
228 }
229 OPENSSL_free(name);
230 name = NULL;
231 OPENSSL_free(desc);
232 desc = NULL;
233 /* Move to the next command */
234 num = ENGINE_ctrl(e, ENGINE_CTRL_GET_NEXT_CMD_TYPE, num, NULL, NULL);
235 } while (num > 0);
236 if (xpos > 0)
237 BIO_printf(out, "\n");
238 ret = 1;
239 err:
240 sk_OPENSSL_STRING_free(cmds);
241 OPENSSL_free(name);
242 OPENSSL_free(desc);
243 return ret;
244 }
245
util_do_cmds(ENGINE * e,STACK_OF (OPENSSL_STRING)* cmds,BIO * out,const char * indent)246 static void util_do_cmds(ENGINE *e, STACK_OF(OPENSSL_STRING) *cmds,
247 BIO *out, const char *indent)
248 {
249 int loop, res, num = sk_OPENSSL_STRING_num(cmds);
250
251 if (num < 0) {
252 BIO_printf(out, "[Error]: internal stack error\n");
253 return;
254 }
255 for (loop = 0; loop < num; loop++) {
256 char buf[256];
257 const char *cmd, *arg;
258 cmd = sk_OPENSSL_STRING_value(cmds, loop);
259 res = 1; /* assume success */
260 /* Check if this command has no ":arg" */
261 if ((arg = strchr(cmd, ':')) == NULL) {
262 if (!ENGINE_ctrl_cmd_string(e, cmd, NULL, 0))
263 res = 0;
264 } else {
265 if ((int)(arg - cmd) > 254) {
266 BIO_printf(out, "[Error]: command name too long\n");
267 return;
268 }
269 memcpy(buf, cmd, (int)(arg - cmd));
270 buf[arg - cmd] = '\0';
271 arg++; /* Move past the ":" */
272 /* Call the command with the argument */
273 if (!ENGINE_ctrl_cmd_string(e, buf, arg, 0))
274 res = 0;
275 }
276 if (res) {
277 BIO_printf(out, "[Success]: %s\n", cmd);
278 } else {
279 BIO_printf(out, "[Failure]: %s\n", cmd);
280 ERR_print_errors(out);
281 }
282 }
283 }
284
285 struct util_store_cap_data {
286 ENGINE *engine;
287 char **cap_buf;
288 int *cap_size;
289 int ok;
290 };
util_store_cap(const OSSL_STORE_LOADER * loader,void * arg)291 static void util_store_cap(const OSSL_STORE_LOADER *loader, void *arg)
292 {
293 struct util_store_cap_data *ctx = arg;
294
295 if (OSSL_STORE_LOADER_get0_engine(loader) == ctx->engine) {
296 char buf[256];
297 BIO_snprintf(buf, sizeof(buf), "STORE(%s)",
298 OSSL_STORE_LOADER_get0_scheme(loader));
299 if (!append_buf(ctx->cap_buf, ctx->cap_size, buf))
300 ctx->ok = 0;
301 }
302 }
303
engine_main(int argc,char ** argv)304 int engine_main(int argc, char **argv)
305 {
306 int ret = 1, i;
307 int verbose = 0, list_cap = 0, test_avail = 0, test_avail_noise = 0;
308 ENGINE *e;
309 STACK_OF(OPENSSL_CSTRING) *engines = sk_OPENSSL_CSTRING_new_null();
310 STACK_OF(OPENSSL_STRING) *pre_cmds = sk_OPENSSL_STRING_new_null();
311 STACK_OF(OPENSSL_STRING) *post_cmds = sk_OPENSSL_STRING_new_null();
312 BIO *out;
313 const char *indent = " ";
314 OPTION_CHOICE o;
315 char *prog;
316 char *argv1;
317
318 out = dup_bio_out(FORMAT_TEXT);
319 if (engines == NULL || pre_cmds == NULL || post_cmds == NULL)
320 goto end;
321
322 /* Remember the original command name, parse/skip any leading engine
323 * names, and then setup to parse the rest of the line as flags. */
324 prog = argv[0];
325 while ((argv1 = argv[1]) != NULL && *argv1 != '-') {
326 if (!sk_OPENSSL_CSTRING_push(engines, argv1))
327 goto end;
328 argc--;
329 argv++;
330 }
331 argv[0] = prog;
332 opt_init(argc, argv, engine_options);
333
334 while ((o = opt_next()) != OPT_EOF) {
335 switch (o) {
336 case OPT_EOF:
337 case OPT_ERR:
338 BIO_printf(bio_err, "%s: Use -help for summary.\n", prog);
339 goto end;
340 case OPT_HELP:
341 opt_help(engine_options);
342 ret = 0;
343 goto end;
344 case OPT_VVVV:
345 case OPT_VVV:
346 case OPT_VV:
347 case OPT_V:
348 /* Convert to an integer from one to four. */
349 i = (int)(o - OPT_V) + 1;
350 if (verbose < i)
351 verbose = i;
352 break;
353 case OPT_C:
354 list_cap = 1;
355 break;
356 case OPT_TT:
357 test_avail_noise++;
358 /* fall through */
359 case OPT_T:
360 test_avail++;
361 break;
362 case OPT_PRE:
363 if (sk_OPENSSL_STRING_push(pre_cmds, opt_arg()) <= 0)
364 goto end;
365 break;
366 case OPT_POST:
367 if (sk_OPENSSL_STRING_push(post_cmds, opt_arg()) <= 0)
368 goto end;
369 break;
370 }
371 }
372
373 /* Any remaining arguments are engine names. */
374 argc = opt_num_rest();
375 argv = opt_rest();
376 for (; *argv; argv++) {
377 if (**argv == '-') {
378 BIO_printf(bio_err, "%s: Cannot mix flags and engine names.\n",
379 prog);
380 BIO_printf(bio_err, "%s: Use -help for summary.\n", prog);
381 goto end;
382 }
383 if (!sk_OPENSSL_CSTRING_push(engines, *argv))
384 goto end;
385 }
386
387 if (sk_OPENSSL_CSTRING_num(engines) == 0) {
388 for (e = ENGINE_get_first(); e != NULL; e = ENGINE_get_next(e)) {
389 if (!sk_OPENSSL_CSTRING_push(engines, ENGINE_get_id(e)))
390 goto end;
391 }
392 }
393
394 ret = 0;
395 for (i = 0; i < sk_OPENSSL_CSTRING_num(engines); i++) {
396 const char *id = sk_OPENSSL_CSTRING_value(engines, i);
397 if ((e = ENGINE_by_id(id)) != NULL) {
398 const char *name = ENGINE_get_name(e);
399 /*
400 * Do "id" first, then "name". Easier to auto-parse.
401 */
402 BIO_printf(out, "(%s) %s\n", id, name);
403 util_do_cmds(e, pre_cmds, out, indent);
404 if (strcmp(ENGINE_get_id(e), id) != 0) {
405 BIO_printf(out, "Loaded: (%s) %s\n",
406 ENGINE_get_id(e), ENGINE_get_name(e));
407 }
408 if (list_cap) {
409 int cap_size = 256;
410 char *cap_buf = NULL;
411 int k, n;
412 const int *nids;
413 ENGINE_CIPHERS_PTR fn_c;
414 ENGINE_DIGESTS_PTR fn_d;
415 ENGINE_PKEY_METHS_PTR fn_pk;
416
417 if (ENGINE_get_RSA(e) != NULL
418 && !append_buf(&cap_buf, &cap_size, "RSA"))
419 goto end;
420 if (ENGINE_get_EC(e) != NULL
421 && !append_buf(&cap_buf, &cap_size, "EC"))
422 goto end;
423 if (ENGINE_get_DSA(e) != NULL
424 && !append_buf(&cap_buf, &cap_size, "DSA"))
425 goto end;
426 if (ENGINE_get_DH(e) != NULL
427 && !append_buf(&cap_buf, &cap_size, "DH"))
428 goto end;
429 if (ENGINE_get_RAND(e) != NULL
430 && !append_buf(&cap_buf, &cap_size, "RAND"))
431 goto end;
432
433 fn_c = ENGINE_get_ciphers(e);
434 if (fn_c == NULL)
435 goto skip_ciphers;
436 n = fn_c(e, NULL, &nids, 0);
437 for (k = 0; k < n; ++k)
438 if (!append_buf(&cap_buf, &cap_size, OBJ_nid2sn(nids[k])))
439 goto end;
440
441 skip_ciphers:
442 fn_d = ENGINE_get_digests(e);
443 if (fn_d == NULL)
444 goto skip_digests;
445 n = fn_d(e, NULL, &nids, 0);
446 for (k = 0; k < n; ++k)
447 if (!append_buf(&cap_buf, &cap_size, OBJ_nid2sn(nids[k])))
448 goto end;
449
450 skip_digests:
451 fn_pk = ENGINE_get_pkey_meths(e);
452 if (fn_pk == NULL)
453 goto skip_pmeths;
454 n = fn_pk(e, NULL, &nids, 0);
455 for (k = 0; k < n; ++k)
456 if (!append_buf(&cap_buf, &cap_size, OBJ_nid2sn(nids[k])))
457 goto end;
458 skip_pmeths: {
459 struct util_store_cap_data store_ctx;
460
461 store_ctx.engine = e;
462 store_ctx.cap_buf = &cap_buf;
463 store_ctx.cap_size = &cap_size;
464 store_ctx.ok = 1;
465
466 OSSL_STORE_do_all_loaders(util_store_cap, &store_ctx);
467 if (!store_ctx.ok)
468 goto end;
469 }
470 if (cap_buf != NULL && (*cap_buf != '\0'))
471 BIO_printf(out, " [%s]\n", cap_buf);
472
473 OPENSSL_free(cap_buf);
474 }
475 if (test_avail) {
476 BIO_printf(out, "%s", indent);
477 if (ENGINE_init(e)) {
478 BIO_printf(out, "[ available ]\n");
479 util_do_cmds(e, post_cmds, out, indent);
480 ENGINE_finish(e);
481 } else {
482 BIO_printf(out, "[ unavailable ]\n");
483 if (test_avail_noise)
484 ERR_print_errors_fp(stdout);
485 ERR_clear_error();
486 }
487 }
488 if ((verbose > 0) && !util_verbose(e, verbose, out, indent))
489 goto end;
490 ENGINE_free(e);
491 } else {
492 ERR_print_errors(bio_err);
493 /* because exit codes above 127 have special meaning on Unix */
494 if (++ret > 127)
495 ret = 127;
496 }
497 }
498
499 end:
500
501 ERR_print_errors(bio_err);
502 sk_OPENSSL_CSTRING_free(engines);
503 sk_OPENSSL_STRING_free(pre_cmds);
504 sk_OPENSSL_STRING_free(post_cmds);
505 BIO_free_all(out);
506 return ret;
507 }
508