Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
freebsd
GitHub Repository: freebsd/freebsd-src
Path: blob/main/crypto/openssl/apps/engine.c
103341 views
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
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
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
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
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
};
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
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
509