xref: /freebsd/crypto/openssl/apps/engine.c (revision f25b8c9fb4f58cf61adb47d7570abe7caa6d385d)
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