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