aboutsummaryrefslogtreecommitdiff
path: root/apps/lib
diff options
context:
space:
mode:
Diffstat (limited to 'apps/lib')
-rw-r--r--apps/lib/app_libctx.c48
-rw-r--r--apps/lib/app_params.c132
-rw-r--r--apps/lib/app_provider.c92
-rw-r--r--apps/lib/app_rand.c124
-rw-r--r--apps/lib/app_x509.c137
-rw-r--r--apps/lib/apps.c3400
-rw-r--r--apps/lib/apps_ui.c223
-rw-r--r--apps/lib/build.info23
-rw-r--r--apps/lib/cmp_mock_srv.c452
-rw-r--r--apps/lib/columns.c27
-rw-r--r--apps/lib/engine.c193
-rw-r--r--apps/lib/engine_loader.c203
-rw-r--r--apps/lib/fmt.c15
-rw-r--r--apps/lib/http_server.c536
-rw-r--r--apps/lib/names.c45
-rw-r--r--apps/lib/opt.c1203
-rw-r--r--apps/lib/s_cb.c1592
-rw-r--r--apps/lib/s_socket.c462
-rw-r--r--apps/lib/tlssrp_depr.c231
-rw-r--r--apps/lib/vms_decc_argv.c72
-rw-r--r--apps/lib/vms_term_sock.c591
-rw-r--r--apps/lib/win32_init.c307
22 files changed, 10108 insertions, 0 deletions
diff --git a/apps/lib/app_libctx.c b/apps/lib/app_libctx.c
new file mode 100644
index 000000000000..4b9ec40e8527
--- /dev/null
+++ b/apps/lib/app_libctx.c
@@ -0,0 +1,48 @@
+/*
+ * Copyright 1995-2021 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the Apache License 2.0 (the "License"). You may not use
+ * this file except in compliance with the License. You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+#include "app_libctx.h"
+#include "apps.h"
+
+static OSSL_LIB_CTX *app_libctx = NULL;
+static const char *app_propq = NULL;
+
+int app_set_propq(const char *arg)
+{
+ app_propq = arg;
+ return 1;
+}
+
+const char *app_get0_propq(void)
+{
+ return app_propq;
+}
+
+OSSL_LIB_CTX *app_get0_libctx(void)
+{
+ return app_libctx;
+}
+
+OSSL_LIB_CTX *app_create_libctx(void)
+{
+ /*
+ * Load the NULL provider into the default library context and create a
+ * library context which will then be used for any OPT_PROV options.
+ */
+ if (app_libctx == NULL) {
+ if (!app_provider_load(NULL, "null")) {
+ opt_printf_stderr( "Failed to create null provider\n");
+ return NULL;
+ }
+ app_libctx = OSSL_LIB_CTX_new();
+ }
+ if (app_libctx == NULL)
+ opt_printf_stderr("Failed to create library context\n");
+ return app_libctx;
+}
+
diff --git a/apps/lib/app_params.c b/apps/lib/app_params.c
new file mode 100644
index 000000000000..95e1298ee926
--- /dev/null
+++ b/apps/lib/app_params.c
@@ -0,0 +1,132 @@
+/*
+ * Copyright 2019-2020 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the Apache License 2.0 (the "License"). You may not use
+ * this file except in compliance with the License. You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include "apps.h"
+#include "app_params.h"
+
+static int describe_param_type(char *buf, size_t bufsz, const OSSL_PARAM *param)
+{
+ const char *type_mod = "";
+ const char *type = NULL;
+ int show_type_number = 0;
+ int printed_len;
+
+ switch (param->data_type) {
+ case OSSL_PARAM_UNSIGNED_INTEGER:
+ type_mod = "unsigned ";
+ /* FALLTHRU */
+ case OSSL_PARAM_INTEGER:
+ type = "integer";
+ break;
+ case OSSL_PARAM_UTF8_PTR:
+ type_mod = "pointer to a ";
+ /* FALLTHRU */
+ case OSSL_PARAM_UTF8_STRING:
+ type = "UTF8 encoded string";
+ break;
+ case OSSL_PARAM_OCTET_PTR:
+ type_mod = "pointer to an ";
+ /* FALLTHRU */
+ case OSSL_PARAM_OCTET_STRING:
+ type = "octet string";
+ break;
+ default:
+ type = "unknown type";
+ show_type_number = 1;
+ break;
+ }
+
+ printed_len = BIO_snprintf(buf, bufsz, "%s: ", param->key);
+ if (printed_len > 0) {
+ buf += printed_len;
+ bufsz -= printed_len;
+ }
+ printed_len = BIO_snprintf(buf, bufsz, "%s%s", type_mod, type);
+ if (printed_len > 0) {
+ buf += printed_len;
+ bufsz -= printed_len;
+ }
+ if (show_type_number) {
+ printed_len = BIO_snprintf(buf, bufsz, " [%d]", param->data_type);
+ if (printed_len > 0) {
+ buf += printed_len;
+ bufsz -= printed_len;
+ }
+ }
+ if (param->data_size == 0)
+ printed_len = BIO_snprintf(buf, bufsz, " (arbitrary size)");
+ else
+ printed_len = BIO_snprintf(buf, bufsz, " (max %zu bytes large)",
+ param->data_size);
+ if (printed_len > 0) {
+ buf += printed_len;
+ bufsz -= printed_len;
+ }
+ *buf = '\0';
+ return 1;
+}
+
+int print_param_types(const char *thing, const OSSL_PARAM *pdefs, int indent)
+{
+ if (pdefs == NULL) {
+ return 1;
+ } else if (pdefs->key == NULL) {
+ /*
+ * An empty list? This shouldn't happen, but let's just make sure to
+ * say something if there's a badly written provider...
+ */
+ BIO_printf(bio_out, "%*sEmpty list of %s (!!!)\n", indent, "", thing);
+ } else {
+ BIO_printf(bio_out, "%*s%s:\n", indent, "", thing);
+ for (; pdefs->key != NULL; pdefs++) {
+ char buf[200]; /* This should be ample space */
+
+ describe_param_type(buf, sizeof(buf), pdefs);
+ BIO_printf(bio_out, "%*s %s\n", indent, "", buf);
+ }
+ }
+ return 1;
+}
+
+void print_param_value(const OSSL_PARAM *p, int indent)
+{
+ int64_t i;
+ uint64_t u;
+
+ printf("%*s%s: ", indent, "", p->key);
+ switch (p->data_type) {
+ case OSSL_PARAM_UNSIGNED_INTEGER:
+ if (OSSL_PARAM_get_uint64(p, &u))
+ BIO_printf(bio_out, "%llu\n", (unsigned long long int)u);
+ else
+ BIO_printf(bio_out, "error getting value\n");
+ break;
+ case OSSL_PARAM_INTEGER:
+ if (OSSL_PARAM_get_int64(p, &i))
+ BIO_printf(bio_out, "%lld\n", (long long int)i);
+ else
+ BIO_printf(bio_out, "error getting value\n");
+ break;
+ case OSSL_PARAM_UTF8_PTR:
+ BIO_printf(bio_out, "'%s'\n", *(char **)(p->data));
+ break;
+ case OSSL_PARAM_UTF8_STRING:
+ BIO_printf(bio_out, "'%s'\n", (char *)p->data);
+ break;
+ case OSSL_PARAM_OCTET_PTR:
+ case OSSL_PARAM_OCTET_STRING:
+ BIO_printf(bio_out, "<%zu bytes>\n", p->data_size);
+ break;
+ default:
+ BIO_printf(bio_out, "unknown type (%u) of %zu bytes\n",
+ p->data_type, p->data_size);
+ break;
+ }
+}
+
diff --git a/apps/lib/app_provider.c b/apps/lib/app_provider.c
new file mode 100644
index 000000000000..63f78ae07d80
--- /dev/null
+++ b/apps/lib/app_provider.c
@@ -0,0 +1,92 @@
+/*
+ * Copyright 2020-2021 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the Apache License 2.0 (the "License"). You may not use
+ * this file except in compliance with the License. You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include "apps.h"
+#include <string.h>
+#include <openssl/err.h>
+#include <openssl/provider.h>
+#include <openssl/safestack.h>
+
+/* Non-zero if any of the provider options have been seen */
+static int provider_option_given = 0;
+
+DEFINE_STACK_OF(OSSL_PROVIDER)
+
+/*
+ * See comments in opt_verify for explanation of this.
+ */
+enum prov_range { OPT_PROV_ENUM };
+
+static STACK_OF(OSSL_PROVIDER) *app_providers = NULL;
+
+static void provider_free(OSSL_PROVIDER *prov)
+{
+ OSSL_PROVIDER_unload(prov);
+}
+
+int app_provider_load(OSSL_LIB_CTX *libctx, const char *provider_name)
+{
+ OSSL_PROVIDER *prov;
+
+ prov = OSSL_PROVIDER_load(libctx, provider_name);
+ if (prov == NULL) {
+ opt_printf_stderr("%s: unable to load provider %s\n"
+ "Hint: use -provider-path option or OPENSSL_MODULES environment variable.\n",
+ opt_getprog(), provider_name);
+ ERR_print_errors(bio_err);
+ return 0;
+ }
+ if (app_providers == NULL)
+ app_providers = sk_OSSL_PROVIDER_new_null();
+ if (app_providers == NULL
+ || !sk_OSSL_PROVIDER_push(app_providers, prov)) {
+ app_providers_cleanup();
+ return 0;
+ }
+ return 1;
+}
+
+void app_providers_cleanup(void)
+{
+ sk_OSSL_PROVIDER_pop_free(app_providers, provider_free);
+ app_providers = NULL;
+}
+
+static int opt_provider_path(const char *path)
+{
+ if (path != NULL && *path == '\0')
+ path = NULL;
+ return OSSL_PROVIDER_set_default_search_path(app_get0_libctx(), path);
+}
+
+int opt_provider(int opt)
+{
+ const int given = provider_option_given;
+
+ provider_option_given = 1;
+ switch ((enum prov_range)opt) {
+ case OPT_PROV__FIRST:
+ case OPT_PROV__LAST:
+ return 1;
+ case OPT_PROV_PROVIDER:
+ return app_provider_load(app_get0_libctx(), opt_arg());
+ case OPT_PROV_PROVIDER_PATH:
+ return opt_provider_path(opt_arg());
+ case OPT_PROV_PROPQUERY:
+ return app_set_propq(opt_arg());
+ }
+ /* Should never get here but if we do, undo what we did earlier */
+ provider_option_given = given;
+ return 0;
+}
+
+int opt_provider_option_given(void)
+{
+ return provider_option_given;
+}
diff --git a/apps/lib/app_rand.c b/apps/lib/app_rand.c
new file mode 100644
index 000000000000..713792ead40a
--- /dev/null
+++ b/apps/lib/app_rand.c
@@ -0,0 +1,124 @@
+/*
+ * Copyright 1995-2021 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the Apache License 2.0 (the "License"). You may not use
+ * this file except in compliance with the License. You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include "apps.h"
+#include <openssl/bio.h>
+#include <openssl/err.h>
+#include <openssl/rand.h>
+#include <openssl/conf.h>
+
+static char *save_rand_file;
+static STACK_OF(OPENSSL_STRING) *randfiles;
+
+void app_RAND_load_conf(CONF *c, const char *section)
+{
+ const char *randfile = NCONF_get_string(c, section, "RANDFILE");
+
+ if (randfile == NULL) {
+ ERR_clear_error();
+ return;
+ }
+ if (RAND_load_file(randfile, -1) < 0) {
+ BIO_printf(bio_err, "Can't load %s into RNG\n", randfile);
+ ERR_print_errors(bio_err);
+ }
+ if (save_rand_file == NULL) {
+ save_rand_file = OPENSSL_strdup(randfile);
+ /* If some internal memory errors have occurred */
+ if (save_rand_file == NULL) {
+ BIO_printf(bio_err, "Can't duplicate %s\n", randfile);
+ ERR_print_errors(bio_err);
+ }
+ }
+}
+
+static int loadfiles(char *name)
+{
+ char *p;
+ int last, ret = 1;
+
+ for ( ; ; ) {
+ last = 0;
+ for (p = name; *p != '\0' && *p != LIST_SEPARATOR_CHAR; p++)
+ continue;
+ if (*p == '\0')
+ last = 1;
+ *p = '\0';
+ if (RAND_load_file(name, -1) < 0) {
+ BIO_printf(bio_err, "Can't load %s into RNG\n", name);
+ ERR_print_errors(bio_err);
+ ret = 0;
+ }
+ if (last)
+ break;
+ name = p + 1;
+ if (*name == '\0')
+ break;
+ }
+ return ret;
+}
+
+int app_RAND_load(void)
+{
+ char *p;
+ int i, ret = 1;
+
+ for (i = 0; i < sk_OPENSSL_STRING_num(randfiles); i++) {
+ p = sk_OPENSSL_STRING_value(randfiles, i);
+ if (!loadfiles(p))
+ ret = 0;
+ }
+ sk_OPENSSL_STRING_free(randfiles);
+ return ret;
+}
+
+int app_RAND_write(void)
+{
+ int ret = 1;
+
+ if (save_rand_file == NULL)
+ return 1;
+ if (RAND_write_file(save_rand_file) == -1) {
+ BIO_printf(bio_err, "Cannot write random bytes:\n");
+ ERR_print_errors(bio_err);
+ ret = 0;
+ }
+ OPENSSL_free(save_rand_file);
+ save_rand_file = NULL;
+ return ret;
+}
+
+
+/*
+ * See comments in opt_verify for explanation of this.
+ */
+enum r_range { OPT_R_ENUM };
+
+int opt_rand(int opt)
+{
+ switch ((enum r_range)opt) {
+ case OPT_R__FIRST:
+ case OPT_R__LAST:
+ break;
+ case OPT_R_RAND:
+ if (randfiles == NULL
+ && (randfiles = sk_OPENSSL_STRING_new_null()) == NULL)
+ return 0;
+ if (!sk_OPENSSL_STRING_push(randfiles, opt_arg()))
+ return 0;
+ break;
+ case OPT_R_WRITERAND:
+ OPENSSL_free(save_rand_file);
+ save_rand_file = OPENSSL_strdup(opt_arg());
+ if (save_rand_file == NULL)
+ return 0;
+ break;
+ }
+ return 1;
+}
diff --git a/apps/lib/app_x509.c b/apps/lib/app_x509.c
new file mode 100644
index 000000000000..f2c22948f201
--- /dev/null
+++ b/apps/lib/app_x509.c
@@ -0,0 +1,137 @@
+/*
+ * Copyright 2020-2021 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the Apache License 2.0 (the "License"). You may not use
+ * this file except in compliance with the License. You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include <string.h>
+#include "apps.h"
+
+/*
+ * X509_ctrl_str() is sorely lacking in libcrypto, but is still needed to
+ * allow the application to process verification options in a manner similar
+ * to signature or other options that pass through EVP_PKEY_CTX_ctrl_str(),
+ * for uniformity.
+ *
+ * As soon as more stuff is added, the code will need serious rework. For
+ * the moment, it only handles the FIPS 196 / SM2 distinguishing ID.
+ */
+#ifdef EVP_PKEY_CTRL_SET1_ID
+static ASN1_OCTET_STRING *mk_octet_string(void *value, size_t value_n)
+{
+ ASN1_OCTET_STRING *v = ASN1_OCTET_STRING_new();
+
+ if (v == NULL) {
+ BIO_printf(bio_err, "error: allocation failed\n");
+ } else if (!ASN1_OCTET_STRING_set(v, value, (int)value_n)) {
+ ASN1_OCTET_STRING_free(v);
+ v = NULL;
+ }
+ return v;
+}
+#endif
+
+static int x509_ctrl(void *object, int cmd, void *value, size_t value_n)
+{
+ switch (cmd) {
+#ifdef EVP_PKEY_CTRL_SET1_ID
+ case EVP_PKEY_CTRL_SET1_ID:
+ {
+ ASN1_OCTET_STRING *v = mk_octet_string(value, value_n);
+
+ if (v == NULL) {
+ BIO_printf(bio_err,
+ "error: setting distinguishing ID in certificate failed\n");
+ return 0;
+ }
+
+ X509_set0_distinguishing_id(object, v);
+ return 1;
+ }
+#endif
+ default:
+ break;
+ }
+ return -2; /* typical EVP_PKEY return for "unsupported" */
+}
+
+static int x509_req_ctrl(void *object, int cmd, void *value, size_t value_n)
+{
+ switch (cmd) {
+#ifdef EVP_PKEY_CTRL_SET1_ID
+ case EVP_PKEY_CTRL_SET1_ID:
+ {
+ ASN1_OCTET_STRING *v = mk_octet_string(value, value_n);
+
+ if (v == NULL) {
+ BIO_printf(bio_err,
+ "error: setting distinguishing ID in certificate signing request failed\n");
+ return 0;
+ }
+
+ X509_REQ_set0_distinguishing_id(object, v);
+ return 1;
+ }
+#endif
+ default:
+ break;
+ }
+ return -2; /* typical EVP_PKEY return for "unsupported" */
+}
+
+static int do_x509_ctrl_string(int (*ctrl)(void *object, int cmd,
+ void *value, size_t value_n),
+ void *object, const char *value)
+{
+ int rv = 0;
+ char *stmp, *vtmp = NULL;
+ size_t vtmp_len = 0;
+ int cmd = 0; /* Will get command values that make sense somehow */
+
+ stmp = OPENSSL_strdup(value);
+ if (stmp == NULL)
+ return -1;
+ vtmp = strchr(stmp, ':');
+ if (vtmp != NULL) {
+ *vtmp = 0;
+ vtmp++;
+ vtmp_len = strlen(vtmp);
+ }
+
+ if (strcmp(stmp, "distid") == 0) {
+#ifdef EVP_PKEY_CTRL_SET1_ID
+ cmd = EVP_PKEY_CTRL_SET1_ID; /* ... except we put it in X509 */
+#endif
+ } else if (strcmp(stmp, "hexdistid") == 0) {
+ if (vtmp != NULL) {
+ void *hexid;
+ long hexid_len = 0;
+
+ hexid = OPENSSL_hexstr2buf((const char *)vtmp, &hexid_len);
+ OPENSSL_free(stmp);
+ stmp = vtmp = hexid;
+ vtmp_len = (size_t)hexid_len;
+ }
+#ifdef EVP_PKEY_CTRL_SET1_ID
+ cmd = EVP_PKEY_CTRL_SET1_ID; /* ... except we put it in X509 */
+#endif
+ }
+
+ rv = ctrl(object, cmd, vtmp, vtmp_len);
+
+ OPENSSL_free(stmp);
+ return rv;
+}
+
+int x509_ctrl_string(X509 *x, const char *value)
+{
+ return do_x509_ctrl_string(x509_ctrl, x, value);
+}
+
+int x509_req_ctrl_string(X509_REQ *x, const char *value)
+{
+ return do_x509_ctrl_string(x509_req_ctrl, x, value);
+}
diff --git a/apps/lib/apps.c b/apps/lib/apps.c
new file mode 100644
index 000000000000..a632b0cff2bf
--- /dev/null
+++ b/apps/lib/apps.c
@@ -0,0 +1,3400 @@
+/*
+ * Copyright 1995-2023 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the Apache License 2.0 (the "License"). You may not use
+ * this file except in compliance with the License. You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#if !defined(_POSIX_C_SOURCE) && defined(OPENSSL_SYS_VMS)
+/*
+ * On VMS, you need to define this to get the declaration of fileno(). The
+ * value 2 is to make sure no function defined in POSIX-2 is left undefined.
+ */
+# define _POSIX_C_SOURCE 2
+#endif
+
+#ifndef OPENSSL_NO_ENGINE
+/* We need to use some deprecated APIs */
+# define OPENSSL_SUPPRESS_DEPRECATED
+# include <openssl/engine.h>
+#endif
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/types.h>
+#ifndef OPENSSL_NO_POSIX_IO
+# include <sys/stat.h>
+# include <fcntl.h>
+#endif
+#include <ctype.h>
+#include <errno.h>
+#include <openssl/err.h>
+#include <openssl/x509.h>
+#include <openssl/x509v3.h>
+#include <openssl/http.h>
+#include <openssl/pem.h>
+#include <openssl/store.h>
+#include <openssl/pkcs12.h>
+#include <openssl/ui.h>
+#include <openssl/safestack.h>
+#include <openssl/rsa.h>
+#include <openssl/rand.h>
+#include <openssl/bn.h>
+#include <openssl/ssl.h>
+#include <openssl/store.h>
+#include <openssl/core_names.h>
+#include "s_apps.h"
+#include "apps.h"
+
+#ifdef _WIN32
+static int WIN32_rename(const char *from, const char *to);
+# define rename(from,to) WIN32_rename((from),(to))
+#endif
+
+#if defined(OPENSSL_SYS_WINDOWS) || defined(OPENSSL_SYS_MSDOS)
+# include <conio.h>
+#endif
+
+#if defined(OPENSSL_SYS_MSDOS) && !defined(_WIN32) || defined(__BORLANDC__)
+# define _kbhit kbhit
+#endif
+
+static BIO *bio_open_default_(const char *filename, char mode, int format,
+ int quiet);
+
+#define PASS_SOURCE_SIZE_MAX 4
+
+DEFINE_STACK_OF(CONF)
+
+typedef struct {
+ const char *name;
+ unsigned long flag;
+ unsigned long mask;
+} NAME_EX_TBL;
+
+static int set_table_opts(unsigned long *flags, const char *arg,
+ const NAME_EX_TBL * in_tbl);
+static int set_multi_opts(unsigned long *flags, const char *arg,
+ const NAME_EX_TBL * in_tbl);
+static
+int load_key_certs_crls_suppress(const char *uri, int format, int maybe_stdin,
+ const char *pass, const char *desc,
+ EVP_PKEY **ppkey, EVP_PKEY **ppubkey,
+ EVP_PKEY **pparams,
+ X509 **pcert, STACK_OF(X509) **pcerts,
+ X509_CRL **pcrl, STACK_OF(X509_CRL) **pcrls,
+ int suppress_decode_errors);
+
+int app_init(long mesgwin);
+
+int chopup_args(ARGS *arg, char *buf)
+{
+ int quoted;
+ char c = '\0', *p = NULL;
+
+ arg->argc = 0;
+ if (arg->size == 0) {
+ arg->size = 20;
+ arg->argv = app_malloc(sizeof(*arg->argv) * arg->size, "argv space");
+ }
+
+ for (p = buf;;) {
+ /* Skip whitespace. */
+ while (*p && isspace(_UC(*p)))
+ p++;
+ if (*p == '\0')
+ break;
+
+ /* The start of something good :-) */
+ if (arg->argc >= arg->size) {
+ char **tmp;
+ arg->size += 20;
+ tmp = OPENSSL_realloc(arg->argv, sizeof(*arg->argv) * arg->size);
+ if (tmp == NULL)
+ return 0;
+ arg->argv = tmp;
+ }
+ quoted = *p == '\'' || *p == '"';
+ if (quoted)
+ c = *p++;
+ arg->argv[arg->argc++] = p;
+
+ /* now look for the end of this */
+ if (quoted) {
+ while (*p && *p != c)
+ p++;
+ *p++ = '\0';
+ } else {
+ while (*p && !isspace(_UC(*p)))
+ p++;
+ if (*p)
+ *p++ = '\0';
+ }
+ }
+ arg->argv[arg->argc] = NULL;
+ return 1;
+}
+
+#ifndef APP_INIT
+int app_init(long mesgwin)
+{
+ return 1;
+}
+#endif
+
+int ctx_set_verify_locations(SSL_CTX *ctx,
+ const char *CAfile, int noCAfile,
+ const char *CApath, int noCApath,
+ const char *CAstore, int noCAstore)
+{
+ if (CAfile == NULL && CApath == NULL && CAstore == NULL) {
+ if (!noCAfile && SSL_CTX_set_default_verify_file(ctx) <= 0)
+ return 0;
+ if (!noCApath && SSL_CTX_set_default_verify_dir(ctx) <= 0)
+ return 0;
+ if (!noCAstore && SSL_CTX_set_default_verify_store(ctx) <= 0)
+ return 0;
+
+ return 1;
+ }
+
+ if (CAfile != NULL && !SSL_CTX_load_verify_file(ctx, CAfile))
+ return 0;
+ if (CApath != NULL && !SSL_CTX_load_verify_dir(ctx, CApath))
+ return 0;
+ if (CAstore != NULL && !SSL_CTX_load_verify_store(ctx, CAstore))
+ return 0;
+ return 1;
+}
+
+#ifndef OPENSSL_NO_CT
+
+int ctx_set_ctlog_list_file(SSL_CTX *ctx, const char *path)
+{
+ if (path == NULL)
+ return SSL_CTX_set_default_ctlog_list_file(ctx);
+
+ return SSL_CTX_set_ctlog_list_file(ctx, path);
+}
+
+#endif
+
+static unsigned long nmflag = 0;
+static char nmflag_set = 0;
+
+int set_nameopt(const char *arg)
+{
+ int ret = set_name_ex(&nmflag, arg);
+
+ if (ret)
+ nmflag_set = 1;
+
+ return ret;
+}
+
+unsigned long get_nameopt(void)
+{
+ return (nmflag_set) ? nmflag : XN_FLAG_ONELINE;
+}
+
+void dump_cert_text(BIO *out, X509 *x)
+{
+ print_name(out, "subject=", X509_get_subject_name(x));
+ print_name(out, "issuer=", X509_get_issuer_name(x));
+}
+
+int wrap_password_callback(char *buf, int bufsiz, int verify, void *userdata)
+{
+ return password_callback(buf, bufsiz, verify, (PW_CB_DATA *)userdata);
+}
+
+
+static char *app_get_pass(const char *arg, int keepbio);
+
+char *get_passwd(const char *pass, const char *desc)
+{
+ char *result = NULL;
+
+ if (desc == NULL)
+ desc = "<unknown>";
+ if (!app_passwd(pass, NULL, &result, NULL))
+ BIO_printf(bio_err, "Error getting password for %s\n", desc);
+ if (pass != NULL && result == NULL) {
+ BIO_printf(bio_err,
+ "Trying plain input string (better precede with 'pass:')\n");
+ result = OPENSSL_strdup(pass);
+ if (result == NULL)
+ BIO_printf(bio_err, "Out of memory getting password for %s\n", desc);
+ }
+ return result;
+}
+
+int app_passwd(const char *arg1, const char *arg2, char **pass1, char **pass2)
+{
+ int same = arg1 != NULL && arg2 != NULL && strcmp(arg1, arg2) == 0;
+
+ if (arg1 != NULL) {
+ *pass1 = app_get_pass(arg1, same);
+ if (*pass1 == NULL)
+ return 0;
+ } else if (pass1 != NULL) {
+ *pass1 = NULL;
+ }
+ if (arg2 != NULL) {
+ *pass2 = app_get_pass(arg2, same ? 2 : 0);
+ if (*pass2 == NULL)
+ return 0;
+ } else if (pass2 != NULL) {
+ *pass2 = NULL;
+ }
+ return 1;
+}
+
+static char *app_get_pass(const char *arg, int keepbio)
+{
+ static BIO *pwdbio = NULL;
+ char *tmp, tpass[APP_PASS_LEN];
+ int i;
+
+ /* PASS_SOURCE_SIZE_MAX = max number of chars before ':' in below strings */
+ if (strncmp(arg, "pass:", 5) == 0)
+ return OPENSSL_strdup(arg + 5);
+ if (strncmp(arg, "env:", 4) == 0) {
+ tmp = getenv(arg + 4);
+ if (tmp == NULL) {
+ BIO_printf(bio_err, "No environment variable %s\n", arg + 4);
+ return NULL;
+ }
+ return OPENSSL_strdup(tmp);
+ }
+ if (!keepbio || pwdbio == NULL) {
+ if (strncmp(arg, "file:", 5) == 0) {
+ pwdbio = BIO_new_file(arg + 5, "r");
+ if (pwdbio == NULL) {
+ BIO_printf(bio_err, "Can't open file %s\n", arg + 5);
+ return NULL;
+ }
+#if !defined(_WIN32)
+ /*
+ * Under _WIN32, which covers even Win64 and CE, file
+ * descriptors referenced by BIO_s_fd are not inherited
+ * by child process and therefore below is not an option.
+ * It could have been an option if bss_fd.c was operating
+ * on real Windows descriptors, such as those obtained
+ * with CreateFile.
+ */
+ } else if (strncmp(arg, "fd:", 3) == 0) {
+ BIO *btmp;
+ i = atoi(arg + 3);
+ if (i >= 0)
+ pwdbio = BIO_new_fd(i, BIO_NOCLOSE);
+ if ((i < 0) || pwdbio == NULL) {
+ BIO_printf(bio_err, "Can't access file descriptor %s\n", arg + 3);
+ return NULL;
+ }
+ /*
+ * Can't do BIO_gets on an fd BIO so add a buffering BIO
+ */
+ btmp = BIO_new(BIO_f_buffer());
+ if (btmp == NULL) {
+ BIO_free_all(pwdbio);
+ pwdbio = NULL;
+ BIO_printf(bio_err, "Out of memory\n");
+ return NULL;
+ }
+ pwdbio = BIO_push(btmp, pwdbio);
+#endif
+ } else if (strcmp(arg, "stdin") == 0) {
+ unbuffer(stdin);
+ pwdbio = dup_bio_in(FORMAT_TEXT);
+ if (pwdbio == NULL) {
+ BIO_printf(bio_err, "Can't open BIO for stdin\n");
+ return NULL;
+ }
+ } else {
+ /* argument syntax error; do not reveal too much about arg */
+ tmp = strchr(arg, ':');
+ if (tmp == NULL || tmp - arg > PASS_SOURCE_SIZE_MAX)
+ BIO_printf(bio_err,
+ "Invalid password argument, missing ':' within the first %d chars\n",
+ PASS_SOURCE_SIZE_MAX + 1);
+ else
+ BIO_printf(bio_err,
+ "Invalid password argument, starting with \"%.*s\"\n",
+ (int)(tmp - arg + 1), arg);
+ return NULL;
+ }
+ }
+ i = BIO_gets(pwdbio, tpass, APP_PASS_LEN);
+ if (keepbio != 1) {
+ BIO_free_all(pwdbio);
+ pwdbio = NULL;
+ }
+ if (i <= 0) {
+ BIO_printf(bio_err, "Error reading password from BIO\n");
+ return NULL;
+ }
+ tmp = strchr(tpass, '\n');
+ if (tmp != NULL)
+ *tmp = 0;
+ return OPENSSL_strdup(tpass);
+}
+
+CONF *app_load_config_bio(BIO *in, const char *filename)
+{
+ long errorline = -1;
+ CONF *conf;
+ int i;
+
+ conf = NCONF_new_ex(app_get0_libctx(), NULL);
+ i = NCONF_load_bio(conf, in, &errorline);
+ if (i > 0)
+ return conf;
+
+ if (errorline <= 0) {
+ BIO_printf(bio_err, "%s: Can't load ", opt_getprog());
+ } else {
+ BIO_printf(bio_err, "%s: Error on line %ld of ", opt_getprog(),
+ errorline);
+ }
+ if (filename != NULL)
+ BIO_printf(bio_err, "config file \"%s\"\n", filename);
+ else
+ BIO_printf(bio_err, "config input");
+
+ NCONF_free(conf);
+ return NULL;
+}
+
+CONF *app_load_config_verbose(const char *filename, int verbose)
+{
+ if (verbose) {
+ if (*filename == '\0')
+ BIO_printf(bio_err, "No configuration used\n");
+ else
+ BIO_printf(bio_err, "Using configuration from %s\n", filename);
+ }
+ return app_load_config_internal(filename, 0);
+}
+
+CONF *app_load_config_internal(const char *filename, int quiet)
+{
+ BIO *in;
+ CONF *conf;
+
+ if (filename == NULL || *filename != '\0') {
+ if ((in = bio_open_default_(filename, 'r', FORMAT_TEXT, quiet)) == NULL)
+ return NULL;
+ conf = app_load_config_bio(in, filename);
+ BIO_free(in);
+ } else {
+ /* Return empty config if filename is empty string. */
+ conf = NCONF_new_ex(app_get0_libctx(), NULL);
+ }
+ return conf;
+}
+
+int app_load_modules(const CONF *config)
+{
+ CONF *to_free = NULL;
+
+ if (config == NULL)
+ config = to_free = app_load_config_quiet(default_config_file);
+ if (config == NULL)
+ return 1;
+
+ if (CONF_modules_load(config, NULL, 0) <= 0) {
+ BIO_printf(bio_err, "Error configuring OpenSSL modules\n");
+ ERR_print_errors(bio_err);
+ NCONF_free(to_free);
+ return 0;
+ }
+ NCONF_free(to_free);
+ return 1;
+}
+
+int add_oid_section(CONF *conf)
+{
+ char *p;
+ STACK_OF(CONF_VALUE) *sktmp;
+ CONF_VALUE *cnf;
+ int i;
+
+ if ((p = NCONF_get_string(conf, NULL, "oid_section")) == NULL) {
+ ERR_clear_error();
+ return 1;
+ }
+ if ((sktmp = NCONF_get_section(conf, p)) == NULL) {
+ BIO_printf(bio_err, "problem loading oid section %s\n", p);
+ return 0;
+ }
+ for (i = 0; i < sk_CONF_VALUE_num(sktmp); i++) {
+ cnf = sk_CONF_VALUE_value(sktmp, i);
+ if (OBJ_create(cnf->value, cnf->name, cnf->name) == NID_undef) {
+ BIO_printf(bio_err, "problem creating object %s=%s\n",
+ cnf->name, cnf->value);
+ return 0;
+ }
+ }
+ return 1;
+}
+
+CONF *app_load_config_modules(const char *configfile)
+{
+ CONF *conf = NULL;
+
+ if (configfile != NULL) {
+ if ((conf = app_load_config_verbose(configfile, 1)) == NULL)
+ return NULL;
+ if (configfile != default_config_file && !app_load_modules(conf)) {
+ NCONF_free(conf);
+ conf = NULL;
+ }
+ }
+ return conf;
+}
+
+#define IS_HTTP(uri) ((uri) != NULL \
+ && strncmp(uri, OSSL_HTTP_PREFIX, strlen(OSSL_HTTP_PREFIX)) == 0)
+#define IS_HTTPS(uri) ((uri) != NULL \
+ && strncmp(uri, OSSL_HTTPS_PREFIX, strlen(OSSL_HTTPS_PREFIX)) == 0)
+
+X509 *load_cert_pass(const char *uri, int format, int maybe_stdin,
+ const char *pass, const char *desc)
+{
+ X509 *cert = NULL;
+
+ if (desc == NULL)
+ desc = "certificate";
+ if (IS_HTTPS(uri))
+ BIO_printf(bio_err, "Loading %s over HTTPS is unsupported\n", desc);
+ else if (IS_HTTP(uri))
+ cert = X509_load_http(uri, NULL, NULL, 0 /* timeout */);
+ else
+ (void)load_key_certs_crls(uri, format, maybe_stdin, pass, desc,
+ NULL, NULL, NULL, &cert, NULL, NULL, NULL);
+ if (cert == NULL) {
+ BIO_printf(bio_err, "Unable to load %s\n", desc);
+ ERR_print_errors(bio_err);
+ }
+ return cert;
+}
+
+X509_CRL *load_crl(const char *uri, int format, int maybe_stdin,
+ const char *desc)
+{
+ X509_CRL *crl = NULL;
+
+ if (desc == NULL)
+ desc = "CRL";
+ if (IS_HTTPS(uri))
+ BIO_printf(bio_err, "Loading %s over HTTPS is unsupported\n", desc);
+ else if (IS_HTTP(uri))
+ crl = X509_CRL_load_http(uri, NULL, NULL, 0 /* timeout */);
+ else
+ (void)load_key_certs_crls(uri, format, maybe_stdin, NULL, desc,
+ NULL, NULL, NULL, NULL, NULL, &crl, NULL);
+ if (crl == NULL) {
+ BIO_printf(bio_err, "Unable to load %s\n", desc);
+ ERR_print_errors(bio_err);
+ }
+ return crl;
+}
+
+X509_REQ *load_csr(const char *file, int format, const char *desc)
+{
+ X509_REQ *req = NULL;
+ BIO *in;
+
+ if (format == FORMAT_UNDEF)
+ format = FORMAT_PEM;
+ if (desc == NULL)
+ desc = "CSR";
+ in = bio_open_default(file, 'r', format);
+ if (in == NULL)
+ goto end;
+
+ if (format == FORMAT_ASN1)
+ req = d2i_X509_REQ_bio(in, NULL);
+ else if (format == FORMAT_PEM)
+ req = PEM_read_bio_X509_REQ(in, NULL, NULL, NULL);
+ else
+ print_format_error(format, OPT_FMT_PEMDER);
+
+ end:
+ if (req == NULL) {
+ BIO_printf(bio_err, "Unable to load %s\n", desc);
+ ERR_print_errors(bio_err);
+ }
+ BIO_free(in);
+ return req;
+}
+
+void cleanse(char *str)
+{
+ if (str != NULL)
+ OPENSSL_cleanse(str, strlen(str));
+}
+
+void clear_free(char *str)
+{
+ if (str != NULL)
+ OPENSSL_clear_free(str, strlen(str));
+}
+
+EVP_PKEY *load_key(const char *uri, int format, int may_stdin,
+ const char *pass, ENGINE *e, const char *desc)
+{
+ EVP_PKEY *pkey = NULL;
+ char *allocated_uri = NULL;
+
+ if (desc == NULL)
+ desc = "private key";
+
+ if (format == FORMAT_ENGINE) {
+ uri = allocated_uri = make_engine_uri(e, uri, desc);
+ }
+ (void)load_key_certs_crls(uri, format, may_stdin, pass, desc,
+ &pkey, NULL, NULL, NULL, NULL, NULL, NULL);
+
+ OPENSSL_free(allocated_uri);
+ return pkey;
+}
+
+EVP_PKEY *load_pubkey(const char *uri, int format, int maybe_stdin,
+ const char *pass, ENGINE *e, const char *desc)
+{
+ EVP_PKEY *pkey = NULL;
+ char *allocated_uri = NULL;
+
+ if (desc == NULL)
+ desc = "public key";
+
+ if (format == FORMAT_ENGINE) {
+ uri = allocated_uri = make_engine_uri(e, uri, desc);
+ }
+ (void)load_key_certs_crls(uri, format, maybe_stdin, pass, desc,
+ NULL, &pkey, NULL, NULL, NULL, NULL, NULL);
+
+ OPENSSL_free(allocated_uri);
+ return pkey;
+}
+
+EVP_PKEY *load_keyparams_suppress(const char *uri, int format, int maybe_stdin,
+ const char *keytype, const char *desc,
+ int suppress_decode_errors)
+{
+ EVP_PKEY *params = NULL;
+
+ if (desc == NULL)
+ desc = "key parameters";
+
+ (void)load_key_certs_crls_suppress(uri, format, maybe_stdin, NULL, desc,
+ NULL, NULL, &params, NULL, NULL, NULL,
+ NULL, suppress_decode_errors);
+ if (params != NULL && keytype != NULL && !EVP_PKEY_is_a(params, keytype)) {
+ if (!suppress_decode_errors) {
+ BIO_printf(bio_err,
+ "Unable to load %s from %s (unexpected parameters type)\n",
+ desc, uri);
+ ERR_print_errors(bio_err);
+ }
+ EVP_PKEY_free(params);
+ params = NULL;
+ }
+ return params;
+}
+
+EVP_PKEY *load_keyparams(const char *uri, int format, int maybe_stdin,
+ const char *keytype, const char *desc)
+{
+ return load_keyparams_suppress(uri, format, maybe_stdin, keytype, desc, 0);
+}
+
+void app_bail_out(char *fmt, ...)
+{
+ va_list args;
+
+ va_start(args, fmt);
+ BIO_vprintf(bio_err, fmt, args);
+ va_end(args);
+ ERR_print_errors(bio_err);
+ exit(EXIT_FAILURE);
+}
+
+void *app_malloc(size_t sz, const char *what)
+{
+ void *vp = OPENSSL_malloc(sz);
+
+ if (vp == NULL)
+ app_bail_out("%s: Could not allocate %zu bytes for %s\n",
+ opt_getprog(), sz, what);
+ return vp;
+}
+
+char *next_item(char *opt) /* in list separated by comma and/or space */
+{
+ /* advance to separator (comma or whitespace), if any */
+ while (*opt != ',' && !isspace(_UC(*opt)) && *opt != '\0')
+ opt++;
+ if (*opt != '\0') {
+ /* terminate current item */
+ *opt++ = '\0';
+ /* skip over any whitespace after separator */
+ while (isspace(_UC(*opt)))
+ opt++;
+ }
+ return *opt == '\0' ? NULL : opt; /* NULL indicates end of input */
+}
+
+static void warn_cert_msg(const char *uri, X509 *cert, const char *msg)
+{
+ char *subj = X509_NAME_oneline(X509_get_subject_name(cert), NULL, 0);
+
+ BIO_printf(bio_err, "Warning: certificate from '%s' with subject '%s' %s\n",
+ uri, subj, msg);
+ OPENSSL_free(subj);
+}
+
+static void warn_cert(const char *uri, X509 *cert, int warn_EE,
+ X509_VERIFY_PARAM *vpm)
+{
+ uint32_t ex_flags = X509_get_extension_flags(cert);
+ int res = X509_cmp_timeframe(vpm, X509_get0_notBefore(cert),
+ X509_get0_notAfter(cert));
+
+ if (res != 0)
+ warn_cert_msg(uri, cert, res > 0 ? "has expired" : "not yet valid");
+ if (warn_EE && (ex_flags & EXFLAG_V1) == 0 && (ex_flags & EXFLAG_CA) == 0)
+ warn_cert_msg(uri, cert, "is not a CA cert");
+}
+
+static void warn_certs(const char *uri, STACK_OF(X509) *certs, int warn_EE,
+ X509_VERIFY_PARAM *vpm)
+{
+ int i;
+
+ for (i = 0; i < sk_X509_num(certs); i++)
+ warn_cert(uri, sk_X509_value(certs, i), warn_EE, vpm);
+}
+
+int load_cert_certs(const char *uri,
+ X509 **pcert, STACK_OF(X509) **pcerts,
+ int exclude_http, const char *pass, const char *desc,
+ X509_VERIFY_PARAM *vpm)
+{
+ int ret = 0;
+ char *pass_string;
+
+ if (exclude_http && (OPENSSL_strncasecmp(uri, "http://", 7) == 0
+ || OPENSSL_strncasecmp(uri, "https://", 8) == 0)) {
+ BIO_printf(bio_err, "error: HTTP retrieval not allowed for %s\n", desc);
+ return ret;
+ }
+ pass_string = get_passwd(pass, desc);
+ ret = load_key_certs_crls(uri, FORMAT_UNDEF, 0, pass_string, desc,
+ NULL, NULL, NULL,
+ pcert, pcerts, NULL, NULL);
+ clear_free(pass_string);
+
+ if (ret) {
+ if (pcert != NULL)
+ warn_cert(uri, *pcert, 0, vpm);
+ if (pcerts != NULL)
+ warn_certs(uri, *pcerts, 1, vpm);
+ } else {
+ if (pcerts != NULL) {
+ sk_X509_pop_free(*pcerts, X509_free);
+ *pcerts = NULL;
+ }
+ }
+ return ret;
+}
+
+STACK_OF(X509) *load_certs_multifile(char *files, const char *pass,
+ const char *desc, X509_VERIFY_PARAM *vpm)
+{
+ STACK_OF(X509) *certs = NULL;
+ STACK_OF(X509) *result = sk_X509_new_null();
+
+ if (files == NULL)
+ goto err;
+ if (result == NULL)
+ goto oom;
+
+ while (files != NULL) {
+ char *next = next_item(files);
+
+ if (!load_cert_certs(files, NULL, &certs, 0, pass, desc, vpm))
+ goto err;
+ if (!X509_add_certs(result, certs,
+ X509_ADD_FLAG_UP_REF | X509_ADD_FLAG_NO_DUP))
+ goto oom;
+ sk_X509_pop_free(certs, X509_free);
+ certs = NULL;
+ files = next;
+ }
+ return result;
+
+ oom:
+ BIO_printf(bio_err, "out of memory\n");
+ err:
+ sk_X509_pop_free(certs, X509_free);
+ sk_X509_pop_free(result, X509_free);
+ return NULL;
+}
+
+static X509_STORE *sk_X509_to_store(X509_STORE *store /* may be NULL */,
+ const STACK_OF(X509) *certs /* may NULL */)
+{
+ int i;
+
+ if (store == NULL)
+ store = X509_STORE_new();
+ if (store == NULL)
+ return NULL;
+ for (i = 0; i < sk_X509_num(certs); i++) {
+ if (!X509_STORE_add_cert(store, sk_X509_value(certs, i))) {
+ X509_STORE_free(store);
+ return NULL;
+ }
+ }
+ return store;
+}
+
+/*
+ * Create cert store structure with certificates read from given file(s).
+ * Returns pointer to created X509_STORE on success, NULL on error.
+ */
+X509_STORE *load_certstore(char *input, const char *pass, const char *desc,
+ X509_VERIFY_PARAM *vpm)
+{
+ X509_STORE *store = NULL;
+ STACK_OF(X509) *certs = NULL;
+
+ while (input != NULL) {
+ char *next = next_item(input);
+ int ok;
+
+ if (!load_cert_certs(input, NULL, &certs, 1, pass, desc, vpm)) {
+ X509_STORE_free(store);
+ return NULL;
+ }
+ ok = (store = sk_X509_to_store(store, certs)) != NULL;
+ sk_X509_pop_free(certs, X509_free);
+ certs = NULL;
+ if (!ok)
+ return NULL;
+ input = next;
+ }
+ return store;
+}
+
+/*
+ * Initialize or extend, if *certs != NULL, a certificate stack.
+ * The caller is responsible for freeing *certs if its value is left not NULL.
+ */
+int load_certs(const char *uri, int maybe_stdin, STACK_OF(X509) **certs,
+ const char *pass, const char *desc)
+{
+ int was_NULL = *certs == NULL;
+ int ret = load_key_certs_crls(uri, FORMAT_UNDEF, maybe_stdin,
+ pass, desc, NULL, NULL,
+ NULL, NULL, certs, NULL, NULL);
+
+ if (!ret && was_NULL) {
+ sk_X509_pop_free(*certs, X509_free);
+ *certs = NULL;
+ }
+ return ret;
+}
+
+/*
+ * Initialize or extend, if *crls != NULL, a certificate stack.
+ * The caller is responsible for freeing *crls if its value is left not NULL.
+ */
+int load_crls(const char *uri, STACK_OF(X509_CRL) **crls,
+ const char *pass, const char *desc)
+{
+ int was_NULL = *crls == NULL;
+ int ret = load_key_certs_crls(uri, FORMAT_UNDEF, 0, pass, desc,
+ NULL, NULL, NULL,
+ NULL, NULL, NULL, crls);
+
+ if (!ret && was_NULL) {
+ sk_X509_CRL_pop_free(*crls, X509_CRL_free);
+ *crls = NULL;
+ }
+ return ret;
+}
+
+static const char *format2string(int format)
+{
+ switch(format) {
+ case FORMAT_PEM:
+ return "PEM";
+ case FORMAT_ASN1:
+ return "DER";
+ }
+ return NULL;
+}
+
+/* Set type expectation, but clear it if objects of different types expected. */
+#define SET_EXPECT(expect, val) ((expect) = (expect) < 0 ? (val) : ((expect) == (val) ? (val) : 0))
+/*
+ * Load those types of credentials for which the result pointer is not NULL.
+ * Reads from stdio if uri is NULL and maybe_stdin is nonzero.
+ * For non-NULL ppkey, pcert, and pcrl the first suitable value found is loaded.
+ * If pcerts is non-NULL and *pcerts == NULL then a new cert list is allocated.
+ * If pcerts is non-NULL then all available certificates are appended to *pcerts
+ * except any certificate assigned to *pcert.
+ * If pcrls is non-NULL and *pcrls == NULL then a new list of CRLs is allocated.
+ * If pcrls is non-NULL then all available CRLs are appended to *pcerts
+ * except any CRL assigned to *pcrl.
+ * In any case (also on error) the caller is responsible for freeing all members
+ * of *pcerts and *pcrls (as far as they are not NULL).
+ */
+static
+int load_key_certs_crls_suppress(const char *uri, int format, int maybe_stdin,
+ const char *pass, const char *desc,
+ EVP_PKEY **ppkey, EVP_PKEY **ppubkey,
+ EVP_PKEY **pparams,
+ X509 **pcert, STACK_OF(X509) **pcerts,
+ X509_CRL **pcrl, STACK_OF(X509_CRL) **pcrls,
+ int suppress_decode_errors)
+{
+ PW_CB_DATA uidata;
+ OSSL_STORE_CTX *ctx = NULL;
+ OSSL_LIB_CTX *libctx = app_get0_libctx();
+ const char *propq = app_get0_propq();
+ int ncerts = 0;
+ int ncrls = 0;
+ const char *failed =
+ ppkey != NULL ? "key" : ppubkey != NULL ? "public key" :
+ pparams != NULL ? "params" : pcert != NULL ? "cert" :
+ pcrl != NULL ? "CRL" : pcerts != NULL ? "certs" :
+ pcrls != NULL ? "CRLs" : NULL;
+ int cnt_expectations = 0;
+ int expect = -1;
+ const char *input_type;
+ OSSL_PARAM itp[2];
+ const OSSL_PARAM *params = NULL;
+
+ if (ppkey != NULL) {
+ *ppkey = NULL;
+ cnt_expectations++;
+ SET_EXPECT(expect, OSSL_STORE_INFO_PKEY);
+ }
+ if (ppubkey != NULL) {
+ *ppubkey = NULL;
+ cnt_expectations++;
+ SET_EXPECT(expect, OSSL_STORE_INFO_PUBKEY);
+ }
+ if (pparams != NULL) {
+ *pparams = NULL;
+ cnt_expectations++;
+ SET_EXPECT(expect, OSSL_STORE_INFO_PARAMS);
+ }
+ if (pcert != NULL) {
+ *pcert = NULL;
+ cnt_expectations++;
+ SET_EXPECT(expect, OSSL_STORE_INFO_CERT);
+ }
+ if (pcerts != NULL) {
+ if (*pcerts == NULL && (*pcerts = sk_X509_new_null()) == NULL) {
+ BIO_printf(bio_err, "Out of memory loading");
+ goto end;
+ }
+ cnt_expectations++;
+ SET_EXPECT(expect, OSSL_STORE_INFO_CERT);
+ }
+ if (pcrl != NULL) {
+ *pcrl = NULL;
+ cnt_expectations++;
+ SET_EXPECT(expect, OSSL_STORE_INFO_CRL);
+ }
+ if (pcrls != NULL) {
+ if (*pcrls == NULL && (*pcrls = sk_X509_CRL_new_null()) == NULL) {
+ BIO_printf(bio_err, "Out of memory loading");
+ goto end;
+ }
+ cnt_expectations++;
+ SET_EXPECT(expect, OSSL_STORE_INFO_CRL);
+ }
+ if (cnt_expectations == 0) {
+ BIO_printf(bio_err, "Internal error: nothing to load from %s\n",
+ uri != NULL ? uri : "<stdin>");
+ return 0;
+ }
+
+ uidata.password = pass;
+ uidata.prompt_info = uri;
+
+ if ((input_type = format2string(format)) != NULL) {
+ itp[0] = OSSL_PARAM_construct_utf8_string(OSSL_STORE_PARAM_INPUT_TYPE,
+ (char *)input_type, 0);
+ itp[1] = OSSL_PARAM_construct_end();
+ params = itp;
+ }
+
+ if (uri == NULL) {
+ BIO *bio;
+
+ if (!maybe_stdin) {
+ BIO_printf(bio_err, "No filename or uri specified for loading\n");
+ goto end;
+ }
+ uri = "<stdin>";
+ unbuffer(stdin);
+ bio = BIO_new_fp(stdin, 0);
+ if (bio != NULL) {
+ ctx = OSSL_STORE_attach(bio, "file", libctx, propq,
+ get_ui_method(), &uidata, params,
+ NULL, NULL);
+ BIO_free(bio);
+ }
+ } else {
+ ctx = OSSL_STORE_open_ex(uri, libctx, propq, get_ui_method(), &uidata,
+ params, NULL, NULL);
+ }
+ if (ctx == NULL) {
+ BIO_printf(bio_err, "Could not open file or uri for loading");
+ goto end;
+ }
+ if (expect > 0 && !OSSL_STORE_expect(ctx, expect)) {
+ BIO_printf(bio_err, "Internal error trying to load");
+ goto end;
+ }
+
+ failed = NULL;
+ while (cnt_expectations > 0 && !OSSL_STORE_eof(ctx)) {
+ OSSL_STORE_INFO *info = OSSL_STORE_load(ctx);
+ int type, ok = 1;
+
+ /*
+ * This can happen (for example) if we attempt to load a file with
+ * multiple different types of things in it - but the thing we just
+ * tried to load wasn't one of the ones we wanted, e.g. if we're trying
+ * to load a certificate but the file has both the private key and the
+ * certificate in it. We just retry until eof.
+ */
+ if (info == NULL) {
+ continue;
+ }
+
+ type = OSSL_STORE_INFO_get_type(info);
+ switch (type) {
+ case OSSL_STORE_INFO_PKEY:
+ if (ppkey != NULL && *ppkey == NULL) {
+ ok = (*ppkey = OSSL_STORE_INFO_get1_PKEY(info)) != NULL;
+ cnt_expectations -= ok;
+ }
+ /*
+ * An EVP_PKEY with private parts also holds the public parts,
+ * so if the caller asked for a public key, and we got a private
+ * key, we can still pass it back.
+ */
+ if (ok && ppubkey != NULL && *ppubkey == NULL) {
+ ok = ((*ppubkey = OSSL_STORE_INFO_get1_PKEY(info)) != NULL);
+ cnt_expectations -= ok;
+ }
+ break;
+ case OSSL_STORE_INFO_PUBKEY:
+ if (ppubkey != NULL && *ppubkey == NULL) {
+ ok = ((*ppubkey = OSSL_STORE_INFO_get1_PUBKEY(info)) != NULL);
+ cnt_expectations -= ok;
+ }
+ break;
+ case OSSL_STORE_INFO_PARAMS:
+ if (pparams != NULL && *pparams == NULL) {
+ ok = ((*pparams = OSSL_STORE_INFO_get1_PARAMS(info)) != NULL);
+ cnt_expectations -= ok;
+ }
+ break;
+ case OSSL_STORE_INFO_CERT:
+ if (pcert != NULL && *pcert == NULL) {
+ ok = (*pcert = OSSL_STORE_INFO_get1_CERT(info)) != NULL;
+ cnt_expectations -= ok;
+ }
+ else if (pcerts != NULL)
+ ok = X509_add_cert(*pcerts,
+ OSSL_STORE_INFO_get1_CERT(info),
+ X509_ADD_FLAG_DEFAULT);
+ ncerts += ok;
+ break;
+ case OSSL_STORE_INFO_CRL:
+ if (pcrl != NULL && *pcrl == NULL) {
+ ok = (*pcrl = OSSL_STORE_INFO_get1_CRL(info)) != NULL;
+ cnt_expectations -= ok;
+ }
+ else if (pcrls != NULL)
+ ok = sk_X509_CRL_push(*pcrls, OSSL_STORE_INFO_get1_CRL(info));
+ ncrls += ok;
+ break;
+ default:
+ /* skip any other type */
+ break;
+ }
+ OSSL_STORE_INFO_free(info);
+ if (!ok) {
+ failed = info == NULL ? NULL : OSSL_STORE_INFO_type_string(type);
+ BIO_printf(bio_err, "Error reading");
+ break;
+ }
+ }
+
+ end:
+ OSSL_STORE_close(ctx);
+ if (failed == NULL) {
+ int any = 0;
+
+ if ((ppkey != NULL && *ppkey == NULL)
+ || (ppubkey != NULL && *ppubkey == NULL)) {
+ failed = "key";
+ } else if (pparams != NULL && *pparams == NULL) {
+ failed = "params";
+ } else if ((pcert != NULL || pcerts != NULL) && ncerts == 0) {
+ if (pcert == NULL)
+ any = 1;
+ failed = "cert";
+ } else if ((pcrl != NULL || pcrls != NULL) && ncrls == 0) {
+ if (pcrl == NULL)
+ any = 1;
+ failed = "CRL";
+ }
+ if (!suppress_decode_errors) {
+ if (failed != NULL)
+ BIO_printf(bio_err, "Could not read");
+ if (any)
+ BIO_printf(bio_err, " any");
+ }
+ }
+ if (!suppress_decode_errors && failed != NULL) {
+ if (desc != NULL && strstr(desc, failed) != NULL) {
+ BIO_printf(bio_err, " %s", desc);
+ } else {
+ BIO_printf(bio_err, " %s", failed);
+ if (desc != NULL)
+ BIO_printf(bio_err, " of %s", desc);
+ }
+ if (uri != NULL)
+ BIO_printf(bio_err, " from %s", uri);
+ BIO_printf(bio_err, "\n");
+ ERR_print_errors(bio_err);
+ }
+ if (suppress_decode_errors || failed == NULL)
+ /* clear any spurious errors */
+ ERR_clear_error();
+ return failed == NULL;
+}
+
+int load_key_certs_crls(const char *uri, int format, int maybe_stdin,
+ const char *pass, const char *desc,
+ EVP_PKEY **ppkey, EVP_PKEY **ppubkey,
+ EVP_PKEY **pparams,
+ X509 **pcert, STACK_OF(X509) **pcerts,
+ X509_CRL **pcrl, STACK_OF(X509_CRL) **pcrls)
+{
+ return load_key_certs_crls_suppress(uri, format, maybe_stdin, pass, desc,
+ ppkey, ppubkey, pparams, pcert, pcerts,
+ pcrl, pcrls, 0);
+}
+
+#define X509V3_EXT_UNKNOWN_MASK (0xfL << 16)
+/* Return error for unknown extensions */
+#define X509V3_EXT_DEFAULT 0
+/* Print error for unknown extensions */
+#define X509V3_EXT_ERROR_UNKNOWN (1L << 16)
+/* ASN1 parse unknown extensions */
+#define X509V3_EXT_PARSE_UNKNOWN (2L << 16)
+/* BIO_dump unknown extensions */
+#define X509V3_EXT_DUMP_UNKNOWN (3L << 16)
+
+#define X509_FLAG_CA (X509_FLAG_NO_ISSUER | X509_FLAG_NO_PUBKEY | \
+ X509_FLAG_NO_HEADER | X509_FLAG_NO_VERSION)
+
+int set_cert_ex(unsigned long *flags, const char *arg)
+{
+ static const NAME_EX_TBL cert_tbl[] = {
+ {"compatible", X509_FLAG_COMPAT, 0xffffffffl},
+ {"ca_default", X509_FLAG_CA, 0xffffffffl},
+ {"no_header", X509_FLAG_NO_HEADER, 0},
+ {"no_version", X509_FLAG_NO_VERSION, 0},
+ {"no_serial", X509_FLAG_NO_SERIAL, 0},
+ {"no_signame", X509_FLAG_NO_SIGNAME, 0},
+ {"no_validity", X509_FLAG_NO_VALIDITY, 0},
+ {"no_subject", X509_FLAG_NO_SUBJECT, 0},
+ {"no_issuer", X509_FLAG_NO_ISSUER, 0},
+ {"no_pubkey", X509_FLAG_NO_PUBKEY, 0},
+ {"no_extensions", X509_FLAG_NO_EXTENSIONS, 0},
+ {"no_sigdump", X509_FLAG_NO_SIGDUMP, 0},
+ {"no_aux", X509_FLAG_NO_AUX, 0},
+ {"no_attributes", X509_FLAG_NO_ATTRIBUTES, 0},
+ {"ext_default", X509V3_EXT_DEFAULT, X509V3_EXT_UNKNOWN_MASK},
+ {"ext_error", X509V3_EXT_ERROR_UNKNOWN, X509V3_EXT_UNKNOWN_MASK},
+ {"ext_parse", X509V3_EXT_PARSE_UNKNOWN, X509V3_EXT_UNKNOWN_MASK},
+ {"ext_dump", X509V3_EXT_DUMP_UNKNOWN, X509V3_EXT_UNKNOWN_MASK},
+ {NULL, 0, 0}
+ };
+ return set_multi_opts(flags, arg, cert_tbl);
+}
+
+int set_name_ex(unsigned long *flags, const char *arg)
+{
+ static const NAME_EX_TBL ex_tbl[] = {
+ {"esc_2253", ASN1_STRFLGS_ESC_2253, 0},
+ {"esc_2254", ASN1_STRFLGS_ESC_2254, 0},
+ {"esc_ctrl", ASN1_STRFLGS_ESC_CTRL, 0},
+ {"esc_msb", ASN1_STRFLGS_ESC_MSB, 0},
+ {"use_quote", ASN1_STRFLGS_ESC_QUOTE, 0},
+ {"utf8", ASN1_STRFLGS_UTF8_CONVERT, 0},
+ {"ignore_type", ASN1_STRFLGS_IGNORE_TYPE, 0},
+ {"show_type", ASN1_STRFLGS_SHOW_TYPE, 0},
+ {"dump_all", ASN1_STRFLGS_DUMP_ALL, 0},
+ {"dump_nostr", ASN1_STRFLGS_DUMP_UNKNOWN, 0},
+ {"dump_der", ASN1_STRFLGS_DUMP_DER, 0},
+ {"compat", XN_FLAG_COMPAT, 0xffffffffL},
+ {"sep_comma_plus", XN_FLAG_SEP_COMMA_PLUS, XN_FLAG_SEP_MASK},
+ {"sep_comma_plus_space", XN_FLAG_SEP_CPLUS_SPC, XN_FLAG_SEP_MASK},
+ {"sep_semi_plus_space", XN_FLAG_SEP_SPLUS_SPC, XN_FLAG_SEP_MASK},
+ {"sep_multiline", XN_FLAG_SEP_MULTILINE, XN_FLAG_SEP_MASK},
+ {"dn_rev", XN_FLAG_DN_REV, 0},
+ {"nofname", XN_FLAG_FN_NONE, XN_FLAG_FN_MASK},
+ {"sname", XN_FLAG_FN_SN, XN_FLAG_FN_MASK},
+ {"lname", XN_FLAG_FN_LN, XN_FLAG_FN_MASK},
+ {"align", XN_FLAG_FN_ALIGN, 0},
+ {"oid", XN_FLAG_FN_OID, XN_FLAG_FN_MASK},
+ {"space_eq", XN_FLAG_SPC_EQ, 0},
+ {"dump_unknown", XN_FLAG_DUMP_UNKNOWN_FIELDS, 0},
+ {"RFC2253", XN_FLAG_RFC2253, 0xffffffffL},
+ {"oneline", XN_FLAG_ONELINE, 0xffffffffL},
+ {"multiline", XN_FLAG_MULTILINE, 0xffffffffL},
+ {"ca_default", XN_FLAG_MULTILINE, 0xffffffffL},
+ {NULL, 0, 0}
+ };
+ if (set_multi_opts(flags, arg, ex_tbl) == 0)
+ return 0;
+ if (*flags != XN_FLAG_COMPAT
+ && (*flags & XN_FLAG_SEP_MASK) == 0)
+ *flags |= XN_FLAG_SEP_CPLUS_SPC;
+ return 1;
+}
+
+int set_dateopt(unsigned long *dateopt, const char *arg)
+{
+ if (OPENSSL_strcasecmp(arg, "rfc_822") == 0)
+ *dateopt = ASN1_DTFLGS_RFC822;
+ else if (OPENSSL_strcasecmp(arg, "iso_8601") == 0)
+ *dateopt = ASN1_DTFLGS_ISO8601;
+ else
+ return 0;
+ return 1;
+}
+
+int set_ext_copy(int *copy_type, const char *arg)
+{
+ if (OPENSSL_strcasecmp(arg, "none") == 0)
+ *copy_type = EXT_COPY_NONE;
+ else if (OPENSSL_strcasecmp(arg, "copy") == 0)
+ *copy_type = EXT_COPY_ADD;
+ else if (OPENSSL_strcasecmp(arg, "copyall") == 0)
+ *copy_type = EXT_COPY_ALL;
+ else
+ return 0;
+ return 1;
+}
+
+int copy_extensions(X509 *x, X509_REQ *req, int copy_type)
+{
+ STACK_OF(X509_EXTENSION) *exts;
+ int i, ret = 0;
+
+ if (x == NULL || req == NULL)
+ return 0;
+ if (copy_type == EXT_COPY_NONE)
+ return 1;
+ exts = X509_REQ_get_extensions(req);
+
+ for (i = 0; i < sk_X509_EXTENSION_num(exts); i++) {
+ X509_EXTENSION *ext = sk_X509_EXTENSION_value(exts, i);
+ ASN1_OBJECT *obj = X509_EXTENSION_get_object(ext);
+ int idx = X509_get_ext_by_OBJ(x, obj, -1);
+
+ /* Does extension exist in target? */
+ if (idx != -1) {
+ /* If normal copy don't override existing extension */
+ if (copy_type == EXT_COPY_ADD)
+ continue;
+ /* Delete all extensions of same type */
+ do {
+ X509_EXTENSION_free(X509_delete_ext(x, idx));
+ idx = X509_get_ext_by_OBJ(x, obj, -1);
+ } while (idx != -1);
+ }
+ if (!X509_add_ext(x, ext, -1))
+ goto end;
+ }
+ ret = 1;
+
+ end:
+ sk_X509_EXTENSION_pop_free(exts, X509_EXTENSION_free);
+ return ret;
+}
+
+static int set_multi_opts(unsigned long *flags, const char *arg,
+ const NAME_EX_TBL * in_tbl)
+{
+ STACK_OF(CONF_VALUE) *vals;
+ CONF_VALUE *val;
+ int i, ret = 1;
+ if (!arg)
+ return 0;
+ vals = X509V3_parse_list(arg);
+ for (i = 0; i < sk_CONF_VALUE_num(vals); i++) {
+ val = sk_CONF_VALUE_value(vals, i);
+ if (!set_table_opts(flags, val->name, in_tbl))
+ ret = 0;
+ }
+ sk_CONF_VALUE_pop_free(vals, X509V3_conf_free);
+ return ret;
+}
+
+static int set_table_opts(unsigned long *flags, const char *arg,
+ const NAME_EX_TBL * in_tbl)
+{
+ char c;
+ const NAME_EX_TBL *ptbl;
+ c = arg[0];
+
+ if (c == '-') {
+ c = 0;
+ arg++;
+ } else if (c == '+') {
+ c = 1;
+ arg++;
+ } else {
+ c = 1;
+ }
+
+ for (ptbl = in_tbl; ptbl->name; ptbl++) {
+ if (OPENSSL_strcasecmp(arg, ptbl->name) == 0) {
+ *flags &= ~ptbl->mask;
+ if (c)
+ *flags |= ptbl->flag;
+ else
+ *flags &= ~ptbl->flag;
+ return 1;
+ }
+ }
+ return 0;
+}
+
+void print_name(BIO *out, const char *title, const X509_NAME *nm)
+{
+ char *buf;
+ char mline = 0;
+ int indent = 0;
+ unsigned long lflags = get_nameopt();
+
+ if (out == NULL)
+ return;
+ if (title != NULL)
+ BIO_puts(out, title);
+ if ((lflags & XN_FLAG_SEP_MASK) == XN_FLAG_SEP_MULTILINE) {
+ mline = 1;
+ indent = 4;
+ }
+ if (lflags == XN_FLAG_COMPAT) {
+ buf = X509_NAME_oneline(nm, 0, 0);
+ BIO_puts(out, buf);
+ BIO_puts(out, "\n");
+ OPENSSL_free(buf);
+ } else {
+ if (mline)
+ BIO_puts(out, "\n");
+ X509_NAME_print_ex(out, nm, indent, lflags);
+ BIO_puts(out, "\n");
+ }
+}
+
+void print_bignum_var(BIO *out, const BIGNUM *in, const char *var,
+ int len, unsigned char *buffer)
+{
+ BIO_printf(out, " static unsigned char %s_%d[] = {", var, len);
+ if (BN_is_zero(in)) {
+ BIO_printf(out, "\n 0x00");
+ } else {
+ int i, l;
+
+ l = BN_bn2bin(in, buffer);
+ for (i = 0; i < l; i++) {
+ BIO_printf(out, (i % 10) == 0 ? "\n " : " ");
+ if (i < l - 1)
+ BIO_printf(out, "0x%02X,", buffer[i]);
+ else
+ BIO_printf(out, "0x%02X", buffer[i]);
+ }
+ }
+ BIO_printf(out, "\n };\n");
+}
+
+void print_array(BIO *out, const char* title, int len, const unsigned char* d)
+{
+ int i;
+
+ BIO_printf(out, "unsigned char %s[%d] = {", title, len);
+ for (i = 0; i < len; i++) {
+ if ((i % 10) == 0)
+ BIO_printf(out, "\n ");
+ if (i < len - 1)
+ BIO_printf(out, "0x%02X, ", d[i]);
+ else
+ BIO_printf(out, "0x%02X", d[i]);
+ }
+ BIO_printf(out, "\n};\n");
+}
+
+X509_STORE *setup_verify(const char *CAfile, int noCAfile,
+ const char *CApath, int noCApath,
+ const char *CAstore, int noCAstore)
+{
+ X509_STORE *store = X509_STORE_new();
+ X509_LOOKUP *lookup;
+ OSSL_LIB_CTX *libctx = app_get0_libctx();
+ const char *propq = app_get0_propq();
+
+ if (store == NULL)
+ goto end;
+
+ if (CAfile != NULL || !noCAfile) {
+ lookup = X509_STORE_add_lookup(store, X509_LOOKUP_file());
+ if (lookup == NULL)
+ goto end;
+ if (CAfile != NULL) {
+ if (X509_LOOKUP_load_file_ex(lookup, CAfile, X509_FILETYPE_PEM,
+ libctx, propq) <= 0) {
+ BIO_printf(bio_err, "Error loading file %s\n", CAfile);
+ goto end;
+ }
+ } else {
+ X509_LOOKUP_load_file_ex(lookup, NULL, X509_FILETYPE_DEFAULT,
+ libctx, propq);
+ }
+ }
+
+ if (CApath != NULL || !noCApath) {
+ lookup = X509_STORE_add_lookup(store, X509_LOOKUP_hash_dir());
+ if (lookup == NULL)
+ goto end;
+ if (CApath != NULL) {
+ if (X509_LOOKUP_add_dir(lookup, CApath, X509_FILETYPE_PEM) <= 0) {
+ BIO_printf(bio_err, "Error loading directory %s\n", CApath);
+ goto end;
+ }
+ } else {
+ X509_LOOKUP_add_dir(lookup, NULL, X509_FILETYPE_DEFAULT);
+ }
+ }
+
+ if (CAstore != NULL || !noCAstore) {
+ lookup = X509_STORE_add_lookup(store, X509_LOOKUP_store());
+ if (lookup == NULL)
+ goto end;
+ if (!X509_LOOKUP_add_store_ex(lookup, CAstore, libctx, propq)) {
+ if (CAstore != NULL)
+ BIO_printf(bio_err, "Error loading store URI %s\n", CAstore);
+ goto end;
+ }
+ }
+
+ ERR_clear_error();
+ return store;
+ end:
+ ERR_print_errors(bio_err);
+ X509_STORE_free(store);
+ return NULL;
+}
+
+static unsigned long index_serial_hash(const OPENSSL_CSTRING *a)
+{
+ const char *n;
+
+ n = a[DB_serial];
+ while (*n == '0')
+ n++;
+ return OPENSSL_LH_strhash(n);
+}
+
+static int index_serial_cmp(const OPENSSL_CSTRING *a,
+ const OPENSSL_CSTRING *b)
+{
+ const char *aa, *bb;
+
+ for (aa = a[DB_serial]; *aa == '0'; aa++) ;
+ for (bb = b[DB_serial]; *bb == '0'; bb++) ;
+ return strcmp(aa, bb);
+}
+
+static int index_name_qual(char **a)
+{
+ return (a[0][0] == 'V');
+}
+
+static unsigned long index_name_hash(const OPENSSL_CSTRING *a)
+{
+ return OPENSSL_LH_strhash(a[DB_name]);
+}
+
+int index_name_cmp(const OPENSSL_CSTRING *a, const OPENSSL_CSTRING *b)
+{
+ return strcmp(a[DB_name], b[DB_name]);
+}
+
+static IMPLEMENT_LHASH_HASH_FN(index_serial, OPENSSL_CSTRING)
+static IMPLEMENT_LHASH_COMP_FN(index_serial, OPENSSL_CSTRING)
+static IMPLEMENT_LHASH_HASH_FN(index_name, OPENSSL_CSTRING)
+static IMPLEMENT_LHASH_COMP_FN(index_name, OPENSSL_CSTRING)
+#undef BSIZE
+#define BSIZE 256
+BIGNUM *load_serial(const char *serialfile, int *exists, int create,
+ ASN1_INTEGER **retai)
+{
+ BIO *in = NULL;
+ BIGNUM *ret = NULL;
+ char buf[1024];
+ ASN1_INTEGER *ai = NULL;
+
+ ai = ASN1_INTEGER_new();
+ if (ai == NULL)
+ goto err;
+
+ in = BIO_new_file(serialfile, "r");
+ if (exists != NULL)
+ *exists = in != NULL;
+ if (in == NULL) {
+ if (!create) {
+ perror(serialfile);
+ goto err;
+ }
+ ERR_clear_error();
+ ret = BN_new();
+ if (ret == NULL) {
+ BIO_printf(bio_err, "Out of memory\n");
+ } else if (!rand_serial(ret, ai)) {
+ BIO_printf(bio_err, "Error creating random number to store in %s\n",
+ serialfile);
+ BN_free(ret);
+ ret = NULL;
+ }
+ } else {
+ if (!a2i_ASN1_INTEGER(in, ai, buf, 1024)) {
+ BIO_printf(bio_err, "Unable to load number from %s\n",
+ serialfile);
+ goto err;
+ }
+ ret = ASN1_INTEGER_to_BN(ai, NULL);
+ if (ret == NULL) {
+ BIO_printf(bio_err, "Error converting number from bin to BIGNUM\n");
+ goto err;
+ }
+ }
+
+ if (ret != NULL && retai != NULL) {
+ *retai = ai;
+ ai = NULL;
+ }
+ err:
+ if (ret == NULL)
+ ERR_print_errors(bio_err);
+ BIO_free(in);
+ ASN1_INTEGER_free(ai);
+ return ret;
+}
+
+int save_serial(const char *serialfile, const char *suffix, const BIGNUM *serial,
+ ASN1_INTEGER **retai)
+{
+ char buf[1][BSIZE];
+ BIO *out = NULL;
+ int ret = 0;
+ ASN1_INTEGER *ai = NULL;
+ int j;
+
+ if (suffix == NULL)
+ j = strlen(serialfile);
+ else
+ j = strlen(serialfile) + strlen(suffix) + 1;
+ if (j >= BSIZE) {
+ BIO_printf(bio_err, "File name too long\n");
+ goto err;
+ }
+
+ if (suffix == NULL)
+ OPENSSL_strlcpy(buf[0], serialfile, BSIZE);
+ else {
+#ifndef OPENSSL_SYS_VMS
+ j = BIO_snprintf(buf[0], sizeof(buf[0]), "%s.%s", serialfile, suffix);
+#else
+ j = BIO_snprintf(buf[0], sizeof(buf[0]), "%s-%s", serialfile, suffix);
+#endif
+ }
+ out = BIO_new_file(buf[0], "w");
+ if (out == NULL) {
+ goto err;
+ }
+
+ if ((ai = BN_to_ASN1_INTEGER(serial, NULL)) == NULL) {
+ BIO_printf(bio_err, "error converting serial to ASN.1 format\n");
+ goto err;
+ }
+ i2a_ASN1_INTEGER(out, ai);
+ BIO_puts(out, "\n");
+ ret = 1;
+ if (retai) {
+ *retai = ai;
+ ai = NULL;
+ }
+ err:
+ if (!ret)
+ ERR_print_errors(bio_err);
+ BIO_free_all(out);
+ ASN1_INTEGER_free(ai);
+ return ret;
+}
+
+int rotate_serial(const char *serialfile, const char *new_suffix,
+ const char *old_suffix)
+{
+ char buf[2][BSIZE];
+ int i, j;
+
+ i = strlen(serialfile) + strlen(old_suffix);
+ j = strlen(serialfile) + strlen(new_suffix);
+ if (i > j)
+ j = i;
+ if (j + 1 >= BSIZE) {
+ BIO_printf(bio_err, "File name too long\n");
+ goto err;
+ }
+#ifndef OPENSSL_SYS_VMS
+ j = BIO_snprintf(buf[0], sizeof(buf[0]), "%s.%s", serialfile, new_suffix);
+ j = BIO_snprintf(buf[1], sizeof(buf[1]), "%s.%s", serialfile, old_suffix);
+#else
+ j = BIO_snprintf(buf[0], sizeof(buf[0]), "%s-%s", serialfile, new_suffix);
+ j = BIO_snprintf(buf[1], sizeof(buf[1]), "%s-%s", serialfile, old_suffix);
+#endif
+ if (rename(serialfile, buf[1]) < 0 && errno != ENOENT
+#ifdef ENOTDIR
+ && errno != ENOTDIR
+#endif
+ ) {
+ BIO_printf(bio_err,
+ "Unable to rename %s to %s\n", serialfile, buf[1]);
+ perror("reason");
+ goto err;
+ }
+ if (rename(buf[0], serialfile) < 0) {
+ BIO_printf(bio_err,
+ "Unable to rename %s to %s\n", buf[0], serialfile);
+ perror("reason");
+ rename(buf[1], serialfile);
+ goto err;
+ }
+ return 1;
+ err:
+ ERR_print_errors(bio_err);
+ return 0;
+}
+
+int rand_serial(BIGNUM *b, ASN1_INTEGER *ai)
+{
+ BIGNUM *btmp;
+ int ret = 0;
+
+ btmp = b == NULL ? BN_new() : b;
+ if (btmp == NULL)
+ return 0;
+
+ if (!BN_rand(btmp, SERIAL_RAND_BITS, BN_RAND_TOP_ANY, BN_RAND_BOTTOM_ANY))
+ goto error;
+ if (ai && !BN_to_ASN1_INTEGER(btmp, ai))
+ goto error;
+
+ ret = 1;
+
+ error:
+
+ if (btmp != b)
+ BN_free(btmp);
+
+ return ret;
+}
+
+CA_DB *load_index(const char *dbfile, DB_ATTR *db_attr)
+{
+ CA_DB *retdb = NULL;
+ TXT_DB *tmpdb = NULL;
+ BIO *in;
+ CONF *dbattr_conf = NULL;
+ char buf[BSIZE];
+#ifndef OPENSSL_NO_POSIX_IO
+ FILE *dbfp;
+ struct stat dbst;
+#endif
+
+ in = BIO_new_file(dbfile, "r");
+ if (in == NULL)
+ goto err;
+
+#ifndef OPENSSL_NO_POSIX_IO
+ BIO_get_fp(in, &dbfp);
+ if (fstat(fileno(dbfp), &dbst) == -1) {
+ ERR_raise_data(ERR_LIB_SYS, errno,
+ "calling fstat(%s)", dbfile);
+ goto err;
+ }
+#endif
+
+ if ((tmpdb = TXT_DB_read(in, DB_NUMBER)) == NULL)
+ goto err;
+
+#ifndef OPENSSL_SYS_VMS
+ BIO_snprintf(buf, sizeof(buf), "%s.attr", dbfile);
+#else
+ BIO_snprintf(buf, sizeof(buf), "%s-attr", dbfile);
+#endif
+ dbattr_conf = app_load_config_quiet(buf);
+
+ retdb = app_malloc(sizeof(*retdb), "new DB");
+ retdb->db = tmpdb;
+ tmpdb = NULL;
+ if (db_attr)
+ retdb->attributes = *db_attr;
+ else {
+ retdb->attributes.unique_subject = 1;
+ }
+
+ if (dbattr_conf) {
+ char *p = NCONF_get_string(dbattr_conf, NULL, "unique_subject");
+ if (p) {
+ retdb->attributes.unique_subject = parse_yesno(p, 1);
+ } else {
+ ERR_clear_error();
+ }
+
+ }
+
+ retdb->dbfname = OPENSSL_strdup(dbfile);
+#ifndef OPENSSL_NO_POSIX_IO
+ retdb->dbst = dbst;
+#endif
+
+ err:
+ ERR_print_errors(bio_err);
+ NCONF_free(dbattr_conf);
+ TXT_DB_free(tmpdb);
+ BIO_free_all(in);
+ return retdb;
+}
+
+/*
+ * Returns > 0 on success, <= 0 on error
+ */
+int index_index(CA_DB *db)
+{
+ if (!TXT_DB_create_index(db->db, DB_serial, NULL,
+ LHASH_HASH_FN(index_serial),
+ LHASH_COMP_FN(index_serial))) {
+ BIO_printf(bio_err,
+ "Error creating serial number index:(%ld,%ld,%ld)\n",
+ db->db->error, db->db->arg1, db->db->arg2);
+ goto err;
+ }
+
+ if (db->attributes.unique_subject
+ && !TXT_DB_create_index(db->db, DB_name, index_name_qual,
+ LHASH_HASH_FN(index_name),
+ LHASH_COMP_FN(index_name))) {
+ BIO_printf(bio_err, "Error creating name index:(%ld,%ld,%ld)\n",
+ db->db->error, db->db->arg1, db->db->arg2);
+ goto err;
+ }
+ return 1;
+ err:
+ ERR_print_errors(bio_err);
+ return 0;
+}
+
+int save_index(const char *dbfile, const char *suffix, CA_DB *db)
+{
+ char buf[3][BSIZE];
+ BIO *out;
+ int j;
+
+ j = strlen(dbfile) + strlen(suffix);
+ if (j + 6 >= BSIZE) {
+ BIO_printf(bio_err, "File name too long\n");
+ goto err;
+ }
+#ifndef OPENSSL_SYS_VMS
+ j = BIO_snprintf(buf[2], sizeof(buf[2]), "%s.attr", dbfile);
+ j = BIO_snprintf(buf[1], sizeof(buf[1]), "%s.attr.%s", dbfile, suffix);
+ j = BIO_snprintf(buf[0], sizeof(buf[0]), "%s.%s", dbfile, suffix);
+#else
+ j = BIO_snprintf(buf[2], sizeof(buf[2]), "%s-attr", dbfile);
+ j = BIO_snprintf(buf[1], sizeof(buf[1]), "%s-attr-%s", dbfile, suffix);
+ j = BIO_snprintf(buf[0], sizeof(buf[0]), "%s-%s", dbfile, suffix);
+#endif
+ out = BIO_new_file(buf[0], "w");
+ if (out == NULL) {
+ perror(dbfile);
+ BIO_printf(bio_err, "Unable to open '%s'\n", dbfile);
+ goto err;
+ }
+ j = TXT_DB_write(out, db->db);
+ BIO_free(out);
+ if (j <= 0)
+ goto err;
+
+ out = BIO_new_file(buf[1], "w");
+ if (out == NULL) {
+ perror(buf[2]);
+ BIO_printf(bio_err, "Unable to open '%s'\n", buf[2]);
+ goto err;
+ }
+ BIO_printf(out, "unique_subject = %s\n",
+ db->attributes.unique_subject ? "yes" : "no");
+ BIO_free(out);
+
+ return 1;
+ err:
+ ERR_print_errors(bio_err);
+ return 0;
+}
+
+int rotate_index(const char *dbfile, const char *new_suffix,
+ const char *old_suffix)
+{
+ char buf[5][BSIZE];
+ int i, j;
+
+ i = strlen(dbfile) + strlen(old_suffix);
+ j = strlen(dbfile) + strlen(new_suffix);
+ if (i > j)
+ j = i;
+ if (j + 6 >= BSIZE) {
+ BIO_printf(bio_err, "File name too long\n");
+ goto err;
+ }
+#ifndef OPENSSL_SYS_VMS
+ j = BIO_snprintf(buf[4], sizeof(buf[4]), "%s.attr", dbfile);
+ j = BIO_snprintf(buf[3], sizeof(buf[3]), "%s.attr.%s", dbfile, old_suffix);
+ j = BIO_snprintf(buf[2], sizeof(buf[2]), "%s.attr.%s", dbfile, new_suffix);
+ j = BIO_snprintf(buf[1], sizeof(buf[1]), "%s.%s", dbfile, old_suffix);
+ j = BIO_snprintf(buf[0], sizeof(buf[0]), "%s.%s", dbfile, new_suffix);
+#else
+ j = BIO_snprintf(buf[4], sizeof(buf[4]), "%s-attr", dbfile);
+ j = BIO_snprintf(buf[3], sizeof(buf[3]), "%s-attr-%s", dbfile, old_suffix);
+ j = BIO_snprintf(buf[2], sizeof(buf[2]), "%s-attr-%s", dbfile, new_suffix);
+ j = BIO_snprintf(buf[1], sizeof(buf[1]), "%s-%s", dbfile, old_suffix);
+ j = BIO_snprintf(buf[0], sizeof(buf[0]), "%s-%s", dbfile, new_suffix);
+#endif
+ if (rename(dbfile, buf[1]) < 0 && errno != ENOENT
+#ifdef ENOTDIR
+ && errno != ENOTDIR
+#endif
+ ) {
+ BIO_printf(bio_err, "Unable to rename %s to %s\n", dbfile, buf[1]);
+ perror("reason");
+ goto err;
+ }
+ if (rename(buf[0], dbfile) < 0) {
+ BIO_printf(bio_err, "Unable to rename %s to %s\n", buf[0], dbfile);
+ perror("reason");
+ rename(buf[1], dbfile);
+ goto err;
+ }
+ if (rename(buf[4], buf[3]) < 0 && errno != ENOENT
+#ifdef ENOTDIR
+ && errno != ENOTDIR
+#endif
+ ) {
+ BIO_printf(bio_err, "Unable to rename %s to %s\n", buf[4], buf[3]);
+ perror("reason");
+ rename(dbfile, buf[0]);
+ rename(buf[1], dbfile);
+ goto err;
+ }
+ if (rename(buf[2], buf[4]) < 0) {
+ BIO_printf(bio_err, "Unable to rename %s to %s\n", buf[2], buf[4]);
+ perror("reason");
+ rename(buf[3], buf[4]);
+ rename(dbfile, buf[0]);
+ rename(buf[1], dbfile);
+ goto err;
+ }
+ return 1;
+ err:
+ ERR_print_errors(bio_err);
+ return 0;
+}
+
+void free_index(CA_DB *db)
+{
+ if (db) {
+ TXT_DB_free(db->db);
+ OPENSSL_free(db->dbfname);
+ OPENSSL_free(db);
+ }
+}
+
+int parse_yesno(const char *str, int def)
+{
+ if (str) {
+ switch (*str) {
+ case 'f': /* false */
+ case 'F': /* FALSE */
+ case 'n': /* no */
+ case 'N': /* NO */
+ case '0': /* 0 */
+ return 0;
+ case 't': /* true */
+ case 'T': /* TRUE */
+ case 'y': /* yes */
+ case 'Y': /* YES */
+ case '1': /* 1 */
+ return 1;
+ }
+ }
+ return def;
+}
+
+/*
+ * name is expected to be in the format /type0=value0/type1=value1/type2=...
+ * where + can be used instead of / to form multi-valued RDNs if canmulti
+ * and characters may be escaped by \
+ */
+X509_NAME *parse_name(const char *cp, int chtype, int canmulti,
+ const char *desc)
+{
+ int nextismulti = 0;
+ char *work;
+ X509_NAME *n;
+
+ if (*cp++ != '/') {
+ BIO_printf(bio_err,
+ "%s: %s name is expected to be in the format "
+ "/type0=value0/type1=value1/type2=... where characters may "
+ "be escaped by \\. This name is not in that format: '%s'\n",
+ opt_getprog(), desc, --cp);
+ return NULL;
+ }
+
+ n = X509_NAME_new();
+ if (n == NULL) {
+ BIO_printf(bio_err, "%s: Out of memory\n", opt_getprog());
+ return NULL;
+ }
+ work = OPENSSL_strdup(cp);
+ if (work == NULL) {
+ BIO_printf(bio_err, "%s: Error copying %s name input\n",
+ opt_getprog(), desc);
+ goto err;
+ }
+
+ while (*cp != '\0') {
+ char *bp = work;
+ char *typestr = bp;
+ unsigned char *valstr;
+ int nid;
+ int ismulti = nextismulti;
+ nextismulti = 0;
+
+ /* Collect the type */
+ while (*cp != '\0' && *cp != '=')
+ *bp++ = *cp++;
+ *bp++ = '\0';
+ if (*cp == '\0') {
+ BIO_printf(bio_err,
+ "%s: Missing '=' after RDN type string '%s' in %s name string\n",
+ opt_getprog(), typestr, desc);
+ goto err;
+ }
+ ++cp;
+
+ /* Collect the value. */
+ valstr = (unsigned char *)bp;
+ for (; *cp != '\0' && *cp != '/'; *bp++ = *cp++) {
+ /* unescaped '+' symbol string signals further member of multiRDN */
+ if (canmulti && *cp == '+') {
+ nextismulti = 1;
+ break;
+ }
+ if (*cp == '\\' && *++cp == '\0') {
+ BIO_printf(bio_err,
+ "%s: Escape character at end of %s name string\n",
+ opt_getprog(), desc);
+ goto err;
+ }
+ }
+ *bp++ = '\0';
+
+ /* If not at EOS (must be + or /), move forward. */
+ if (*cp != '\0')
+ ++cp;
+
+ /* Parse */
+ nid = OBJ_txt2nid(typestr);
+ if (nid == NID_undef) {
+ BIO_printf(bio_err,
+ "%s warning: Skipping unknown %s name attribute \"%s\"\n",
+ opt_getprog(), desc, typestr);
+ if (ismulti)
+ BIO_printf(bio_err,
+ "%s hint: a '+' in a value string needs be escaped using '\\' else a new member of a multi-valued RDN is expected\n",
+ opt_getprog());
+ continue;
+ }
+ if (*valstr == '\0') {
+ BIO_printf(bio_err,
+ "%s warning: No value provided for %s name attribute \"%s\", skipped\n",
+ opt_getprog(), desc, typestr);
+ continue;
+ }
+ if (!X509_NAME_add_entry_by_NID(n, nid, chtype,
+ valstr, strlen((char *)valstr),
+ -1, ismulti ? -1 : 0)) {
+ ERR_print_errors(bio_err);
+ BIO_printf(bio_err,
+ "%s: Error adding %s name attribute \"/%s=%s\"\n",
+ opt_getprog(), desc, typestr ,valstr);
+ goto err;
+ }
+ }
+
+ OPENSSL_free(work);
+ return n;
+
+ err:
+ X509_NAME_free(n);
+ OPENSSL_free(work);
+ return NULL;
+}
+
+/*
+ * Read whole contents of a BIO into an allocated memory buffer and return
+ * it.
+ */
+
+int bio_to_mem(unsigned char **out, int maxlen, BIO *in)
+{
+ BIO *mem;
+ int len, ret;
+ unsigned char tbuf[1024];
+
+ mem = BIO_new(BIO_s_mem());
+ if (mem == NULL)
+ return -1;
+ for (;;) {
+ if ((maxlen != -1) && maxlen < 1024)
+ len = maxlen;
+ else
+ len = 1024;
+ len = BIO_read(in, tbuf, len);
+ if (len < 0) {
+ BIO_free(mem);
+ return -1;
+ }
+ if (len == 0)
+ break;
+ if (BIO_write(mem, tbuf, len) != len) {
+ BIO_free(mem);
+ return -1;
+ }
+ if (maxlen != -1)
+ maxlen -= len;
+
+ if (maxlen == 0)
+ break;
+ }
+ ret = BIO_get_mem_data(mem, (char **)out);
+ BIO_set_flags(mem, BIO_FLAGS_MEM_RDONLY);
+ BIO_free(mem);
+ return ret;
+}
+
+int pkey_ctrl_string(EVP_PKEY_CTX *ctx, const char *value)
+{
+ int rv = 0;
+ char *stmp, *vtmp = NULL;
+
+ stmp = OPENSSL_strdup(value);
+ if (stmp == NULL)
+ return -1;
+ vtmp = strchr(stmp, ':');
+ if (vtmp == NULL)
+ goto err;
+
+ *vtmp = 0;
+ vtmp++;
+ rv = EVP_PKEY_CTX_ctrl_str(ctx, stmp, vtmp);
+
+ err:
+ OPENSSL_free(stmp);
+ return rv;
+}
+
+static void nodes_print(const char *name, STACK_OF(X509_POLICY_NODE) *nodes)
+{
+ X509_POLICY_NODE *node;
+ int i;
+
+ BIO_printf(bio_err, "%s Policies:", name);
+ if (nodes) {
+ BIO_puts(bio_err, "\n");
+ for (i = 0; i < sk_X509_POLICY_NODE_num(nodes); i++) {
+ node = sk_X509_POLICY_NODE_value(nodes, i);
+ X509_POLICY_NODE_print(bio_err, node, 2);
+ }
+ } else {
+ BIO_puts(bio_err, " <empty>\n");
+ }
+}
+
+void policies_print(X509_STORE_CTX *ctx)
+{
+ X509_POLICY_TREE *tree;
+ int explicit_policy;
+ tree = X509_STORE_CTX_get0_policy_tree(ctx);
+ explicit_policy = X509_STORE_CTX_get_explicit_policy(ctx);
+
+ BIO_printf(bio_err, "Require explicit Policy: %s\n",
+ explicit_policy ? "True" : "False");
+
+ nodes_print("Authority", X509_policy_tree_get0_policies(tree));
+ nodes_print("User", X509_policy_tree_get0_user_policies(tree));
+}
+
+/*-
+ * next_protos_parse parses a comma separated list of strings into a string
+ * in a format suitable for passing to SSL_CTX_set_next_protos_advertised.
+ * outlen: (output) set to the length of the resulting buffer on success.
+ * err: (maybe NULL) on failure, an error message line is written to this BIO.
+ * in: a NUL terminated string like "abc,def,ghi"
+ *
+ * returns: a malloc'd buffer or NULL on failure.
+ */
+unsigned char *next_protos_parse(size_t *outlen, const char *in)
+{
+ size_t len;
+ unsigned char *out;
+ size_t i, start = 0;
+ size_t skipped = 0;
+
+ len = strlen(in);
+ if (len == 0 || len >= 65535)
+ return NULL;
+
+ out = app_malloc(len + 1, "NPN buffer");
+ for (i = 0; i <= len; ++i) {
+ if (i == len || in[i] == ',') {
+ /*
+ * Zero-length ALPN elements are invalid on the wire, we could be
+ * strict and reject the entire string, but just ignoring extra
+ * commas seems harmless and more friendly.
+ *
+ * Every comma we skip in this way puts the input buffer another
+ * byte ahead of the output buffer, so all stores into the output
+ * buffer need to be decremented by the number commas skipped.
+ */
+ if (i == start) {
+ ++start;
+ ++skipped;
+ continue;
+ }
+ if (i - start > 255) {
+ OPENSSL_free(out);
+ return NULL;
+ }
+ out[start-skipped] = (unsigned char)(i - start);
+ start = i + 1;
+ } else {
+ out[i + 1 - skipped] = in[i];
+ }
+ }
+
+ if (len <= skipped) {
+ OPENSSL_free(out);
+ return NULL;
+ }
+
+ *outlen = len + 1 - skipped;
+ return out;
+}
+
+void print_cert_checks(BIO *bio, X509 *x,
+ const char *checkhost,
+ const char *checkemail, const char *checkip)
+{
+ if (x == NULL)
+ return;
+ if (checkhost) {
+ BIO_printf(bio, "Hostname %s does%s match certificate\n",
+ checkhost,
+ X509_check_host(x, checkhost, 0, 0, NULL) == 1
+ ? "" : " NOT");
+ }
+
+ if (checkemail) {
+ BIO_printf(bio, "Email %s does%s match certificate\n",
+ checkemail, X509_check_email(x, checkemail, 0, 0)
+ ? "" : " NOT");
+ }
+
+ if (checkip) {
+ BIO_printf(bio, "IP %s does%s match certificate\n",
+ checkip, X509_check_ip_asc(x, checkip, 0) ? "" : " NOT");
+ }
+}
+
+static int do_pkey_ctx_init(EVP_PKEY_CTX *pkctx, STACK_OF(OPENSSL_STRING) *opts)
+{
+ int i;
+
+ if (opts == NULL)
+ return 1;
+
+ for (i = 0; i < sk_OPENSSL_STRING_num(opts); i++) {
+ char *opt = sk_OPENSSL_STRING_value(opts, i);
+ if (pkey_ctrl_string(pkctx, opt) <= 0) {
+ BIO_printf(bio_err, "parameter error \"%s\"\n", opt);
+ ERR_print_errors(bio_err);
+ return 0;
+ }
+ }
+
+ return 1;
+}
+
+static int do_x509_init(X509 *x, STACK_OF(OPENSSL_STRING) *opts)
+{
+ int i;
+
+ if (opts == NULL)
+ return 1;
+
+ for (i = 0; i < sk_OPENSSL_STRING_num(opts); i++) {
+ char *opt = sk_OPENSSL_STRING_value(opts, i);
+ if (x509_ctrl_string(x, opt) <= 0) {
+ BIO_printf(bio_err, "parameter error \"%s\"\n", opt);
+ ERR_print_errors(bio_err);
+ return 0;
+ }
+ }
+
+ return 1;
+}
+
+static int do_x509_req_init(X509_REQ *x, STACK_OF(OPENSSL_STRING) *opts)
+{
+ int i;
+
+ if (opts == NULL)
+ return 1;
+
+ for (i = 0; i < sk_OPENSSL_STRING_num(opts); i++) {
+ char *opt = sk_OPENSSL_STRING_value(opts, i);
+ if (x509_req_ctrl_string(x, opt) <= 0) {
+ BIO_printf(bio_err, "parameter error \"%s\"\n", opt);
+ ERR_print_errors(bio_err);
+ return 0;
+ }
+ }
+
+ return 1;
+}
+
+static int do_sign_init(EVP_MD_CTX *ctx, EVP_PKEY *pkey,
+ const char *md, STACK_OF(OPENSSL_STRING) *sigopts)
+{
+ EVP_PKEY_CTX *pkctx = NULL;
+ char def_md[80];
+
+ if (ctx == NULL)
+ return 0;
+ /*
+ * EVP_PKEY_get_default_digest_name() returns 2 if the digest is mandatory
+ * for this algorithm.
+ */
+ if (EVP_PKEY_get_default_digest_name(pkey, def_md, sizeof(def_md)) == 2
+ && strcmp(def_md, "UNDEF") == 0) {
+ /* The signing algorithm requires there to be no digest */
+ md = NULL;
+ }
+
+ return EVP_DigestSignInit_ex(ctx, &pkctx, md, app_get0_libctx(),
+ app_get0_propq(), pkey, NULL)
+ && do_pkey_ctx_init(pkctx, sigopts);
+}
+
+static int adapt_keyid_ext(X509 *cert, X509V3_CTX *ext_ctx,
+ const char *name, const char *value, int add_default)
+{
+ const STACK_OF(X509_EXTENSION) *exts = X509_get0_extensions(cert);
+ X509_EXTENSION *new_ext = X509V3_EXT_nconf(NULL, ext_ctx, name, value);
+ int idx, rv = 0;
+
+ if (new_ext == NULL)
+ return rv;
+
+ idx = X509v3_get_ext_by_OBJ(exts, X509_EXTENSION_get_object(new_ext), -1);
+ if (idx >= 0) {
+ X509_EXTENSION *found_ext = X509v3_get_ext(exts, idx);
+ ASN1_OCTET_STRING *data = X509_EXTENSION_get_data(found_ext);
+ int disabled = ASN1_STRING_length(data) <= 2; /* config said "none" */
+
+ if (disabled) {
+ X509_delete_ext(cert, idx);
+ X509_EXTENSION_free(found_ext);
+ } /* else keep existing key identifier, which might be outdated */
+ rv = 1;
+ } else {
+ rv = !add_default || X509_add_ext(cert, new_ext, -1);
+ }
+ X509_EXTENSION_free(new_ext);
+ return rv;
+}
+
+/* Ensure RFC 5280 compliance, adapt keyIDs as needed, and sign the cert info */
+int do_X509_sign(X509 *cert, EVP_PKEY *pkey, const char *md,
+ STACK_OF(OPENSSL_STRING) *sigopts, X509V3_CTX *ext_ctx)
+{
+ const STACK_OF(X509_EXTENSION) *exts = X509_get0_extensions(cert);
+ EVP_MD_CTX *mctx = EVP_MD_CTX_new();
+ int self_sign;
+ int rv = 0;
+
+ if (sk_X509_EXTENSION_num(exts /* may be NULL */) > 0) {
+ /* Prevent X509_V_ERR_EXTENSIONS_REQUIRE_VERSION_3 */
+ if (!X509_set_version(cert, X509_VERSION_3))
+ goto end;
+
+ /*
+ * Add default SKID before such that default AKID can make use of it
+ * in case the certificate is self-signed
+ */
+ /* Prevent X509_V_ERR_MISSING_SUBJECT_KEY_IDENTIFIER */
+ if (!adapt_keyid_ext(cert, ext_ctx, "subjectKeyIdentifier", "hash", 1))
+ goto end;
+ /* Prevent X509_V_ERR_MISSING_AUTHORITY_KEY_IDENTIFIER */
+ ERR_set_mark();
+ self_sign = X509_check_private_key(cert, pkey);
+ ERR_pop_to_mark();
+ if (!adapt_keyid_ext(cert, ext_ctx, "authorityKeyIdentifier",
+ "keyid, issuer", !self_sign))
+ goto end;
+ }
+
+ if (mctx != NULL && do_sign_init(mctx, pkey, md, sigopts) > 0)
+ rv = (X509_sign_ctx(cert, mctx) > 0);
+ end:
+ EVP_MD_CTX_free(mctx);
+ return rv;
+}
+
+/* Sign the certificate request info */
+int do_X509_REQ_sign(X509_REQ *x, EVP_PKEY *pkey, const char *md,
+ STACK_OF(OPENSSL_STRING) *sigopts)
+{
+ int rv = 0;
+ EVP_MD_CTX *mctx = EVP_MD_CTX_new();
+
+ if (do_sign_init(mctx, pkey, md, sigopts) > 0)
+ rv = (X509_REQ_sign_ctx(x, mctx) > 0);
+ EVP_MD_CTX_free(mctx);
+ return rv;
+}
+
+/* Sign the CRL info */
+int do_X509_CRL_sign(X509_CRL *x, EVP_PKEY *pkey, const char *md,
+ STACK_OF(OPENSSL_STRING) *sigopts)
+{
+ int rv = 0;
+ EVP_MD_CTX *mctx = EVP_MD_CTX_new();
+
+ if (do_sign_init(mctx, pkey, md, sigopts) > 0)
+ rv = (X509_CRL_sign_ctx(x, mctx) > 0);
+ EVP_MD_CTX_free(mctx);
+ return rv;
+}
+
+/*
+ * do_X509_verify returns 1 if the signature is valid,
+ * 0 if the signature check fails, or -1 if error occurs.
+ */
+int do_X509_verify(X509 *x, EVP_PKEY *pkey, STACK_OF(OPENSSL_STRING) *vfyopts)
+{
+ int rv = 0;
+
+ if (do_x509_init(x, vfyopts) > 0)
+ rv = X509_verify(x, pkey);
+ else
+ rv = -1;
+ return rv;
+}
+
+/*
+ * do_X509_REQ_verify returns 1 if the signature is valid,
+ * 0 if the signature check fails, or -1 if error occurs.
+ */
+int do_X509_REQ_verify(X509_REQ *x, EVP_PKEY *pkey,
+ STACK_OF(OPENSSL_STRING) *vfyopts)
+{
+ int rv = 0;
+
+ if (do_x509_req_init(x, vfyopts) > 0)
+ rv = X509_REQ_verify_ex(x, pkey,
+ app_get0_libctx(), app_get0_propq());
+ else
+ rv = -1;
+ return rv;
+}
+
+/* Get first http URL from a DIST_POINT structure */
+
+static const char *get_dp_url(DIST_POINT *dp)
+{
+ GENERAL_NAMES *gens;
+ GENERAL_NAME *gen;
+ int i, gtype;
+ ASN1_STRING *uri;
+ if (!dp->distpoint || dp->distpoint->type != 0)
+ return NULL;
+ gens = dp->distpoint->name.fullname;
+ for (i = 0; i < sk_GENERAL_NAME_num(gens); i++) {
+ gen = sk_GENERAL_NAME_value(gens, i);
+ uri = GENERAL_NAME_get0_value(gen, &gtype);
+ if (gtype == GEN_URI && ASN1_STRING_length(uri) > 6) {
+ const char *uptr = (const char *)ASN1_STRING_get0_data(uri);
+
+ if (IS_HTTP(uptr)) /* can/should not use HTTPS here */
+ return uptr;
+ }
+ }
+ return NULL;
+}
+
+/*
+ * Look through a CRLDP structure and attempt to find an http URL to
+ * downloads a CRL from.
+ */
+
+static X509_CRL *load_crl_crldp(STACK_OF(DIST_POINT) *crldp)
+{
+ int i;
+ const char *urlptr = NULL;
+ for (i = 0; i < sk_DIST_POINT_num(crldp); i++) {
+ DIST_POINT *dp = sk_DIST_POINT_value(crldp, i);
+ urlptr = get_dp_url(dp);
+ if (urlptr != NULL)
+ return load_crl(urlptr, FORMAT_UNDEF, 0, "CRL via CDP");
+ }
+ return NULL;
+}
+
+/*
+ * Example of downloading CRLs from CRLDP:
+ * not usable for real world as it always downloads and doesn't cache anything.
+ */
+
+static STACK_OF(X509_CRL) *crls_http_cb(const X509_STORE_CTX *ctx,
+ const X509_NAME *nm)
+{
+ X509 *x;
+ STACK_OF(X509_CRL) *crls = NULL;
+ X509_CRL *crl;
+ STACK_OF(DIST_POINT) *crldp;
+
+ crls = sk_X509_CRL_new_null();
+ if (!crls)
+ return NULL;
+ x = X509_STORE_CTX_get_current_cert(ctx);
+ crldp = X509_get_ext_d2i(x, NID_crl_distribution_points, NULL, NULL);
+ crl = load_crl_crldp(crldp);
+ sk_DIST_POINT_pop_free(crldp, DIST_POINT_free);
+ if (!crl) {
+ sk_X509_CRL_free(crls);
+ return NULL;
+ }
+ sk_X509_CRL_push(crls, crl);
+ /* Try to download delta CRL */
+ crldp = X509_get_ext_d2i(x, NID_freshest_crl, NULL, NULL);
+ crl = load_crl_crldp(crldp);
+ sk_DIST_POINT_pop_free(crldp, DIST_POINT_free);
+ if (crl)
+ sk_X509_CRL_push(crls, crl);
+ return crls;
+}
+
+void store_setup_crl_download(X509_STORE *st)
+{
+ X509_STORE_set_lookup_crls_cb(st, crls_http_cb);
+}
+
+#ifndef OPENSSL_NO_SOCK
+static const char *tls_error_hint(void)
+{
+ unsigned long err = ERR_peek_error();
+
+ if (ERR_GET_LIB(err) != ERR_LIB_SSL)
+ err = ERR_peek_last_error();
+ if (ERR_GET_LIB(err) != ERR_LIB_SSL)
+ return NULL;
+
+ switch (ERR_GET_REASON(err)) {
+ case SSL_R_WRONG_VERSION_NUMBER:
+ return "The server does not support (a suitable version of) TLS";
+ case SSL_R_UNKNOWN_PROTOCOL:
+ return "The server does not support HTTPS";
+ case SSL_R_CERTIFICATE_VERIFY_FAILED:
+ return "Cannot authenticate server via its TLS certificate, likely due to mismatch with our trusted TLS certs or missing revocation status";
+ case SSL_AD_REASON_OFFSET + TLS1_AD_UNKNOWN_CA:
+ return "Server did not accept our TLS certificate, likely due to mismatch with server's trust anchor or missing revocation status";
+ case SSL_AD_REASON_OFFSET + SSL3_AD_HANDSHAKE_FAILURE:
+ return "TLS handshake failure. Possibly the server requires our TLS certificate but did not receive it";
+ default: /* no error or no hint available for error */
+ return NULL;
+ }
+}
+
+/* HTTP callback function that supports TLS connection also via HTTPS proxy */
+BIO *app_http_tls_cb(BIO *bio, void *arg, int connect, int detail)
+{
+ APP_HTTP_TLS_INFO *info = (APP_HTTP_TLS_INFO *)arg;
+ SSL_CTX *ssl_ctx = info->ssl_ctx;
+
+ if (ssl_ctx == NULL) /* not using TLS */
+ return bio;
+ if (connect) {
+ SSL *ssl;
+ BIO *sbio = NULL;
+ X509_STORE *ts = SSL_CTX_get_cert_store(ssl_ctx);
+ X509_VERIFY_PARAM *vpm = X509_STORE_get0_param(ts);
+ const char *host = vpm == NULL ? NULL :
+ X509_VERIFY_PARAM_get0_host(vpm, 0 /* first hostname */);
+
+ /* adapt after fixing callback design flaw, see #17088 */
+ if ((info->use_proxy
+ && !OSSL_HTTP_proxy_connect(bio, info->server, info->port,
+ NULL, NULL, /* no proxy credentials */
+ info->timeout, bio_err, opt_getprog()))
+ || (sbio = BIO_new(BIO_f_ssl())) == NULL) {
+ return NULL;
+ }
+ if (ssl_ctx == NULL || (ssl = SSL_new(ssl_ctx)) == NULL) {
+ BIO_free(sbio);
+ return NULL;
+ }
+
+ if (vpm != NULL)
+ SSL_set_tlsext_host_name(ssl, host /* may be NULL */);
+
+ SSL_set_connect_state(ssl);
+ BIO_set_ssl(sbio, ssl, BIO_CLOSE);
+
+ bio = BIO_push(sbio, bio);
+ }
+ if (!connect) {
+ const char *hint;
+ BIO *cbio;
+
+ if (!detail) { /* disconnecting after error */
+ hint = tls_error_hint();
+ if (hint != NULL)
+ ERR_add_error_data(2, " : ", hint);
+ }
+ if (ssl_ctx != NULL) {
+ (void)ERR_set_mark();
+ BIO_ssl_shutdown(bio);
+ cbio = BIO_pop(bio); /* connect+HTTP BIO */
+ BIO_free(bio); /* SSL BIO */
+ (void)ERR_pop_to_mark(); /* hide SSL_R_READ_BIO_NOT_SET etc. */
+ bio = cbio;
+ }
+ }
+ return bio;
+}
+
+void APP_HTTP_TLS_INFO_free(APP_HTTP_TLS_INFO *info)
+{
+ if (info != NULL) {
+ SSL_CTX_free(info->ssl_ctx);
+ OPENSSL_free(info);
+ }
+}
+
+ASN1_VALUE *app_http_get_asn1(const char *url, const char *proxy,
+ const char *no_proxy, SSL_CTX *ssl_ctx,
+ const STACK_OF(CONF_VALUE) *headers,
+ long timeout, const char *expected_content_type,
+ const ASN1_ITEM *it)
+{
+ APP_HTTP_TLS_INFO info;
+ char *server;
+ char *port;
+ int use_ssl;
+ BIO *mem;
+ ASN1_VALUE *resp = NULL;
+
+ if (url == NULL || it == NULL) {
+ ERR_raise(ERR_LIB_HTTP, ERR_R_PASSED_NULL_PARAMETER);
+ return NULL;
+ }
+
+ if (!OSSL_HTTP_parse_url(url, &use_ssl, NULL /* userinfo */, &server, &port,
+ NULL /* port_num, */, NULL, NULL, NULL))
+ return NULL;
+ if (use_ssl && ssl_ctx == NULL) {
+ ERR_raise_data(ERR_LIB_HTTP, ERR_R_PASSED_NULL_PARAMETER,
+ "missing SSL_CTX");
+ goto end;
+ }
+ if (!use_ssl && ssl_ctx != NULL) {
+ ERR_raise_data(ERR_LIB_HTTP, ERR_R_PASSED_INVALID_ARGUMENT,
+ "SSL_CTX given but use_ssl == 0");
+ goto end;
+ }
+
+ info.server = server;
+ info.port = port;
+ info.use_proxy = /* workaround for callback design flaw, see #17088 */
+ OSSL_HTTP_adapt_proxy(proxy, no_proxy, server, use_ssl) != NULL;
+ info.timeout = timeout;
+ info.ssl_ctx = ssl_ctx;
+ mem = OSSL_HTTP_get(url, proxy, no_proxy, NULL /* bio */, NULL /* rbio */,
+ app_http_tls_cb, &info, 0 /* buf_size */, headers,
+ expected_content_type, 1 /* expect_asn1 */,
+ OSSL_HTTP_DEFAULT_MAX_RESP_LEN, timeout);
+ resp = ASN1_item_d2i_bio(it, mem, NULL);
+ BIO_free(mem);
+
+ end:
+ OPENSSL_free(server);
+ OPENSSL_free(port);
+ return resp;
+
+}
+
+ASN1_VALUE *app_http_post_asn1(const char *host, const char *port,
+ const char *path, const char *proxy,
+ const char *no_proxy, SSL_CTX *ssl_ctx,
+ const STACK_OF(CONF_VALUE) *headers,
+ const char *content_type,
+ ASN1_VALUE *req, const ASN1_ITEM *req_it,
+ const char *expected_content_type,
+ long timeout, const ASN1_ITEM *rsp_it)
+{
+ int use_ssl = ssl_ctx != NULL;
+ APP_HTTP_TLS_INFO info;
+ BIO *rsp, *req_mem = ASN1_item_i2d_mem_bio(req_it, req);
+ ASN1_VALUE *res;
+
+ if (req_mem == NULL)
+ return NULL;
+
+ info.server = host;
+ info.port = port;
+ info.use_proxy = /* workaround for callback design flaw, see #17088 */
+ OSSL_HTTP_adapt_proxy(proxy, no_proxy, host, use_ssl) != NULL;
+ info.timeout = timeout;
+ info.ssl_ctx = ssl_ctx;
+ rsp = OSSL_HTTP_transfer(NULL, host, port, path, use_ssl,
+ proxy, no_proxy, NULL /* bio */, NULL /* rbio */,
+ app_http_tls_cb, &info,
+ 0 /* buf_size */, headers, content_type, req_mem,
+ expected_content_type, 1 /* expect_asn1 */,
+ OSSL_HTTP_DEFAULT_MAX_RESP_LEN, timeout,
+ 0 /* keep_alive */);
+ BIO_free(req_mem);
+ res = ASN1_item_d2i_bio(rsp_it, rsp, NULL);
+ BIO_free(rsp);
+ return res;
+}
+
+#endif
+
+/*
+ * Platform-specific sections
+ */
+#if defined(_WIN32)
+# ifdef fileno
+# undef fileno
+# define fileno(a) (int)_fileno(a)
+# endif
+
+# include <windows.h>
+# include <tchar.h>
+
+static int WIN32_rename(const char *from, const char *to)
+{
+ TCHAR *tfrom = NULL, *tto;
+ DWORD err;
+ int ret = 0;
+
+ if (sizeof(TCHAR) == 1) {
+ tfrom = (TCHAR *)from;
+ tto = (TCHAR *)to;
+ } else { /* UNICODE path */
+
+ size_t i, flen = strlen(from) + 1, tlen = strlen(to) + 1;
+ tfrom = malloc(sizeof(*tfrom) * (flen + tlen));
+ if (tfrom == NULL)
+ goto err;
+ tto = tfrom + flen;
+# if !defined(_WIN32_WCE) || _WIN32_WCE>=101
+ if (!MultiByteToWideChar(CP_ACP, 0, from, flen, (WCHAR *)tfrom, flen))
+# endif
+ for (i = 0; i < flen; i++)
+ tfrom[i] = (TCHAR)from[i];
+# if !defined(_WIN32_WCE) || _WIN32_WCE>=101
+ if (!MultiByteToWideChar(CP_ACP, 0, to, tlen, (WCHAR *)tto, tlen))
+# endif
+ for (i = 0; i < tlen; i++)
+ tto[i] = (TCHAR)to[i];
+ }
+
+ if (MoveFile(tfrom, tto))
+ goto ok;
+ err = GetLastError();
+ if (err == ERROR_ALREADY_EXISTS || err == ERROR_FILE_EXISTS) {
+ if (DeleteFile(tto) && MoveFile(tfrom, tto))
+ goto ok;
+ err = GetLastError();
+ }
+ if (err == ERROR_FILE_NOT_FOUND || err == ERROR_PATH_NOT_FOUND)
+ errno = ENOENT;
+ else if (err == ERROR_ACCESS_DENIED)
+ errno = EACCES;
+ else
+ errno = EINVAL; /* we could map more codes... */
+ err:
+ ret = -1;
+ ok:
+ if (tfrom != NULL && tfrom != (TCHAR *)from)
+ free(tfrom);
+ return ret;
+}
+#endif
+
+/* app_tminterval section */
+#if defined(_WIN32)
+double app_tminterval(int stop, int usertime)
+{
+ FILETIME now;
+ double ret = 0;
+ static ULARGE_INTEGER tmstart;
+ static int warning = 1;
+# ifdef _WIN32_WINNT
+ static HANDLE proc = NULL;
+
+ if (proc == NULL) {
+ if (check_winnt())
+ proc = OpenProcess(PROCESS_QUERY_INFORMATION, FALSE,
+ GetCurrentProcessId());
+ if (proc == NULL)
+ proc = (HANDLE) - 1;
+ }
+
+ if (usertime && proc != (HANDLE) - 1) {
+ FILETIME junk;
+ GetProcessTimes(proc, &junk, &junk, &junk, &now);
+ } else
+# endif
+ {
+ SYSTEMTIME systime;
+
+ if (usertime && warning) {
+ BIO_printf(bio_err, "To get meaningful results, run "
+ "this program on idle system.\n");
+ warning = 0;
+ }
+ GetSystemTime(&systime);
+ SystemTimeToFileTime(&systime, &now);
+ }
+
+ if (stop == TM_START) {
+ tmstart.u.LowPart = now.dwLowDateTime;
+ tmstart.u.HighPart = now.dwHighDateTime;
+ } else {
+ ULARGE_INTEGER tmstop;
+
+ tmstop.u.LowPart = now.dwLowDateTime;
+ tmstop.u.HighPart = now.dwHighDateTime;
+
+ ret = (__int64)(tmstop.QuadPart - tmstart.QuadPart) * 1e-7;
+ }
+
+ return ret;
+}
+#elif defined(OPENSSL_SYS_VXWORKS)
+# include <time.h>
+
+double app_tminterval(int stop, int usertime)
+{
+ double ret = 0;
+# ifdef CLOCK_REALTIME
+ static struct timespec tmstart;
+ struct timespec now;
+# else
+ static unsigned long tmstart;
+ unsigned long now;
+# endif
+ static int warning = 1;
+
+ if (usertime && warning) {
+ BIO_printf(bio_err, "To get meaningful results, run "
+ "this program on idle system.\n");
+ warning = 0;
+ }
+# ifdef CLOCK_REALTIME
+ clock_gettime(CLOCK_REALTIME, &now);
+ if (stop == TM_START)
+ tmstart = now;
+ else
+ ret = ((now.tv_sec + now.tv_nsec * 1e-9)
+ - (tmstart.tv_sec + tmstart.tv_nsec * 1e-9));
+# else
+ now = tickGet();
+ if (stop == TM_START)
+ tmstart = now;
+ else
+ ret = (now - tmstart) / (double)sysClkRateGet();
+# endif
+ return ret;
+}
+
+#elif defined(_SC_CLK_TCK) /* by means of unistd.h */
+# include <sys/times.h>
+
+double app_tminterval(int stop, int usertime)
+{
+ double ret = 0;
+ struct tms rus;
+ clock_t now = times(&rus);
+ static clock_t tmstart;
+
+ if (usertime)
+ now = rus.tms_utime;
+
+ if (stop == TM_START) {
+ tmstart = now;
+ } else {
+ long int tck = sysconf(_SC_CLK_TCK);
+ ret = (now - tmstart) / (double)tck;
+ }
+
+ return ret;
+}
+
+#else
+# include <sys/time.h>
+# include <sys/resource.h>
+
+double app_tminterval(int stop, int usertime)
+{
+ double ret = 0;
+ struct rusage rus;
+ struct timeval now;
+ static struct timeval tmstart;
+
+ if (usertime)
+ getrusage(RUSAGE_SELF, &rus), now = rus.ru_utime;
+ else
+ gettimeofday(&now, NULL);
+
+ if (stop == TM_START)
+ tmstart = now;
+ else
+ ret = ((now.tv_sec + now.tv_usec * 1e-6)
+ - (tmstart.tv_sec + tmstart.tv_usec * 1e-6));
+
+ return ret;
+}
+#endif
+
+int app_access(const char* name, int flag)
+{
+#ifdef _WIN32
+ return _access(name, flag);
+#else
+ return access(name, flag);
+#endif
+}
+
+int app_isdir(const char *name)
+{
+ return opt_isdir(name);
+}
+
+/* raw_read|write section */
+#if defined(__VMS)
+# include "vms_term_sock.h"
+static int stdin_sock = -1;
+
+static void close_stdin_sock(void)
+{
+ TerminalSocket (TERM_SOCK_DELETE, &stdin_sock);
+}
+
+int fileno_stdin(void)
+{
+ if (stdin_sock == -1) {
+ TerminalSocket(TERM_SOCK_CREATE, &stdin_sock);
+ atexit(close_stdin_sock);
+ }
+
+ return stdin_sock;
+}
+#else
+int fileno_stdin(void)
+{
+ return fileno(stdin);
+}
+#endif
+
+int fileno_stdout(void)
+{
+ return fileno(stdout);
+}
+
+#if defined(_WIN32) && defined(STD_INPUT_HANDLE)
+int raw_read_stdin(void *buf, int siz)
+{
+ DWORD n;
+ if (ReadFile(GetStdHandle(STD_INPUT_HANDLE), buf, siz, &n, NULL))
+ return n;
+ else
+ return -1;
+}
+#elif defined(__VMS)
+# include <sys/socket.h>
+
+int raw_read_stdin(void *buf, int siz)
+{
+ return recv(fileno_stdin(), buf, siz, 0);
+}
+#else
+# if defined(__TANDEM)
+# if defined(OPENSSL_TANDEM_FLOSS)
+# include <floss.h(floss_read)>
+# endif
+# endif
+int raw_read_stdin(void *buf, int siz)
+{
+ return read(fileno_stdin(), buf, siz);
+}
+#endif
+
+#if defined(_WIN32) && defined(STD_OUTPUT_HANDLE)
+int raw_write_stdout(const void *buf, int siz)
+{
+ DWORD n;
+ if (WriteFile(GetStdHandle(STD_OUTPUT_HANDLE), buf, siz, &n, NULL))
+ return n;
+ else
+ return -1;
+}
+#elif defined(OPENSSL_SYS_TANDEM) && defined(OPENSSL_THREADS) && defined(_SPT_MODEL_)
+# if defined(__TANDEM)
+# if defined(OPENSSL_TANDEM_FLOSS)
+# include <floss.h(floss_write)>
+# endif
+# endif
+int raw_write_stdout(const void *buf,int siz)
+{
+ return write(fileno(stdout),(void*)buf,siz);
+}
+#else
+# if defined(__TANDEM)
+# if defined(OPENSSL_TANDEM_FLOSS)
+# include <floss.h(floss_write)>
+# endif
+# endif
+int raw_write_stdout(const void *buf, int siz)
+{
+ return write(fileno_stdout(), buf, siz);
+}
+#endif
+
+/*
+ * Centralized handling of input and output files with format specification
+ * The format is meant to show what the input and output is supposed to be,
+ * and is therefore a show of intent more than anything else. However, it
+ * does impact behavior on some platforms, such as differentiating between
+ * text and binary input/output on non-Unix platforms
+ */
+BIO *dup_bio_in(int format)
+{
+ return BIO_new_fp(stdin,
+ BIO_NOCLOSE | (FMT_istext(format) ? BIO_FP_TEXT : 0));
+}
+
+BIO *dup_bio_out(int format)
+{
+ BIO *b = BIO_new_fp(stdout,
+ BIO_NOCLOSE | (FMT_istext(format) ? BIO_FP_TEXT : 0));
+ void *prefix = NULL;
+
+ if (b == NULL)
+ return NULL;
+
+#ifdef OPENSSL_SYS_VMS
+ if (FMT_istext(format))
+ b = BIO_push(BIO_new(BIO_f_linebuffer()), b);
+#endif
+
+ if (FMT_istext(format)
+ && (prefix = getenv("HARNESS_OSSL_PREFIX")) != NULL) {
+ b = BIO_push(BIO_new(BIO_f_prefix()), b);
+ BIO_set_prefix(b, prefix);
+ }
+
+ return b;
+}
+
+BIO *dup_bio_err(int format)
+{
+ BIO *b = BIO_new_fp(stderr,
+ BIO_NOCLOSE | (FMT_istext(format) ? BIO_FP_TEXT : 0));
+#ifdef OPENSSL_SYS_VMS
+ if (b != NULL && FMT_istext(format))
+ b = BIO_push(BIO_new(BIO_f_linebuffer()), b);
+#endif
+ return b;
+}
+
+void unbuffer(FILE *fp)
+{
+/*
+ * On VMS, setbuf() will only take 32-bit pointers, and a compilation
+ * with /POINTER_SIZE=64 will give off a MAYLOSEDATA2 warning here.
+ * However, we trust that the C RTL will never give us a FILE pointer
+ * above the first 4 GB of memory, so we simply turn off the warning
+ * temporarily.
+ */
+#if defined(OPENSSL_SYS_VMS) && defined(__DECC)
+# pragma environment save
+# pragma message disable maylosedata2
+#endif
+ setbuf(fp, NULL);
+#if defined(OPENSSL_SYS_VMS) && defined(__DECC)
+# pragma environment restore
+#endif
+}
+
+static const char *modestr(char mode, int format)
+{
+ OPENSSL_assert(mode == 'a' || mode == 'r' || mode == 'w');
+
+ switch (mode) {
+ case 'a':
+ return FMT_istext(format) ? "a" : "ab";
+ case 'r':
+ return FMT_istext(format) ? "r" : "rb";
+ case 'w':
+ return FMT_istext(format) ? "w" : "wb";
+ }
+ /* The assert above should make sure we never reach this point */
+ return NULL;
+}
+
+static const char *modeverb(char mode)
+{
+ switch (mode) {
+ case 'a':
+ return "appending";
+ case 'r':
+ return "reading";
+ case 'w':
+ return "writing";
+ }
+ return "(doing something)";
+}
+
+/*
+ * Open a file for writing, owner-read-only.
+ */
+BIO *bio_open_owner(const char *filename, int format, int private)
+{
+ FILE *fp = NULL;
+ BIO *b = NULL;
+ int textmode, bflags;
+#ifndef OPENSSL_NO_POSIX_IO
+ int fd = -1, mode;
+#endif
+
+ if (!private || filename == NULL || strcmp(filename, "-") == 0)
+ return bio_open_default(filename, 'w', format);
+
+ textmode = FMT_istext(format);
+#ifndef OPENSSL_NO_POSIX_IO
+ mode = O_WRONLY;
+# ifdef O_CREAT
+ mode |= O_CREAT;
+# endif
+# ifdef O_TRUNC
+ mode |= O_TRUNC;
+# endif
+ if (!textmode) {
+# ifdef O_BINARY
+ mode |= O_BINARY;
+# elif defined(_O_BINARY)
+ mode |= _O_BINARY;
+# endif
+ }
+
+# ifdef OPENSSL_SYS_VMS
+ /* VMS doesn't have O_BINARY, it just doesn't make sense. But,
+ * it still needs to know that we're going binary, or fdopen()
+ * will fail with "invalid argument"... so we tell VMS what the
+ * context is.
+ */
+ if (!textmode)
+ fd = open(filename, mode, 0600, "ctx=bin");
+ else
+# endif
+ fd = open(filename, mode, 0600);
+ if (fd < 0)
+ goto err;
+ fp = fdopen(fd, modestr('w', format));
+#else /* OPENSSL_NO_POSIX_IO */
+ /* Have stdio but not Posix IO, do the best we can */
+ fp = fopen(filename, modestr('w', format));
+#endif /* OPENSSL_NO_POSIX_IO */
+ if (fp == NULL)
+ goto err;
+ bflags = BIO_CLOSE;
+ if (textmode)
+ bflags |= BIO_FP_TEXT;
+ b = BIO_new_fp(fp, bflags);
+ if (b != NULL)
+ return b;
+
+ err:
+ BIO_printf(bio_err, "%s: Can't open \"%s\" for writing, %s\n",
+ opt_getprog(), filename, strerror(errno));
+ ERR_print_errors(bio_err);
+ /* If we have fp, then fdopen took over fd, so don't close both. */
+ if (fp != NULL)
+ fclose(fp);
+#ifndef OPENSSL_NO_POSIX_IO
+ else if (fd >= 0)
+ close(fd);
+#endif
+ return NULL;
+}
+
+static BIO *bio_open_default_(const char *filename, char mode, int format,
+ int quiet)
+{
+ BIO *ret;
+
+ if (filename == NULL || strcmp(filename, "-") == 0) {
+ ret = mode == 'r' ? dup_bio_in(format) : dup_bio_out(format);
+ if (quiet) {
+ ERR_clear_error();
+ return ret;
+ }
+ if (ret != NULL)
+ return ret;
+ BIO_printf(bio_err,
+ "Can't open %s, %s\n",
+ mode == 'r' ? "stdin" : "stdout", strerror(errno));
+ } else {
+ ret = BIO_new_file(filename, modestr(mode, format));
+ if (quiet) {
+ ERR_clear_error();
+ return ret;
+ }
+ if (ret != NULL)
+ return ret;
+ BIO_printf(bio_err,
+ "Can't open \"%s\" for %s, %s\n",
+ filename, modeverb(mode), strerror(errno));
+ }
+ ERR_print_errors(bio_err);
+ return NULL;
+}
+
+BIO *bio_open_default(const char *filename, char mode, int format)
+{
+ return bio_open_default_(filename, mode, format, 0);
+}
+
+BIO *bio_open_default_quiet(const char *filename, char mode, int format)
+{
+ return bio_open_default_(filename, mode, format, 1);
+}
+
+void wait_for_async(SSL *s)
+{
+ /* On Windows select only works for sockets, so we simply don't wait */
+#ifndef OPENSSL_SYS_WINDOWS
+ int width = 0;
+ fd_set asyncfds;
+ OSSL_ASYNC_FD *fds;
+ size_t numfds;
+ size_t i;
+
+ if (!SSL_get_all_async_fds(s, NULL, &numfds))
+ return;
+ if (numfds == 0)
+ return;
+ fds = app_malloc(sizeof(OSSL_ASYNC_FD) * numfds, "allocate async fds");
+ if (!SSL_get_all_async_fds(s, fds, &numfds)) {
+ OPENSSL_free(fds);
+ return;
+ }
+
+ FD_ZERO(&asyncfds);
+ for (i = 0; i < numfds; i++) {
+ if (width <= (int)fds[i])
+ width = (int)fds[i] + 1;
+ openssl_fdset((int)fds[i], &asyncfds);
+ }
+ select(width, (void *)&asyncfds, NULL, NULL, NULL);
+ OPENSSL_free(fds);
+#endif
+}
+
+/* if OPENSSL_SYS_WINDOWS is defined then so is OPENSSL_SYS_MSDOS */
+#if defined(OPENSSL_SYS_MSDOS)
+int has_stdin_waiting(void)
+{
+# if defined(OPENSSL_SYS_WINDOWS)
+ HANDLE inhand = GetStdHandle(STD_INPUT_HANDLE);
+ DWORD events = 0;
+ INPUT_RECORD inputrec;
+ DWORD insize = 1;
+ BOOL peeked;
+
+ if (inhand == INVALID_HANDLE_VALUE) {
+ return 0;
+ }
+
+ peeked = PeekConsoleInput(inhand, &inputrec, insize, &events);
+ if (!peeked) {
+ /* Probably redirected input? _kbhit() does not work in this case */
+ if (!feof(stdin)) {
+ return 1;
+ }
+ return 0;
+ }
+# endif
+ return _kbhit();
+}
+#endif
+
+/* Corrupt a signature by modifying final byte */
+void corrupt_signature(const ASN1_STRING *signature)
+{
+ unsigned char *s = signature->data;
+ s[signature->length - 1] ^= 0x1;
+}
+
+int set_cert_times(X509 *x, const char *startdate, const char *enddate,
+ int days)
+{
+ if (startdate == NULL || strcmp(startdate, "today") == 0) {
+ if (X509_gmtime_adj(X509_getm_notBefore(x), 0) == NULL)
+ return 0;
+ } else {
+ if (!ASN1_TIME_set_string_X509(X509_getm_notBefore(x), startdate))
+ return 0;
+ }
+ if (enddate == NULL) {
+ if (X509_time_adj_ex(X509_getm_notAfter(x), days, 0, NULL)
+ == NULL)
+ return 0;
+ } else if (!ASN1_TIME_set_string_X509(X509_getm_notAfter(x), enddate)) {
+ return 0;
+ }
+ return 1;
+}
+
+int set_crl_lastupdate(X509_CRL *crl, const char *lastupdate)
+{
+ int ret = 0;
+ ASN1_TIME *tm = ASN1_TIME_new();
+
+ if (tm == NULL)
+ goto end;
+
+ if (lastupdate == NULL) {
+ if (X509_gmtime_adj(tm, 0) == NULL)
+ goto end;
+ } else {
+ if (!ASN1_TIME_set_string_X509(tm, lastupdate))
+ goto end;
+ }
+
+ if (!X509_CRL_set1_lastUpdate(crl, tm))
+ goto end;
+
+ ret = 1;
+end:
+ ASN1_TIME_free(tm);
+ return ret;
+}
+
+int set_crl_nextupdate(X509_CRL *crl, const char *nextupdate,
+ long days, long hours, long secs)
+{
+ int ret = 0;
+ ASN1_TIME *tm = ASN1_TIME_new();
+
+ if (tm == NULL)
+ goto end;
+
+ if (nextupdate == NULL) {
+ if (X509_time_adj_ex(tm, days, hours * 60 * 60 + secs, NULL) == NULL)
+ goto end;
+ } else {
+ if (!ASN1_TIME_set_string_X509(tm, nextupdate))
+ goto end;
+ }
+
+ if (!X509_CRL_set1_nextUpdate(crl, tm))
+ goto end;
+
+ ret = 1;
+end:
+ ASN1_TIME_free(tm);
+ return ret;
+}
+
+void make_uppercase(char *string)
+{
+ int i;
+
+ for (i = 0; string[i] != '\0'; i++)
+ string[i] = toupper((unsigned char)string[i]);
+}
+
+/* This function is defined here due to visibility of bio_err */
+int opt_printf_stderr(const char *fmt, ...)
+{
+ va_list ap;
+ int ret;
+
+ va_start(ap, fmt);
+ ret = BIO_vprintf(bio_err, fmt, ap);
+ va_end(ap);
+ return ret;
+}
+
+OSSL_PARAM *app_params_new_from_opts(STACK_OF(OPENSSL_STRING) *opts,
+ const OSSL_PARAM *paramdefs)
+{
+ OSSL_PARAM *params = NULL;
+ size_t sz = (size_t)sk_OPENSSL_STRING_num(opts);
+ size_t params_n;
+ char *opt = "", *stmp, *vtmp = NULL;
+ int found = 1;
+
+ if (opts == NULL)
+ return NULL;
+
+ params = OPENSSL_zalloc(sizeof(OSSL_PARAM) * (sz + 1));
+ if (params == NULL)
+ return NULL;
+
+ for (params_n = 0; params_n < sz; params_n++) {
+ opt = sk_OPENSSL_STRING_value(opts, (int)params_n);
+ if ((stmp = OPENSSL_strdup(opt)) == NULL
+ || (vtmp = strchr(stmp, ':')) == NULL)
+ goto err;
+ /* Replace ':' with 0 to terminate the string pointed to by stmp */
+ *vtmp = 0;
+ /* Skip over the separator so that vmtp points to the value */
+ vtmp++;
+ if (!OSSL_PARAM_allocate_from_text(&params[params_n], paramdefs,
+ stmp, vtmp, strlen(vtmp), &found))
+ goto err;
+ OPENSSL_free(stmp);
+ }
+ params[params_n] = OSSL_PARAM_construct_end();
+ return params;
+err:
+ OPENSSL_free(stmp);
+ BIO_printf(bio_err, "Parameter %s '%s'\n", found ? "error" : "unknown",
+ opt);
+ ERR_print_errors(bio_err);
+ app_params_free(params);
+ return NULL;
+}
+
+void app_params_free(OSSL_PARAM *params)
+{
+ int i;
+
+ if (params != NULL) {
+ for (i = 0; params[i].key != NULL; ++i)
+ OPENSSL_free(params[i].data);
+ OPENSSL_free(params);
+ }
+}
+
+EVP_PKEY *app_keygen(EVP_PKEY_CTX *ctx, const char *alg, int bits, int verbose)
+{
+ EVP_PKEY *res = NULL;
+
+ if (verbose && alg != NULL) {
+ BIO_printf(bio_err, "Generating %s key", alg);
+ if (bits > 0)
+ BIO_printf(bio_err, " with %d bits\n", bits);
+ else
+ BIO_printf(bio_err, "\n");
+ }
+ if (!RAND_status())
+ BIO_printf(bio_err, "Warning: generating random key material may take a long time\n"
+ "if the system has a poor entropy source\n");
+ if (EVP_PKEY_keygen(ctx, &res) <= 0)
+ BIO_printf(bio_err, "%s: Error generating %s key\n", opt_getprog(),
+ alg != NULL ? alg : "asymmetric");
+ return res;
+}
+
+EVP_PKEY *app_paramgen(EVP_PKEY_CTX *ctx, const char *alg)
+{
+ EVP_PKEY *res = NULL;
+
+ if (!RAND_status())
+ BIO_printf(bio_err, "Warning: generating random key parameters may take a long time\n"
+ "if the system has a poor entropy source\n");
+ if (EVP_PKEY_paramgen(ctx, &res) <= 0)
+ BIO_printf(bio_err, "%s: Generating %s key parameters failed\n",
+ opt_getprog(), alg != NULL ? alg : "asymmetric");
+ return res;
+}
+
+/*
+ * Return non-zero if the legacy path is still an option.
+ * This decision is based on the global command line operations and the
+ * behaviour thus far.
+ */
+int opt_legacy_okay(void)
+{
+ int provider_options = opt_provider_option_given();
+ int libctx = app_get0_libctx() != NULL || app_get0_propq() != NULL;
+ /*
+ * Having a provider option specified or a custom library context or
+ * property query, is a sure sign we're not using legacy.
+ */
+ if (provider_options || libctx)
+ return 0;
+ return 1;
+}
diff --git a/apps/lib/apps_ui.c b/apps/lib/apps_ui.c
new file mode 100644
index 000000000000..00e0ba5d9996
--- /dev/null
+++ b/apps/lib/apps_ui.c
@@ -0,0 +1,223 @@
+/*
+ * Copyright 1995-2020 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the Apache License 2.0 (the "License"). You may not use
+ * this file except in compliance with the License. You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include <string.h>
+#include <openssl/err.h>
+#include <openssl/ui.h>
+#include "apps_ui.h"
+
+static UI_METHOD *ui_method = NULL;
+static const UI_METHOD *ui_base_method = NULL;
+
+static int ui_open(UI *ui)
+{
+ int (*opener)(UI *ui) = UI_method_get_opener(ui_base_method);
+
+ if (opener != NULL)
+ return opener(ui);
+ return 1;
+}
+
+static int ui_read(UI *ui, UI_STRING *uis)
+{
+ int (*reader)(UI *ui, UI_STRING *uis) = NULL;
+
+ if (UI_get_input_flags(uis) & UI_INPUT_FLAG_DEFAULT_PWD
+ && UI_get0_user_data(ui)) {
+ switch (UI_get_string_type(uis)) {
+ case UIT_PROMPT:
+ case UIT_VERIFY:
+ {
+ const char *password =
+ ((PW_CB_DATA *)UI_get0_user_data(ui))->password;
+
+ if (password != NULL) {
+ UI_set_result(ui, uis, password);
+ return 1;
+ }
+ }
+ break;
+ case UIT_NONE:
+ case UIT_BOOLEAN:
+ case UIT_INFO:
+ case UIT_ERROR:
+ break;
+ }
+ }
+
+ reader = UI_method_get_reader(ui_base_method);
+ if (reader != NULL)
+ return reader(ui, uis);
+ /* Default to the empty password if we've got nothing better */
+ UI_set_result(ui, uis, "");
+ return 1;
+}
+
+static int ui_write(UI *ui, UI_STRING *uis)
+{
+ int (*writer)(UI *ui, UI_STRING *uis) = NULL;
+
+ if (UI_get_input_flags(uis) & UI_INPUT_FLAG_DEFAULT_PWD
+ && UI_get0_user_data(ui)) {
+ switch (UI_get_string_type(uis)) {
+ case UIT_PROMPT:
+ case UIT_VERIFY:
+ {
+ const char *password =
+ ((PW_CB_DATA *)UI_get0_user_data(ui))->password;
+
+ if (password != NULL)
+ return 1;
+ }
+ break;
+ case UIT_NONE:
+ case UIT_BOOLEAN:
+ case UIT_INFO:
+ case UIT_ERROR:
+ break;
+ }
+ }
+
+ writer = UI_method_get_writer(ui_base_method);
+ if (writer != NULL)
+ return writer(ui, uis);
+ return 1;
+}
+
+static int ui_close(UI *ui)
+{
+ int (*closer)(UI *ui) = UI_method_get_closer(ui_base_method);
+
+ if (closer != NULL)
+ return closer(ui);
+ return 1;
+}
+
+/* object_name defaults to prompt_info from ui user data if present */
+static char *ui_prompt_construct(UI *ui, const char *phrase_desc,
+ const char *object_name)
+{
+ PW_CB_DATA *cb_data = (PW_CB_DATA *)UI_get0_user_data(ui);
+
+ if (phrase_desc == NULL)
+ phrase_desc = "pass phrase";
+ if (object_name == NULL && cb_data != NULL)
+ object_name = cb_data->prompt_info;
+ return UI_construct_prompt(NULL, phrase_desc, object_name);
+}
+
+int set_base_ui_method(const UI_METHOD *ui_meth)
+{
+ if (ui_meth == NULL)
+ ui_meth = UI_null();
+ ui_base_method = ui_meth;
+ return 1;
+}
+
+int setup_ui_method(void)
+{
+ ui_base_method = UI_null();
+#ifndef OPENSSL_NO_UI_CONSOLE
+ ui_base_method = UI_OpenSSL();
+#endif
+ ui_method = UI_create_method("OpenSSL application user interface");
+ return ui_method != NULL
+ && 0 == UI_method_set_opener(ui_method, ui_open)
+ && 0 == UI_method_set_reader(ui_method, ui_read)
+ && 0 == UI_method_set_writer(ui_method, ui_write)
+ && 0 == UI_method_set_closer(ui_method, ui_close)
+ && 0 == UI_method_set_prompt_constructor(ui_method,
+ ui_prompt_construct);
+}
+
+void destroy_ui_method(void)
+{
+ if (ui_method != NULL) {
+ UI_destroy_method(ui_method);
+ ui_method = NULL;
+ }
+}
+
+const UI_METHOD *get_ui_method(void)
+{
+ return ui_method;
+}
+
+static void *ui_malloc(int sz, const char *what)
+{
+ void *vp = OPENSSL_malloc(sz);
+
+ if (vp == NULL) {
+ BIO_printf(bio_err, "Could not allocate %d bytes for %s\n", sz, what);
+ ERR_print_errors(bio_err);
+ exit(1);
+ }
+ return vp;
+}
+
+int password_callback(char *buf, int bufsiz, int verify, PW_CB_DATA *cb_data)
+{
+ int res = 0;
+ UI *ui;
+ int ok = 0;
+ char *buff = NULL;
+ int ui_flags = 0;
+ const char *prompt_info = NULL;
+ char *prompt;
+
+ if ((ui = UI_new_method(ui_method)) == NULL)
+ return 0;
+
+ if (cb_data != NULL && cb_data->prompt_info != NULL)
+ prompt_info = cb_data->prompt_info;
+ prompt = UI_construct_prompt(ui, "pass phrase", prompt_info);
+ if (prompt == NULL) {
+ BIO_printf(bio_err, "Out of memory\n");
+ UI_free(ui);
+ return 0;
+ }
+
+ ui_flags |= UI_INPUT_FLAG_DEFAULT_PWD;
+ UI_ctrl(ui, UI_CTRL_PRINT_ERRORS, 1, 0, 0);
+
+ /* We know that there is no previous user data to return to us */
+ (void)UI_add_user_data(ui, cb_data);
+
+ ok = UI_add_input_string(ui, prompt, ui_flags, buf,
+ PW_MIN_LENGTH, bufsiz - 1);
+
+ if (ok >= 0 && verify) {
+ buff = ui_malloc(bufsiz, "password buffer");
+ ok = UI_add_verify_string(ui, prompt, ui_flags, buff,
+ PW_MIN_LENGTH, bufsiz - 1, buf);
+ }
+ if (ok >= 0)
+ do {
+ ok = UI_process(ui);
+ } while (ok < 0 && UI_ctrl(ui, UI_CTRL_IS_REDOABLE, 0, 0, 0));
+
+ OPENSSL_clear_free(buff, (unsigned int)bufsiz);
+
+ if (ok >= 0)
+ res = strlen(buf);
+ if (ok == -1) {
+ BIO_printf(bio_err, "User interface error\n");
+ ERR_print_errors(bio_err);
+ OPENSSL_cleanse(buf, (unsigned int)bufsiz);
+ res = 0;
+ }
+ if (ok == -2) {
+ BIO_printf(bio_err, "aborted!\n");
+ OPENSSL_cleanse(buf, (unsigned int)bufsiz);
+ res = 0;
+ }
+ UI_free(ui);
+ OPENSSL_free(prompt);
+ return res;
+}
diff --git a/apps/lib/build.info b/apps/lib/build.info
new file mode 100644
index 000000000000..923ef5d92b83
--- /dev/null
+++ b/apps/lib/build.info
@@ -0,0 +1,23 @@
+# Auxiliary program source
+IF[{- $config{target} =~ /^(?:VC-|mingw|BC-)/ -}]
+ # It's called 'init', but doesn't have much 'init' in it...
+ $AUXLIBAPPSSRC=win32_init.c
+ENDIF
+IF[{- $config{target} =~ /^vms-/ -}]
+ $AUXLIBAPPSSRC=vms_term_sock.c vms_decc_argv.c
+ENDIF
+
+# Source for libapps
+$LIBAPPSSRC=apps.c apps_ui.c opt.c fmt.c s_cb.c s_socket.c app_rand.c \
+ columns.c app_params.c names.c app_provider.c app_x509.c http_server.c \
+ engine.c engine_loader.c app_libctx.c
+
+IF[{- !$disabled{apps} -}]
+ LIBS{noinst}=../libapps.a
+ SOURCE[../libapps.a]=$LIBAPPSSRC $AUXLIBAPPSSRC
+ INCLUDE[../libapps.a]=../.. ../../include ../include
+ENDIF
+
+IF[{- !$disabled{srp} -}]
+ SOURCE[../libapps.a]=tlssrp_depr.c
+ENDIF
diff --git a/apps/lib/cmp_mock_srv.c b/apps/lib/cmp_mock_srv.c
new file mode 100644
index 000000000000..637bd1d0b7a4
--- /dev/null
+++ b/apps/lib/cmp_mock_srv.c
@@ -0,0 +1,452 @@
+/*
+ * Copyright 2018-2023 The OpenSSL Project Authors. All Rights Reserved.
+ * Copyright Siemens AG 2018-2020
+ *
+ * Licensed under the Apache License 2.0 (the "License"). You may not use
+ * this file except in compliance with the License. You can obtain a copy
+ * in the file LICENSE in the source distribution or atf
+ * https://www.openssl.org/source/license.html
+ */
+
+#include "apps.h"
+#include "cmp_mock_srv.h"
+
+#include <openssl/cmp.h>
+#include <openssl/err.h>
+#include <openssl/cmperr.h>
+
+/* the context for the CMP mock server */
+typedef struct
+{
+ X509 *certOut; /* certificate to be returned in cp/ip/kup msg */
+ STACK_OF(X509) *chainOut; /* chain of certOut to add to extraCerts field */
+ STACK_OF(X509) *caPubsOut; /* certs to return in caPubs field of ip msg */
+ OSSL_CMP_PKISI *statusOut; /* status for ip/cp/kup/rp msg unless polling */
+ int sendError; /* send error response on given request type */
+ OSSL_CMP_MSG *certReq; /* ir/cr/p10cr/kur remembered while polling */
+ int pollCount; /* number of polls before actual cert response */
+ int curr_pollCount; /* number of polls so far for current request */
+ int checkAfterTime; /* time the client should wait between polling */
+} mock_srv_ctx;
+
+
+static void mock_srv_ctx_free(mock_srv_ctx *ctx)
+{
+ if (ctx == NULL)
+ return;
+
+ OSSL_CMP_PKISI_free(ctx->statusOut);
+ X509_free(ctx->certOut);
+ sk_X509_pop_free(ctx->chainOut, X509_free);
+ sk_X509_pop_free(ctx->caPubsOut, X509_free);
+ OSSL_CMP_MSG_free(ctx->certReq);
+ OPENSSL_free(ctx);
+}
+
+static mock_srv_ctx *mock_srv_ctx_new(void)
+{
+ mock_srv_ctx *ctx = OPENSSL_zalloc(sizeof(mock_srv_ctx));
+
+ if (ctx == NULL)
+ goto err;
+
+ if ((ctx->statusOut = OSSL_CMP_PKISI_new()) == NULL)
+ goto err;
+
+ ctx->sendError = -1;
+
+ /* all other elements are initialized to 0 or NULL, respectively */
+ return ctx;
+ err:
+ mock_srv_ctx_free(ctx);
+ return NULL;
+}
+
+int ossl_cmp_mock_srv_set1_certOut(OSSL_CMP_SRV_CTX *srv_ctx, X509 *cert)
+{
+ mock_srv_ctx *ctx = OSSL_CMP_SRV_CTX_get0_custom_ctx(srv_ctx);
+
+ if (ctx == NULL) {
+ ERR_raise(ERR_LIB_CMP, CMP_R_NULL_ARGUMENT);
+ return 0;
+ }
+ if (cert == NULL || X509_up_ref(cert)) {
+ X509_free(ctx->certOut);
+ ctx->certOut = cert;
+ return 1;
+ }
+ return 0;
+}
+
+int ossl_cmp_mock_srv_set1_chainOut(OSSL_CMP_SRV_CTX *srv_ctx,
+ STACK_OF(X509) *chain)
+{
+ mock_srv_ctx *ctx = OSSL_CMP_SRV_CTX_get0_custom_ctx(srv_ctx);
+ STACK_OF(X509) *chain_copy = NULL;
+
+ if (ctx == NULL) {
+ ERR_raise(ERR_LIB_CMP, CMP_R_NULL_ARGUMENT);
+ return 0;
+ }
+ if (chain != NULL && (chain_copy = X509_chain_up_ref(chain)) == NULL)
+ return 0;
+ sk_X509_pop_free(ctx->chainOut, X509_free);
+ ctx->chainOut = chain_copy;
+ return 1;
+}
+
+int ossl_cmp_mock_srv_set1_caPubsOut(OSSL_CMP_SRV_CTX *srv_ctx,
+ STACK_OF(X509) *caPubs)
+{
+ mock_srv_ctx *ctx = OSSL_CMP_SRV_CTX_get0_custom_ctx(srv_ctx);
+ STACK_OF(X509) *caPubs_copy = NULL;
+
+ if (ctx == NULL) {
+ ERR_raise(ERR_LIB_CMP, CMP_R_NULL_ARGUMENT);
+ return 0;
+ }
+ if (caPubs != NULL && (caPubs_copy = X509_chain_up_ref(caPubs)) == NULL)
+ return 0;
+ sk_X509_pop_free(ctx->caPubsOut, X509_free);
+ ctx->caPubsOut = caPubs_copy;
+ return 1;
+}
+
+int ossl_cmp_mock_srv_set_statusInfo(OSSL_CMP_SRV_CTX *srv_ctx, int status,
+ int fail_info, const char *text)
+{
+ mock_srv_ctx *ctx = OSSL_CMP_SRV_CTX_get0_custom_ctx(srv_ctx);
+ OSSL_CMP_PKISI *si;
+
+ if (ctx == NULL) {
+ ERR_raise(ERR_LIB_CMP, CMP_R_NULL_ARGUMENT);
+ return 0;
+ }
+ if ((si = OSSL_CMP_STATUSINFO_new(status, fail_info, text)) == NULL)
+ return 0;
+ OSSL_CMP_PKISI_free(ctx->statusOut);
+ ctx->statusOut = si;
+ return 1;
+}
+
+int ossl_cmp_mock_srv_set_sendError(OSSL_CMP_SRV_CTX *srv_ctx, int bodytype)
+{
+ mock_srv_ctx *ctx = OSSL_CMP_SRV_CTX_get0_custom_ctx(srv_ctx);
+
+ if (ctx == NULL) {
+ ERR_raise(ERR_LIB_CMP, CMP_R_NULL_ARGUMENT);
+ return 0;
+ }
+ /* might check bodytype, but this would require exporting all body types */
+ ctx->sendError = bodytype;
+ return 1;
+}
+
+int ossl_cmp_mock_srv_set_pollCount(OSSL_CMP_SRV_CTX *srv_ctx, int count)
+{
+ mock_srv_ctx *ctx = OSSL_CMP_SRV_CTX_get0_custom_ctx(srv_ctx);
+
+ if (ctx == NULL) {
+ ERR_raise(ERR_LIB_CMP, CMP_R_NULL_ARGUMENT);
+ return 0;
+ }
+ if (count < 0) {
+ ERR_raise(ERR_LIB_CMP, CMP_R_INVALID_ARGS);
+ return 0;
+ }
+ ctx->pollCount = count;
+ return 1;
+}
+
+int ossl_cmp_mock_srv_set_checkAfterTime(OSSL_CMP_SRV_CTX *srv_ctx, int sec)
+{
+ mock_srv_ctx *ctx = OSSL_CMP_SRV_CTX_get0_custom_ctx(srv_ctx);
+
+ if (ctx == NULL) {
+ ERR_raise(ERR_LIB_CMP, CMP_R_NULL_ARGUMENT);
+ return 0;
+ }
+ ctx->checkAfterTime = sec;
+ return 1;
+}
+
+static OSSL_CMP_PKISI *process_cert_request(OSSL_CMP_SRV_CTX *srv_ctx,
+ const OSSL_CMP_MSG *cert_req,
+ ossl_unused int certReqId,
+ const OSSL_CRMF_MSG *crm,
+ const X509_REQ *p10cr,
+ X509 **certOut,
+ STACK_OF(X509) **chainOut,
+ STACK_OF(X509) **caPubs)
+{
+ mock_srv_ctx *ctx = OSSL_CMP_SRV_CTX_get0_custom_ctx(srv_ctx);
+ OSSL_CMP_PKISI *si = NULL;
+
+ if (ctx == NULL || cert_req == NULL
+ || certOut == NULL || chainOut == NULL || caPubs == NULL) {
+ ERR_raise(ERR_LIB_CMP, CMP_R_NULL_ARGUMENT);
+ return NULL;
+ }
+ if (ctx->sendError == 1
+ || ctx->sendError == OSSL_CMP_MSG_get_bodytype(cert_req)) {
+ ERR_raise(ERR_LIB_CMP, CMP_R_ERROR_PROCESSING_MESSAGE);
+ return NULL;
+ }
+
+ *certOut = NULL;
+ *chainOut = NULL;
+ *caPubs = NULL;
+
+ if (ctx->pollCount > 0 && ctx->curr_pollCount == 0) {
+ /* start polling */
+ if (ctx->certReq != NULL) {
+ /* already in polling mode */
+ ERR_raise(ERR_LIB_CMP, CMP_R_UNEXPECTED_PKIBODY);
+ return NULL;
+ }
+ if ((ctx->certReq = OSSL_CMP_MSG_dup(cert_req)) == NULL)
+ return NULL;
+ return OSSL_CMP_STATUSINFO_new(OSSL_CMP_PKISTATUS_waiting, 0, NULL);
+ }
+ if (ctx->curr_pollCount >= ctx->pollCount)
+ /* give final response after polling */
+ ctx->curr_pollCount = 0;
+
+ if (OSSL_CMP_MSG_get_bodytype(cert_req) == OSSL_CMP_KUR
+ && crm != NULL && ctx->certOut != NULL) {
+ const OSSL_CRMF_CERTID *cid = OSSL_CRMF_MSG_get0_regCtrl_oldCertID(crm);
+ const X509_NAME *issuer = X509_get_issuer_name(ctx->certOut);
+ const ASN1_INTEGER *serial = X509_get0_serialNumber(ctx->certOut);
+
+ if (cid == NULL) {
+ ERR_raise(ERR_LIB_CMP, CMP_R_MISSING_CERTID);
+ return NULL;
+ }
+ if (issuer != NULL
+ && X509_NAME_cmp(issuer, OSSL_CRMF_CERTID_get0_issuer(cid)) != 0) {
+ ERR_raise(ERR_LIB_CMP, CMP_R_WRONG_CERTID);
+ return NULL;
+ }
+ if (serial != NULL
+ && ASN1_INTEGER_cmp(serial,
+ OSSL_CRMF_CERTID_get0_serialNumber(cid)) != 0) {
+ ERR_raise(ERR_LIB_CMP, CMP_R_WRONG_CERTID);
+ return NULL;
+ }
+ }
+
+ if (ctx->certOut != NULL
+ && (*certOut = X509_dup(ctx->certOut)) == NULL)
+ goto err;
+ if (ctx->chainOut != NULL
+ && (*chainOut = X509_chain_up_ref(ctx->chainOut)) == NULL)
+ goto err;
+ if (ctx->caPubsOut != NULL
+ && (*caPubs = X509_chain_up_ref(ctx->caPubsOut)) == NULL)
+ goto err;
+ if (ctx->statusOut != NULL
+ && (si = OSSL_CMP_PKISI_dup(ctx->statusOut)) == NULL)
+ goto err;
+ return si;
+
+ err:
+ X509_free(*certOut);
+ *certOut = NULL;
+ sk_X509_pop_free(*chainOut, X509_free);
+ *chainOut = NULL;
+ sk_X509_pop_free(*caPubs, X509_free);
+ *caPubs = NULL;
+ return NULL;
+}
+
+static OSSL_CMP_PKISI *process_rr(OSSL_CMP_SRV_CTX *srv_ctx,
+ const OSSL_CMP_MSG *rr,
+ const X509_NAME *issuer,
+ const ASN1_INTEGER *serial)
+{
+ mock_srv_ctx *ctx = OSSL_CMP_SRV_CTX_get0_custom_ctx(srv_ctx);
+
+ if (ctx == NULL || rr == NULL) {
+ ERR_raise(ERR_LIB_CMP, CMP_R_NULL_ARGUMENT);
+ return NULL;
+ }
+ if (ctx->certOut == NULL || ctx->sendError == 1
+ || ctx->sendError == OSSL_CMP_MSG_get_bodytype(rr)) {
+ ERR_raise(ERR_LIB_CMP, CMP_R_ERROR_PROCESSING_MESSAGE);
+ return NULL;
+ }
+
+ /* Allow any RR derived from CSR, which may include subject and serial */
+ if (issuer == NULL || serial == NULL)
+ return OSSL_CMP_PKISI_dup(ctx->statusOut);
+
+ /* accept revocation only for the certificate we sent in ir/cr/kur */
+ if (X509_NAME_cmp(issuer, X509_get_issuer_name(ctx->certOut)) != 0
+ || ASN1_INTEGER_cmp(serial,
+ X509_get0_serialNumber(ctx->certOut)) != 0) {
+ ERR_raise_data(ERR_LIB_CMP, CMP_R_REQUEST_NOT_ACCEPTED,
+ "wrong certificate to revoke");
+ return NULL;
+ }
+ return OSSL_CMP_PKISI_dup(ctx->statusOut);
+}
+
+static int process_genm(OSSL_CMP_SRV_CTX *srv_ctx,
+ const OSSL_CMP_MSG *genm,
+ const STACK_OF(OSSL_CMP_ITAV) *in,
+ STACK_OF(OSSL_CMP_ITAV) **out)
+{
+ mock_srv_ctx *ctx = OSSL_CMP_SRV_CTX_get0_custom_ctx(srv_ctx);
+
+ if (ctx == NULL || genm == NULL || in == NULL || out == NULL) {
+ ERR_raise(ERR_LIB_CMP, CMP_R_NULL_ARGUMENT);
+ return 0;
+ }
+ if (ctx->sendError == 1
+ || ctx->sendError == OSSL_CMP_MSG_get_bodytype(genm)
+ || sk_OSSL_CMP_ITAV_num(in) > 1) {
+ ERR_raise(ERR_LIB_CMP, CMP_R_ERROR_PROCESSING_MESSAGE);
+ return 0;
+ }
+
+ *out = sk_OSSL_CMP_ITAV_deep_copy(in, OSSL_CMP_ITAV_dup,
+ OSSL_CMP_ITAV_free);
+ return *out != NULL;
+}
+
+static void process_error(OSSL_CMP_SRV_CTX *srv_ctx, const OSSL_CMP_MSG *error,
+ const OSSL_CMP_PKISI *statusInfo,
+ const ASN1_INTEGER *errorCode,
+ const OSSL_CMP_PKIFREETEXT *errorDetails)
+{
+ mock_srv_ctx *ctx = OSSL_CMP_SRV_CTX_get0_custom_ctx(srv_ctx);
+ char buf[OSSL_CMP_PKISI_BUFLEN];
+ char *sibuf;
+ int i;
+
+ if (ctx == NULL || error == NULL) {
+ ERR_raise(ERR_LIB_CMP, CMP_R_NULL_ARGUMENT);
+ return;
+ }
+
+ BIO_printf(bio_err, "mock server received error:\n");
+
+ if (statusInfo == NULL) {
+ BIO_printf(bio_err, "pkiStatusInfo absent\n");
+ } else {
+ sibuf = OSSL_CMP_snprint_PKIStatusInfo(statusInfo, buf, sizeof(buf));
+ BIO_printf(bio_err, "pkiStatusInfo: %s\n",
+ sibuf != NULL ? sibuf: "<invalid>");
+ }
+
+ if (errorCode == NULL)
+ BIO_printf(bio_err, "errorCode absent\n");
+ else
+ BIO_printf(bio_err, "errorCode: %ld\n", ASN1_INTEGER_get(errorCode));
+
+ if (sk_ASN1_UTF8STRING_num(errorDetails) <= 0) {
+ BIO_printf(bio_err, "errorDetails absent\n");
+ } else {
+ BIO_printf(bio_err, "errorDetails: ");
+ for (i = 0; i < sk_ASN1_UTF8STRING_num(errorDetails); i++) {
+ if (i > 0)
+ BIO_printf(bio_err, ", ");
+ BIO_printf(bio_err, "\"");
+ ASN1_STRING_print(bio_err,
+ sk_ASN1_UTF8STRING_value(errorDetails, i));
+ BIO_printf(bio_err, "\"");
+ }
+ BIO_printf(bio_err, "\n");
+ }
+}
+
+static int process_certConf(OSSL_CMP_SRV_CTX *srv_ctx,
+ const OSSL_CMP_MSG *certConf,
+ ossl_unused int certReqId,
+ const ASN1_OCTET_STRING *certHash,
+ const OSSL_CMP_PKISI *si)
+{
+ mock_srv_ctx *ctx = OSSL_CMP_SRV_CTX_get0_custom_ctx(srv_ctx);
+ ASN1_OCTET_STRING *digest;
+
+ if (ctx == NULL || certConf == NULL || certHash == NULL) {
+ ERR_raise(ERR_LIB_CMP, CMP_R_NULL_ARGUMENT);
+ return 0;
+ }
+ if (ctx->sendError == 1
+ || ctx->sendError == OSSL_CMP_MSG_get_bodytype(certConf)
+ || ctx->certOut == NULL) {
+ ERR_raise(ERR_LIB_CMP, CMP_R_ERROR_PROCESSING_MESSAGE);
+ return 0;
+ }
+
+ if ((digest = X509_digest_sig(ctx->certOut, NULL, NULL)) == NULL)
+ return 0;
+ if (ASN1_OCTET_STRING_cmp(certHash, digest) != 0) {
+ ASN1_OCTET_STRING_free(digest);
+ ERR_raise(ERR_LIB_CMP, CMP_R_CERTHASH_UNMATCHED);
+ return 0;
+ }
+ ASN1_OCTET_STRING_free(digest);
+ return 1;
+}
+
+static int process_pollReq(OSSL_CMP_SRV_CTX *srv_ctx,
+ const OSSL_CMP_MSG *pollReq,
+ ossl_unused int certReqId,
+ OSSL_CMP_MSG **certReq, int64_t *check_after)
+{
+ mock_srv_ctx *ctx = OSSL_CMP_SRV_CTX_get0_custom_ctx(srv_ctx);
+
+ if (ctx == NULL || pollReq == NULL
+ || certReq == NULL || check_after == NULL) {
+ ERR_raise(ERR_LIB_CMP, CMP_R_NULL_ARGUMENT);
+ return 0;
+ }
+ if (ctx->sendError == 1
+ || ctx->sendError == OSSL_CMP_MSG_get_bodytype(pollReq)) {
+ *certReq = NULL;
+ ERR_raise(ERR_LIB_CMP, CMP_R_ERROR_PROCESSING_MESSAGE);
+ return 0;
+ }
+ if (ctx->certReq == NULL) {
+ /* not currently in polling mode */
+ *certReq = NULL;
+ ERR_raise(ERR_LIB_CMP, CMP_R_UNEXPECTED_PKIBODY);
+ return 0;
+ }
+
+ if (++ctx->curr_pollCount >= ctx->pollCount) {
+ /* end polling */
+ *certReq = ctx->certReq;
+ ctx->certReq = NULL;
+ *check_after = 0;
+ } else {
+ *certReq = NULL;
+ *check_after = ctx->checkAfterTime;
+ }
+ return 1;
+}
+
+OSSL_CMP_SRV_CTX *ossl_cmp_mock_srv_new(OSSL_LIB_CTX *libctx, const char *propq)
+{
+ OSSL_CMP_SRV_CTX *srv_ctx = OSSL_CMP_SRV_CTX_new(libctx, propq);
+ mock_srv_ctx *ctx = mock_srv_ctx_new();
+
+ if (srv_ctx != NULL && ctx != NULL
+ && OSSL_CMP_SRV_CTX_init(srv_ctx, ctx, process_cert_request,
+ process_rr, process_genm, process_error,
+ process_certConf, process_pollReq))
+ return srv_ctx;
+
+ mock_srv_ctx_free(ctx);
+ OSSL_CMP_SRV_CTX_free(srv_ctx);
+ return NULL;
+}
+
+void ossl_cmp_mock_srv_free(OSSL_CMP_SRV_CTX *srv_ctx)
+{
+ if (srv_ctx != NULL)
+ mock_srv_ctx_free(OSSL_CMP_SRV_CTX_get0_custom_ctx(srv_ctx));
+ OSSL_CMP_SRV_CTX_free(srv_ctx);
+}
diff --git a/apps/lib/columns.c b/apps/lib/columns.c
new file mode 100644
index 000000000000..aa58fe1781f5
--- /dev/null
+++ b/apps/lib/columns.c
@@ -0,0 +1,27 @@
+/*
+ * Copyright 2017-2019 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the Apache License 2.0 (the "License"). You may not use
+ * this file except in compliance with the License. You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include <string.h>
+#include "apps.h"
+#include "function.h"
+
+void calculate_columns(FUNCTION *functions, DISPLAY_COLUMNS *dc)
+{
+ FUNCTION *f;
+ int len, maxlen = 0;
+
+ for (f = functions; f->name != NULL; ++f)
+ if (f->type == FT_general || f->type == FT_md || f->type == FT_cipher)
+ if ((len = strlen(f->name)) > maxlen)
+ maxlen = len;
+
+ dc->width = maxlen + 2;
+ dc->columns = (80 - 1) / dc->width;
+}
+
diff --git a/apps/lib/engine.c b/apps/lib/engine.c
new file mode 100644
index 000000000000..209c4b6b03c2
--- /dev/null
+++ b/apps/lib/engine.c
@@ -0,0 +1,193 @@
+/*
+ * Copyright 2020 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the Apache License 2.0 (the "License"). You may not use
+ * this file except in compliance with the License. You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+/*
+ * Here is a set of wrappers for the ENGINE API, which are no-ops when the
+ * ENGINE API is disabled / removed.
+ * We need to suppress deprecation warnings to make this work.
+ */
+#define OPENSSL_SUPPRESS_DEPRECATED
+
+#include <string.h> /* strcmp */
+
+#include <openssl/types.h> /* Ensure we have the ENGINE type, regardless */
+#include <openssl/err.h>
+#ifndef OPENSSL_NO_ENGINE
+# include <openssl/engine.h>
+#endif
+#include "apps.h"
+
+#ifndef OPENSSL_NO_ENGINE
+/* Try to load an engine in a shareable library */
+static ENGINE *try_load_engine(const char *engine)
+{
+ ENGINE *e = NULL;
+
+ if ((e = ENGINE_by_id("dynamic")) != NULL) {
+ if (!ENGINE_ctrl_cmd_string(e, "SO_PATH", engine, 0)
+ || !ENGINE_ctrl_cmd_string(e, "LOAD", NULL, 0)) {
+ ENGINE_free(e);
+ e = NULL;
+ }
+ }
+ return e;
+}
+#endif
+
+ENGINE *setup_engine_methods(const char *id, unsigned int methods, int debug)
+{
+ ENGINE *e = NULL;
+
+#ifndef OPENSSL_NO_ENGINE
+ if (id != NULL) {
+ if (strcmp(id, "auto") == 0) {
+ BIO_printf(bio_err, "Enabling auto ENGINE support\n");
+ ENGINE_register_all_complete();
+ return NULL;
+ }
+ if ((e = ENGINE_by_id(id)) == NULL
+ && (e = try_load_engine(id)) == NULL) {
+ BIO_printf(bio_err, "Invalid engine \"%s\"\n", id);
+ ERR_print_errors(bio_err);
+ return NULL;
+ }
+ if (debug)
+ (void)ENGINE_ctrl(e, ENGINE_CTRL_SET_LOGSTREAM, 0, bio_err, 0);
+ if (!ENGINE_ctrl_cmd(e, "SET_USER_INTERFACE", 0,
+ (void *)get_ui_method(), 0, 1)
+ || !ENGINE_set_default(e, methods)) {
+ BIO_printf(bio_err, "Cannot use engine \"%s\"\n", ENGINE_get_id(e));
+ ERR_print_errors(bio_err);
+ ENGINE_free(e);
+ return NULL;
+ }
+
+ BIO_printf(bio_err, "Engine \"%s\" set.\n", ENGINE_get_id(e));
+ }
+#endif
+ return e;
+}
+
+void release_engine(ENGINE *e)
+{
+#ifndef OPENSSL_NO_ENGINE
+ /* Free our "structural" reference. */
+ ENGINE_free(e);
+#endif
+}
+
+int init_engine(ENGINE *e)
+{
+ int rv = 1;
+
+#ifndef OPENSSL_NO_ENGINE
+ rv = ENGINE_init(e);
+#endif
+ return rv;
+}
+
+int finish_engine(ENGINE *e)
+{
+ int rv = 1;
+
+#ifndef OPENSSL_NO_ENGINE
+ rv = ENGINE_finish(e);
+#endif
+ return rv;
+}
+
+char *make_engine_uri(ENGINE *e, const char *key_id, const char *desc)
+{
+ char *new_uri = NULL;
+
+#ifndef OPENSSL_NO_ENGINE
+ if (e == NULL) {
+ BIO_printf(bio_err, "No engine specified for loading %s\n", desc);
+ } else if (key_id == NULL) {
+ BIO_printf(bio_err, "No engine key id specified for loading %s\n", desc);
+ } else {
+ const char *engineid = ENGINE_get_id(e);
+ size_t uri_sz =
+ sizeof(ENGINE_SCHEME_COLON) - 1
+ + strlen(engineid)
+ + 1 /* : */
+ + strlen(key_id)
+ + 1 /* \0 */
+ ;
+
+ new_uri = OPENSSL_malloc(uri_sz);
+ if (new_uri != NULL) {
+ OPENSSL_strlcpy(new_uri, ENGINE_SCHEME_COLON, uri_sz);
+ OPENSSL_strlcat(new_uri, engineid, uri_sz);
+ OPENSSL_strlcat(new_uri, ":", uri_sz);
+ OPENSSL_strlcat(new_uri, key_id, uri_sz);
+ }
+ }
+#else
+ BIO_printf(bio_err, "Engines not supported for loading %s\n", desc);
+#endif
+ return new_uri;
+}
+
+int get_legacy_pkey_id(OSSL_LIB_CTX *libctx, const char *algname, ENGINE *e)
+{
+ const EVP_PKEY_ASN1_METHOD *ameth;
+ ENGINE *tmpeng = NULL;
+ int pkey_id = NID_undef;
+
+ ERR_set_mark();
+ ameth = EVP_PKEY_asn1_find_str(&tmpeng, algname, -1);
+
+#if !defined(OPENSSL_NO_ENGINE)
+ ENGINE_finish(tmpeng);
+
+ if (ameth == NULL && e != NULL)
+ ameth = ENGINE_get_pkey_asn1_meth_str(e, algname, -1);
+ else
+#endif
+ /* We're only interested if it comes from an ENGINE */
+ if (tmpeng == NULL)
+ ameth = NULL;
+
+ ERR_pop_to_mark();
+ if (ameth == NULL)
+ return NID_undef;
+
+ EVP_PKEY_asn1_get0_info(&pkey_id, NULL, NULL, NULL, NULL, ameth);
+
+ return pkey_id;
+}
+
+const EVP_MD *get_digest_from_engine(const char *name)
+{
+#ifndef OPENSSL_NO_ENGINE
+ ENGINE *eng;
+
+ eng = ENGINE_get_digest_engine(OBJ_sn2nid(name));
+ if (eng != NULL) {
+ ENGINE_finish(eng);
+ return EVP_get_digestbyname(name);
+ }
+#endif
+ return NULL;
+}
+
+const EVP_CIPHER *get_cipher_from_engine(const char *name)
+{
+#ifndef OPENSSL_NO_ENGINE
+ ENGINE *eng;
+
+ eng = ENGINE_get_cipher_engine(OBJ_sn2nid(name));
+ if (eng != NULL) {
+ ENGINE_finish(eng);
+ return EVP_get_cipherbyname(name);
+ }
+#endif
+ return NULL;
+}
diff --git a/apps/lib/engine_loader.c b/apps/lib/engine_loader.c
new file mode 100644
index 000000000000..42775a89f361
--- /dev/null
+++ b/apps/lib/engine_loader.c
@@ -0,0 +1,203 @@
+/*
+ * Copyright 2018-2022 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the Apache License 2.0 (the "License"). You may not use
+ * this file except in compliance with the License. You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+/*
+ * Here is an STORE loader for ENGINE backed keys. It relies on deprecated
+ * functions, and therefore need to have deprecation warnings suppressed.
+ * This file is not compiled at all in a '--api=3 no-deprecated' configuration.
+ */
+#define OPENSSL_SUPPRESS_DEPRECATED
+
+#include "apps.h"
+
+#ifndef OPENSSL_NO_ENGINE
+
+# include <stdarg.h>
+# include <string.h>
+# include <openssl/engine.h>
+# include <openssl/store.h>
+
+/*
+ * Support for legacy private engine keys via the 'org.openssl.engine:' scheme
+ *
+ * org.openssl.engine:{engineid}:{keyid}
+ *
+ * Note: we ONLY support ENGINE_load_private_key() and ENGINE_load_public_key()
+ * Note 2: This scheme has a precedent in code in PKIX-SSH. for exactly
+ * this sort of purpose.
+ */
+
+/* Local definition of OSSL_STORE_LOADER_CTX */
+struct ossl_store_loader_ctx_st {
+ ENGINE *e; /* Structural reference */
+ char *keyid;
+ int expected;
+ int loaded; /* 0 = key not loaded yet, 1 = key loaded */
+};
+
+static OSSL_STORE_LOADER_CTX *OSSL_STORE_LOADER_CTX_new(ENGINE *e, char *keyid)
+{
+ OSSL_STORE_LOADER_CTX *ctx = OPENSSL_zalloc(sizeof(*ctx));
+
+ if (ctx != NULL) {
+ ctx->e = e;
+ ctx->keyid = keyid;
+ }
+ return ctx;
+}
+
+static void OSSL_STORE_LOADER_CTX_free(OSSL_STORE_LOADER_CTX *ctx)
+{
+ if (ctx != NULL) {
+ ENGINE_free(ctx->e);
+ OPENSSL_free(ctx->keyid);
+ OPENSSL_free(ctx);
+ }
+}
+
+static OSSL_STORE_LOADER_CTX *engine_open(const OSSL_STORE_LOADER *loader,
+ const char *uri,
+ const UI_METHOD *ui_method,
+ void *ui_data)
+{
+ const char *p = uri, *q;
+ ENGINE *e = NULL;
+ char *keyid = NULL;
+ OSSL_STORE_LOADER_CTX *ctx = NULL;
+
+ if (OPENSSL_strncasecmp(p, ENGINE_SCHEME_COLON, sizeof(ENGINE_SCHEME_COLON) - 1)
+ != 0)
+ return NULL;
+ p += sizeof(ENGINE_SCHEME_COLON) - 1;
+
+ /* Look for engine ID */
+ q = strchr(p, ':');
+ if (q != NULL /* There is both an engine ID and a key ID */
+ && p[0] != ':' /* The engine ID is at least one character */
+ && q[1] != '\0') { /* The key ID is at least one character */
+ char engineid[256];
+ size_t engineid_l = q - p;
+
+ strncpy(engineid, p, engineid_l);
+ engineid[engineid_l] = '\0';
+ e = ENGINE_by_id(engineid);
+
+ keyid = OPENSSL_strdup(q + 1);
+ }
+
+ if (e != NULL && keyid != NULL)
+ ctx = OSSL_STORE_LOADER_CTX_new(e, keyid);
+
+ if (ctx == NULL) {
+ OPENSSL_free(keyid);
+ ENGINE_free(e);
+ }
+
+ return ctx;
+}
+
+static int engine_expect(OSSL_STORE_LOADER_CTX *ctx, int expected)
+{
+ if (expected == 0
+ || expected == OSSL_STORE_INFO_PUBKEY
+ || expected == OSSL_STORE_INFO_PKEY) {
+ ctx->expected = expected;
+ return 1;
+ }
+ return 0;
+}
+
+static OSSL_STORE_INFO *engine_load(OSSL_STORE_LOADER_CTX *ctx,
+ const UI_METHOD *ui_method, void *ui_data)
+{
+ EVP_PKEY *pkey = NULL, *pubkey = NULL;
+ OSSL_STORE_INFO *info = NULL;
+
+ if (ctx->loaded == 0) {
+ if (ENGINE_init(ctx->e)) {
+ if (ctx->expected == 0
+ || ctx->expected == OSSL_STORE_INFO_PKEY)
+ pkey =
+ ENGINE_load_private_key(ctx->e, ctx->keyid,
+ (UI_METHOD *)ui_method, ui_data);
+ if ((pkey == NULL && ctx->expected == 0)
+ || ctx->expected == OSSL_STORE_INFO_PUBKEY)
+ pubkey =
+ ENGINE_load_public_key(ctx->e, ctx->keyid,
+ (UI_METHOD *)ui_method, ui_data);
+ ENGINE_finish(ctx->e);
+ }
+ }
+
+ ctx->loaded = 1;
+
+ if (pubkey != NULL)
+ info = OSSL_STORE_INFO_new_PUBKEY(pubkey);
+ else if (pkey != NULL)
+ info = OSSL_STORE_INFO_new_PKEY(pkey);
+ if (info == NULL) {
+ EVP_PKEY_free(pkey);
+ EVP_PKEY_free(pubkey);
+ }
+ return info;
+}
+
+static int engine_eof(OSSL_STORE_LOADER_CTX *ctx)
+{
+ return ctx->loaded != 0;
+}
+
+static int engine_error(OSSL_STORE_LOADER_CTX *ctx)
+{
+ return 0;
+}
+
+static int engine_close(OSSL_STORE_LOADER_CTX *ctx)
+{
+ OSSL_STORE_LOADER_CTX_free(ctx);
+ return 1;
+}
+
+int setup_engine_loader(void)
+{
+ OSSL_STORE_LOADER *loader = NULL;
+
+ if ((loader = OSSL_STORE_LOADER_new(NULL, ENGINE_SCHEME)) == NULL
+ || !OSSL_STORE_LOADER_set_open(loader, engine_open)
+ || !OSSL_STORE_LOADER_set_expect(loader, engine_expect)
+ || !OSSL_STORE_LOADER_set_load(loader, engine_load)
+ || !OSSL_STORE_LOADER_set_eof(loader, engine_eof)
+ || !OSSL_STORE_LOADER_set_error(loader, engine_error)
+ || !OSSL_STORE_LOADER_set_close(loader, engine_close)
+ || !OSSL_STORE_register_loader(loader)) {
+ OSSL_STORE_LOADER_free(loader);
+ loader = NULL;
+ }
+
+ return loader != NULL;
+}
+
+void destroy_engine_loader(void)
+{
+ OSSL_STORE_LOADER *loader = OSSL_STORE_unregister_loader(ENGINE_SCHEME);
+ OSSL_STORE_LOADER_free(loader);
+}
+
+#else /* !OPENSSL_NO_ENGINE */
+
+int setup_engine_loader(void)
+{
+ return 0;
+}
+
+void destroy_engine_loader(void)
+{
+}
+
+#endif
diff --git a/apps/lib/fmt.c b/apps/lib/fmt.c
new file mode 100644
index 000000000000..af0e63b85b14
--- /dev/null
+++ b/apps/lib/fmt.c
@@ -0,0 +1,15 @@
+/*
+ * Copyright 2018-2020 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the Apache License 2.0 (the "License"). You may not use
+ * this file except in compliance with the License. You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include "fmt.h"
+
+int FMT_istext(int format)
+{
+ return (format & B_FORMAT_TEXT) == B_FORMAT_TEXT;
+}
diff --git a/apps/lib/http_server.c b/apps/lib/http_server.c
new file mode 100644
index 000000000000..33ae886d4a1c
--- /dev/null
+++ b/apps/lib/http_server.c
@@ -0,0 +1,536 @@
+/*
+ * Copyright 1995-2022 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the Apache License 2.0 (the "License"). You may not use
+ * this file except in compliance with the License. You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+/* Very basic HTTP server */
+
+#if !defined(_POSIX_C_SOURCE) && defined(OPENSSL_SYS_VMS)
+/*
+ * On VMS, you need to define this to get the declaration of fileno(). The
+ * value 2 is to make sure no function defined in POSIX-2 is left undefined.
+ */
+# define _POSIX_C_SOURCE 2
+#endif
+
+#include <string.h>
+#include <ctype.h>
+#include "http_server.h"
+#include "internal/sockets.h"
+#include <openssl/err.h>
+#include <openssl/rand.h>
+#include "s_apps.h"
+
+#if defined(__TANDEM)
+# if defined(OPENSSL_TANDEM_FLOSS)
+# include <floss.h(floss_fork)>
+# endif
+#endif
+
+static int verbosity = LOG_INFO;
+
+#define HTTP_PREFIX "HTTP/"
+#define HTTP_VERSION_PATT "1." /* allow 1.x */
+#define HTTP_PREFIX_VERSION HTTP_PREFIX""HTTP_VERSION_PATT
+#define HTTP_1_0 HTTP_PREFIX_VERSION"0" /* "HTTP/1.0" */
+
+#ifdef HTTP_DAEMON
+
+int multi = 0; /* run multiple responder processes */
+int acfd = (int) INVALID_SOCKET;
+
+static int print_syslog(const char *str, size_t len, void *levPtr)
+{
+ int level = *(int *)levPtr;
+ int ilen = len > MAXERRLEN ? MAXERRLEN : len;
+
+ syslog(level, "%.*s", ilen, str);
+
+ return ilen;
+}
+#endif
+
+void log_message(const char *prog, int level, const char *fmt, ...)
+{
+ va_list ap;
+
+ if (verbosity < level)
+ return;
+
+ va_start(ap, fmt);
+#ifdef HTTP_DAEMON
+ if (multi) {
+ char buf[1024];
+
+ if (vsnprintf(buf, sizeof(buf), fmt, ap) > 0)
+ syslog(level, "%s", buf);
+ if (level <= LOG_ERR)
+ ERR_print_errors_cb(print_syslog, &level);
+ } else
+#endif
+ {
+ BIO_printf(bio_err, "%s: ", prog);
+ BIO_vprintf(bio_err, fmt, ap);
+ BIO_printf(bio_err, "\n");
+ (void)BIO_flush(bio_err);
+ }
+ va_end(ap);
+}
+
+#ifdef HTTP_DAEMON
+void socket_timeout(int signum)
+{
+ if (acfd != (int)INVALID_SOCKET)
+ (void)shutdown(acfd, SHUT_RD);
+}
+
+static void killall(int ret, pid_t *kidpids)
+{
+ int i;
+
+ for (i = 0; i < multi; ++i)
+ if (kidpids[i] != 0)
+ (void)kill(kidpids[i], SIGTERM);
+ OPENSSL_free(kidpids);
+ ossl_sleep(1000);
+ exit(ret);
+}
+
+static int termsig = 0;
+
+static void noteterm(int sig)
+{
+ termsig = sig;
+}
+
+/*
+ * Loop spawning up to `multi` child processes, only child processes return
+ * from this function. The parent process loops until receiving a termination
+ * signal, kills extant children and exits without returning.
+ */
+void spawn_loop(const char *prog)
+{
+ pid_t *kidpids = NULL;
+ int status;
+ int procs = 0;
+ int i;
+
+ openlog(prog, LOG_PID, LOG_DAEMON);
+
+ if (setpgid(0, 0)) {
+ syslog(LOG_ERR, "fatal: error detaching from parent process group: %s",
+ strerror(errno));
+ exit(1);
+ }
+ kidpids = app_malloc(multi * sizeof(*kidpids), "child PID array");
+ for (i = 0; i < multi; ++i)
+ kidpids[i] = 0;
+
+ signal(SIGINT, noteterm);
+ signal(SIGTERM, noteterm);
+
+ while (termsig == 0) {
+ pid_t fpid;
+
+ /*
+ * Wait for a child to replace when we're at the limit.
+ * Slow down if a child exited abnormally or waitpid() < 0
+ */
+ while (termsig == 0 && procs >= multi) {
+ if ((fpid = waitpid(-1, &status, 0)) > 0) {
+ for (i = 0; i < procs; ++i) {
+ if (kidpids[i] == fpid) {
+ kidpids[i] = 0;
+ --procs;
+ break;
+ }
+ }
+ if (i >= multi) {
+ syslog(LOG_ERR, "fatal: internal error: "
+ "no matching child slot for pid: %ld",
+ (long) fpid);
+ killall(1, kidpids);
+ }
+ if (status != 0) {
+ if (WIFEXITED(status))
+ syslog(LOG_WARNING, "child process: %ld, exit status: %d",
+ (long)fpid, WEXITSTATUS(status));
+ else if (WIFSIGNALED(status))
+ syslog(LOG_WARNING, "child process: %ld, term signal %d%s",
+ (long)fpid, WTERMSIG(status),
+# ifdef WCOREDUMP
+ WCOREDUMP(status) ? " (core dumped)" :
+# endif
+ "");
+ ossl_sleep(1000);
+ }
+ break;
+ } else if (errno != EINTR) {
+ syslog(LOG_ERR, "fatal: waitpid(): %s", strerror(errno));
+ killall(1, kidpids);
+ }
+ }
+ if (termsig)
+ break;
+
+ switch (fpid = fork()) {
+ case -1: /* error */
+ /* System critically low on memory, pause and try again later */
+ ossl_sleep(30000);
+ break;
+ case 0: /* child */
+ OPENSSL_free(kidpids);
+ signal(SIGINT, SIG_DFL);
+ signal(SIGTERM, SIG_DFL);
+ if (termsig)
+ _exit(0);
+ if (RAND_poll() <= 0) {
+ syslog(LOG_ERR, "fatal: RAND_poll() failed");
+ _exit(1);
+ }
+ return;
+ default: /* parent */
+ for (i = 0; i < multi; ++i) {
+ if (kidpids[i] == 0) {
+ kidpids[i] = fpid;
+ procs++;
+ break;
+ }
+ }
+ if (i >= multi) {
+ syslog(LOG_ERR, "fatal: internal error: no free child slots");
+ killall(1, kidpids);
+ }
+ break;
+ }
+ }
+
+ /* The loop above can only break on termsig */
+ syslog(LOG_INFO, "terminating on signal: %d", termsig);
+ killall(0, kidpids);
+}
+#endif
+
+#ifndef OPENSSL_NO_SOCK
+BIO *http_server_init_bio(const char *prog, const char *port)
+{
+ BIO *acbio = NULL, *bufbio;
+ int asock;
+ char name[40];
+
+ snprintf(name, sizeof(name), "[::]:%s", port); /* port may be "0" */
+ bufbio = BIO_new(BIO_f_buffer());
+ if (bufbio == NULL)
+ goto err;
+ acbio = BIO_new(BIO_s_accept());
+ if (acbio == NULL
+ || BIO_set_accept_ip_family(acbio, BIO_FAMILY_IPANY) <= 0 /* IPv4/6 */
+ || BIO_set_bind_mode(acbio, BIO_BIND_REUSEADDR) <= 0
+ || BIO_set_accept_name(acbio, name) <= 0) {
+ log_message(prog, LOG_ERR, "Error setting up accept BIO");
+ goto err;
+ }
+
+ BIO_set_accept_bios(acbio, bufbio);
+ bufbio = NULL;
+ if (BIO_do_accept(acbio) <= 0) {
+ log_message(prog, LOG_ERR, "Error starting accept");
+ goto err;
+ }
+
+ /* Report back what address and port are used */
+ BIO_get_fd(acbio, &asock);
+ if (!report_server_accept(bio_out, asock, 1, 1)) {
+ log_message(prog, LOG_ERR, "Error printing ACCEPT string");
+ goto err;
+ }
+
+ return acbio;
+
+ err:
+ BIO_free_all(acbio);
+ BIO_free(bufbio);
+ return NULL;
+}
+
+/*
+ * Decode %xx URL-decoding in-place. Ignores malformed sequences.
+ */
+static int urldecode(char *p)
+{
+ unsigned char *out = (unsigned char *)p;
+ unsigned char *save = out;
+
+ for (; *p; p++) {
+ if (*p != '%') {
+ *out++ = *p;
+ } else if (isxdigit(_UC(p[1])) && isxdigit(_UC(p[2]))) {
+ /* Don't check, can't fail because of ixdigit() call. */
+ *out++ = (OPENSSL_hexchar2int(p[1]) << 4)
+ | OPENSSL_hexchar2int(p[2]);
+ p += 2;
+ } else {
+ return -1;
+ }
+ }
+ *out = '\0';
+ return (int)(out - save);
+}
+
+/* if *pcbio != NULL, continue given connected session, else accept new */
+/* if found_keep_alive != NULL, return this way connection persistence state */
+int http_server_get_asn1_req(const ASN1_ITEM *it, ASN1_VALUE **preq,
+ char **ppath, BIO **pcbio, BIO *acbio,
+ int *found_keep_alive,
+ const char *prog, const char *port,
+ int accept_get, int timeout)
+{
+ BIO *cbio = *pcbio, *getbio = NULL, *b64 = NULL;
+ int len;
+ char reqbuf[2048], inbuf[2048];
+ char *meth, *url, *end;
+ ASN1_VALUE *req;
+ int ret = 0;
+
+ *preq = NULL;
+ if (ppath != NULL)
+ *ppath = NULL;
+
+ if (cbio == NULL) {
+ log_message(prog, LOG_DEBUG,
+ "Awaiting new connection on port %s...", port);
+ if (BIO_do_accept(acbio) <= 0)
+ /* Connection loss before accept() is routine, ignore silently */
+ return ret;
+
+ *pcbio = cbio = BIO_pop(acbio);
+ } else {
+ log_message(prog, LOG_DEBUG, "Awaiting next request...");
+ }
+ if (cbio == NULL) {
+ /* Cannot call http_server_send_status(cbio, ...) */
+ ret = -1;
+ goto out;
+ }
+
+# ifdef HTTP_DAEMON
+ if (timeout > 0) {
+ (void)BIO_get_fd(cbio, &acfd);
+ alarm(timeout);
+ }
+# endif
+
+ /* Read the request line. */
+ len = BIO_gets(cbio, reqbuf, sizeof(reqbuf));
+ if (len == 0)
+ return ret;
+ ret = 1;
+ if (len < 0) {
+ log_message(prog, LOG_WARNING, "Request line read error");
+ (void)http_server_send_status(cbio, 400, "Bad Request");
+ goto out;
+ }
+ if ((end = strchr(reqbuf, '\r')) != NULL
+ || (end = strchr(reqbuf, '\n')) != NULL)
+ *end = '\0';
+ log_message(prog, LOG_INFO, "Received request, 1st line: %s", reqbuf);
+
+ meth = reqbuf;
+ url = meth + 3;
+ if ((accept_get && strncmp(meth, "GET ", 4) == 0)
+ || (url++, strncmp(meth, "POST ", 5) == 0)) {
+ static const char http_version_str[] = " "HTTP_PREFIX_VERSION;
+ static const size_t http_version_str_len = sizeof(http_version_str) - 1;
+
+ /* Expecting (GET|POST) {sp} /URL {sp} HTTP/1.x */
+ *(url++) = '\0';
+ while (*url == ' ')
+ url++;
+ if (*url != '/') {
+ log_message(prog, LOG_WARNING,
+ "Invalid %s -- URL does not begin with '/': %s",
+ meth, url);
+ (void)http_server_send_status(cbio, 400, "Bad Request");
+ goto out;
+ }
+ url++;
+
+ /* Splice off the HTTP version identifier. */
+ for (end = url; *end != '\0'; end++)
+ if (*end == ' ')
+ break;
+ if (strncmp(end, http_version_str, http_version_str_len) != 0) {
+ log_message(prog, LOG_WARNING,
+ "Invalid %s -- bad HTTP/version string: %s",
+ meth, end + 1);
+ (void)http_server_send_status(cbio, 400, "Bad Request");
+ goto out;
+ }
+ *end = '\0';
+ /* above HTTP 1.0, connection persistence is the default */
+ if (found_keep_alive != NULL)
+ *found_keep_alive = end[http_version_str_len] > '0';
+
+ /*-
+ * Skip "GET / HTTP..." requests often used by load-balancers.
+ * 'url' was incremented above to point to the first byte *after*
+ * the leading slash, so in case 'GET / ' it is now an empty string.
+ */
+ if (strlen(meth) == 3 && url[0] == '\0') {
+ (void)http_server_send_status(cbio, 200, "OK");
+ goto out;
+ }
+
+ len = urldecode(url);
+ if (len < 0) {
+ log_message(prog, LOG_WARNING,
+ "Invalid %s request -- bad URL encoding: %s",
+ meth, url);
+ (void)http_server_send_status(cbio, 400, "Bad Request");
+ goto out;
+ }
+ if (strlen(meth) == 3) { /* GET */
+ if ((getbio = BIO_new_mem_buf(url, len)) == NULL
+ || (b64 = BIO_new(BIO_f_base64())) == NULL) {
+ log_message(prog, LOG_ERR,
+ "Could not allocate base64 bio with size = %d",
+ len);
+ goto fatal;
+ }
+ BIO_set_flags(b64, BIO_FLAGS_BASE64_NO_NL);
+ getbio = BIO_push(b64, getbio);
+ }
+ } else {
+ log_message(prog, LOG_WARNING,
+ "HTTP request does not begin with %sPOST: %s",
+ accept_get ? "GET or " : "", reqbuf);
+ (void)http_server_send_status(cbio, 400, "Bad Request");
+ goto out;
+ }
+
+ /* chop any further/duplicate leading or trailing '/' */
+ while (*url == '/')
+ url++;
+ while (end >= url + 2 && end[-2] == '/' && end[-1] == '/')
+ end--;
+ *end = '\0';
+
+ /* Read and skip past the headers. */
+ for (;;) {
+ char *key, *value, *line_end = NULL;
+
+ len = BIO_gets(cbio, inbuf, sizeof(inbuf));
+ if (len <= 0) {
+ log_message(prog, LOG_WARNING, "Error reading HTTP header");
+ (void)http_server_send_status(cbio, 400, "Bad Request");
+ goto out;
+ }
+
+ if (inbuf[0] == '\r' || inbuf[0] == '\n')
+ break;
+
+ key = inbuf;
+ value = strchr(key, ':');
+ if (value == NULL) {
+ log_message(prog, LOG_WARNING,
+ "Error parsing HTTP header: missing ':'");
+ (void)http_server_send_status(cbio, 400, "Bad Request");
+ goto out;
+ }
+ *(value++) = '\0';
+ while (*value == ' ')
+ value++;
+ line_end = strchr(value, '\r');
+ if (line_end == NULL) {
+ line_end = strchr(value, '\n');
+ if (line_end == NULL) {
+ log_message(prog, LOG_WARNING,
+ "Error parsing HTTP header: missing end of line");
+ (void)http_server_send_status(cbio, 400, "Bad Request");
+ goto out;
+ }
+ }
+ *line_end = '\0';
+ /* https://tools.ietf.org/html/rfc7230#section-6.3 Persistence */
+ if (found_keep_alive != NULL
+ && OPENSSL_strcasecmp(key, "Connection") == 0) {
+ if (OPENSSL_strcasecmp(value, "keep-alive") == 0)
+ *found_keep_alive = 1;
+ else if (OPENSSL_strcasecmp(value, "close") == 0)
+ *found_keep_alive = 0;
+ }
+ }
+
+# ifdef HTTP_DAEMON
+ /* Clear alarm before we close the client socket */
+ alarm(0);
+ timeout = 0;
+# endif
+
+ /* Try to read and parse request */
+ req = ASN1_item_d2i_bio(it, getbio != NULL ? getbio : cbio, NULL);
+ if (req == NULL) {
+ log_message(prog, LOG_WARNING,
+ "Error parsing DER-encoded request content");
+ (void)http_server_send_status(cbio, 400, "Bad Request");
+ } else if (ppath != NULL && (*ppath = OPENSSL_strdup(url)) == NULL) {
+ log_message(prog, LOG_ERR,
+ "Out of memory allocating %zu bytes", strlen(url) + 1);
+ ASN1_item_free(req, it);
+ goto fatal;
+ }
+
+ *preq = req;
+
+ out:
+ BIO_free_all(getbio);
+# ifdef HTTP_DAEMON
+ if (timeout > 0)
+ alarm(0);
+ acfd = (int)INVALID_SOCKET;
+# endif
+ return ret;
+
+ fatal:
+ (void)http_server_send_status(cbio, 500, "Internal Server Error");
+ if (ppath != NULL) {
+ OPENSSL_free(*ppath);
+ *ppath = NULL;
+ }
+ BIO_free_all(cbio);
+ *pcbio = NULL;
+ ret = -1;
+ goto out;
+}
+
+/* assumes that cbio does not do an encoding that changes the output length */
+int http_server_send_asn1_resp(BIO *cbio, int keep_alive,
+ const char *content_type,
+ const ASN1_ITEM *it, const ASN1_VALUE *resp)
+{
+ int ret = BIO_printf(cbio, HTTP_1_0" 200 OK\r\n%s"
+ "Content-type: %s\r\n"
+ "Content-Length: %d\r\n\r\n",
+ keep_alive ? "Connection: keep-alive\r\n" : "",
+ content_type,
+ ASN1_item_i2d(resp, NULL, it)) > 0
+ && ASN1_item_i2d_bio(it, cbio, resp) > 0;
+
+ (void)BIO_flush(cbio);
+ return ret;
+}
+
+int http_server_send_status(BIO *cbio, int status, const char *reason)
+{
+ int ret = BIO_printf(cbio, HTTP_1_0" %d %s\r\n\r\n",
+ /* This implicitly cancels keep-alive */
+ status, reason) > 0;
+
+ (void)BIO_flush(cbio);
+ return ret;
+}
+#endif
diff --git a/apps/lib/names.c b/apps/lib/names.c
new file mode 100644
index 000000000000..4788ae84b915
--- /dev/null
+++ b/apps/lib/names.c
@@ -0,0 +1,45 @@
+/*
+ * Copyright 2019-2022 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the Apache License 2.0 (the "License"). You may not use
+ * this file except in compliance with the License. You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include <string.h>
+#include <openssl/bio.h>
+#include <openssl/safestack.h>
+#include "names.h"
+#include "openssl/crypto.h"
+
+int name_cmp(const char * const *a, const char * const *b)
+{
+ return OPENSSL_strcasecmp(*a, *b);
+}
+
+void collect_names(const char *name, void *vdata)
+{
+ STACK_OF(OPENSSL_CSTRING) *names = vdata;
+
+ sk_OPENSSL_CSTRING_push(names, name);
+}
+
+void print_names(BIO *out, STACK_OF(OPENSSL_CSTRING) *names)
+{
+ int i = sk_OPENSSL_CSTRING_num(names);
+ int j;
+
+ sk_OPENSSL_CSTRING_sort(names);
+ if (i > 1)
+ BIO_printf(out, "{ ");
+ for (j = 0; j < i; j++) {
+ const char *name = sk_OPENSSL_CSTRING_value(names, j);
+
+ if (j > 0)
+ BIO_printf(out, ", ");
+ BIO_printf(out, "%s", name);
+ }
+ if (i > 1)
+ BIO_printf(out, " }");
+}
diff --git a/apps/lib/opt.c b/apps/lib/opt.c
new file mode 100644
index 000000000000..88db9ad6947b
--- /dev/null
+++ b/apps/lib/opt.c
@@ -0,0 +1,1203 @@
+/*
+ * Copyright 2015-2024 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the Apache License 2.0 (the "License"). You may not use
+ * this file except in compliance with the License. You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+/*
+ * This file is also used by the test suite. Do not #include "apps.h".
+ */
+#include "opt.h"
+#include "fmt.h"
+#include "app_libctx.h"
+#include "internal/nelem.h"
+#include "internal/numbers.h"
+#include <string.h>
+#if !defined(OPENSSL_SYS_MSDOS)
+# include <unistd.h>
+#endif
+
+#include <stdlib.h>
+#include <errno.h>
+#include <ctype.h>
+#include <limits.h>
+#include <openssl/err.h>
+#include <openssl/bio.h>
+#include <openssl/x509v3.h>
+
+#define MAX_OPT_HELP_WIDTH 30
+const char OPT_HELP_STR[] = "-H";
+const char OPT_MORE_STR[] = "-M";
+const char OPT_SECTION_STR[] = "-S";
+const char OPT_PARAM_STR[] = "-P";
+
+/* Our state */
+static char **argv;
+static int argc;
+static int opt_index;
+static char *arg;
+static char *flag;
+static char *dunno;
+static const OPTIONS *unknown;
+static const OPTIONS *opts;
+static char prog[40];
+
+/*
+ * Return the simple name of the program; removing various platform gunk.
+ */
+#if defined(OPENSSL_SYS_WIN32)
+
+const char *opt_path_end(const char *filename)
+{
+ const char *p;
+
+ /* find the last '/', '\' or ':' */
+ for (p = filename + strlen(filename); --p > filename; )
+ if (*p == '/' || *p == '\\' || *p == ':') {
+ p++;
+ break;
+ }
+ return p;
+}
+
+char *opt_progname(const char *argv0)
+{
+ size_t i, n;
+ const char *p;
+ char *q;
+
+ p = opt_path_end(argv0);
+
+ /* Strip off trailing nonsense. */
+ n = strlen(p);
+ if (n > 4 &&
+ (strcmp(&p[n - 4], ".exe") == 0 || strcmp(&p[n - 4], ".EXE") == 0))
+ n -= 4;
+
+ /* Copy over the name, in lowercase. */
+ if (n > sizeof(prog) - 1)
+ n = sizeof(prog) - 1;
+ for (q = prog, i = 0; i < n; i++, p++)
+ *q++ = tolower((unsigned char)*p);
+ *q = '\0';
+ return prog;
+}
+
+#elif defined(OPENSSL_SYS_VMS)
+
+const char *opt_path_end(const char *filename)
+{
+ const char *p;
+
+ /* Find last special character sys:[foo.bar]openssl */
+ for (p = filename + strlen(filename); --p > filename;)
+ if (*p == ':' || *p == ']' || *p == '>') {
+ p++;
+ break;
+ }
+ return p;
+}
+
+char *opt_progname(const char *argv0)
+{
+ const char *p, *q;
+
+ /* Find last special character sys:[foo.bar]openssl */
+ p = opt_path_end(argv0);
+ q = strrchr(p, '.');
+ if (prog != p)
+ strncpy(prog, p, sizeof(prog) - 1);
+ prog[sizeof(prog) - 1] = '\0';
+ if (q != NULL && q - p < sizeof(prog))
+ prog[q - p] = '\0';
+ return prog;
+}
+
+#else
+
+const char *opt_path_end(const char *filename)
+{
+ const char *p;
+
+ /* Could use strchr, but this is like the ones above. */
+ for (p = filename + strlen(filename); --p > filename;)
+ if (*p == '/') {
+ p++;
+ break;
+ }
+ return p;
+}
+
+char *opt_progname(const char *argv0)
+{
+ const char *p;
+
+ p = opt_path_end(argv0);
+ if (prog != p)
+ strncpy(prog, p, sizeof(prog) - 1);
+ prog[sizeof(prog) - 1] = '\0';
+ return prog;
+}
+#endif
+
+char *opt_appname(const char *argv0)
+{
+ size_t len = strlen(prog);
+
+ if (argv0 != NULL)
+ BIO_snprintf(prog + len, sizeof(prog) - len - 1, " %s", argv0);
+ return prog;
+}
+
+char *opt_getprog(void)
+{
+ return prog;
+}
+
+/* Set up the arg parsing. */
+char *opt_init(int ac, char **av, const OPTIONS *o)
+{
+ /* Store state. */
+ argc = ac;
+ argv = av;
+ opt_begin();
+ opts = o;
+ unknown = NULL;
+
+ /* Make sure prog name is set for usage output */
+ (void)opt_progname(argv[0]);
+
+ /* Check all options up until the PARAM marker (if present) */
+ for (; o->name != NULL && o->name != OPT_PARAM_STR; ++o) {
+#ifndef NDEBUG
+ const OPTIONS *next;
+ int duplicated, i;
+#endif
+
+ if (o->name == OPT_HELP_STR
+ || o->name == OPT_MORE_STR
+ || o->name == OPT_SECTION_STR)
+ continue;
+#ifndef NDEBUG
+ i = o->valtype;
+
+ /* Make sure options are legit. */
+ OPENSSL_assert(o->name[0] != '-');
+ if (o->valtype == '.')
+ OPENSSL_assert(o->retval == OPT_PARAM);
+ else
+ OPENSSL_assert(o->retval == OPT_DUP || o->retval > OPT_PARAM);
+ switch (i) {
+ case 0: case '-': case '.':
+ case '/': case '<': case '>': case 'E': case 'F':
+ case 'M': case 'U': case 'f': case 'l': case 'n': case 'p': case 's':
+ case 'u': case 'c': case ':': case 'N':
+ break;
+ default:
+ OPENSSL_assert(0);
+ }
+
+ /* Make sure there are no duplicates. */
+ for (next = o + 1; next->name; ++next) {
+ /*
+ * Some compilers inline strcmp and the assert string is too long.
+ */
+ duplicated = next->retval != OPT_DUP
+ && strcmp(o->name, next->name) == 0;
+ if (duplicated) {
+ opt_printf_stderr("%s: Internal error: duplicate option %s\n",
+ prog, o->name);
+ OPENSSL_assert(!duplicated);
+ }
+ }
+#endif
+ if (o->name[0] == '\0') {
+ OPENSSL_assert(unknown == NULL);
+ unknown = o;
+ OPENSSL_assert(unknown->valtype == 0 || unknown->valtype == '-');
+ }
+ }
+ return prog;
+}
+
+static OPT_PAIR formats[] = {
+ {"PEM/DER", OPT_FMT_PEMDER},
+ {"pkcs12", OPT_FMT_PKCS12},
+ {"smime", OPT_FMT_SMIME},
+ {"engine", OPT_FMT_ENGINE},
+ {"msblob", OPT_FMT_MSBLOB},
+ {"nss", OPT_FMT_NSS},
+ {"text", OPT_FMT_TEXT},
+ {"http", OPT_FMT_HTTP},
+ {"pvk", OPT_FMT_PVK},
+ {NULL}
+};
+
+/* Print an error message about a failed format parse. */
+static int opt_format_error(const char *s, unsigned long flags)
+{
+ OPT_PAIR *ap;
+
+ if (flags == OPT_FMT_PEMDER) {
+ opt_printf_stderr("%s: Bad format \"%s\"; must be pem or der\n",
+ prog, s);
+ } else {
+ opt_printf_stderr("%s: Bad format \"%s\"; must be one of:\n",
+ prog, s);
+ for (ap = formats; ap->name; ap++)
+ if (flags & ap->retval)
+ opt_printf_stderr(" %s\n", ap->name);
+ }
+ return 0;
+}
+
+/* Parse a format string, put it into *result; return 0 on failure, else 1. */
+int opt_format(const char *s, unsigned long flags, int *result)
+{
+ switch (*s) {
+ default:
+ opt_printf_stderr("%s: Bad format \"%s\"\n", prog, s);
+ return 0;
+ case 'D':
+ case 'd':
+ if ((flags & OPT_FMT_PEMDER) == 0)
+ return opt_format_error(s, flags);
+ *result = FORMAT_ASN1;
+ break;
+ case 'T':
+ case 't':
+ if ((flags & OPT_FMT_TEXT) == 0)
+ return opt_format_error(s, flags);
+ *result = FORMAT_TEXT;
+ break;
+ case 'N':
+ case 'n':
+ if ((flags & OPT_FMT_NSS) == 0)
+ return opt_format_error(s, flags);
+ if (strcmp(s, "NSS") != 0 && strcmp(s, "nss") != 0)
+ return opt_format_error(s, flags);
+ *result = FORMAT_NSS;
+ break;
+ case 'S':
+ case 's':
+ if ((flags & OPT_FMT_SMIME) == 0)
+ return opt_format_error(s, flags);
+ *result = FORMAT_SMIME;
+ break;
+ case 'M':
+ case 'm':
+ if ((flags & OPT_FMT_MSBLOB) == 0)
+ return opt_format_error(s, flags);
+ *result = FORMAT_MSBLOB;
+ break;
+ case 'E':
+ case 'e':
+ if ((flags & OPT_FMT_ENGINE) == 0)
+ return opt_format_error(s, flags);
+ *result = FORMAT_ENGINE;
+ break;
+ case 'H':
+ case 'h':
+ if ((flags & OPT_FMT_HTTP) == 0)
+ return opt_format_error(s, flags);
+ *result = FORMAT_HTTP;
+ break;
+ case '1':
+ if ((flags & OPT_FMT_PKCS12) == 0)
+ return opt_format_error(s, flags);
+ *result = FORMAT_PKCS12;
+ break;
+ case 'P':
+ case 'p':
+ if (s[1] == '\0' || strcmp(s, "PEM") == 0 || strcmp(s, "pem") == 0) {
+ if ((flags & OPT_FMT_PEMDER) == 0)
+ return opt_format_error(s, flags);
+ *result = FORMAT_PEM;
+ } else if (strcmp(s, "PVK") == 0 || strcmp(s, "pvk") == 0) {
+ if ((flags & OPT_FMT_PVK) == 0)
+ return opt_format_error(s, flags);
+ *result = FORMAT_PVK;
+ } else if (strcmp(s, "P12") == 0 || strcmp(s, "p12") == 0
+ || strcmp(s, "PKCS12") == 0 || strcmp(s, "pkcs12") == 0) {
+ if ((flags & OPT_FMT_PKCS12) == 0)
+ return opt_format_error(s, flags);
+ *result = FORMAT_PKCS12;
+ } else {
+ opt_printf_stderr("%s: Bad format \"%s\"\n", prog, s);
+ return 0;
+ }
+ break;
+ }
+ return 1;
+}
+
+/* Return string representing the given format. */
+static const char *format2str(int format)
+{
+ switch (format) {
+ default:
+ return "(undefined)";
+ case FORMAT_PEM:
+ return "PEM";
+ case FORMAT_ASN1:
+ return "DER";
+ case FORMAT_TEXT:
+ return "TEXT";
+ case FORMAT_NSS:
+ return "NSS";
+ case FORMAT_SMIME:
+ return "SMIME";
+ case FORMAT_MSBLOB:
+ return "MSBLOB";
+ case FORMAT_ENGINE:
+ return "ENGINE";
+ case FORMAT_HTTP:
+ return "HTTP";
+ case FORMAT_PKCS12:
+ return "P12";
+ case FORMAT_PVK:
+ return "PVK";
+ }
+}
+
+/* Print an error message about unsuitable/unsupported format requested. */
+void print_format_error(int format, unsigned long flags)
+{
+ (void)opt_format_error(format2str(format), flags);
+}
+
+/*
+ * Parse a cipher name, put it in *cipherp after freeing what was there, if
+ * cipherp is not NULL. Return 0 on failure, else 1.
+ */
+int opt_cipher_silent(const char *name, EVP_CIPHER **cipherp)
+{
+ EVP_CIPHER *c;
+
+ ERR_set_mark();
+ if ((c = EVP_CIPHER_fetch(app_get0_libctx(), name,
+ app_get0_propq())) != NULL
+ || (opt_legacy_okay()
+ && (c = (EVP_CIPHER *)EVP_get_cipherbyname(name)) != NULL)) {
+ ERR_pop_to_mark();
+ if (cipherp != NULL) {
+ EVP_CIPHER_free(*cipherp);
+ *cipherp = c;
+ } else {
+ EVP_CIPHER_free(c);
+ }
+ return 1;
+ }
+ ERR_clear_last_mark();
+ return 0;
+}
+
+int opt_cipher_any(const char *name, EVP_CIPHER **cipherp)
+{
+ int ret;
+
+ if ((ret = opt_cipher_silent(name, cipherp)) == 0)
+ opt_printf_stderr("%s: Unknown cipher: %s\n", prog, name);
+ return ret;
+}
+
+int opt_cipher(const char *name, EVP_CIPHER **cipherp)
+{
+ int mode, ret = 0;
+ unsigned long int flags;
+ EVP_CIPHER *c = NULL;
+
+ if (opt_cipher_any(name, &c)) {
+ mode = EVP_CIPHER_get_mode(c);
+ flags = EVP_CIPHER_get_flags(c);
+ if (mode == EVP_CIPH_XTS_MODE) {
+ opt_printf_stderr("%s XTS ciphers not supported\n", prog);
+ } else if ((flags & EVP_CIPH_FLAG_AEAD_CIPHER) != 0) {
+ opt_printf_stderr("%s: AEAD ciphers not supported\n", prog);
+ } else {
+ ret = 1;
+ if (cipherp != NULL)
+ *cipherp = c;
+ }
+ }
+ return ret;
+}
+
+/*
+ * Parse message digest name, put it in *EVP_MD; return 0 on failure, else 1.
+ */
+int opt_md_silent(const char *name, EVP_MD **mdp)
+{
+ EVP_MD *md;
+
+ ERR_set_mark();
+ if ((md = EVP_MD_fetch(app_get0_libctx(), name, app_get0_propq())) != NULL
+ || (opt_legacy_okay()
+ && (md = (EVP_MD *)EVP_get_digestbyname(name)) != NULL)) {
+ ERR_pop_to_mark();
+ if (mdp != NULL) {
+ EVP_MD_free(*mdp);
+ *mdp = md;
+ } else {
+ EVP_MD_free(md);
+ }
+ return 1;
+ }
+ ERR_clear_last_mark();
+ return 0;
+}
+
+int opt_md(const char *name, EVP_MD **mdp)
+{
+ int ret;
+
+ if ((ret = opt_md_silent(name, mdp)) == 0)
+ opt_printf_stderr("%s: Unknown option or message digest: %s\n", prog,
+ name != NULL ? name : "\"\"");
+ return ret;
+}
+
+/* Look through a list of name/value pairs. */
+int opt_pair(const char *name, const OPT_PAIR* pairs, int *result)
+{
+ const OPT_PAIR *pp;
+
+ for (pp = pairs; pp->name; pp++)
+ if (strcmp(pp->name, name) == 0) {
+ *result = pp->retval;
+ return 1;
+ }
+ opt_printf_stderr("%s: Value must be one of:\n", prog);
+ for (pp = pairs; pp->name; pp++)
+ opt_printf_stderr("\t%s\n", pp->name);
+ return 0;
+}
+
+/* Look through a list of valid names */
+int opt_string(const char *name, const char **options)
+{
+ const char **p;
+
+ for (p = options; *p != NULL; p++)
+ if (strcmp(*p, name) == 0)
+ return 1;
+ opt_printf_stderr("%s: Value must be one of:\n", prog);
+ for (p = options; *p != NULL; p++)
+ opt_printf_stderr("\t%s\n", *p);
+ return 0;
+}
+
+/* Parse an int, put it into *result; return 0 on failure, else 1. */
+int opt_int(const char *value, int *result)
+{
+ long l;
+
+ if (!opt_long(value, &l))
+ return 0;
+ *result = (int)l;
+ if (*result != l) {
+ opt_printf_stderr("%s: Value \"%s\" outside integer range\n",
+ prog, value);
+ return 0;
+ }
+ return 1;
+}
+
+/* Parse and return an integer, assuming range has been checked before. */
+int opt_int_arg(void)
+{
+ int result = -1;
+
+ (void)opt_int(arg, &result);
+ return result;
+}
+
+static void opt_number_error(const char *v)
+{
+ size_t i = 0;
+ struct strstr_pair_st {
+ char *prefix;
+ char *name;
+ } b[] = {
+ {"0x", "a hexadecimal"},
+ {"0X", "a hexadecimal"},
+ {"0", "an octal"}
+ };
+
+ for (i = 0; i < OSSL_NELEM(b); i++) {
+ if (strncmp(v, b[i].prefix, strlen(b[i].prefix)) == 0) {
+ opt_printf_stderr("%s: Can't parse \"%s\" as %s number\n",
+ prog, v, b[i].name);
+ return;
+ }
+ }
+ opt_printf_stderr("%s: Can't parse \"%s\" as a number\n", prog, v);
+ return;
+}
+
+/* Parse a long, put it into *result; return 0 on failure, else 1. */
+int opt_long(const char *value, long *result)
+{
+ int oerrno = errno;
+ long l;
+ char *endp;
+
+ errno = 0;
+ l = strtol(value, &endp, 0);
+ if (*endp
+ || endp == value
+ || ((l == LONG_MAX || l == LONG_MIN) && errno == ERANGE)
+ || (l == 0 && errno != 0)) {
+ opt_number_error(value);
+ errno = oerrno;
+ return 0;
+ }
+ *result = l;
+ errno = oerrno;
+ return 1;
+}
+
+#if defined(__STDC_VERSION__) && __STDC_VERSION__ >= 199901L && \
+ defined(INTMAX_MAX) && defined(UINTMAX_MAX) && \
+ !defined(OPENSSL_NO_INTTYPES_H)
+
+/* Parse an intmax_t, put it into *result; return 0 on failure, else 1. */
+int opt_intmax(const char *value, ossl_intmax_t *result)
+{
+ int oerrno = errno;
+ intmax_t m;
+ char *endp;
+
+ errno = 0;
+ m = strtoimax(value, &endp, 0);
+ if (*endp
+ || endp == value
+ || ((m == INTMAX_MAX || m == INTMAX_MIN)
+ && errno == ERANGE)
+ || (m == 0 && errno != 0)) {
+ opt_number_error(value);
+ errno = oerrno;
+ return 0;
+ }
+ /* Ensure that the value in |m| is never too big for |*result| */
+ if (sizeof(m) > sizeof(*result)
+ && (m < OSSL_INTMAX_MIN || m > OSSL_INTMAX_MAX)) {
+ opt_number_error(value);
+ return 0;
+ }
+ *result = (ossl_intmax_t)m;
+ errno = oerrno;
+ return 1;
+}
+
+/* Parse a uintmax_t, put it into *result; return 0 on failure, else 1. */
+int opt_uintmax(const char *value, ossl_uintmax_t *result)
+{
+ int oerrno = errno;
+ uintmax_t m;
+ char *endp;
+
+ errno = 0;
+ m = strtoumax(value, &endp, 0);
+ if (*endp
+ || endp == value
+ || (m == UINTMAX_MAX && errno == ERANGE)
+ || (m == 0 && errno != 0)) {
+ opt_number_error(value);
+ errno = oerrno;
+ return 0;
+ }
+ /* Ensure that the value in |m| is never too big for |*result| */
+ if (sizeof(m) > sizeof(*result)
+ && m > OSSL_UINTMAX_MAX) {
+ opt_number_error(value);
+ return 0;
+ }
+ *result = (ossl_uintmax_t)m;
+ errno = oerrno;
+ return 1;
+}
+#else
+/* Fallback implementations based on long */
+int opt_intmax(const char *value, ossl_intmax_t *result)
+{
+ long m;
+ int ret;
+
+ if ((ret = opt_long(value, &m)))
+ *result = m;
+ return ret;
+}
+
+int opt_uintmax(const char *value, ossl_uintmax_t *result)
+{
+ unsigned long m;
+ int ret;
+
+ if ((ret = opt_ulong(value, &m)))
+ *result = m;
+ return ret;
+}
+#endif
+
+/*
+ * Parse an unsigned long, put it into *result; return 0 on failure, else 1.
+ */
+int opt_ulong(const char *value, unsigned long *result)
+{
+ int oerrno = errno;
+ char *endptr;
+ unsigned long l;
+
+ errno = 0;
+ l = strtoul(value, &endptr, 0);
+ if (*endptr
+ || endptr == value
+ || ((l == ULONG_MAX) && errno == ERANGE)
+ || (l == 0 && errno != 0)) {
+ opt_number_error(value);
+ errno = oerrno;
+ return 0;
+ }
+ *result = l;
+ errno = oerrno;
+ return 1;
+}
+
+/*
+ * We pass opt as an int but cast it to "enum range" so that all the
+ * items in the OPT_V_ENUM enumeration are caught; this makes -Wswitch
+ * in gcc do the right thing.
+ */
+enum range { OPT_V_ENUM };
+
+int opt_verify(int opt, X509_VERIFY_PARAM *vpm)
+{
+ int i;
+ ossl_intmax_t t = 0;
+ ASN1_OBJECT *otmp;
+ X509_PURPOSE *xptmp;
+ const X509_VERIFY_PARAM *vtmp;
+
+ OPENSSL_assert(vpm != NULL);
+ OPENSSL_assert(opt > OPT_V__FIRST);
+ OPENSSL_assert(opt < OPT_V__LAST);
+
+ switch ((enum range)opt) {
+ case OPT_V__FIRST:
+ case OPT_V__LAST:
+ return 0;
+ case OPT_V_POLICY:
+ otmp = OBJ_txt2obj(opt_arg(), 0);
+ if (otmp == NULL) {
+ opt_printf_stderr("%s: Invalid Policy %s\n", prog, opt_arg());
+ return 0;
+ }
+ if (!X509_VERIFY_PARAM_add0_policy(vpm, otmp)) {
+ ASN1_OBJECT_free(otmp);
+ opt_printf_stderr("%s: Internal error adding Policy %s\n",
+ prog, opt_arg());
+ return 0;
+ }
+ break;
+ case OPT_V_PURPOSE:
+ /* purpose name -> purpose index */
+ i = X509_PURPOSE_get_by_sname(opt_arg());
+ if (i < 0) {
+ opt_printf_stderr("%s: Invalid purpose %s\n", prog, opt_arg());
+ return 0;
+ }
+
+ /* purpose index -> purpose object */
+ xptmp = X509_PURPOSE_get0(i);
+
+ /* purpose object -> purpose value */
+ i = X509_PURPOSE_get_id(xptmp);
+
+ if (!X509_VERIFY_PARAM_set_purpose(vpm, i)) {
+ opt_printf_stderr("%s: Internal error setting purpose %s\n",
+ prog, opt_arg());
+ return 0;
+ }
+ break;
+ case OPT_V_VERIFY_NAME:
+ vtmp = X509_VERIFY_PARAM_lookup(opt_arg());
+ if (vtmp == NULL) {
+ opt_printf_stderr("%s: Invalid verify name %s\n",
+ prog, opt_arg());
+ return 0;
+ }
+ X509_VERIFY_PARAM_set1(vpm, vtmp);
+ break;
+ case OPT_V_VERIFY_DEPTH:
+ i = atoi(opt_arg());
+ if (i >= 0)
+ X509_VERIFY_PARAM_set_depth(vpm, i);
+ break;
+ case OPT_V_VERIFY_AUTH_LEVEL:
+ i = atoi(opt_arg());
+ if (i >= 0)
+ X509_VERIFY_PARAM_set_auth_level(vpm, i);
+ break;
+ case OPT_V_ATTIME:
+ if (!opt_intmax(opt_arg(), &t))
+ return 0;
+ if (t != (time_t)t) {
+ opt_printf_stderr("%s: epoch time out of range %s\n",
+ prog, opt_arg());
+ return 0;
+ }
+ X509_VERIFY_PARAM_set_time(vpm, (time_t)t);
+ break;
+ case OPT_V_VERIFY_HOSTNAME:
+ if (!X509_VERIFY_PARAM_set1_host(vpm, opt_arg(), 0))
+ return 0;
+ break;
+ case OPT_V_VERIFY_EMAIL:
+ if (!X509_VERIFY_PARAM_set1_email(vpm, opt_arg(), 0))
+ return 0;
+ break;
+ case OPT_V_VERIFY_IP:
+ if (!X509_VERIFY_PARAM_set1_ip_asc(vpm, opt_arg()))
+ return 0;
+ break;
+ case OPT_V_IGNORE_CRITICAL:
+ X509_VERIFY_PARAM_set_flags(vpm, X509_V_FLAG_IGNORE_CRITICAL);
+ break;
+ case OPT_V_ISSUER_CHECKS:
+ /* NOP, deprecated */
+ break;
+ case OPT_V_CRL_CHECK:
+ X509_VERIFY_PARAM_set_flags(vpm, X509_V_FLAG_CRL_CHECK);
+ break;
+ case OPT_V_CRL_CHECK_ALL:
+ X509_VERIFY_PARAM_set_flags(vpm,
+ X509_V_FLAG_CRL_CHECK |
+ X509_V_FLAG_CRL_CHECK_ALL);
+ break;
+ case OPT_V_POLICY_CHECK:
+ X509_VERIFY_PARAM_set_flags(vpm, X509_V_FLAG_POLICY_CHECK);
+ break;
+ case OPT_V_EXPLICIT_POLICY:
+ X509_VERIFY_PARAM_set_flags(vpm, X509_V_FLAG_EXPLICIT_POLICY);
+ break;
+ case OPT_V_INHIBIT_ANY:
+ X509_VERIFY_PARAM_set_flags(vpm, X509_V_FLAG_INHIBIT_ANY);
+ break;
+ case OPT_V_INHIBIT_MAP:
+ X509_VERIFY_PARAM_set_flags(vpm, X509_V_FLAG_INHIBIT_MAP);
+ break;
+ case OPT_V_X509_STRICT:
+ X509_VERIFY_PARAM_set_flags(vpm, X509_V_FLAG_X509_STRICT);
+ break;
+ case OPT_V_EXTENDED_CRL:
+ X509_VERIFY_PARAM_set_flags(vpm, X509_V_FLAG_EXTENDED_CRL_SUPPORT);
+ break;
+ case OPT_V_USE_DELTAS:
+ X509_VERIFY_PARAM_set_flags(vpm, X509_V_FLAG_USE_DELTAS);
+ break;
+ case OPT_V_POLICY_PRINT:
+ X509_VERIFY_PARAM_set_flags(vpm, X509_V_FLAG_NOTIFY_POLICY);
+ break;
+ case OPT_V_CHECK_SS_SIG:
+ X509_VERIFY_PARAM_set_flags(vpm, X509_V_FLAG_CHECK_SS_SIGNATURE);
+ break;
+ case OPT_V_TRUSTED_FIRST:
+ X509_VERIFY_PARAM_set_flags(vpm, X509_V_FLAG_TRUSTED_FIRST);
+ break;
+ case OPT_V_SUITEB_128_ONLY:
+ X509_VERIFY_PARAM_set_flags(vpm, X509_V_FLAG_SUITEB_128_LOS_ONLY);
+ break;
+ case OPT_V_SUITEB_128:
+ X509_VERIFY_PARAM_set_flags(vpm, X509_V_FLAG_SUITEB_128_LOS);
+ break;
+ case OPT_V_SUITEB_192:
+ X509_VERIFY_PARAM_set_flags(vpm, X509_V_FLAG_SUITEB_192_LOS);
+ break;
+ case OPT_V_PARTIAL_CHAIN:
+ X509_VERIFY_PARAM_set_flags(vpm, X509_V_FLAG_PARTIAL_CHAIN);
+ break;
+ case OPT_V_NO_ALT_CHAINS:
+ X509_VERIFY_PARAM_set_flags(vpm, X509_V_FLAG_NO_ALT_CHAINS);
+ break;
+ case OPT_V_NO_CHECK_TIME:
+ X509_VERIFY_PARAM_set_flags(vpm, X509_V_FLAG_NO_CHECK_TIME);
+ break;
+ case OPT_V_ALLOW_PROXY_CERTS:
+ X509_VERIFY_PARAM_set_flags(vpm, X509_V_FLAG_ALLOW_PROXY_CERTS);
+ break;
+ }
+ return 1;
+
+}
+
+void opt_begin(void)
+{
+ opt_index = 1;
+ arg = NULL;
+ flag = NULL;
+}
+
+/*
+ * Parse the next flag (and value if specified), return 0 if done, -1 on
+ * error, otherwise the flag's retval.
+ */
+int opt_next(void)
+{
+ char *p;
+ const OPTIONS *o;
+ int ival;
+ long lval;
+ unsigned long ulval;
+ ossl_intmax_t imval;
+ ossl_uintmax_t umval;
+
+ /* Look at current arg; at end of the list? */
+ arg = NULL;
+ p = argv[opt_index];
+ if (p == NULL)
+ return 0;
+
+ /* If word doesn't start with a -, we're done. */
+ if (*p != '-')
+ return 0;
+
+ /* Hit "--" ? We're done. */
+ opt_index++;
+ if (strcmp(p, "--") == 0)
+ return 0;
+
+ /* Allow -nnn and --nnn */
+ if (*++p == '-')
+ p++;
+ flag = p - 1;
+
+ /* If we have --flag=foo, snip it off */
+ if ((arg = strchr(p, '=')) != NULL)
+ *arg++ = '\0';
+ for (o = opts; o->name; ++o) {
+ /* If not this option, move on to the next one. */
+ if (!(strcmp(p, "h") == 0 && strcmp(o->name, "help") == 0)
+ && strcmp(p, o->name) != 0)
+ continue;
+
+ /* If it doesn't take a value, make sure none was given. */
+ if (o->valtype == 0 || o->valtype == '-') {
+ if (arg) {
+ opt_printf_stderr("%s: Option -%s does not take a value\n",
+ prog, p);
+ return -1;
+ }
+ return o->retval;
+ }
+
+ /* Want a value; get the next param if =foo not used. */
+ if (arg == NULL) {
+ if (argv[opt_index] == NULL) {
+ opt_printf_stderr("%s: Option -%s needs a value\n",
+ prog, o->name);
+ return -1;
+ }
+ arg = argv[opt_index++];
+ }
+
+ /* Syntax-check value. */
+ switch (o->valtype) {
+ default:
+ case 's':
+ case ':':
+ /* Just a string. */
+ break;
+ case '.':
+ /* Parameters */
+ break;
+ case '/':
+ if (opt_isdir(arg) > 0)
+ break;
+ opt_printf_stderr("%s: Not a directory: %s\n", prog, arg);
+ return -1;
+ case '<':
+ /* Input file. */
+ break;
+ case '>':
+ /* Output file. */
+ break;
+ case 'p':
+ case 'n':
+ case 'N':
+ if (!opt_int(arg, &ival))
+ return -1;
+ if (o->valtype == 'p' && ival <= 0) {
+ opt_printf_stderr("%s: Non-positive number \"%s\" for option -%s\n",
+ prog, arg, o->name);
+ return -1;
+ }
+ if (o->valtype == 'N' && ival < 0) {
+ opt_printf_stderr("%s: Negative number \"%s\" for option -%s\n",
+ prog, arg, o->name);
+ return -1;
+ }
+ break;
+ case 'M':
+ if (!opt_intmax(arg, &imval))
+ return -1;
+ break;
+ case 'U':
+ if (!opt_uintmax(arg, &umval))
+ return -1;
+ break;
+ case 'l':
+ if (!opt_long(arg, &lval))
+ return -1;
+ break;
+ case 'u':
+ if (!opt_ulong(arg, &ulval))
+ return -1;
+ break;
+ case 'c':
+ case 'E':
+ case 'F':
+ case 'f':
+ if (opt_format(arg,
+ o->valtype == 'c' ? OPT_FMT_PDS :
+ o->valtype == 'E' ? OPT_FMT_PDE :
+ o->valtype == 'F' ? OPT_FMT_PEMDER
+ : OPT_FMT_ANY, &ival))
+ break;
+ opt_printf_stderr("%s: Invalid format \"%s\" for option -%s\n",
+ prog, arg, o->name);
+ return -1;
+ }
+
+ /* Return the flag value. */
+ return o->retval;
+ }
+ if (unknown != NULL) {
+ dunno = p;
+ return unknown->retval;
+ }
+ opt_printf_stderr("%s: Unknown option: -%s\n", prog, p);
+ return -1;
+}
+
+/* Return the most recent flag parameter. */
+char *opt_arg(void)
+{
+ return arg;
+}
+
+/* Return the most recent flag (option name including the preceding '-'). */
+char *opt_flag(void)
+{
+ return flag;
+}
+
+/* Return the unknown option. */
+char *opt_unknown(void)
+{
+ return dunno;
+}
+
+/* Return the rest of the arguments after parsing flags. */
+char **opt_rest(void)
+{
+ return &argv[opt_index];
+}
+
+/* How many items in remaining args? */
+int opt_num_rest(void)
+{
+ int i = 0;
+ char **pp;
+
+ for (pp = opt_rest(); *pp; pp++, i++)
+ continue;
+ return i;
+}
+
+/* Return a string describing the parameter type. */
+static const char *valtype2param(const OPTIONS *o)
+{
+ switch (o->valtype) {
+ case 0:
+ case '-':
+ return "";
+ case ':':
+ return "uri";
+ case 's':
+ return "val";
+ case '/':
+ return "dir";
+ case '<':
+ return "infile";
+ case '>':
+ return "outfile";
+ case 'p':
+ return "+int";
+ case 'n':
+ return "int";
+ case 'l':
+ return "long";
+ case 'u':
+ return "ulong";
+ case 'E':
+ return "PEM|DER|ENGINE";
+ case 'F':
+ return "PEM|DER";
+ case 'f':
+ return "format";
+ case 'M':
+ return "intmax";
+ case 'N':
+ return "nonneg";
+ case 'U':
+ return "uintmax";
+ }
+ return "parm";
+}
+
+static void opt_print(const OPTIONS *o, int doingparams, int width)
+{
+ const char* help;
+ char start[80 + 1];
+ char *p;
+
+ help = o->helpstr ? o->helpstr : "(No additional info)";
+ if (o->name == OPT_HELP_STR) {
+ opt_printf_stderr(help, prog);
+ return;
+ }
+ if (o->name == OPT_SECTION_STR) {
+ opt_printf_stderr("\n");
+ opt_printf_stderr(help, prog);
+ return;
+ }
+ if (o->name == OPT_PARAM_STR) {
+ opt_printf_stderr("\nParameters:\n");
+ return;
+ }
+
+ /* Pad out prefix */
+ memset(start, ' ', sizeof(start) - 1);
+ start[sizeof(start) - 1] = '\0';
+
+ if (o->name == OPT_MORE_STR) {
+ /* Continuation of previous line; pad and print. */
+ start[width] = '\0';
+ opt_printf_stderr("%s %s\n", start, help);
+ return;
+ }
+
+ /* Build up the "-flag [param]" part. */
+ p = start;
+ *p++ = ' ';
+ if (!doingparams)
+ *p++ = '-';
+ if (o->name[0])
+ p += strlen(strcpy(p, o->name));
+ else
+ *p++ = '*';
+ if (o->valtype != '-') {
+ *p++ = ' ';
+ p += strlen(strcpy(p, valtype2param(o)));
+ }
+ *p = ' ';
+ if ((int)(p - start) >= MAX_OPT_HELP_WIDTH) {
+ *p = '\0';
+ opt_printf_stderr("%s\n", start);
+ memset(start, ' ', sizeof(start));
+ }
+ start[width] = '\0';
+ opt_printf_stderr("%s %s\n", start, help);
+}
+
+void opt_help(const OPTIONS *list)
+{
+ const OPTIONS *o;
+ int i, sawparams = 0, width = 5;
+ int standard_prolog;
+ char start[80 + 1];
+
+ /* Starts with its own help message? */
+ standard_prolog = list[0].name != OPT_HELP_STR;
+
+ /* Find the widest help. */
+ for (o = list; o->name; o++) {
+ if (o->name == OPT_MORE_STR)
+ continue;
+ i = 2 + (int)strlen(o->name);
+ if (o->valtype != '-')
+ i += 1 + strlen(valtype2param(o));
+ if (i < MAX_OPT_HELP_WIDTH && i > width)
+ width = i;
+ OPENSSL_assert(i < (int)sizeof(start));
+ }
+
+ if (standard_prolog) {
+ opt_printf_stderr("Usage: %s [options]\n", prog);
+ if (list[0].name != OPT_SECTION_STR)
+ opt_printf_stderr("Valid options are:\n", prog);
+ }
+
+ /* Now let's print. */
+ for (o = list; o->name; o++) {
+ if (o->name == OPT_PARAM_STR)
+ sawparams = 1;
+ opt_print(o, sawparams, width);
+ }
+}
+
+/* opt_isdir section */
+#ifdef _WIN32
+# include <windows.h>
+int opt_isdir(const char *name)
+{
+ DWORD attr;
+# if defined(UNICODE) || defined(_UNICODE)
+ size_t i, len_0 = strlen(name) + 1;
+ WCHAR tempname[MAX_PATH];
+
+ if (len_0 > MAX_PATH)
+ return -1;
+
+# if !defined(_WIN32_WCE) || _WIN32_WCE>=101
+ if (!MultiByteToWideChar(CP_ACP, 0, name, len_0, tempname, MAX_PATH))
+# endif
+ for (i = 0; i < len_0; i++)
+ tempname[i] = (WCHAR)name[i];
+
+ attr = GetFileAttributes(tempname);
+# else
+ attr = GetFileAttributes(name);
+# endif
+ if (attr == INVALID_FILE_ATTRIBUTES)
+ return -1;
+ return ((attr & FILE_ATTRIBUTE_DIRECTORY) != 0);
+}
+#else
+# include <sys/stat.h>
+# ifndef S_ISDIR
+# if defined(_S_IFMT) && defined(_S_IFDIR)
+# define S_ISDIR(a) (((a) & _S_IFMT) == _S_IFDIR)
+# else
+# define S_ISDIR(a) (((a) & S_IFMT) == S_IFDIR)
+# endif
+# endif
+
+int opt_isdir(const char *name)
+{
+# if defined(S_ISDIR)
+ struct stat st;
+
+ if (stat(name, &st) == 0)
+ return S_ISDIR(st.st_mode);
+ else
+ return -1;
+# else
+ return -1;
+# endif
+}
+#endif
diff --git a/apps/lib/s_cb.c b/apps/lib/s_cb.c
new file mode 100644
index 000000000000..9f33c24c4e35
--- /dev/null
+++ b/apps/lib/s_cb.c
@@ -0,0 +1,1592 @@
+/*
+ * Copyright 1995-2024 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the Apache License 2.0 (the "License"). You may not use
+ * this file except in compliance with the License. You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+/* callback functions used by s_client, s_server, and s_time */
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h> /* for memcpy() and strcmp() */
+#include "apps.h"
+#include <openssl/core_names.h>
+#include <openssl/params.h>
+#include <openssl/err.h>
+#include <openssl/rand.h>
+#include <openssl/x509.h>
+#include <openssl/ssl.h>
+#include <openssl/bn.h>
+#ifndef OPENSSL_NO_DH
+# include <openssl/dh.h>
+#endif
+#include "s_apps.h"
+
+#define COOKIE_SECRET_LENGTH 16
+
+VERIFY_CB_ARGS verify_args = { -1, 0, X509_V_OK, 0 };
+
+#ifndef OPENSSL_NO_SOCK
+static unsigned char cookie_secret[COOKIE_SECRET_LENGTH];
+static int cookie_initialized = 0;
+#endif
+static BIO *bio_keylog = NULL;
+
+static const char *lookup(int val, const STRINT_PAIR* list, const char* def)
+{
+ for ( ; list->name; ++list)
+ if (list->retval == val)
+ return list->name;
+ return def;
+}
+
+int verify_callback(int ok, X509_STORE_CTX *ctx)
+{
+ X509 *err_cert;
+ int err, depth;
+
+ err_cert = X509_STORE_CTX_get_current_cert(ctx);
+ err = X509_STORE_CTX_get_error(ctx);
+ depth = X509_STORE_CTX_get_error_depth(ctx);
+
+ if (!verify_args.quiet || !ok) {
+ BIO_printf(bio_err, "depth=%d ", depth);
+ if (err_cert != NULL) {
+ X509_NAME_print_ex(bio_err,
+ X509_get_subject_name(err_cert),
+ 0, get_nameopt());
+ BIO_puts(bio_err, "\n");
+ } else {
+ BIO_puts(bio_err, "<no cert>\n");
+ }
+ }
+ if (!ok) {
+ BIO_printf(bio_err, "verify error:num=%d:%s\n", err,
+ X509_verify_cert_error_string(err));
+ if (verify_args.depth < 0 || verify_args.depth >= depth) {
+ if (!verify_args.return_error)
+ ok = 1;
+ verify_args.error = err;
+ } else {
+ ok = 0;
+ verify_args.error = X509_V_ERR_CERT_CHAIN_TOO_LONG;
+ }
+ }
+ switch (err) {
+ case X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT:
+ if (err_cert != NULL) {
+ BIO_puts(bio_err, "issuer= ");
+ X509_NAME_print_ex(bio_err, X509_get_issuer_name(err_cert),
+ 0, get_nameopt());
+ BIO_puts(bio_err, "\n");
+ }
+ break;
+ case X509_V_ERR_CERT_NOT_YET_VALID:
+ case X509_V_ERR_ERROR_IN_CERT_NOT_BEFORE_FIELD:
+ if (err_cert != NULL) {
+ BIO_printf(bio_err, "notBefore=");
+ ASN1_TIME_print(bio_err, X509_get0_notBefore(err_cert));
+ BIO_printf(bio_err, "\n");
+ }
+ break;
+ case X509_V_ERR_CERT_HAS_EXPIRED:
+ case X509_V_ERR_ERROR_IN_CERT_NOT_AFTER_FIELD:
+ if (err_cert != NULL) {
+ BIO_printf(bio_err, "notAfter=");
+ ASN1_TIME_print(bio_err, X509_get0_notAfter(err_cert));
+ BIO_printf(bio_err, "\n");
+ }
+ break;
+ case X509_V_ERR_NO_EXPLICIT_POLICY:
+ if (!verify_args.quiet)
+ policies_print(ctx);
+ break;
+ }
+ if (err == X509_V_OK && ok == 2 && !verify_args.quiet)
+ policies_print(ctx);
+ if (ok && !verify_args.quiet)
+ BIO_printf(bio_err, "verify return:%d\n", ok);
+ return ok;
+}
+
+int set_cert_stuff(SSL_CTX *ctx, char *cert_file, char *key_file)
+{
+ if (cert_file != NULL) {
+ if (SSL_CTX_use_certificate_file(ctx, cert_file,
+ SSL_FILETYPE_PEM) <= 0) {
+ BIO_printf(bio_err, "unable to get certificate from '%s'\n",
+ cert_file);
+ ERR_print_errors(bio_err);
+ return 0;
+ }
+ if (key_file == NULL)
+ key_file = cert_file;
+ if (SSL_CTX_use_PrivateKey_file(ctx, key_file, SSL_FILETYPE_PEM) <= 0) {
+ BIO_printf(bio_err, "unable to get private key from '%s'\n",
+ key_file);
+ ERR_print_errors(bio_err);
+ return 0;
+ }
+
+ /*
+ * If we are using DSA, we can copy the parameters from the private
+ * key
+ */
+
+ /*
+ * Now we know that a key and cert have been set against the SSL
+ * context
+ */
+ if (!SSL_CTX_check_private_key(ctx)) {
+ BIO_printf(bio_err,
+ "Private key does not match the certificate public key\n");
+ return 0;
+ }
+ }
+ return 1;
+}
+
+int set_cert_key_stuff(SSL_CTX *ctx, X509 *cert, EVP_PKEY *key,
+ STACK_OF(X509) *chain, int build_chain)
+{
+ int chflags = chain ? SSL_BUILD_CHAIN_FLAG_CHECK : 0;
+
+ if (cert == NULL)
+ return 1;
+ if (SSL_CTX_use_certificate(ctx, cert) <= 0) {
+ BIO_printf(bio_err, "error setting certificate\n");
+ ERR_print_errors(bio_err);
+ return 0;
+ }
+
+ if (SSL_CTX_use_PrivateKey(ctx, key) <= 0) {
+ BIO_printf(bio_err, "error setting private key\n");
+ ERR_print_errors(bio_err);
+ return 0;
+ }
+
+ /*
+ * Now we know that a key and cert have been set against the SSL context
+ */
+ if (!SSL_CTX_check_private_key(ctx)) {
+ BIO_printf(bio_err,
+ "Private key does not match the certificate public key\n");
+ return 0;
+ }
+ if (chain && !SSL_CTX_set1_chain(ctx, chain)) {
+ BIO_printf(bio_err, "error setting certificate chain\n");
+ ERR_print_errors(bio_err);
+ return 0;
+ }
+ if (build_chain && !SSL_CTX_build_cert_chain(ctx, chflags)) {
+ BIO_printf(bio_err, "error building certificate chain\n");
+ ERR_print_errors(bio_err);
+ return 0;
+ }
+ return 1;
+}
+
+static STRINT_PAIR cert_type_list[] = {
+ {"RSA sign", TLS_CT_RSA_SIGN},
+ {"DSA sign", TLS_CT_DSS_SIGN},
+ {"RSA fixed DH", TLS_CT_RSA_FIXED_DH},
+ {"DSS fixed DH", TLS_CT_DSS_FIXED_DH},
+ {"ECDSA sign", TLS_CT_ECDSA_SIGN},
+ {"RSA fixed ECDH", TLS_CT_RSA_FIXED_ECDH},
+ {"ECDSA fixed ECDH", TLS_CT_ECDSA_FIXED_ECDH},
+ {"GOST01 Sign", TLS_CT_GOST01_SIGN},
+ {"GOST12 Sign", TLS_CT_GOST12_IANA_SIGN},
+ {NULL}
+};
+
+static void ssl_print_client_cert_types(BIO *bio, SSL *s)
+{
+ const unsigned char *p;
+ int i;
+ int cert_type_num = SSL_get0_certificate_types(s, &p);
+
+ if (!cert_type_num)
+ return;
+ BIO_puts(bio, "Client Certificate Types: ");
+ for (i = 0; i < cert_type_num; i++) {
+ unsigned char cert_type = p[i];
+ const char *cname = lookup((int)cert_type, cert_type_list, NULL);
+
+ if (i)
+ BIO_puts(bio, ", ");
+ if (cname != NULL)
+ BIO_puts(bio, cname);
+ else
+ BIO_printf(bio, "UNKNOWN (%d),", cert_type);
+ }
+ BIO_puts(bio, "\n");
+}
+
+static const char *get_sigtype(int nid)
+{
+ switch (nid) {
+ case EVP_PKEY_RSA:
+ return "RSA";
+
+ case EVP_PKEY_RSA_PSS:
+ return "RSA-PSS";
+
+ case EVP_PKEY_DSA:
+ return "DSA";
+
+ case EVP_PKEY_EC:
+ return "ECDSA";
+
+ case NID_ED25519:
+ return "ed25519";
+
+ case NID_ED448:
+ return "ed448";
+
+ case NID_id_GostR3410_2001:
+ return "gost2001";
+
+ case NID_id_GostR3410_2012_256:
+ return "gost2012_256";
+
+ case NID_id_GostR3410_2012_512:
+ return "gost2012_512";
+
+ default:
+ return NULL;
+ }
+}
+
+static int do_print_sigalgs(BIO *out, SSL *s, int shared)
+{
+ int i, nsig, client;
+
+ client = SSL_is_server(s) ? 0 : 1;
+ if (shared)
+ nsig = SSL_get_shared_sigalgs(s, 0, NULL, NULL, NULL, NULL, NULL);
+ else
+ nsig = SSL_get_sigalgs(s, -1, NULL, NULL, NULL, NULL, NULL);
+ if (nsig == 0)
+ return 1;
+
+ if (shared)
+ BIO_puts(out, "Shared ");
+
+ if (client)
+ BIO_puts(out, "Requested ");
+ BIO_puts(out, "Signature Algorithms: ");
+ for (i = 0; i < nsig; i++) {
+ int hash_nid, sign_nid;
+ unsigned char rhash, rsign;
+ const char *sstr = NULL;
+ if (shared)
+ SSL_get_shared_sigalgs(s, i, &sign_nid, &hash_nid, NULL,
+ &rsign, &rhash);
+ else
+ SSL_get_sigalgs(s, i, &sign_nid, &hash_nid, NULL, &rsign, &rhash);
+ if (i)
+ BIO_puts(out, ":");
+ switch (rsign | rhash << 8) {
+ case 0x0809:
+ BIO_puts(out, "rsa_pss_pss_sha256");
+ continue;
+ case 0x080a:
+ BIO_puts(out, "rsa_pss_pss_sha384");
+ continue;
+ case 0x080b:
+ BIO_puts(out, "rsa_pss_pss_sha512");
+ continue;
+ case 0x081a:
+ BIO_puts(out, "ecdsa_brainpoolP256r1_sha256");
+ continue;
+ case 0x081b:
+ BIO_puts(out, "ecdsa_brainpoolP384r1_sha384");
+ continue;
+ case 0x081c:
+ BIO_puts(out, "ecdsa_brainpoolP512r1_sha512");
+ continue;
+ }
+ sstr = get_sigtype(sign_nid);
+ if (sstr)
+ BIO_printf(out, "%s", sstr);
+ else
+ BIO_printf(out, "0x%02X", (int)rsign);
+ if (hash_nid != NID_undef)
+ BIO_printf(out, "+%s", OBJ_nid2sn(hash_nid));
+ else if (sstr == NULL)
+ BIO_printf(out, "+0x%02X", (int)rhash);
+ }
+ BIO_puts(out, "\n");
+ return 1;
+}
+
+int ssl_print_sigalgs(BIO *out, SSL *s)
+{
+ int nid;
+
+ if (!SSL_is_server(s))
+ ssl_print_client_cert_types(out, s);
+ do_print_sigalgs(out, s, 0);
+ do_print_sigalgs(out, s, 1);
+ if (SSL_get_peer_signature_nid(s, &nid) && nid != NID_undef)
+ BIO_printf(out, "Peer signing digest: %s\n", OBJ_nid2sn(nid));
+ if (SSL_get_peer_signature_type_nid(s, &nid))
+ BIO_printf(out, "Peer signature type: %s\n", get_sigtype(nid));
+ return 1;
+}
+
+#ifndef OPENSSL_NO_EC
+int ssl_print_point_formats(BIO *out, SSL *s)
+{
+ int i, nformats;
+ const char *pformats;
+
+ nformats = SSL_get0_ec_point_formats(s, &pformats);
+ if (nformats <= 0)
+ return 1;
+ BIO_puts(out, "Supported Elliptic Curve Point Formats: ");
+ for (i = 0; i < nformats; i++, pformats++) {
+ if (i)
+ BIO_puts(out, ":");
+ switch (*pformats) {
+ case TLSEXT_ECPOINTFORMAT_uncompressed:
+ BIO_puts(out, "uncompressed");
+ break;
+
+ case TLSEXT_ECPOINTFORMAT_ansiX962_compressed_prime:
+ BIO_puts(out, "ansiX962_compressed_prime");
+ break;
+
+ case TLSEXT_ECPOINTFORMAT_ansiX962_compressed_char2:
+ BIO_puts(out, "ansiX962_compressed_char2");
+ break;
+
+ default:
+ BIO_printf(out, "unknown(%d)", (int)*pformats);
+ break;
+
+ }
+ }
+ BIO_puts(out, "\n");
+ return 1;
+}
+
+int ssl_print_groups(BIO *out, SSL *s, int noshared)
+{
+ int i, ngroups, *groups, nid;
+
+ ngroups = SSL_get1_groups(s, NULL);
+ if (ngroups <= 0)
+ return 1;
+ groups = app_malloc(ngroups * sizeof(int), "groups to print");
+ SSL_get1_groups(s, groups);
+
+ BIO_puts(out, "Supported groups: ");
+ for (i = 0; i < ngroups; i++) {
+ if (i)
+ BIO_puts(out, ":");
+ nid = groups[i];
+ BIO_printf(out, "%s", SSL_group_to_name(s, nid));
+ }
+ OPENSSL_free(groups);
+ if (noshared) {
+ BIO_puts(out, "\n");
+ return 1;
+ }
+ BIO_puts(out, "\nShared groups: ");
+ ngroups = SSL_get_shared_group(s, -1);
+ for (i = 0; i < ngroups; i++) {
+ if (i)
+ BIO_puts(out, ":");
+ nid = SSL_get_shared_group(s, i);
+ BIO_printf(out, "%s", SSL_group_to_name(s, nid));
+ }
+ if (ngroups == 0)
+ BIO_puts(out, "NONE");
+ BIO_puts(out, "\n");
+ return 1;
+}
+#endif
+
+int ssl_print_tmp_key(BIO *out, SSL *s)
+{
+ EVP_PKEY *key;
+
+ if (!SSL_get_peer_tmp_key(s, &key))
+ return 1;
+ BIO_puts(out, "Server Temp Key: ");
+ switch (EVP_PKEY_get_id(key)) {
+ case EVP_PKEY_RSA:
+ BIO_printf(out, "RSA, %d bits\n", EVP_PKEY_get_bits(key));
+ break;
+
+ case EVP_PKEY_DH:
+ BIO_printf(out, "DH, %d bits\n", EVP_PKEY_get_bits(key));
+ break;
+#ifndef OPENSSL_NO_EC
+ case EVP_PKEY_EC:
+ {
+ char name[80];
+ size_t name_len;
+
+ if (!EVP_PKEY_get_utf8_string_param(key, OSSL_PKEY_PARAM_GROUP_NAME,
+ name, sizeof(name), &name_len))
+ strcpy(name, "?");
+ BIO_printf(out, "ECDH, %s, %d bits\n", name, EVP_PKEY_get_bits(key));
+ }
+ break;
+#endif
+ default:
+ BIO_printf(out, "%s, %d bits\n", OBJ_nid2sn(EVP_PKEY_get_id(key)),
+ EVP_PKEY_get_bits(key));
+ }
+ EVP_PKEY_free(key);
+ return 1;
+}
+
+long bio_dump_callback(BIO *bio, int cmd, const char *argp, size_t len,
+ int argi, long argl, int ret, size_t *processed)
+{
+ BIO *out;
+
+ out = (BIO *)BIO_get_callback_arg(bio);
+ if (out == NULL)
+ return ret;
+
+ if (cmd == (BIO_CB_READ | BIO_CB_RETURN)) {
+ if (ret > 0 && processed != NULL) {
+ BIO_printf(out, "read from %p [%p] (%zu bytes => %zu (0x%zX))\n",
+ (void *)bio, (void *)argp, len, *processed, *processed);
+ BIO_dump(out, argp, (int)*processed);
+ } else {
+ BIO_printf(out, "read from %p [%p] (%zu bytes => %d)\n",
+ (void *)bio, (void *)argp, len, ret);
+ }
+ } else if (cmd == (BIO_CB_WRITE | BIO_CB_RETURN)) {
+ if (ret > 0 && processed != NULL) {
+ BIO_printf(out, "write to %p [%p] (%zu bytes => %zu (0x%zX))\n",
+ (void *)bio, (void *)argp, len, *processed, *processed);
+ BIO_dump(out, argp, (int)*processed);
+ } else {
+ BIO_printf(out, "write to %p [%p] (%zu bytes => %d)\n",
+ (void *)bio, (void *)argp, len, ret);
+ }
+ }
+ return ret;
+}
+
+void apps_ssl_info_callback(const SSL *s, int where, int ret)
+{
+ const char *str;
+ int w;
+
+ w = where & ~SSL_ST_MASK;
+
+ if (w & SSL_ST_CONNECT)
+ str = "SSL_connect";
+ else if (w & SSL_ST_ACCEPT)
+ str = "SSL_accept";
+ else
+ str = "undefined";
+
+ if (where & SSL_CB_LOOP) {
+ BIO_printf(bio_err, "%s:%s\n", str, SSL_state_string_long(s));
+ } else if (where & SSL_CB_ALERT) {
+ str = (where & SSL_CB_READ) ? "read" : "write";
+ BIO_printf(bio_err, "SSL3 alert %s:%s:%s\n",
+ str,
+ SSL_alert_type_string_long(ret),
+ SSL_alert_desc_string_long(ret));
+ } else if (where & SSL_CB_EXIT) {
+ if (ret == 0)
+ BIO_printf(bio_err, "%s:failed in %s\n",
+ str, SSL_state_string_long(s));
+ else if (ret < 0)
+ BIO_printf(bio_err, "%s:error in %s\n",
+ str, SSL_state_string_long(s));
+ }
+}
+
+static STRINT_PAIR ssl_versions[] = {
+ {"SSL 3.0", SSL3_VERSION},
+ {"TLS 1.0", TLS1_VERSION},
+ {"TLS 1.1", TLS1_1_VERSION},
+ {"TLS 1.2", TLS1_2_VERSION},
+ {"TLS 1.3", TLS1_3_VERSION},
+ {"DTLS 1.0", DTLS1_VERSION},
+ {"DTLS 1.0 (bad)", DTLS1_BAD_VER},
+ {NULL}
+};
+
+static STRINT_PAIR alert_types[] = {
+ {" close_notify", 0},
+ {" end_of_early_data", 1},
+ {" unexpected_message", 10},
+ {" bad_record_mac", 20},
+ {" decryption_failed", 21},
+ {" record_overflow", 22},
+ {" decompression_failure", 30},
+ {" handshake_failure", 40},
+ {" bad_certificate", 42},
+ {" unsupported_certificate", 43},
+ {" certificate_revoked", 44},
+ {" certificate_expired", 45},
+ {" certificate_unknown", 46},
+ {" illegal_parameter", 47},
+ {" unknown_ca", 48},
+ {" access_denied", 49},
+ {" decode_error", 50},
+ {" decrypt_error", 51},
+ {" export_restriction", 60},
+ {" protocol_version", 70},
+ {" insufficient_security", 71},
+ {" internal_error", 80},
+ {" inappropriate_fallback", 86},
+ {" user_canceled", 90},
+ {" no_renegotiation", 100},
+ {" missing_extension", 109},
+ {" unsupported_extension", 110},
+ {" certificate_unobtainable", 111},
+ {" unrecognized_name", 112},
+ {" bad_certificate_status_response", 113},
+ {" bad_certificate_hash_value", 114},
+ {" unknown_psk_identity", 115},
+ {" certificate_required", 116},
+ {NULL}
+};
+
+static STRINT_PAIR handshakes[] = {
+ {", HelloRequest", SSL3_MT_HELLO_REQUEST},
+ {", ClientHello", SSL3_MT_CLIENT_HELLO},
+ {", ServerHello", SSL3_MT_SERVER_HELLO},
+ {", HelloVerifyRequest", DTLS1_MT_HELLO_VERIFY_REQUEST},
+ {", NewSessionTicket", SSL3_MT_NEWSESSION_TICKET},
+ {", EndOfEarlyData", SSL3_MT_END_OF_EARLY_DATA},
+ {", EncryptedExtensions", SSL3_MT_ENCRYPTED_EXTENSIONS},
+ {", Certificate", SSL3_MT_CERTIFICATE},
+ {", ServerKeyExchange", SSL3_MT_SERVER_KEY_EXCHANGE},
+ {", CertificateRequest", SSL3_MT_CERTIFICATE_REQUEST},
+ {", ServerHelloDone", SSL3_MT_SERVER_DONE},
+ {", CertificateVerify", SSL3_MT_CERTIFICATE_VERIFY},
+ {", ClientKeyExchange", SSL3_MT_CLIENT_KEY_EXCHANGE},
+ {", Finished", SSL3_MT_FINISHED},
+ {", CertificateUrl", SSL3_MT_CERTIFICATE_URL},
+ {", CertificateStatus", SSL3_MT_CERTIFICATE_STATUS},
+ {", SupplementalData", SSL3_MT_SUPPLEMENTAL_DATA},
+ {", KeyUpdate", SSL3_MT_KEY_UPDATE},
+#ifndef OPENSSL_NO_NEXTPROTONEG
+ {", NextProto", SSL3_MT_NEXT_PROTO},
+#endif
+ {", MessageHash", SSL3_MT_MESSAGE_HASH},
+ {NULL}
+};
+
+void msg_cb(int write_p, int version, int content_type, const void *buf,
+ size_t len, SSL *ssl, void *arg)
+{
+ BIO *bio = arg;
+ const char *str_write_p = write_p ? ">>>" : "<<<";
+ char tmpbuf[128];
+ const char *str_version, *str_content_type = "", *str_details1 = "", *str_details2 = "";
+ const unsigned char* bp = buf;
+
+ if (version == SSL3_VERSION ||
+ version == TLS1_VERSION ||
+ version == TLS1_1_VERSION ||
+ version == TLS1_2_VERSION ||
+ version == TLS1_3_VERSION ||
+ version == DTLS1_VERSION || version == DTLS1_BAD_VER) {
+ str_version = lookup(version, ssl_versions, "???");
+ switch (content_type) {
+ case SSL3_RT_CHANGE_CIPHER_SPEC:
+ /* type 20 */
+ str_content_type = ", ChangeCipherSpec";
+ break;
+ case SSL3_RT_ALERT:
+ /* type 21 */
+ str_content_type = ", Alert";
+ str_details1 = ", ???";
+ if (len == 2) {
+ switch (bp[0]) {
+ case 1:
+ str_details1 = ", warning";
+ break;
+ case 2:
+ str_details1 = ", fatal";
+ break;
+ }
+ str_details2 = lookup((int)bp[1], alert_types, " ???");
+ }
+ break;
+ case SSL3_RT_HANDSHAKE:
+ /* type 22 */
+ str_content_type = ", Handshake";
+ str_details1 = "???";
+ if (len > 0)
+ str_details1 = lookup((int)bp[0], handshakes, "???");
+ break;
+ case SSL3_RT_APPLICATION_DATA:
+ /* type 23 */
+ str_content_type = ", ApplicationData";
+ break;
+ case SSL3_RT_HEADER:
+ /* type 256 */
+ str_content_type = ", RecordHeader";
+ break;
+ case SSL3_RT_INNER_CONTENT_TYPE:
+ /* type 257 */
+ str_content_type = ", InnerContent";
+ break;
+ default:
+ BIO_snprintf(tmpbuf, sizeof(tmpbuf)-1, ", Unknown (content_type=%d)", content_type);
+ str_content_type = tmpbuf;
+ }
+ } else {
+ BIO_snprintf(tmpbuf, sizeof(tmpbuf)-1, "Not TLS data or unknown version (version=%d, content_type=%d)", version, content_type);
+ str_version = tmpbuf;
+ }
+
+ BIO_printf(bio, "%s %s%s [length %04lx]%s%s\n", str_write_p, str_version,
+ str_content_type, (unsigned long)len, str_details1,
+ str_details2);
+
+ if (len > 0) {
+ size_t num, i;
+
+ BIO_printf(bio, " ");
+ num = len;
+ for (i = 0; i < num; i++) {
+ if (i % 16 == 0 && i > 0)
+ BIO_printf(bio, "\n ");
+ BIO_printf(bio, " %02x", ((const unsigned char *)buf)[i]);
+ }
+ if (i < len)
+ BIO_printf(bio, " ...");
+ BIO_printf(bio, "\n");
+ }
+ (void)BIO_flush(bio);
+}
+
+static const STRINT_PAIR tlsext_types[] = {
+ {"server name", TLSEXT_TYPE_server_name},
+ {"max fragment length", TLSEXT_TYPE_max_fragment_length},
+ {"client certificate URL", TLSEXT_TYPE_client_certificate_url},
+ {"trusted CA keys", TLSEXT_TYPE_trusted_ca_keys},
+ {"truncated HMAC", TLSEXT_TYPE_truncated_hmac},
+ {"status request", TLSEXT_TYPE_status_request},
+ {"user mapping", TLSEXT_TYPE_user_mapping},
+ {"client authz", TLSEXT_TYPE_client_authz},
+ {"server authz", TLSEXT_TYPE_server_authz},
+ {"cert type", TLSEXT_TYPE_cert_type},
+ {"supported_groups", TLSEXT_TYPE_supported_groups},
+ {"EC point formats", TLSEXT_TYPE_ec_point_formats},
+ {"SRP", TLSEXT_TYPE_srp},
+ {"signature algorithms", TLSEXT_TYPE_signature_algorithms},
+ {"use SRTP", TLSEXT_TYPE_use_srtp},
+ {"session ticket", TLSEXT_TYPE_session_ticket},
+ {"renegotiation info", TLSEXT_TYPE_renegotiate},
+ {"signed certificate timestamps", TLSEXT_TYPE_signed_certificate_timestamp},
+ {"TLS padding", TLSEXT_TYPE_padding},
+#ifdef TLSEXT_TYPE_next_proto_neg
+ {"next protocol", TLSEXT_TYPE_next_proto_neg},
+#endif
+#ifdef TLSEXT_TYPE_encrypt_then_mac
+ {"encrypt-then-mac", TLSEXT_TYPE_encrypt_then_mac},
+#endif
+#ifdef TLSEXT_TYPE_application_layer_protocol_negotiation
+ {"application layer protocol negotiation",
+ TLSEXT_TYPE_application_layer_protocol_negotiation},
+#endif
+#ifdef TLSEXT_TYPE_extended_master_secret
+ {"extended master secret", TLSEXT_TYPE_extended_master_secret},
+#endif
+ {"key share", TLSEXT_TYPE_key_share},
+ {"supported versions", TLSEXT_TYPE_supported_versions},
+ {"psk", TLSEXT_TYPE_psk},
+ {"psk kex modes", TLSEXT_TYPE_psk_kex_modes},
+ {"certificate authorities", TLSEXT_TYPE_certificate_authorities},
+ {"post handshake auth", TLSEXT_TYPE_post_handshake_auth},
+ {"early_data", TLSEXT_TYPE_early_data},
+ {NULL}
+};
+
+/* from rfc8446 4.2.3. + gost (https://tools.ietf.org/id/draft-smyshlyaev-tls12-gost-suites-04.html) */
+static STRINT_PAIR signature_tls13_scheme_list[] = {
+ {"rsa_pkcs1_sha1", 0x0201 /* TLSEXT_SIGALG_rsa_pkcs1_sha1 */},
+ {"ecdsa_sha1", 0x0203 /* TLSEXT_SIGALG_ecdsa_sha1 */},
+/* {"rsa_pkcs1_sha224", 0x0301 TLSEXT_SIGALG_rsa_pkcs1_sha224}, not in rfc8446 */
+/* {"ecdsa_sha224", 0x0303 TLSEXT_SIGALG_ecdsa_sha224} not in rfc8446 */
+ {"rsa_pkcs1_sha256", 0x0401 /* TLSEXT_SIGALG_rsa_pkcs1_sha256 */},
+ {"ecdsa_secp256r1_sha256", 0x0403 /* TLSEXT_SIGALG_ecdsa_secp256r1_sha256 */},
+ {"rsa_pkcs1_sha384", 0x0501 /* TLSEXT_SIGALG_rsa_pkcs1_sha384 */},
+ {"ecdsa_secp384r1_sha384", 0x0503 /* TLSEXT_SIGALG_ecdsa_secp384r1_sha384 */},
+ {"rsa_pkcs1_sha512", 0x0601 /* TLSEXT_SIGALG_rsa_pkcs1_sha512 */},
+ {"ecdsa_secp521r1_sha512", 0x0603 /* TLSEXT_SIGALG_ecdsa_secp521r1_sha512 */},
+ {"rsa_pss_rsae_sha256", 0x0804 /* TLSEXT_SIGALG_rsa_pss_rsae_sha256 */},
+ {"rsa_pss_rsae_sha384", 0x0805 /* TLSEXT_SIGALG_rsa_pss_rsae_sha384 */},
+ {"rsa_pss_rsae_sha512", 0x0806 /* TLSEXT_SIGALG_rsa_pss_rsae_sha512 */},
+ {"ed25519", 0x0807 /* TLSEXT_SIGALG_ed25519 */},
+ {"ed448", 0x0808 /* TLSEXT_SIGALG_ed448 */},
+ {"rsa_pss_pss_sha256", 0x0809 /* TLSEXT_SIGALG_rsa_pss_pss_sha256 */},
+ {"rsa_pss_pss_sha384", 0x080a /* TLSEXT_SIGALG_rsa_pss_pss_sha384 */},
+ {"rsa_pss_pss_sha512", 0x080b /* TLSEXT_SIGALG_rsa_pss_pss_sha512 */},
+ {"gostr34102001", 0xeded /* TLSEXT_SIGALG_gostr34102001_gostr3411 */},
+ {"gostr34102012_256", 0xeeee /* TLSEXT_SIGALG_gostr34102012_256_gostr34112012_256 */},
+ {"gostr34102012_512", 0xefef /* TLSEXT_SIGALG_gostr34102012_512_gostr34112012_512 */},
+ {NULL}
+};
+
+/* from rfc5246 7.4.1.4.1. */
+static STRINT_PAIR signature_tls12_alg_list[] = {
+ {"anonymous", TLSEXT_signature_anonymous /* 0 */},
+ {"RSA", TLSEXT_signature_rsa /* 1 */},
+ {"DSA", TLSEXT_signature_dsa /* 2 */},
+ {"ECDSA", TLSEXT_signature_ecdsa /* 3 */},
+ {NULL}
+};
+
+/* from rfc5246 7.4.1.4.1. */
+static STRINT_PAIR signature_tls12_hash_list[] = {
+ {"none", TLSEXT_hash_none /* 0 */},
+ {"MD5", TLSEXT_hash_md5 /* 1 */},
+ {"SHA1", TLSEXT_hash_sha1 /* 2 */},
+ {"SHA224", TLSEXT_hash_sha224 /* 3 */},
+ {"SHA256", TLSEXT_hash_sha256 /* 4 */},
+ {"SHA384", TLSEXT_hash_sha384 /* 5 */},
+ {"SHA512", TLSEXT_hash_sha512 /* 6 */},
+ {NULL}
+};
+
+void tlsext_cb(SSL *s, int client_server, int type,
+ const unsigned char *data, int len, void *arg)
+{
+ BIO *bio = arg;
+ const char *extname = lookup(type, tlsext_types, "unknown");
+
+ BIO_printf(bio, "TLS %s extension \"%s\" (id=%d), len=%d\n",
+ client_server ? "server" : "client", extname, type, len);
+ BIO_dump(bio, (const char *)data, len);
+ (void)BIO_flush(bio);
+}
+
+#ifndef OPENSSL_NO_SOCK
+int generate_stateless_cookie_callback(SSL *ssl, unsigned char *cookie,
+ size_t *cookie_len)
+{
+ unsigned char *buffer = NULL;
+ size_t length = 0;
+ unsigned short port;
+ BIO_ADDR *lpeer = NULL, *peer = NULL;
+ int res = 0;
+
+ /* Initialize a random secret */
+ if (!cookie_initialized) {
+ if (RAND_bytes(cookie_secret, COOKIE_SECRET_LENGTH) <= 0) {
+ BIO_printf(bio_err, "error setting random cookie secret\n");
+ return 0;
+ }
+ cookie_initialized = 1;
+ }
+
+ if (SSL_is_dtls(ssl)) {
+ lpeer = peer = BIO_ADDR_new();
+ if (peer == NULL) {
+ BIO_printf(bio_err, "memory full\n");
+ return 0;
+ }
+
+ /* Read peer information */
+ (void)BIO_dgram_get_peer(SSL_get_rbio(ssl), peer);
+ } else {
+ peer = ourpeer;
+ }
+
+ /* Create buffer with peer's address and port */
+ if (!BIO_ADDR_rawaddress(peer, NULL, &length)) {
+ BIO_printf(bio_err, "Failed getting peer address\n");
+ BIO_ADDR_free(lpeer);
+ return 0;
+ }
+ OPENSSL_assert(length != 0);
+ port = BIO_ADDR_rawport(peer);
+ length += sizeof(port);
+ buffer = app_malloc(length, "cookie generate buffer");
+
+ memcpy(buffer, &port, sizeof(port));
+ BIO_ADDR_rawaddress(peer, buffer + sizeof(port), NULL);
+
+ if (EVP_Q_mac(NULL, "HMAC", NULL, "SHA1", NULL,
+ cookie_secret, COOKIE_SECRET_LENGTH, buffer, length,
+ cookie, DTLS1_COOKIE_LENGTH, cookie_len) == NULL) {
+ BIO_printf(bio_err,
+ "Error calculating HMAC-SHA1 of buffer with secret\n");
+ goto end;
+ }
+ res = 1;
+end:
+ OPENSSL_free(buffer);
+ BIO_ADDR_free(lpeer);
+
+ return res;
+}
+
+int verify_stateless_cookie_callback(SSL *ssl, const unsigned char *cookie,
+ size_t cookie_len)
+{
+ unsigned char result[EVP_MAX_MD_SIZE];
+ size_t resultlength;
+
+ /* Note: we check cookie_initialized because if it's not,
+ * it cannot be valid */
+ if (cookie_initialized
+ && generate_stateless_cookie_callback(ssl, result, &resultlength)
+ && cookie_len == resultlength
+ && memcmp(result, cookie, resultlength) == 0)
+ return 1;
+
+ return 0;
+}
+
+int generate_cookie_callback(SSL *ssl, unsigned char *cookie,
+ unsigned int *cookie_len)
+{
+ size_t temp = 0;
+ int res = generate_stateless_cookie_callback(ssl, cookie, &temp);
+
+ if (res != 0)
+ *cookie_len = (unsigned int)temp;
+ return res;
+}
+
+int verify_cookie_callback(SSL *ssl, const unsigned char *cookie,
+ unsigned int cookie_len)
+{
+ return verify_stateless_cookie_callback(ssl, cookie, cookie_len);
+}
+
+#endif
+
+/*
+ * Example of extended certificate handling. Where the standard support of
+ * one certificate per algorithm is not sufficient an application can decide
+ * which certificate(s) to use at runtime based on whatever criteria it deems
+ * appropriate.
+ */
+
+/* Linked list of certificates, keys and chains */
+struct ssl_excert_st {
+ int certform;
+ const char *certfile;
+ int keyform;
+ const char *keyfile;
+ const char *chainfile;
+ X509 *cert;
+ EVP_PKEY *key;
+ STACK_OF(X509) *chain;
+ int build_chain;
+ struct ssl_excert_st *next, *prev;
+};
+
+static STRINT_PAIR chain_flags[] = {
+ {"Overall Validity", CERT_PKEY_VALID},
+ {"Sign with EE key", CERT_PKEY_SIGN},
+ {"EE signature", CERT_PKEY_EE_SIGNATURE},
+ {"CA signature", CERT_PKEY_CA_SIGNATURE},
+ {"EE key parameters", CERT_PKEY_EE_PARAM},
+ {"CA key parameters", CERT_PKEY_CA_PARAM},
+ {"Explicitly sign with EE key", CERT_PKEY_EXPLICIT_SIGN},
+ {"Issuer Name", CERT_PKEY_ISSUER_NAME},
+ {"Certificate Type", CERT_PKEY_CERT_TYPE},
+ {NULL}
+};
+
+static void print_chain_flags(SSL *s, int flags)
+{
+ STRINT_PAIR *pp;
+
+ for (pp = chain_flags; pp->name; ++pp)
+ BIO_printf(bio_err, "\t%s: %s\n",
+ pp->name,
+ (flags & pp->retval) ? "OK" : "NOT OK");
+ BIO_printf(bio_err, "\tSuite B: ");
+ if (SSL_set_cert_flags(s, 0) & SSL_CERT_FLAG_SUITEB_128_LOS)
+ BIO_puts(bio_err, flags & CERT_PKEY_SUITEB ? "OK\n" : "NOT OK\n");
+ else
+ BIO_printf(bio_err, "not tested\n");
+}
+
+/*
+ * Very basic selection callback: just use any certificate chain reported as
+ * valid. More sophisticated could prioritise according to local policy.
+ */
+static int set_cert_cb(SSL *ssl, void *arg)
+{
+ int i, rv;
+ SSL_EXCERT *exc = arg;
+#ifdef CERT_CB_TEST_RETRY
+ static int retry_cnt;
+
+ if (retry_cnt < 5) {
+ retry_cnt++;
+ BIO_printf(bio_err,
+ "Certificate callback retry test: count %d\n",
+ retry_cnt);
+ return -1;
+ }
+#endif
+ SSL_certs_clear(ssl);
+
+ if (exc == NULL)
+ return 1;
+
+ /*
+ * Go to end of list and traverse backwards since we prepend newer
+ * entries this retains the original order.
+ */
+ while (exc->next != NULL)
+ exc = exc->next;
+
+ i = 0;
+
+ while (exc != NULL) {
+ i++;
+ rv = SSL_check_chain(ssl, exc->cert, exc->key, exc->chain);
+ BIO_printf(bio_err, "Checking cert chain %d:\nSubject: ", i);
+ X509_NAME_print_ex(bio_err, X509_get_subject_name(exc->cert), 0,
+ get_nameopt());
+ BIO_puts(bio_err, "\n");
+ print_chain_flags(ssl, rv);
+ if (rv & CERT_PKEY_VALID) {
+ if (!SSL_use_certificate(ssl, exc->cert)
+ || !SSL_use_PrivateKey(ssl, exc->key)) {
+ return 0;
+ }
+ /*
+ * NB: we wouldn't normally do this as it is not efficient
+ * building chains on each connection better to cache the chain
+ * in advance.
+ */
+ if (exc->build_chain) {
+ if (!SSL_build_cert_chain(ssl, 0))
+ return 0;
+ } else if (exc->chain != NULL) {
+ if (!SSL_set1_chain(ssl, exc->chain))
+ return 0;
+ }
+ }
+ exc = exc->prev;
+ }
+ return 1;
+}
+
+void ssl_ctx_set_excert(SSL_CTX *ctx, SSL_EXCERT *exc)
+{
+ SSL_CTX_set_cert_cb(ctx, set_cert_cb, exc);
+}
+
+static int ssl_excert_prepend(SSL_EXCERT **pexc)
+{
+ SSL_EXCERT *exc = app_malloc(sizeof(*exc), "prepend cert");
+
+ memset(exc, 0, sizeof(*exc));
+
+ exc->next = *pexc;
+ *pexc = exc;
+
+ if (exc->next) {
+ exc->certform = exc->next->certform;
+ exc->keyform = exc->next->keyform;
+ exc->next->prev = exc;
+ } else {
+ exc->certform = FORMAT_PEM;
+ exc->keyform = FORMAT_PEM;
+ }
+ return 1;
+
+}
+
+void ssl_excert_free(SSL_EXCERT *exc)
+{
+ SSL_EXCERT *curr;
+
+ if (exc == NULL)
+ return;
+ while (exc) {
+ X509_free(exc->cert);
+ EVP_PKEY_free(exc->key);
+ sk_X509_pop_free(exc->chain, X509_free);
+ curr = exc;
+ exc = exc->next;
+ OPENSSL_free(curr);
+ }
+}
+
+int load_excert(SSL_EXCERT **pexc)
+{
+ SSL_EXCERT *exc = *pexc;
+
+ if (exc == NULL)
+ return 1;
+ /* If nothing in list, free and set to NULL */
+ if (exc->certfile == NULL && exc->next == NULL) {
+ ssl_excert_free(exc);
+ *pexc = NULL;
+ return 1;
+ }
+ for (; exc; exc = exc->next) {
+ if (exc->certfile == NULL) {
+ BIO_printf(bio_err, "Missing filename\n");
+ return 0;
+ }
+ exc->cert = load_cert(exc->certfile, exc->certform,
+ "Server Certificate");
+ if (exc->cert == NULL)
+ return 0;
+ if (exc->keyfile != NULL) {
+ exc->key = load_key(exc->keyfile, exc->keyform,
+ 0, NULL, NULL, "server key");
+ } else {
+ exc->key = load_key(exc->certfile, exc->certform,
+ 0, NULL, NULL, "server key");
+ }
+ if (exc->key == NULL)
+ return 0;
+ if (exc->chainfile != NULL) {
+ if (!load_certs(exc->chainfile, 0, &exc->chain, NULL, "server chain"))
+ return 0;
+ }
+ }
+ return 1;
+}
+
+enum range { OPT_X_ENUM };
+
+int args_excert(int opt, SSL_EXCERT **pexc)
+{
+ SSL_EXCERT *exc = *pexc;
+
+ assert(opt > OPT_X__FIRST);
+ assert(opt < OPT_X__LAST);
+
+ if (exc == NULL) {
+ if (!ssl_excert_prepend(&exc)) {
+ BIO_printf(bio_err, " %s: Error initialising xcert\n",
+ opt_getprog());
+ goto err;
+ }
+ *pexc = exc;
+ }
+
+ switch ((enum range)opt) {
+ case OPT_X__FIRST:
+ case OPT_X__LAST:
+ return 0;
+ case OPT_X_CERT:
+ if (exc->certfile != NULL && !ssl_excert_prepend(&exc)) {
+ BIO_printf(bio_err, "%s: Error adding xcert\n", opt_getprog());
+ goto err;
+ }
+ *pexc = exc;
+ exc->certfile = opt_arg();
+ break;
+ case OPT_X_KEY:
+ if (exc->keyfile != NULL) {
+ BIO_printf(bio_err, "%s: Key already specified\n", opt_getprog());
+ goto err;
+ }
+ exc->keyfile = opt_arg();
+ break;
+ case OPT_X_CHAIN:
+ if (exc->chainfile != NULL) {
+ BIO_printf(bio_err, "%s: Chain already specified\n",
+ opt_getprog());
+ goto err;
+ }
+ exc->chainfile = opt_arg();
+ break;
+ case OPT_X_CHAIN_BUILD:
+ exc->build_chain = 1;
+ break;
+ case OPT_X_CERTFORM:
+ if (!opt_format(opt_arg(), OPT_FMT_ANY, &exc->certform))
+ return 0;
+ break;
+ case OPT_X_KEYFORM:
+ if (!opt_format(opt_arg(), OPT_FMT_ANY, &exc->keyform))
+ return 0;
+ break;
+ }
+ return 1;
+
+ err:
+ ERR_print_errors(bio_err);
+ ssl_excert_free(exc);
+ *pexc = NULL;
+ return 0;
+}
+
+static void print_raw_cipherlist(SSL *s)
+{
+ const unsigned char *rlist;
+ static const unsigned char scsv_id[] = { 0, 0xFF };
+ size_t i, rlistlen, num;
+
+ if (!SSL_is_server(s))
+ return;
+ num = SSL_get0_raw_cipherlist(s, NULL);
+ OPENSSL_assert(num == 2);
+ rlistlen = SSL_get0_raw_cipherlist(s, &rlist);
+ BIO_puts(bio_err, "Client cipher list: ");
+ for (i = 0; i < rlistlen; i += num, rlist += num) {
+ const SSL_CIPHER *c = SSL_CIPHER_find(s, rlist);
+ if (i)
+ BIO_puts(bio_err, ":");
+ if (c != NULL) {
+ BIO_puts(bio_err, SSL_CIPHER_get_name(c));
+ } else if (memcmp(rlist, scsv_id, num) == 0) {
+ BIO_puts(bio_err, "SCSV");
+ } else {
+ size_t j;
+ BIO_puts(bio_err, "0x");
+ for (j = 0; j < num; j++)
+ BIO_printf(bio_err, "%02X", rlist[j]);
+ }
+ }
+ BIO_puts(bio_err, "\n");
+}
+
+/*
+ * Hex encoder for TLSA RRdata, not ':' delimited.
+ */
+static char *hexencode(const unsigned char *data, size_t len)
+{
+ static const char *hex = "0123456789abcdef";
+ char *out;
+ char *cp;
+ size_t outlen = 2 * len + 1;
+ int ilen = (int) outlen;
+
+ if (outlen < len || ilen < 0 || outlen != (size_t)ilen) {
+ BIO_printf(bio_err, "%s: %zu-byte buffer too large to hexencode\n",
+ opt_getprog(), len);
+ exit(1);
+ }
+ cp = out = app_malloc(ilen, "TLSA hex data buffer");
+
+ while (len-- > 0) {
+ *cp++ = hex[(*data >> 4) & 0x0f];
+ *cp++ = hex[*data++ & 0x0f];
+ }
+ *cp = '\0';
+ return out;
+}
+
+void print_verify_detail(SSL *s, BIO *bio)
+{
+ int mdpth;
+ EVP_PKEY *mspki;
+ long verify_err = SSL_get_verify_result(s);
+
+ if (verify_err == X509_V_OK) {
+ const char *peername = SSL_get0_peername(s);
+
+ BIO_printf(bio, "Verification: OK\n");
+ if (peername != NULL)
+ BIO_printf(bio, "Verified peername: %s\n", peername);
+ } else {
+ const char *reason = X509_verify_cert_error_string(verify_err);
+
+ BIO_printf(bio, "Verification error: %s\n", reason);
+ }
+
+ if ((mdpth = SSL_get0_dane_authority(s, NULL, &mspki)) >= 0) {
+ uint8_t usage, selector, mtype;
+ const unsigned char *data = NULL;
+ size_t dlen = 0;
+ char *hexdata;
+
+ mdpth = SSL_get0_dane_tlsa(s, &usage, &selector, &mtype, &data, &dlen);
+
+ /*
+ * The TLSA data field can be quite long when it is a certificate,
+ * public key or even a SHA2-512 digest. Because the initial octets of
+ * ASN.1 certificates and public keys contain mostly boilerplate OIDs
+ * and lengths, we show the last 12 bytes of the data instead, as these
+ * are more likely to distinguish distinct TLSA records.
+ */
+#define TLSA_TAIL_SIZE 12
+ if (dlen > TLSA_TAIL_SIZE)
+ hexdata = hexencode(data + dlen - TLSA_TAIL_SIZE, TLSA_TAIL_SIZE);
+ else
+ hexdata = hexencode(data, dlen);
+ BIO_printf(bio, "DANE TLSA %d %d %d %s%s %s at depth %d\n",
+ usage, selector, mtype,
+ (dlen > TLSA_TAIL_SIZE) ? "..." : "", hexdata,
+ (mspki != NULL) ? "signed the certificate" :
+ mdpth ? "matched TA certificate" : "matched EE certificate",
+ mdpth);
+ OPENSSL_free(hexdata);
+ }
+}
+
+void print_ssl_summary(SSL *s)
+{
+ const SSL_CIPHER *c;
+ X509 *peer;
+
+ BIO_printf(bio_err, "Protocol version: %s\n", SSL_get_version(s));
+ print_raw_cipherlist(s);
+ c = SSL_get_current_cipher(s);
+ BIO_printf(bio_err, "Ciphersuite: %s\n", SSL_CIPHER_get_name(c));
+ do_print_sigalgs(bio_err, s, 0);
+ peer = SSL_get0_peer_certificate(s);
+ if (peer != NULL) {
+ int nid;
+
+ BIO_puts(bio_err, "Peer certificate: ");
+ X509_NAME_print_ex(bio_err, X509_get_subject_name(peer),
+ 0, get_nameopt());
+ BIO_puts(bio_err, "\n");
+ if (SSL_get_peer_signature_nid(s, &nid))
+ BIO_printf(bio_err, "Hash used: %s\n", OBJ_nid2sn(nid));
+ if (SSL_get_peer_signature_type_nid(s, &nid))
+ BIO_printf(bio_err, "Signature type: %s\n", get_sigtype(nid));
+ print_verify_detail(s, bio_err);
+ } else {
+ BIO_puts(bio_err, "No peer certificate\n");
+ }
+#ifndef OPENSSL_NO_EC
+ ssl_print_point_formats(bio_err, s);
+ if (SSL_is_server(s))
+ ssl_print_groups(bio_err, s, 1);
+ else
+ ssl_print_tmp_key(bio_err, s);
+#else
+ if (!SSL_is_server(s))
+ ssl_print_tmp_key(bio_err, s);
+#endif
+}
+
+int config_ctx(SSL_CONF_CTX *cctx, STACK_OF(OPENSSL_STRING) *str,
+ SSL_CTX *ctx)
+{
+ int i;
+
+ SSL_CONF_CTX_set_ssl_ctx(cctx, ctx);
+ for (i = 0; i < sk_OPENSSL_STRING_num(str); i += 2) {
+ const char *flag = sk_OPENSSL_STRING_value(str, i);
+ const char *arg = sk_OPENSSL_STRING_value(str, i + 1);
+
+ if (SSL_CONF_cmd(cctx, flag, arg) <= 0) {
+ BIO_printf(bio_err, "Call to SSL_CONF_cmd(%s, %s) failed\n",
+ flag, arg == NULL ? "<NULL>" : arg);
+ ERR_print_errors(bio_err);
+ return 0;
+ }
+ }
+ if (!SSL_CONF_CTX_finish(cctx)) {
+ BIO_puts(bio_err, "Error finishing context\n");
+ ERR_print_errors(bio_err);
+ return 0;
+ }
+ return 1;
+}
+
+static int add_crls_store(X509_STORE *st, STACK_OF(X509_CRL) *crls)
+{
+ X509_CRL *crl;
+ int i, ret = 1;
+
+ for (i = 0; i < sk_X509_CRL_num(crls); i++) {
+ crl = sk_X509_CRL_value(crls, i);
+ if (!X509_STORE_add_crl(st, crl))
+ ret = 0;
+ }
+ return ret;
+}
+
+int ssl_ctx_add_crls(SSL_CTX *ctx, STACK_OF(X509_CRL) *crls, int crl_download)
+{
+ X509_STORE *st;
+
+ st = SSL_CTX_get_cert_store(ctx);
+ add_crls_store(st, crls);
+ if (crl_download)
+ store_setup_crl_download(st);
+ return 1;
+}
+
+int ssl_load_stores(SSL_CTX *ctx,
+ const char *vfyCApath, const char *vfyCAfile,
+ const char *vfyCAstore,
+ const char *chCApath, const char *chCAfile,
+ const char *chCAstore,
+ STACK_OF(X509_CRL) *crls, int crl_download)
+{
+ X509_STORE *vfy = NULL, *ch = NULL;
+ int rv = 0;
+
+ if (vfyCApath != NULL || vfyCAfile != NULL || vfyCAstore != NULL) {
+ vfy = X509_STORE_new();
+ if (vfy == NULL)
+ goto err;
+ if (vfyCAfile != NULL && !X509_STORE_load_file(vfy, vfyCAfile))
+ goto err;
+ if (vfyCApath != NULL && !X509_STORE_load_path(vfy, vfyCApath))
+ goto err;
+ if (vfyCAstore != NULL && !X509_STORE_load_store(vfy, vfyCAstore))
+ goto err;
+ add_crls_store(vfy, crls);
+ if (SSL_CTX_set1_verify_cert_store(ctx, vfy) == 0)
+ goto err;
+ if (crl_download)
+ store_setup_crl_download(vfy);
+ }
+ if (chCApath != NULL || chCAfile != NULL || chCAstore != NULL) {
+ ch = X509_STORE_new();
+ if (ch == NULL)
+ goto err;
+ if (chCAfile != NULL && !X509_STORE_load_file(ch, chCAfile))
+ goto err;
+ if (chCApath != NULL && !X509_STORE_load_path(ch, chCApath))
+ goto err;
+ if (chCAstore != NULL && !X509_STORE_load_store(ch, chCAstore))
+ goto err;
+ if (SSL_CTX_set1_chain_cert_store(ctx, ch) == 0)
+ goto err;
+ }
+ rv = 1;
+ err:
+ X509_STORE_free(vfy);
+ X509_STORE_free(ch);
+ return rv;
+}
+
+/* Verbose print out of security callback */
+
+typedef struct {
+ BIO *out;
+ int verbose;
+ int (*old_cb) (const SSL *s, const SSL_CTX *ctx, int op, int bits, int nid,
+ void *other, void *ex);
+} security_debug_ex;
+
+static STRINT_PAIR callback_types[] = {
+ {"Supported Ciphersuite", SSL_SECOP_CIPHER_SUPPORTED},
+ {"Shared Ciphersuite", SSL_SECOP_CIPHER_SHARED},
+ {"Check Ciphersuite", SSL_SECOP_CIPHER_CHECK},
+#ifndef OPENSSL_NO_DH
+ {"Temp DH key bits", SSL_SECOP_TMP_DH},
+#endif
+ {"Supported Curve", SSL_SECOP_CURVE_SUPPORTED},
+ {"Shared Curve", SSL_SECOP_CURVE_SHARED},
+ {"Check Curve", SSL_SECOP_CURVE_CHECK},
+ {"Supported Signature Algorithm", SSL_SECOP_SIGALG_SUPPORTED},
+ {"Shared Signature Algorithm", SSL_SECOP_SIGALG_SHARED},
+ {"Check Signature Algorithm", SSL_SECOP_SIGALG_CHECK},
+ {"Signature Algorithm mask", SSL_SECOP_SIGALG_MASK},
+ {"Certificate chain EE key", SSL_SECOP_EE_KEY},
+ {"Certificate chain CA key", SSL_SECOP_CA_KEY},
+ {"Peer Chain EE key", SSL_SECOP_PEER_EE_KEY},
+ {"Peer Chain CA key", SSL_SECOP_PEER_CA_KEY},
+ {"Certificate chain CA digest", SSL_SECOP_CA_MD},
+ {"Peer chain CA digest", SSL_SECOP_PEER_CA_MD},
+ {"SSL compression", SSL_SECOP_COMPRESSION},
+ {"Session ticket", SSL_SECOP_TICKET},
+ {NULL}
+};
+
+static int security_callback_debug(const SSL *s, const SSL_CTX *ctx,
+ int op, int bits, int nid,
+ void *other, void *ex)
+{
+ security_debug_ex *sdb = ex;
+ int rv, show_bits = 1, cert_md = 0;
+ const char *nm;
+ int show_nm;
+
+ rv = sdb->old_cb(s, ctx, op, bits, nid, other, ex);
+ if (rv == 1 && sdb->verbose < 2)
+ return 1;
+ BIO_puts(sdb->out, "Security callback: ");
+
+ nm = lookup(op, callback_types, NULL);
+ show_nm = nm != NULL;
+ switch (op) {
+ case SSL_SECOP_TICKET:
+ case SSL_SECOP_COMPRESSION:
+ show_bits = 0;
+ show_nm = 0;
+ break;
+ case SSL_SECOP_VERSION:
+ BIO_printf(sdb->out, "Version=%s", lookup(nid, ssl_versions, "???"));
+ show_bits = 0;
+ show_nm = 0;
+ break;
+ case SSL_SECOP_CA_MD:
+ case SSL_SECOP_PEER_CA_MD:
+ cert_md = 1;
+ break;
+ case SSL_SECOP_SIGALG_SUPPORTED:
+ case SSL_SECOP_SIGALG_SHARED:
+ case SSL_SECOP_SIGALG_CHECK:
+ case SSL_SECOP_SIGALG_MASK:
+ show_nm = 0;
+ break;
+ }
+ if (show_nm)
+ BIO_printf(sdb->out, "%s=", nm);
+
+ switch (op & SSL_SECOP_OTHER_TYPE) {
+
+ case SSL_SECOP_OTHER_CIPHER:
+ BIO_puts(sdb->out, SSL_CIPHER_get_name(other));
+ break;
+
+#ifndef OPENSSL_NO_EC
+ case SSL_SECOP_OTHER_CURVE:
+ {
+ const char *cname;
+ cname = EC_curve_nid2nist(nid);
+ if (cname == NULL)
+ cname = OBJ_nid2sn(nid);
+ BIO_puts(sdb->out, cname);
+ }
+ break;
+#endif
+ case SSL_SECOP_OTHER_CERT:
+ {
+ if (cert_md) {
+ int sig_nid = X509_get_signature_nid(other);
+
+ BIO_puts(sdb->out, OBJ_nid2sn(sig_nid));
+ } else {
+ EVP_PKEY *pkey = X509_get0_pubkey(other);
+
+ if (pkey == NULL) {
+ BIO_printf(sdb->out, "Public key missing");
+ } else {
+ const char *algname = "";
+
+ EVP_PKEY_asn1_get0_info(NULL, NULL, NULL, NULL,
+ &algname, EVP_PKEY_get0_asn1(pkey));
+ BIO_printf(sdb->out, "%s, bits=%d",
+ algname, EVP_PKEY_get_bits(pkey));
+ }
+ }
+ break;
+ }
+ case SSL_SECOP_OTHER_SIGALG:
+ {
+ const unsigned char *salg = other;
+ const char *sname = NULL;
+ int raw_sig_code = (salg[0] << 8) + salg[1]; /* always big endian (msb, lsb) */
+ /* raw_sig_code: signature_scheme from tls1.3, or signature_and_hash from tls1.2 */
+
+ if (nm != NULL)
+ BIO_printf(sdb->out, "%s", nm);
+ else
+ BIO_printf(sdb->out, "s_cb.c:security_callback_debug op=0x%x", op);
+
+ sname = lookup(raw_sig_code, signature_tls13_scheme_list, NULL);
+ if (sname != NULL) {
+ BIO_printf(sdb->out, " scheme=%s", sname);
+ } else {
+ int alg_code = salg[1];
+ int hash_code = salg[0];
+ const char *alg_str = lookup(alg_code, signature_tls12_alg_list, NULL);
+ const char *hash_str = lookup(hash_code, signature_tls12_hash_list, NULL);
+
+ if (alg_str != NULL && hash_str != NULL)
+ BIO_printf(sdb->out, " digest=%s, algorithm=%s", hash_str, alg_str);
+ else
+ BIO_printf(sdb->out, " scheme=unknown(0x%04x)", raw_sig_code);
+ }
+ }
+
+ }
+
+ if (show_bits)
+ BIO_printf(sdb->out, ", security bits=%d", bits);
+ BIO_printf(sdb->out, ": %s\n", rv ? "yes" : "no");
+ return rv;
+}
+
+void ssl_ctx_security_debug(SSL_CTX *ctx, int verbose)
+{
+ static security_debug_ex sdb;
+
+ sdb.out = bio_err;
+ sdb.verbose = verbose;
+ sdb.old_cb = SSL_CTX_get_security_callback(ctx);
+ SSL_CTX_set_security_callback(ctx, security_callback_debug);
+ SSL_CTX_set0_security_ex_data(ctx, &sdb);
+}
+
+static void keylog_callback(const SSL *ssl, const char *line)
+{
+ if (bio_keylog == NULL) {
+ BIO_printf(bio_err, "Keylog callback is invoked without valid file!\n");
+ return;
+ }
+
+ /*
+ * There might be concurrent writers to the keylog file, so we must ensure
+ * that the given line is written at once.
+ */
+ BIO_printf(bio_keylog, "%s\n", line);
+ (void)BIO_flush(bio_keylog);
+}
+
+int set_keylog_file(SSL_CTX *ctx, const char *keylog_file)
+{
+ /* Close any open files */
+ BIO_free_all(bio_keylog);
+ bio_keylog = NULL;
+
+ if (ctx == NULL || keylog_file == NULL) {
+ /* Keylogging is disabled, OK. */
+ return 0;
+ }
+
+ /*
+ * Append rather than write in order to allow concurrent modification.
+ * Furthermore, this preserves existing keylog files which is useful when
+ * the tool is run multiple times.
+ */
+ bio_keylog = BIO_new_file(keylog_file, "a");
+ if (bio_keylog == NULL) {
+ BIO_printf(bio_err, "Error writing keylog file %s\n", keylog_file);
+ return 1;
+ }
+
+ /* Write a header for seekable, empty files (this excludes pipes). */
+ if (BIO_tell(bio_keylog) == 0) {
+ BIO_puts(bio_keylog,
+ "# SSL/TLS secrets log file, generated by OpenSSL\n");
+ (void)BIO_flush(bio_keylog);
+ }
+ SSL_CTX_set_keylog_callback(ctx, keylog_callback);
+ return 0;
+}
+
+void print_ca_names(BIO *bio, SSL *s)
+{
+ const char *cs = SSL_is_server(s) ? "server" : "client";
+ const STACK_OF(X509_NAME) *sk = SSL_get0_peer_CA_list(s);
+ int i;
+
+ if (sk == NULL || sk_X509_NAME_num(sk) == 0) {
+ if (!SSL_is_server(s))
+ BIO_printf(bio, "---\nNo %s certificate CA names sent\n", cs);
+ return;
+ }
+
+ BIO_printf(bio, "---\nAcceptable %s certificate CA names\n",cs);
+ for (i = 0; i < sk_X509_NAME_num(sk); i++) {
+ X509_NAME_print_ex(bio, sk_X509_NAME_value(sk, i), 0, get_nameopt());
+ BIO_write(bio, "\n", 1);
+ }
+}
diff --git a/apps/lib/s_socket.c b/apps/lib/s_socket.c
new file mode 100644
index 000000000000..8c6020d01692
--- /dev/null
+++ b/apps/lib/s_socket.c
@@ -0,0 +1,462 @@
+/*
+ * Copyright 1995-2025 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the Apache License 2.0 (the "License"). You may not use
+ * this file except in compliance with the License. You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+/* socket-related functions used by s_client and s_server */
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <signal.h>
+#include <openssl/opensslconf.h>
+
+/*
+ * With IPv6, it looks like Digital has mixed up the proper order of
+ * recursive header file inclusion, resulting in the compiler complaining
+ * that u_int isn't defined, but only if _POSIX_C_SOURCE is defined, which is
+ * needed to have fileno() declared correctly... So let's define u_int
+ */
+#if defined(OPENSSL_SYS_VMS_DECC) && !defined(__U_INT)
+# define __U_INT
+typedef unsigned int u_int;
+#endif
+
+#ifdef _WIN32
+# include <process.h>
+
+/* MSVC renamed some POSIX functions to have an underscore prefix. */
+# ifdef _MSC_VER
+# define getpid _getpid
+# endif
+#endif
+
+#ifndef OPENSSL_NO_SOCK
+
+# include "apps.h"
+# include "s_apps.h"
+# include "internal/sockets.h"
+
+# if defined(__TANDEM)
+# if defined(OPENSSL_TANDEM_FLOSS)
+# include <floss.h(floss_read)>
+# endif
+# endif
+
+# include <openssl/bio.h>
+# include <openssl/err.h>
+
+/* Keep track of our peer's address for the cookie callback */
+BIO_ADDR *ourpeer = NULL;
+
+/*
+ * init_client - helper routine to set up socket communication
+ * @sock: pointer to storage of resulting socket.
+ * @host: the host name or path (for AF_UNIX) to connect to.
+ * @port: the port to connect to (ignored for AF_UNIX).
+ * @bindhost: source host or path (for AF_UNIX).
+ * @bindport: source port (ignored for AF_UNIX).
+ * @family: desired socket family, may be AF_INET, AF_INET6, AF_UNIX or
+ * AF_UNSPEC
+ * @type: socket type, must be SOCK_STREAM or SOCK_DGRAM
+ * @protocol: socket protocol, e.g. IPPROTO_TCP or IPPROTO_UDP (or 0 for any)
+ *
+ * This will create a socket and use it to connect to a host:port, or if
+ * family == AF_UNIX, to the path found in host.
+ *
+ * If the host has more than one address, it will try them one by one until
+ * a successful connection is established. The resulting socket will be
+ * found in *sock on success, it will be given INVALID_SOCKET otherwise.
+ *
+ * Returns 1 on success, 0 on failure.
+ */
+int init_client(int *sock, const char *host, const char *port,
+ const char *bindhost, const char *bindport,
+ int family, int type, int protocol)
+{
+ BIO_ADDRINFO *res = NULL;
+ BIO_ADDRINFO *bindaddr = NULL;
+ const BIO_ADDRINFO *ai = NULL;
+ const BIO_ADDRINFO *bi = NULL;
+ int found = 0;
+ int ret;
+
+ if (BIO_sock_init() != 1)
+ return 0;
+
+ ret = BIO_lookup_ex(host, port, BIO_LOOKUP_CLIENT, family, type, protocol,
+ &res);
+ if (ret == 0) {
+ ERR_print_errors(bio_err);
+ return 0;
+ }
+
+ if (bindhost != NULL || bindport != NULL) {
+ ret = BIO_lookup_ex(bindhost, bindport, BIO_LOOKUP_CLIENT,
+ family, type, protocol, &bindaddr);
+ if (ret == 0) {
+ ERR_print_errors (bio_err);
+ goto out;
+ }
+ }
+
+ ret = 0;
+ for (ai = res; ai != NULL; ai = BIO_ADDRINFO_next(ai)) {
+ /* Admittedly, these checks are quite paranoid, we should not get
+ * anything in the BIO_ADDRINFO chain that we haven't
+ * asked for. */
+ OPENSSL_assert((family == AF_UNSPEC
+ || family == BIO_ADDRINFO_family(ai))
+ && (type == 0 || type == BIO_ADDRINFO_socktype(ai))
+ && (protocol == 0
+ || protocol == BIO_ADDRINFO_protocol(ai)));
+
+ if (bindaddr != NULL) {
+ for (bi = bindaddr; bi != NULL; bi = BIO_ADDRINFO_next(bi)) {
+ if (BIO_ADDRINFO_family(bi) == BIO_ADDRINFO_family(ai))
+ break;
+ }
+ if (bi == NULL)
+ continue;
+ ++found;
+ }
+
+ *sock = BIO_socket(BIO_ADDRINFO_family(ai), BIO_ADDRINFO_socktype(ai),
+ BIO_ADDRINFO_protocol(ai), 0);
+ if (*sock == INVALID_SOCKET) {
+ /* Maybe the kernel doesn't support the socket family, even if
+ * BIO_lookup() added it in the returned result...
+ */
+ continue;
+ }
+
+ if (bi != NULL) {
+ if (!BIO_bind(*sock, BIO_ADDRINFO_address(bi),
+ BIO_SOCK_REUSEADDR)) {
+ BIO_closesocket(*sock);
+ *sock = INVALID_SOCKET;
+ break;
+ }
+ }
+
+#ifndef OPENSSL_NO_SCTP
+ if (protocol == IPPROTO_SCTP) {
+ /*
+ * For SCTP we have to set various options on the socket prior to
+ * connecting. This is done automatically by BIO_new_dgram_sctp().
+ * We don't actually need the created BIO though so we free it again
+ * immediately.
+ */
+ BIO *tmpbio = BIO_new_dgram_sctp(*sock, BIO_NOCLOSE);
+
+ if (tmpbio == NULL) {
+ ERR_print_errors(bio_err);
+ return 0;
+ }
+ BIO_free(tmpbio);
+ }
+#endif
+
+ if (!BIO_connect(*sock, BIO_ADDRINFO_address(ai),
+ BIO_ADDRINFO_protocol(ai) == IPPROTO_TCP ? BIO_SOCK_NODELAY : 0)) {
+ BIO_closesocket(*sock);
+ *sock = INVALID_SOCKET;
+ continue;
+ }
+
+ /* Success, don't try any more addresses */
+ break;
+ }
+
+ if (*sock == INVALID_SOCKET) {
+ if (bindaddr != NULL && !found) {
+ BIO_printf(bio_err, "Can't bind %saddress for %s%s%s\n",
+#ifdef AF_INET6
+ BIO_ADDRINFO_family(res) == AF_INET6 ? "IPv6 " :
+#endif
+ BIO_ADDRINFO_family(res) == AF_INET ? "IPv4 " :
+ BIO_ADDRINFO_family(res) == AF_UNIX ? "unix " : "",
+ bindhost != NULL ? bindhost : "",
+ bindport != NULL ? ":" : "",
+ bindport != NULL ? bindport : "");
+ ERR_clear_error();
+ ret = 0;
+ }
+ ERR_print_errors(bio_err);
+ } else {
+ /* Remove any stale errors from previous connection attempts */
+ ERR_clear_error();
+ ret = 1;
+ }
+out:
+ if (bindaddr != NULL) {
+ BIO_ADDRINFO_free (bindaddr);
+ }
+ BIO_ADDRINFO_free(res);
+ return ret;
+}
+
+int report_server_accept(BIO *out, int asock, int with_address, int with_pid)
+{
+ int success = 1;
+
+ if (BIO_printf(out, "ACCEPT") <= 0)
+ return 0;
+ if (with_address) {
+ union BIO_sock_info_u info;
+ char *hostname = NULL;
+ char *service = NULL;
+
+ if ((info.addr = BIO_ADDR_new()) != NULL
+ && BIO_sock_info(asock, BIO_SOCK_INFO_ADDRESS, &info)
+ && (hostname = BIO_ADDR_hostname_string(info.addr, 1)) != NULL
+ && (service = BIO_ADDR_service_string(info.addr, 1)) != NULL) {
+ success = BIO_printf(out,
+ strchr(hostname, ':') == NULL
+ ? /* IPv4 */ " %s:%s"
+ : /* IPv6 */ " [%s]:%s",
+ hostname, service) > 0;
+ } else {
+ (void)BIO_printf(out, "unknown:error\n");
+ success = 0;
+ }
+ OPENSSL_free(hostname);
+ OPENSSL_free(service);
+ BIO_ADDR_free(info.addr);
+ }
+ if (with_pid)
+ success = success && BIO_printf(out, " PID=%d", getpid()) > 0;
+ success = success && BIO_printf(out, "\n") > 0;
+ (void)BIO_flush(out);
+
+ return success;
+}
+
+/*
+ * do_server - helper routine to perform a server operation
+ * @accept_sock: pointer to storage of resulting socket.
+ * @host: the host name or path (for AF_UNIX) to connect to.
+ * @port: the port to connect to (ignored for AF_UNIX).
+ * @family: desired socket family, may be AF_INET, AF_INET6, AF_UNIX or
+ * AF_UNSPEC
+ * @type: socket type, must be SOCK_STREAM or SOCK_DGRAM
+ * @cb: pointer to a function that receives the accepted socket and
+ * should perform the communication with the connecting client.
+ * @context: pointer to memory that's passed verbatim to the cb function.
+ * @naccept: number of times an incoming connect should be accepted. If -1,
+ * unlimited number.
+ *
+ * This will create a socket and use it to listen to a host:port, or if
+ * family == AF_UNIX, to the path found in host, then start accepting
+ * incoming connections and run cb on the resulting socket.
+ *
+ * 0 on failure, something other on success.
+ */
+int do_server(int *accept_sock, const char *host, const char *port,
+ int family, int type, int protocol, do_server_cb cb,
+ unsigned char *context, int naccept, BIO *bio_s_out)
+{
+ int asock = 0;
+ int sock;
+ int i;
+ BIO_ADDRINFO *res = NULL;
+ const BIO_ADDRINFO *next;
+ int sock_family, sock_type, sock_protocol, sock_port;
+ const BIO_ADDR *sock_address;
+ int sock_family_fallback = AF_UNSPEC;
+ const BIO_ADDR *sock_address_fallback = NULL;
+ int sock_options = BIO_SOCK_REUSEADDR;
+ int ret = 0;
+
+ if (BIO_sock_init() != 1)
+ return 0;
+
+ if (!BIO_lookup_ex(host, port, BIO_LOOKUP_SERVER, family, type, protocol,
+ &res)) {
+ ERR_print_errors(bio_err);
+ return 0;
+ }
+
+ /* Admittedly, these checks are quite paranoid, we should not get
+ * anything in the BIO_ADDRINFO chain that we haven't asked for */
+ OPENSSL_assert((family == AF_UNSPEC || family == BIO_ADDRINFO_family(res))
+ && (type == 0 || type == BIO_ADDRINFO_socktype(res))
+ && (protocol == 0 || protocol == BIO_ADDRINFO_protocol(res)));
+
+ sock_family = BIO_ADDRINFO_family(res);
+ sock_type = BIO_ADDRINFO_socktype(res);
+ sock_protocol = BIO_ADDRINFO_protocol(res);
+ sock_address = BIO_ADDRINFO_address(res);
+ next = BIO_ADDRINFO_next(res);
+#ifdef AF_INET6
+ if (sock_family == AF_INET6)
+ sock_options |= BIO_SOCK_V6_ONLY;
+ if (next != NULL
+ && BIO_ADDRINFO_socktype(next) == sock_type
+ && BIO_ADDRINFO_protocol(next) == sock_protocol) {
+ if (sock_family == AF_INET
+ && BIO_ADDRINFO_family(next) == AF_INET6) {
+ /* In case AF_INET6 is returned but not supported by the
+ * kernel, retry with the first detected address family */
+ sock_family_fallback = sock_family;
+ sock_address_fallback = sock_address;
+ sock_family = AF_INET6;
+ sock_address = BIO_ADDRINFO_address(next);
+ } else if (sock_family == AF_INET6
+ && BIO_ADDRINFO_family(next) == AF_INET) {
+ sock_options &= ~BIO_SOCK_V6_ONLY;
+ }
+ }
+#endif
+
+ asock = BIO_socket(sock_family, sock_type, sock_protocol, 0);
+ if (asock == INVALID_SOCKET && sock_family_fallback != AF_UNSPEC) {
+ asock = BIO_socket(sock_family_fallback, sock_type, sock_protocol, 0);
+ sock_address = sock_address_fallback;
+ }
+ if (asock == INVALID_SOCKET
+ || !BIO_listen(asock, sock_address, sock_options)) {
+ BIO_ADDRINFO_free(res);
+ ERR_print_errors(bio_err);
+ if (asock != INVALID_SOCKET)
+ BIO_closesocket(asock);
+ goto end;
+ }
+
+#ifndef OPENSSL_NO_SCTP
+ if (protocol == IPPROTO_SCTP) {
+ /*
+ * For SCTP we have to set various options on the socket prior to
+ * accepting. This is done automatically by BIO_new_dgram_sctp().
+ * We don't actually need the created BIO though so we free it again
+ * immediately.
+ */
+ BIO *tmpbio = BIO_new_dgram_sctp(asock, BIO_NOCLOSE);
+
+ if (tmpbio == NULL) {
+ BIO_closesocket(asock);
+ ERR_print_errors(bio_err);
+ goto end;
+ }
+ BIO_free(tmpbio);
+ }
+#endif
+
+ sock_port = BIO_ADDR_rawport(sock_address);
+
+ BIO_ADDRINFO_free(res);
+ res = NULL;
+
+ if (!report_server_accept(bio_s_out, asock, sock_port == 0, 0)) {
+ BIO_closesocket(asock);
+ ERR_print_errors(bio_err);
+ goto end;
+ }
+
+ if (accept_sock != NULL)
+ *accept_sock = asock;
+ for (;;) {
+ char sink[64];
+ struct timeval timeout;
+ fd_set readfds;
+
+ if (type == SOCK_STREAM) {
+ BIO_ADDR_free(ourpeer);
+ ourpeer = BIO_ADDR_new();
+ if (ourpeer == NULL) {
+ BIO_closesocket(asock);
+ ERR_print_errors(bio_err);
+ goto end;
+ }
+ do {
+ sock = BIO_accept_ex(asock, ourpeer, 0);
+ } while (sock < 0 && BIO_sock_should_retry(sock));
+ if (sock < 0) {
+ ERR_print_errors(bio_err);
+ BIO_closesocket(asock);
+ break;
+ }
+
+ if (naccept != -1)
+ naccept--;
+ if (naccept == 0)
+ BIO_closesocket(asock);
+
+ BIO_set_tcp_ndelay(sock, 1);
+ i = (*cb)(sock, type, protocol, context);
+
+ /*
+ * If we ended with an alert being sent, but still with data in the
+ * network buffer to be read, then calling BIO_closesocket() will
+ * result in a TCP-RST being sent. On some platforms (notably
+ * Windows) then this will result in the peer immediately abandoning
+ * the connection including any buffered alert data before it has
+ * had a chance to be read. Shutting down the sending side first,
+ * and then closing the socket sends TCP-FIN first followed by
+ * TCP-RST. This seems to allow the peer to read the alert data.
+ */
+ shutdown(sock, 1); /* SHUT_WR */
+ /*
+ * We just said we have nothing else to say, but it doesn't mean
+ * that the other side has nothing. It's even recommended to
+ * consume incoming data. [In testing context this ensures that
+ * alerts are passed on...]
+ */
+ timeout.tv_sec = 0;
+ timeout.tv_usec = 500000; /* some extreme round-trip */
+ do {
+ FD_ZERO(&readfds);
+ openssl_fdset(sock, &readfds);
+ } while (select(sock + 1, &readfds, NULL, NULL, &timeout) > 0
+ && readsocket(sock, sink, sizeof(sink)) > 0);
+
+ BIO_closesocket(sock);
+ } else {
+ if (naccept != -1)
+ naccept--;
+
+ i = (*cb)(asock, type, protocol, context);
+ }
+
+ if (i < 0 || naccept == 0) {
+ BIO_closesocket(asock);
+ ret = i;
+ break;
+ }
+ }
+ end:
+# ifdef AF_UNIX
+ if (family == AF_UNIX)
+ unlink(host);
+# endif
+ BIO_ADDR_free(ourpeer);
+ ourpeer = NULL;
+ return ret;
+}
+
+void do_ssl_shutdown(SSL *ssl)
+{
+ int ret;
+
+ do {
+ /* We only do unidirectional shutdown */
+ ret = SSL_shutdown(ssl);
+ if (ret < 0) {
+ switch (SSL_get_error(ssl, ret)) {
+ case SSL_ERROR_WANT_READ:
+ case SSL_ERROR_WANT_WRITE:
+ case SSL_ERROR_WANT_ASYNC:
+ case SSL_ERROR_WANT_ASYNC_JOB:
+ /* We just do busy waiting. Nothing clever */
+ continue;
+ }
+ ret = 0;
+ }
+ } while (ret < 0);
+}
+
+#endif /* OPENSSL_NO_SOCK */
diff --git a/apps/lib/tlssrp_depr.c b/apps/lib/tlssrp_depr.c
new file mode 100644
index 000000000000..91c19b096e9a
--- /dev/null
+++ b/apps/lib/tlssrp_depr.c
@@ -0,0 +1,231 @@
+/*
+ * Copyright 1995-2021 The OpenSSL Project Authors. All Rights Reserved.
+ * Copyright 2005 Nokia. All rights reserved.
+ *
+ * Licensed under the Apache License 2.0 (the "License"). You may not use
+ * this file except in compliance with the License. You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+/*
+ * This file is to enable backwards compatibility for the SRP features of
+ * s_client, s_server and ciphers. All of those features are deprecated and will
+ * eventually disappear. In the meantime, to continue to support them, we
+ * need to access deprecated SRP APIs.
+ */
+#define OPENSSL_SUPPRESS_DEPRECATED
+
+#include <openssl/bn.h>
+#include <openssl/bio.h>
+#include <openssl/ssl.h>
+#include <openssl/srp.h>
+#include "apps_ui.h"
+#include "apps.h"
+#include "s_apps.h"
+
+static int srp_Verify_N_and_g(const BIGNUM *N, const BIGNUM *g)
+{
+ BN_CTX *bn_ctx = BN_CTX_new();
+ BIGNUM *p = BN_new();
+ BIGNUM *r = BN_new();
+ int ret =
+ g != NULL && N != NULL && bn_ctx != NULL && BN_is_odd(N) &&
+ BN_check_prime(N, bn_ctx, NULL) == 1 &&
+ p != NULL && BN_rshift1(p, N) &&
+ /* p = (N-1)/2 */
+ BN_check_prime(p, bn_ctx, NULL) == 1 &&
+ r != NULL &&
+ /* verify g^((N-1)/2) == -1 (mod N) */
+ BN_mod_exp(r, g, p, N, bn_ctx) &&
+ BN_add_word(r, 1) && BN_cmp(r, N) == 0;
+
+ BN_free(r);
+ BN_free(p);
+ BN_CTX_free(bn_ctx);
+ return ret;
+}
+
+/*-
+ * This callback is used here for two purposes:
+ * - extended debugging
+ * - making some primality tests for unknown groups
+ * The callback is only called for a non default group.
+ *
+ * An application does not need the call back at all if
+ * only the standard groups are used. In real life situations,
+ * client and server already share well known groups,
+ * thus there is no need to verify them.
+ * Furthermore, in case that a server actually proposes a group that
+ * is not one of those defined in RFC 5054, it is more appropriate
+ * to add the group to a static list and then compare since
+ * primality tests are rather cpu consuming.
+ */
+
+static int ssl_srp_verify_param_cb(SSL *s, void *arg)
+{
+ SRP_ARG *srp_arg = (SRP_ARG *)arg;
+ BIGNUM *N = NULL, *g = NULL;
+
+ if (((N = SSL_get_srp_N(s)) == NULL) || ((g = SSL_get_srp_g(s)) == NULL))
+ return 0;
+ if (srp_arg->debug || srp_arg->msg || srp_arg->amp == 1) {
+ BIO_printf(bio_err, "SRP parameters:\n");
+ BIO_printf(bio_err, "\tN=");
+ BN_print(bio_err, N);
+ BIO_printf(bio_err, "\n\tg=");
+ BN_print(bio_err, g);
+ BIO_printf(bio_err, "\n");
+ }
+
+ if (SRP_check_known_gN_param(g, N))
+ return 1;
+
+ if (srp_arg->amp == 1) {
+ if (srp_arg->debug)
+ BIO_printf(bio_err,
+ "SRP param N and g are not known params, going to check deeper.\n");
+
+ /*
+ * The srp_moregroups is a real debugging feature. Implementors
+ * should rather add the value to the known ones. The minimal size
+ * has already been tested.
+ */
+ if (BN_num_bits(g) <= BN_BITS && srp_Verify_N_and_g(N, g))
+ return 1;
+ }
+ BIO_printf(bio_err, "SRP param N and g rejected.\n");
+ return 0;
+}
+
+#define PWD_STRLEN 1024
+
+static char *ssl_give_srp_client_pwd_cb(SSL *s, void *arg)
+{
+ SRP_ARG *srp_arg = (SRP_ARG *)arg;
+ char *pass = app_malloc(PWD_STRLEN + 1, "SRP password buffer");
+ PW_CB_DATA cb_tmp;
+ int l;
+
+ cb_tmp.password = (char *)srp_arg->srppassin;
+ cb_tmp.prompt_info = "SRP user";
+ if ((l = password_callback(pass, PWD_STRLEN, 0, &cb_tmp)) < 0) {
+ BIO_printf(bio_err, "Can't read Password\n");
+ OPENSSL_free(pass);
+ return NULL;
+ }
+ *(pass + l) = '\0';
+
+ return pass;
+}
+
+int set_up_srp_arg(SSL_CTX *ctx, SRP_ARG *srp_arg, int srp_lateuser, int c_msg,
+ int c_debug)
+{
+ if (!srp_lateuser && !SSL_CTX_set_srp_username(ctx, srp_arg->srplogin)) {
+ BIO_printf(bio_err, "Unable to set SRP username\n");
+ return 0;
+ }
+ srp_arg->msg = c_msg;
+ srp_arg->debug = c_debug;
+ SSL_CTX_set_srp_cb_arg(ctx, &srp_arg);
+ SSL_CTX_set_srp_client_pwd_callback(ctx, ssl_give_srp_client_pwd_cb);
+ SSL_CTX_set_srp_strength(ctx, srp_arg->strength);
+ if (c_msg || c_debug || srp_arg->amp == 0)
+ SSL_CTX_set_srp_verify_param_callback(ctx, ssl_srp_verify_param_cb);
+
+ return 1;
+}
+
+static char *dummy_srp(SSL *ssl, void *arg)
+{
+ return "";
+}
+
+void set_up_dummy_srp(SSL_CTX *ctx)
+{
+ SSL_CTX_set_srp_client_pwd_callback(ctx, dummy_srp);
+}
+
+/*
+ * This callback pretends to require some asynchronous logic in order to
+ * obtain a verifier. When the callback is called for a new connection we
+ * return with a negative value. This will provoke the accept etc to return
+ * with an LOOKUP_X509. The main logic of the reinvokes the suspended call
+ * (which would normally occur after a worker has finished) and we set the
+ * user parameters.
+ */
+static int ssl_srp_server_param_cb(SSL *s, int *ad, void *arg)
+{
+ srpsrvparm *p = (srpsrvparm *) arg;
+ int ret = SSL3_AL_FATAL;
+
+ if (p->login == NULL && p->user == NULL) {
+ p->login = SSL_get_srp_username(s);
+ BIO_printf(bio_err, "SRP username = \"%s\"\n", p->login);
+ return -1;
+ }
+
+ if (p->user == NULL) {
+ BIO_printf(bio_err, "User %s doesn't exist\n", p->login);
+ goto err;
+ }
+
+ if (SSL_set_srp_server_param
+ (s, p->user->N, p->user->g, p->user->s, p->user->v,
+ p->user->info) < 0) {
+ *ad = SSL_AD_INTERNAL_ERROR;
+ goto err;
+ }
+ BIO_printf(bio_err,
+ "SRP parameters set: username = \"%s\" info=\"%s\" \n",
+ p->login, p->user->info);
+ ret = SSL_ERROR_NONE;
+
+ err:
+ SRP_user_pwd_free(p->user);
+ p->user = NULL;
+ p->login = NULL;
+ return ret;
+}
+
+int set_up_srp_verifier_file(SSL_CTX *ctx, srpsrvparm *srp_callback_parm,
+ char *srpuserseed, char *srp_verifier_file)
+{
+ int ret;
+
+ srp_callback_parm->vb = SRP_VBASE_new(srpuserseed);
+ srp_callback_parm->user = NULL;
+ srp_callback_parm->login = NULL;
+
+ if (srp_callback_parm->vb == NULL) {
+ BIO_printf(bio_err, "Failed to initialize SRP verifier file \n");
+ return 0;
+ }
+ if ((ret =
+ SRP_VBASE_init(srp_callback_parm->vb,
+ srp_verifier_file)) != SRP_NO_ERROR) {
+ BIO_printf(bio_err,
+ "Cannot initialize SRP verifier file \"%s\":ret=%d\n",
+ srp_verifier_file, ret);
+ return 0;
+ }
+ SSL_CTX_set_verify(ctx, SSL_VERIFY_NONE, verify_callback);
+ SSL_CTX_set_srp_cb_arg(ctx, &srp_callback_parm);
+ SSL_CTX_set_srp_username_callback(ctx, ssl_srp_server_param_cb);
+
+ return 1;
+}
+
+void lookup_srp_user(srpsrvparm *srp_callback_parm, BIO *bio_s_out)
+{
+ SRP_user_pwd_free(srp_callback_parm->user);
+ srp_callback_parm->user = SRP_VBASE_get1_by_user(srp_callback_parm->vb,
+ srp_callback_parm->login);
+
+ if (srp_callback_parm->user != NULL)
+ BIO_printf(bio_s_out, "LOOKUP done %s\n",
+ srp_callback_parm->user->info);
+ else
+ BIO_printf(bio_s_out, "LOOKUP not successful\n");
+}
diff --git a/apps/lib/vms_decc_argv.c b/apps/lib/vms_decc_argv.c
new file mode 100644
index 000000000000..031e5afdeca0
--- /dev/null
+++ b/apps/lib/vms_decc_argv.c
@@ -0,0 +1,72 @@
+/*
+ * Copyright 2015-2021 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the Apache License 2.0 (the "License"). You may not use
+ * this file except in compliance with the License. You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include <stdlib.h>
+#include <openssl/crypto.h>
+#include "platform.h" /* for copy_argv() */
+
+char **newargv = NULL;
+
+static void cleanup_argv(void)
+{
+ OPENSSL_free(newargv);
+ newargv = NULL;
+}
+
+char **copy_argv(int *argc, char *argv[])
+{
+ /*-
+ * The note below is for historical purpose. On VMS now we always
+ * copy argv "safely."
+ *
+ * 2011-03-22 SMS.
+ * If we have 32-bit pointers everywhere, then we're safe, and
+ * we bypass this mess, as on non-VMS systems.
+ * Problem 1: Compaq/HP C before V7.3 always used 32-bit
+ * pointers for argv[].
+ * Fix 1: For a 32-bit argv[], when we're using 64-bit pointers
+ * everywhere else, we always allocate and use a 64-bit
+ * duplicate of argv[].
+ * Problem 2: Compaq/HP C V7.3 (Alpha, IA64) before ECO1 failed
+ * to NULL-terminate a 64-bit argv[]. (As this was written, the
+ * compiler ECO was available only on IA64.)
+ * Fix 2: Unless advised not to (VMS_TRUST_ARGV), we test a
+ * 64-bit argv[argc] for NULL, and, if necessary, use a
+ * (properly) NULL-terminated (64-bit) duplicate of argv[].
+ * The same code is used in either case to duplicate argv[].
+ * Some of these decisions could be handled in preprocessing,
+ * but the code tends to get even uglier, and the penalty for
+ * deciding at compile- or run-time is tiny.
+ */
+
+ int i, count = *argc;
+ char **p = newargv;
+
+ cleanup_argv();
+
+ /*
+ * We purposefully use OPENSSL_malloc() rather than app_malloc() here,
+ * to avoid symbol name clashes in test programs that would otherwise
+ * get them when linking with all of libapps.a.
+ * See comment in test/build.info.
+ */
+ newargv = OPENSSL_malloc(sizeof(*newargv) * (count + 1));
+ if (newargv == NULL)
+ return NULL;
+
+ /* Register automatic cleanup on first use */
+ if (p == NULL)
+ OPENSSL_atexit(cleanup_argv);
+
+ for (i = 0; i < count; i++)
+ newargv[i] = argv[i];
+ newargv[i] = NULL;
+ *argc = i;
+ return newargv;
+}
diff --git a/apps/lib/vms_term_sock.c b/apps/lib/vms_term_sock.c
new file mode 100644
index 000000000000..1a413376b20b
--- /dev/null
+++ b/apps/lib/vms_term_sock.c
@@ -0,0 +1,591 @@
+/*
+ * Copyright 2016-2022 The OpenSSL Project Authors. All Rights Reserved.
+ * Copyright 2016 VMS Software, Inc. All Rights Reserved.
+ *
+ * Licensed under the Apache License 2.0 (the "License"). You may not use
+ * this file except in compliance with the License. You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#ifdef __VMS
+# define OPENSSL_SYS_VMS
+# pragma message disable DOLLARID
+
+
+# include <openssl/opensslconf.h>
+
+# if !defined(_POSIX_C_SOURCE) && defined(OPENSSL_SYS_VMS)
+/*
+ * On VMS, you need to define this to get the declaration of fileno(). The
+ * value 2 is to make sure no function defined in POSIX-2 is left undefined.
+ */
+# define _POSIX_C_SOURCE 2
+# endif
+
+# include <stdio.h>
+
+# undef _POSIX_C_SOURCE
+
+# include <sys/types.h>
+# include <sys/socket.h>
+# include <netinet/in.h>
+# include <inet.h>
+# include <unistd.h>
+# include <string.h>
+# include <errno.h>
+# include <starlet.h>
+# include <iodef.h>
+# ifdef __alpha
+# include <iosbdef.h>
+# else
+typedef struct _iosb { /* Copied from IOSBDEF.H for Alpha */
+# pragma __nomember_alignment
+ __union {
+ __struct {
+ unsigned short int iosb$w_status; /* Final I/O status */
+ __union {
+ __struct { /* 16-bit byte count variant */
+ unsigned short int iosb$w_bcnt; /* 16-bit byte count */
+ __union {
+ unsigned int iosb$l_dev_depend; /* 32-bit device dependent info */
+ unsigned int iosb$l_pid; /* 32-bit pid */
+ } iosb$r_l;
+ } iosb$r_bcnt_16;
+ __struct { /* 32-bit byte count variant */
+ unsigned int iosb$l_bcnt; /* 32-bit byte count (unaligned) */
+ unsigned short int iosb$w_dev_depend_high; /* 16-bit device dependent info */
+ } iosb$r_bcnt_32;
+ } iosb$r_devdepend;
+ } iosb$r_io_64;
+ __struct {
+ __union {
+ unsigned int iosb$l_getxxi_status; /* Final GETxxI status */
+ unsigned int iosb$l_reg_status; /* Final $Registry status */
+ } iosb$r_l_status;
+ unsigned int iosb$l_reserved; /* Reserved field */
+ } iosb$r_get_64;
+ } iosb$r_io_get;
+} IOSB;
+
+# if !defined(__VAXC)
+# define iosb$w_status iosb$r_io_get.iosb$r_io_64.iosb$w_status
+# define iosb$w_bcnt iosb$r_io_get.iosb$r_io_64.iosb$r_devdepend.iosb$r_bcnt_16.iosb$w_bcnt
+# define iosb$r_l iosb$r_io_get.iosb$r_io_64.iosb$r_devdepend.iosb$r_bcnt_16.iosb$r_l
+# define iosb$l_dev_depend iosb$r_l.iosb$l_dev_depend
+# define iosb$l_pid iosb$r_l.iosb$l_pid
+# define iosb$l_bcnt iosb$r_io_get.iosb$r_io_64.iosb$r_devdepend.iosb$r_bcnt_32.iosb$l_bcnt
+# define iosb$w_dev_depend_high iosb$r_io_get.iosb$r_io_64.iosb$r_devdepend.iosb$r_bcnt_32.iosb$w_dev_depend_high
+# define iosb$l_getxxi_status iosb$r_io_get.iosb$r_get_64.iosb$r_l_status.iosb$l_getxxi_status
+# define iosb$l_reg_status iosb$r_io_get.iosb$r_get_64.iosb$r_l_status.iosb$l_reg_status
+# endif /* #if !defined(__VAXC) */
+
+# endif /* End of IOSBDEF */
+
+# include <efndef.h>
+# include <stdlib.h>
+# include <ssdef.h>
+# include <time.h>
+# include <stdarg.h>
+# include <descrip.h>
+
+# include "vms_term_sock.h"
+
+# ifdef __alpha
+static struct _iosb TerminalDeviceIosb;
+# else
+IOSB TerminalDeviceIosb;
+# endif
+
+static char TerminalDeviceBuff[255 + 2];
+static int TerminalSocketPair[2] = {0, 0};
+static unsigned short TerminalDeviceChan = 0;
+
+static int CreateSocketPair (int, int, int, int *);
+static void SocketPairTimeoutAst (int);
+static int TerminalDeviceAst (int);
+static void LogMessage (char *, ...);
+
+/*
+** Socket Pair Timeout Value (must be 0-59 seconds)
+*/
+# define SOCKET_PAIR_TIMEOUT_VALUE 20
+
+/*
+** Socket Pair Timeout Block which is passed to timeout AST
+*/
+typedef struct _SocketPairTimeoutBlock {
+ unsigned short SockChan1;
+ unsigned short SockChan2;
+} SPTB;
+
+# ifdef TERM_SOCK_TEST
+
+/*----------------------------------------------------------------------------*/
+/* */
+/*----------------------------------------------------------------------------*/
+int main (int argc, char *argv[], char *envp[])
+{
+ char TermBuff[80];
+ int TermSock,
+ status,
+ len;
+
+ LogMessage ("Enter 'q' or 'Q' to quit ...");
+ while (OPENSSL_strcasecmp (TermBuff, "Q")) {
+ /*
+ ** Create the terminal socket
+ */
+ status = TerminalSocket (TERM_SOCK_CREATE, &TermSock);
+ if (status != TERM_SOCK_SUCCESS)
+ exit (1);
+
+ /*
+ ** Process the terminal input
+ */
+ LogMessage ("Waiting on terminal I/O ...\n");
+ len = recv (TermSock, TermBuff, sizeof(TermBuff), 0) ;
+ TermBuff[len] = '\0';
+ LogMessage ("Received terminal I/O [%s]", TermBuff);
+
+ /*
+ ** Delete the terminal socket
+ */
+ status = TerminalSocket (TERM_SOCK_DELETE, &TermSock);
+ if (status != TERM_SOCK_SUCCESS)
+ exit (1);
+ }
+
+ return 1;
+
+}
+# endif
+
+/*----------------------------------------------------------------------------*/
+/* */
+/*----------------------------------------------------------------------------*/
+int TerminalSocket (int FunctionCode, int *ReturnSocket)
+{
+ int status;
+ $DESCRIPTOR (TerminalDeviceDesc, "SYS$COMMAND");
+
+ /*
+ ** Process the requested function code
+ */
+ switch (FunctionCode) {
+ case TERM_SOCK_CREATE:
+ /*
+ ** Create a socket pair
+ */
+ status = CreateSocketPair (AF_INET, SOCK_STREAM, 0, TerminalSocketPair);
+ if (status == -1) {
+ LogMessage ("TerminalSocket: CreateSocketPair () - %08X", status);
+ if (TerminalSocketPair[0])
+ close (TerminalSocketPair[0]);
+ if (TerminalSocketPair[1])
+ close (TerminalSocketPair[1]);
+ return TERM_SOCK_FAILURE;
+ }
+
+ /*
+ ** Assign a channel to the terminal device
+ */
+ status = sys$assign (&TerminalDeviceDesc,
+ &TerminalDeviceChan,
+ 0, 0, 0);
+ if (! (status & 1)) {
+ LogMessage ("TerminalSocket: SYS$ASSIGN () - %08X", status);
+ close (TerminalSocketPair[0]);
+ close (TerminalSocketPair[1]);
+ return TERM_SOCK_FAILURE;
+ }
+
+ /*
+ ** Queue an async IO to the terminal device
+ */
+ status = sys$qio (EFN$C_ENF,
+ TerminalDeviceChan,
+ IO$_READVBLK,
+ &TerminalDeviceIosb,
+ TerminalDeviceAst,
+ 0,
+ TerminalDeviceBuff,
+ sizeof(TerminalDeviceBuff) - 2,
+ 0, 0, 0, 0);
+ if (! (status & 1)) {
+ LogMessage ("TerminalSocket: SYS$QIO () - %08X", status);
+ close (TerminalSocketPair[0]);
+ close (TerminalSocketPair[1]);
+ return TERM_SOCK_FAILURE;
+ }
+
+ /*
+ ** Return the input side of the socket pair
+ */
+ *ReturnSocket = TerminalSocketPair[1];
+ break;
+
+ case TERM_SOCK_DELETE:
+ /*
+ ** Cancel any pending IO on the terminal channel
+ */
+ status = sys$cancel (TerminalDeviceChan);
+ if (! (status & 1)) {
+ LogMessage ("TerminalSocket: SYS$CANCEL () - %08X", status);
+ close (TerminalSocketPair[0]);
+ close (TerminalSocketPair[1]);
+ return TERM_SOCK_FAILURE;
+ }
+
+ /*
+ ** Deassign the terminal channel
+ */
+ status = sys$dassgn (TerminalDeviceChan);
+ if (! (status & 1)) {
+ LogMessage ("TerminalSocket: SYS$DASSGN () - %08X", status);
+ close (TerminalSocketPair[0]);
+ close (TerminalSocketPair[1]);
+ return TERM_SOCK_FAILURE;
+ }
+
+ /*
+ ** Close the terminal socket pair
+ */
+ close (TerminalSocketPair[0]);
+ close (TerminalSocketPair[1]);
+
+ /*
+ ** Return the initialized socket
+ */
+ *ReturnSocket = 0;
+ break;
+
+ default:
+ /*
+ ** Invalid function code
+ */
+ LogMessage ("TerminalSocket: Invalid Function Code - %d", FunctionCode);
+ return TERM_SOCK_FAILURE;
+ break;
+ }
+
+ /*
+ ** Return success
+ */
+ return TERM_SOCK_SUCCESS;
+
+}
+
+/*----------------------------------------------------------------------------*/
+/* */
+/*----------------------------------------------------------------------------*/
+static int CreateSocketPair (int SocketFamily,
+ int SocketType,
+ int SocketProtocol,
+ int *SocketPair)
+{
+ struct dsc$descriptor AscTimeDesc = {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, NULL};
+ static const char* LocalHostAddr = {"127.0.0.1"};
+ unsigned short TcpAcceptChan = 0,
+ TcpDeviceChan = 0;
+ unsigned long BinTimeBuff[2];
+ struct sockaddr_in sin;
+ char AscTimeBuff[32];
+ short LocalHostPort;
+ int status;
+ unsigned int slen;
+
+# ifdef __alpha
+ struct _iosb iosb;
+# else
+ IOSB iosb;
+# endif
+
+ int SockDesc1 = 0,
+ SockDesc2 = 0;
+ SPTB sptb;
+ $DESCRIPTOR (TcpDeviceDesc, "TCPIP$DEVICE");
+
+ /*
+ ** Create a socket
+ */
+ SockDesc1 = socket (SocketFamily, SocketType, 0);
+ if (SockDesc1 < 0) {
+ LogMessage ("CreateSocketPair: socket () - %d", errno);
+ return -1;
+ }
+
+ /*
+ ** Initialize the socket information
+ */
+ slen = sizeof(sin);
+ memset ((char *) &sin, 0, slen);
+ sin.sin_family = SocketFamily;
+ sin.sin_addr.s_addr = inet_addr (LocalHostAddr);
+ sin.sin_port = 0;
+
+ /*
+ ** Bind the socket to the local IP
+ */
+ status = bind (SockDesc1, (struct sockaddr *) &sin, slen);
+ if (status < 0) {
+ LogMessage ("CreateSocketPair: bind () - %d", errno);
+ close (SockDesc1);
+ return -1;
+ }
+
+ /*
+ ** Get the socket name so we can save the port number
+ */
+ status = getsockname (SockDesc1, (struct sockaddr *) &sin, &slen);
+ if (status < 0) {
+ LogMessage ("CreateSocketPair: getsockname () - %d", errno);
+ close (SockDesc1);
+ return -1;
+ } else
+ LocalHostPort = sin.sin_port;
+
+ /*
+ ** Setup a listen for the socket
+ */
+ listen (SockDesc1, 5);
+
+ /*
+ ** Get the binary (64-bit) time of the specified timeout value
+ */
+ BIO_snprintf(AscTimeBuff, sizeof(AscTimeBuff), "0 0:0:%02d.00", SOCKET_PAIR_TIMEOUT_VALUE);
+ AscTimeDesc.dsc$w_length = strlen (AscTimeBuff);
+ AscTimeDesc.dsc$a_pointer = AscTimeBuff;
+ status = sys$bintim (&AscTimeDesc, BinTimeBuff);
+ if (! (status & 1)) {
+ LogMessage ("CreateSocketPair: SYS$BINTIM () - %08X", status);
+ close (SockDesc1);
+ return -1;
+ }
+
+ /*
+ ** Assign another channel to the TCP/IP device for the accept.
+ ** This is the channel that ends up being connected to.
+ */
+ status = sys$assign (&TcpDeviceDesc, &TcpDeviceChan, 0, 0, 0);
+ if (! (status & 1)) {
+ LogMessage ("CreateSocketPair: SYS$ASSIGN () - %08X", status);
+ close (SockDesc1);
+ return -1;
+ }
+
+ /*
+ ** Get the channel of the first socket for the accept
+ */
+ TcpAcceptChan = decc$get_sdc (SockDesc1);
+
+ /*
+ ** Perform the accept using $QIO so we can do this asynchronously
+ */
+ status = sys$qio (EFN$C_ENF,
+ TcpAcceptChan,
+ IO$_ACCESS | IO$M_ACCEPT,
+ &iosb,
+ 0, 0, 0, 0, 0,
+ &TcpDeviceChan,
+ 0, 0);
+ if (! (status & 1)) {
+ LogMessage ("CreateSocketPair: SYS$QIO () - %08X", status);
+ close (SockDesc1);
+ sys$dassgn (TcpDeviceChan);
+ return -1;
+ }
+
+ /*
+ ** Create the second socket to do the connect
+ */
+ SockDesc2 = socket (SocketFamily, SocketType, 0);
+ if (SockDesc2 < 0) {
+ LogMessage ("CreateSocketPair: socket () - %d", errno);
+ sys$cancel (TcpAcceptChan);
+ close (SockDesc1);
+ sys$dassgn (TcpDeviceChan);
+ return (-1) ;
+ }
+
+ /*
+ ** Setup the Socket Pair Timeout Block
+ */
+ sptb.SockChan1 = TcpAcceptChan;
+ sptb.SockChan2 = decc$get_sdc (SockDesc2);
+
+ /*
+ ** Before we block on the connect, set a timer that can cancel I/O on our
+ ** two sockets if it never connects.
+ */
+ status = sys$setimr (EFN$C_ENF,
+ BinTimeBuff,
+ SocketPairTimeoutAst,
+ &sptb,
+ 0);
+ if (! (status & 1)) {
+ LogMessage ("CreateSocketPair: SYS$SETIMR () - %08X", status);
+ sys$cancel (TcpAcceptChan);
+ close (SockDesc1);
+ close (SockDesc2);
+ sys$dassgn (TcpDeviceChan);
+ return -1;
+ }
+
+ /*
+ ** Now issue the connect
+ */
+ memset ((char *) &sin, 0, sizeof(sin)) ;
+ sin.sin_family = SocketFamily;
+ sin.sin_addr.s_addr = inet_addr (LocalHostAddr) ;
+ sin.sin_port = LocalHostPort ;
+
+ status = connect (SockDesc2, (struct sockaddr *) &sin, sizeof(sin));
+ if (status < 0 ) {
+ LogMessage ("CreateSocketPair: connect () - %d", errno);
+ sys$cantim (&sptb, 0);
+ sys$cancel (TcpAcceptChan);
+ close (SockDesc1);
+ close (SockDesc2);
+ sys$dassgn (TcpDeviceChan);
+ return -1;
+ }
+
+ /*
+ ** Wait for the asynch $QIO to finish. Note that if the I/O was aborted
+ ** (SS$_ABORT), then we probably canceled it from the AST routine - so log
+ ** a timeout.
+ */
+ status = sys$synch (EFN$C_ENF, &iosb);
+ if (! (iosb.iosb$w_status & 1)) {
+ if (iosb.iosb$w_status == SS$_ABORT)
+ LogMessage ("CreateSocketPair: SYS$QIO(iosb) timeout");
+ else {
+ LogMessage ("CreateSocketPair: SYS$QIO(iosb) - %d",
+ iosb.iosb$w_status);
+ sys$cantim (&sptb, 0);
+ }
+ close (SockDesc1);
+ close (SockDesc2);
+ sys$dassgn (TcpDeviceChan);
+ return -1;
+ }
+
+ /*
+ ** Here we're successfully connected, so cancel the timer, convert the
+ ** I/O channel to a socket fd, close the listener socket and return the
+ ** connected pair.
+ */
+ sys$cantim (&sptb, 0);
+
+ close (SockDesc1) ;
+ SocketPair[0] = SockDesc2 ;
+ SocketPair[1] = socket_fd (TcpDeviceChan);
+
+ return (0) ;
+
+}
+
+/*----------------------------------------------------------------------------*/
+/* */
+/*----------------------------------------------------------------------------*/
+static void SocketPairTimeoutAst (int astparm)
+{
+ SPTB *sptb = (SPTB *) astparm;
+
+ sys$cancel (sptb->SockChan2); /* Cancel the connect() */
+ sys$cancel (sptb->SockChan1); /* Cancel the accept() */
+
+ return;
+
+}
+
+/*----------------------------------------------------------------------------*/
+/* */
+/*----------------------------------------------------------------------------*/
+static int TerminalDeviceAst (int astparm)
+{
+ int status;
+
+ /*
+ ** Terminate the terminal buffer
+ */
+ TerminalDeviceBuff[TerminalDeviceIosb.iosb$w_bcnt] = '\0';
+ strcat (TerminalDeviceBuff, "\n");
+
+ /*
+ ** Send the data read from the terminal device through the socket pair
+ */
+ send (TerminalSocketPair[0], TerminalDeviceBuff,
+ TerminalDeviceIosb.iosb$w_bcnt + 1, 0);
+
+ /*
+ ** Queue another async IO to the terminal device
+ */
+ status = sys$qio (EFN$C_ENF,
+ TerminalDeviceChan,
+ IO$_READVBLK,
+ &TerminalDeviceIosb,
+ TerminalDeviceAst,
+ 0,
+ TerminalDeviceBuff,
+ sizeof(TerminalDeviceBuff) - 1,
+ 0, 0, 0, 0);
+
+ /*
+ ** Return status
+ */
+ return status;
+
+}
+
+/*----------------------------------------------------------------------------*/
+/* */
+/*----------------------------------------------------------------------------*/
+static void LogMessage (char *msg, ...)
+{
+ char *Month[] = {"Jan", "Feb", "Mar", "Apr", "May", "Jun",
+ "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"};
+ static unsigned int pid = 0;
+ va_list args;
+ time_t CurTime;
+ struct tm *LocTime;
+ char MsgBuff[256];
+
+ /*
+ ** Get the process pid
+ */
+ if (pid == 0)
+ pid = getpid ();
+
+ /*
+ ** Convert the current time into local time
+ */
+ CurTime = time (NULL);
+ LocTime = localtime (&CurTime);
+
+ /*
+ ** Format the message buffer
+ */
+ BIO_snprintf(MsgBuff, sizeof(MsgBuff), "%02d-%s-%04d %02d:%02d:%02d [%08X] %s\n",
+ LocTime->tm_mday, Month[LocTime->tm_mon],
+ (LocTime->tm_year + 1900), LocTime->tm_hour, LocTime->tm_min,
+ LocTime->tm_sec, pid, msg);
+
+ /*
+ ** Get any variable arguments and add them to the print of the message
+ ** buffer
+ */
+ va_start (args, msg);
+ vfprintf (stderr, MsgBuff, args);
+ va_end (args);
+
+ /*
+ ** Flush standard error output
+ */
+ fsync (fileno (stderr));
+
+ return;
+
+}
+#endif
diff --git a/apps/lib/win32_init.c b/apps/lib/win32_init.c
new file mode 100644
index 000000000000..6d2be0c62942
--- /dev/null
+++ b/apps/lib/win32_init.c
@@ -0,0 +1,307 @@
+/*
+ * Copyright 2016-2020 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the Apache License 2.0 (the "License"). You may not use
+ * this file except in compliance with the License. You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include <windows.h>
+#include <stdlib.h>
+#include <string.h>
+#include <malloc.h>
+
+#if defined(CP_UTF8)
+
+static UINT saved_cp;
+static int newargc;
+static char **newargv;
+
+static void cleanup(void)
+{
+ int i;
+
+ SetConsoleOutputCP(saved_cp);
+
+ for (i = 0; i < newargc; i++)
+ free(newargv[i]);
+
+ free(newargv);
+}
+
+/*
+ * Incrementally [re]allocate newargv and keep it NULL-terminated.
+ */
+static int validate_argv(int argc)
+{
+ static int size = 0;
+
+ if (argc >= size) {
+ char **ptr;
+
+ while (argc >= size)
+ size += 64;
+
+ ptr = realloc(newargv, size * sizeof(newargv[0]));
+ if (ptr == NULL)
+ return 0;
+
+ (newargv = ptr)[argc] = NULL;
+ } else {
+ newargv[argc] = NULL;
+ }
+
+ return 1;
+}
+
+static int process_glob(WCHAR *wstr, int wlen)
+{
+ int i, slash, udlen;
+ WCHAR saved_char;
+ WIN32_FIND_DATAW data;
+ HANDLE h;
+
+ /*
+ * Note that we support wildcard characters only in filename part
+ * of the path, and not in directories. Windows users are used to
+ * this, that's why recursive glob processing is not implemented.
+ */
+ /*
+ * Start by looking for last slash or backslash, ...
+ */
+ for (slash = 0, i = 0; i < wlen; i++)
+ if (wstr[i] == L'/' || wstr[i] == L'\\')
+ slash = i + 1;
+ /*
+ * ... then look for asterisk or question mark in the file name.
+ */
+ for (i = slash; i < wlen; i++)
+ if (wstr[i] == L'*' || wstr[i] == L'?')
+ break;
+
+ if (i == wlen)
+ return 0; /* definitely not a glob */
+
+ saved_char = wstr[wlen];
+ wstr[wlen] = L'\0';
+ h = FindFirstFileW(wstr, &data);
+ wstr[wlen] = saved_char;
+ if (h == INVALID_HANDLE_VALUE)
+ return 0; /* not a valid glob, just pass... */
+
+ if (slash)
+ udlen = WideCharToMultiByte(CP_UTF8, 0, wstr, slash,
+ NULL, 0, NULL, NULL);
+ else
+ udlen = 0;
+
+ do {
+ int uflen;
+ char *arg;
+
+ /*
+ * skip over . and ..
+ */
+ if (data.cFileName[0] == L'.') {
+ if ((data.cFileName[1] == L'\0') ||
+ (data.cFileName[1] == L'.' && data.cFileName[2] == L'\0'))
+ continue;
+ }
+
+ if (!validate_argv(newargc + 1))
+ break;
+
+ /*
+ * -1 below means "scan for trailing '\0' *and* count it",
+ * so that |uflen| covers even trailing '\0'.
+ */
+ uflen = WideCharToMultiByte(CP_UTF8, 0, data.cFileName, -1,
+ NULL, 0, NULL, NULL);
+
+ arg = malloc(udlen + uflen);
+ if (arg == NULL)
+ break;
+
+ if (udlen)
+ WideCharToMultiByte(CP_UTF8, 0, wstr, slash,
+ arg, udlen, NULL, NULL);
+
+ WideCharToMultiByte(CP_UTF8, 0, data.cFileName, -1,
+ arg + udlen, uflen, NULL, NULL);
+
+ newargv[newargc++] = arg;
+ } while (FindNextFileW(h, &data));
+
+ CloseHandle(h);
+
+ return 1;
+}
+
+void win32_utf8argv(int *argc, char **argv[])
+{
+ const WCHAR *wcmdline;
+ WCHAR *warg, *wend, *p;
+ int wlen, ulen, valid = 1;
+ char *arg;
+
+ if (GetEnvironmentVariableW(L"OPENSSL_WIN32_UTF8", NULL, 0) == 0)
+ return;
+
+ newargc = 0;
+ newargv = NULL;
+ if (!validate_argv(newargc))
+ return;
+
+ wcmdline = GetCommandLineW();
+ if (wcmdline == NULL) return;
+
+ /*
+ * make a copy of the command line, since we might have to modify it...
+ */
+ wlen = wcslen(wcmdline);
+ p = _alloca((wlen + 1) * sizeof(WCHAR));
+ wcscpy(p, wcmdline);
+
+ while (*p != L'\0') {
+ int in_quote = 0;
+
+ if (*p == L' ' || *p == L'\t') {
+ p++; /* skip over whitespace */
+ continue;
+ }
+
+ /*
+ * Note: because we may need to fiddle with the number of backslashes,
+ * the argument string is copied into itself. This is safe because
+ * the number of characters will never expand.
+ */
+ warg = wend = p;
+ while (*p != L'\0'
+ && (in_quote || (*p != L' ' && *p != L'\t'))) {
+ switch (*p) {
+ case L'\\':
+ /*
+ * Microsoft documentation on how backslashes are treated
+ * is:
+ *
+ * + Backslashes are interpreted literally, unless they
+ * immediately precede a double quotation mark.
+ * + If an even number of backslashes is followed by a double
+ * quotation mark, one backslash is placed in the argv array
+ * for every pair of backslashes, and the double quotation
+ * mark is interpreted as a string delimiter.
+ * + If an odd number of backslashes is followed by a double
+ * quotation mark, one backslash is placed in the argv array
+ * for every pair of backslashes, and the double quotation
+ * mark is "escaped" by the remaining backslash, causing a
+ * literal double quotation mark (") to be placed in argv.
+ *
+ * Ref: https://msdn.microsoft.com/en-us/library/17w5ykft.aspx
+ *
+ * Though referred page doesn't mention it, multiple qouble
+ * quotes are also special. Pair of double quotes in quoted
+ * string is counted as single double quote.
+ */
+ {
+ const WCHAR *q = p;
+ int i;
+
+ while (*p == L'\\')
+ p++;
+
+ if (*p == L'"') {
+ int i;
+
+ for (i = (p - q) / 2; i > 0; i--)
+ *wend++ = L'\\';
+
+ /*
+ * if odd amount of backslashes before the quote,
+ * said quote is part of the argument, not a delimiter
+ */
+ if ((p - q) % 2 == 1)
+ *wend++ = *p++;
+ } else {
+ for (i = p - q; i > 0; i--)
+ *wend++ = L'\\';
+ }
+ }
+ break;
+ case L'"':
+ /*
+ * Without the preceding backslash (or when preceded with an
+ * even number of backslashes), the double quote is a simple
+ * string delimiter and just slightly change the parsing state
+ */
+ if (in_quote && p[1] == L'"')
+ *wend++ = *p++;
+ else
+ in_quote = !in_quote;
+ p++;
+ break;
+ default:
+ /*
+ * Any other non-delimiter character is just taken verbatim
+ */
+ *wend++ = *p++;
+ }
+ }
+
+ wlen = wend - warg;
+
+ if (wlen == 0 || !process_glob(warg, wlen)) {
+ if (!validate_argv(newargc + 1)) {
+ valid = 0;
+ break;
+ }
+
+ ulen = 0;
+ if (wlen > 0) {
+ ulen = WideCharToMultiByte(CP_UTF8, 0, warg, wlen,
+ NULL, 0, NULL, NULL);
+ if (ulen <= 0)
+ continue;
+ }
+
+ arg = malloc(ulen + 1);
+ if (arg == NULL) {
+ valid = 0;
+ break;
+ }
+
+ if (wlen > 0)
+ WideCharToMultiByte(CP_UTF8, 0, warg, wlen,
+ arg, ulen, NULL, NULL);
+ arg[ulen] = '\0';
+
+ newargv[newargc++] = arg;
+ }
+ }
+
+ if (valid) {
+ saved_cp = GetConsoleOutputCP();
+ SetConsoleOutputCP(CP_UTF8);
+
+ *argc = newargc;
+ *argv = newargv;
+
+ atexit(cleanup);
+ } else if (newargv != NULL) {
+ int i;
+
+ for (i = 0; i < newargc; i++)
+ free(newargv[i]);
+
+ free(newargv);
+
+ newargc = 0;
+ newargv = NULL;
+ }
+
+ return;
+}
+#else
+void win32_utf8argv(int *argc, char **argv[])
+{ return; }
+#endif