summaryrefslogtreecommitdiff
path: root/apps/lib
diff options
context:
space:
mode:
authorRichard Levitte <levitte@openssl.org>2019-08-28 22:27:47 +0200
committerRichard Levitte <levitte@openssl.org>2019-09-04 17:26:07 +0200
commit2ad75c6c1341db8a3122bbeaf863440057d71307 (patch)
treefbcb1a16b4410414083f21ddaa92aa8e450a0baa /apps/lib
parent1e8e75d18be8856e753a57771754b9926c3f4264 (diff)
downloadopenssl-new-2ad75c6c1341db8a3122bbeaf863440057d71307.tar.gz
Move libapps.a source to apps/lib
This makes it clearer what's what. The 'openssl' application and its sub-commands remain in apps/ Reviewed-by: Tomas Mraz <tmraz@fedoraproject.org> (Merged from https://github.com/openssl/openssl/pull/9723)
Diffstat (limited to 'apps/lib')
-rw-r--r--apps/lib/app_rand.c93
-rw-r--r--apps/lib/apps.c2618
-rw-r--r--apps/lib/apps_ui.c197
-rw-r--r--apps/lib/bf_prefix.c177
-rw-r--r--apps/lib/build.info18
-rw-r--r--apps/lib/columns.c27
-rw-r--r--apps/lib/fmt.c15
-rw-r--r--apps/lib/opt.c955
-rw-r--r--apps/lib/s_cb.c1521
-rw-r--r--apps/lib/s_socket.c395
-rw-r--r--apps/lib/vms_decc_argv.c67
-rw-r--r--apps/lib/vms_term_sock.c591
-rw-r--r--apps/lib/win32_init.c307
13 files changed, 6981 insertions, 0 deletions
diff --git a/apps/lib/app_rand.c b/apps/lib/app_rand.c
new file mode 100644
index 0000000000..1861343a9c
--- /dev/null
+++ b/apps/lib/app_rand.c
@@ -0,0 +1,93 @@
+/*
+ * Copyright 1995-2018 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;
+
+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);
+}
+
+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;
+}
+
+void app_RAND_write(void)
+{
+ if (save_rand_file == NULL)
+ return;
+ if (RAND_write_file(save_rand_file) == -1) {
+ BIO_printf(bio_err, "Cannot write random bytes:\n");
+ ERR_print_errors(bio_err);
+ }
+ OPENSSL_free(save_rand_file);
+ save_rand_file = NULL;
+}
+
+
+/*
+ * 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:
+ return loadfiles(opt_arg());
+ break;
+ case OPT_R_WRITERAND:
+ OPENSSL_free(save_rand_file);
+ save_rand_file = OPENSSL_strdup(opt_arg());
+ break;
+ }
+ return 1;
+}
diff --git a/apps/lib/apps.c b/apps/lib/apps.c
new file mode 100644
index 0000000000..5038817750
--- /dev/null
+++ b/apps/lib/apps.c
@@ -0,0 +1,2618 @@
+/*
+ * Copyright 1995-2018 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
+
+#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/pem.h>
+#include <openssl/pkcs12.h>
+#include <openssl/ui.h>
+#include <openssl/safestack.h>
+#ifndef OPENSSL_NO_ENGINE
+# include <openssl/engine.h>
+#endif
+#ifndef OPENSSL_NO_RSA
+# include <openssl/rsa.h>
+#endif
+#include <openssl/bn.h>
+#include <openssl/ssl.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)
+# define _kbhit kbhit
+#endif
+
+#define PASS_SOURCE_SIZE_MAX 4
+
+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);
+
+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)
+ 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,
+ const char *CApath, int noCAfile, int noCApath)
+{
+ if (CAfile == NULL && CApath == 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;
+
+ return 1;
+ }
+ return SSL_CTX_load_verify_locations(ctx, CAfile, CApath);
+}
+
+#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;
+}
+
+int dump_cert_text(BIO *out, X509 *x)
+{
+ print_name(out, "subject=", X509_get_subject_name(x), get_nameopt());
+ BIO_puts(out, "\n");
+ print_name(out, "issuer=", X509_get_issuer_name(x), get_nameopt());
+ BIO_puts(out, "\n");
+
+ return 0;
+}
+
+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);
+
+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) {
+ 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());
+ pwdbio = BIO_push(btmp, pwdbio);
+#endif
+ } else if (strcmp(arg, "stdin") == 0) {
+ pwdbio = dup_bio_in(FORMAT_TEXT);
+ if (!pwdbio) {
+ 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(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(const char *filename)
+{
+ BIO *in;
+ CONF *conf;
+
+ in = bio_open_default(filename, 'r', FORMAT_TEXT);
+ if (in == NULL)
+ return NULL;
+
+ conf = app_load_config_bio(in, filename);
+ BIO_free(in);
+ return conf;
+}
+
+CONF *app_load_config_quiet(const char *filename)
+{
+ BIO *in;
+ CONF *conf;
+
+ in = bio_open_default_quiet(filename, 'r', FORMAT_TEXT);
+ if (in == NULL)
+ return NULL;
+
+ conf = app_load_config_bio(in, filename);
+ BIO_free(in);
+ 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;
+}
+
+static int load_pkcs12(BIO *in, const char *desc,
+ pem_password_cb *pem_cb, PW_CB_DATA *cb_data,
+ EVP_PKEY **pkey, X509 **cert, STACK_OF(X509) **ca)
+{
+ const char *pass;
+ char tpass[PEM_BUFSIZE];
+ int len, ret = 0;
+ PKCS12 *p12;
+ p12 = d2i_PKCS12_bio(in, NULL);
+ if (p12 == NULL) {
+ BIO_printf(bio_err, "Error loading PKCS12 file for %s\n", desc);
+ goto die;
+ }
+ /* See if an empty password will do */
+ if (PKCS12_verify_mac(p12, "", 0) || PKCS12_verify_mac(p12, NULL, 0)) {
+ pass = "";
+ } else {
+ if (!pem_cb)
+ pem_cb = (pem_password_cb *)password_callback;
+ len = pem_cb(tpass, PEM_BUFSIZE, 0, cb_data);
+ if (len < 0) {
+ BIO_printf(bio_err, "Passphrase callback error for %s\n", desc);
+ goto die;
+ }
+ if (len < PEM_BUFSIZE)
+ tpass[len] = 0;
+ if (!PKCS12_verify_mac(p12, tpass, len)) {
+ BIO_printf(bio_err,
+ "Mac verify error (wrong password?) in PKCS12 file for %s\n",
+ desc);
+ goto die;
+ }
+ pass = tpass;
+ }
+ ret = PKCS12_parse(p12, pass, pkey, cert, ca);
+ die:
+ PKCS12_free(p12);
+ return ret;
+}
+
+#if !defined(OPENSSL_NO_OCSP) && !defined(OPENSSL_NO_SOCK)
+static int load_cert_crl_http(const char *url, X509 **pcert, X509_CRL **pcrl)
+{
+ char *host = NULL, *port = NULL, *path = NULL;
+ BIO *bio = NULL;
+ OCSP_REQ_CTX *rctx = NULL;
+ int use_ssl, rv = 0;
+ if (!OCSP_parse_url(url, &host, &port, &path, &use_ssl))
+ goto err;
+ if (use_ssl) {
+ BIO_puts(bio_err, "https not supported\n");
+ goto err;
+ }
+ bio = BIO_new_connect(host);
+ if (!bio || !BIO_set_conn_port(bio, port))
+ goto err;
+ rctx = OCSP_REQ_CTX_new(bio, 1024);
+ if (rctx == NULL)
+ goto err;
+ if (!OCSP_REQ_CTX_http(rctx, "GET", path))
+ goto err;
+ if (!OCSP_REQ_CTX_add1_header(rctx, "Host", host))
+ goto err;
+ if (pcert) {
+ do {
+ rv = X509_http_nbio(rctx, pcert);
+ } while (rv == -1);
+ } else {
+ do {
+ rv = X509_CRL_http_nbio(rctx, pcrl);
+ } while (rv == -1);
+ }
+
+ err:
+ OPENSSL_free(host);
+ OPENSSL_free(path);
+ OPENSSL_free(port);
+ BIO_free_all(bio);
+ OCSP_REQ_CTX_free(rctx);
+ if (rv != 1) {
+ BIO_printf(bio_err, "Error loading %s from %s\n",
+ pcert ? "certificate" : "CRL", url);
+ ERR_print_errors(bio_err);
+ }
+ return rv;
+}
+#endif
+
+X509 *load_cert(const char *file, int format, const char *cert_descrip)
+{
+ X509 *x = NULL;
+ BIO *cert;
+
+ if (format == FORMAT_HTTP) {
+#if !defined(OPENSSL_NO_OCSP) && !defined(OPENSSL_NO_SOCK)
+ load_cert_crl_http(file, &x, NULL);
+#endif
+ return x;
+ }
+
+ if (file == NULL) {
+ unbuffer(stdin);
+ cert = dup_bio_in(format);
+ } else {
+ cert = bio_open_default(file, 'r', format);
+ }
+ if (cert == NULL)
+ goto end;
+
+ if (format == FORMAT_ASN1) {
+ x = d2i_X509_bio(cert, NULL);
+ } else if (format == FORMAT_PEM) {
+ x = PEM_read_bio_X509_AUX(cert, NULL,
+ (pem_password_cb *)password_callback, NULL);
+ } else if (format == FORMAT_PKCS12) {
+ if (!load_pkcs12(cert, cert_descrip, NULL, NULL, NULL, &x, NULL))
+ goto end;
+ } else {
+ BIO_printf(bio_err, "bad input format specified for %s\n", cert_descrip);
+ goto end;
+ }
+ end:
+ if (x == NULL) {
+ BIO_printf(bio_err, "unable to load certificate\n");
+ ERR_print_errors(bio_err);
+ }
+ BIO_free(cert);
+ return x;
+}
+
+X509_CRL *load_crl(const char *infile, int format)
+{
+ X509_CRL *x = NULL;
+ BIO *in = NULL;
+
+ if (format == FORMAT_HTTP) {
+#if !defined(OPENSSL_NO_OCSP) && !defined(OPENSSL_NO_SOCK)
+ load_cert_crl_http(infile, NULL, &x);
+#endif
+ return x;
+ }
+
+ in = bio_open_default(infile, 'r', format);
+ if (in == NULL)
+ goto end;
+ if (format == FORMAT_ASN1) {
+ x = d2i_X509_CRL_bio(in, NULL);
+ } else if (format == FORMAT_PEM) {
+ x = PEM_read_bio_X509_CRL(in, NULL, NULL, NULL);
+ } else {
+ BIO_printf(bio_err, "bad input format specified for input crl\n");
+ goto end;
+ }
+ if (x == NULL) {
+ BIO_printf(bio_err, "unable to load CRL\n");
+ ERR_print_errors(bio_err);
+ goto end;
+ }
+
+ end:
+ BIO_free(in);
+ return x;
+}
+
+EVP_PKEY *load_key(const char *file, int format, int maybe_stdin,
+ const char *pass, ENGINE *e, const char *key_descrip)
+{
+ BIO *key = NULL;
+ EVP_PKEY *pkey = NULL;
+ PW_CB_DATA cb_data;
+
+ cb_data.password = pass;
+ cb_data.prompt_info = file;
+
+ if (file == NULL && (!maybe_stdin || format == FORMAT_ENGINE)) {
+ BIO_printf(bio_err, "no keyfile specified\n");
+ goto end;
+ }
+ if (format == FORMAT_ENGINE) {
+ if (e == NULL) {
+ BIO_printf(bio_err, "no engine specified\n");
+ } else {
+#ifndef OPENSSL_NO_ENGINE
+ if (ENGINE_init(e)) {
+ pkey = ENGINE_load_private_key(e, file,
+ (UI_METHOD *)get_ui_method(),
+ &cb_data);
+ ENGINE_finish(e);
+ }
+ if (pkey == NULL) {
+ BIO_printf(bio_err, "cannot load %s from engine\n", key_descrip);
+ ERR_print_errors(bio_err);
+ }
+#else
+ BIO_printf(bio_err, "engines not supported\n");
+#endif
+ }
+ goto end;
+ }
+ if (file == NULL && maybe_stdin) {
+ unbuffer(stdin);
+ key = dup_bio_in(format);
+ } else {
+ key = bio_open_default(file, 'r', format);
+ }
+ if (key == NULL)
+ goto end;
+ if (format == FORMAT_ASN1) {
+ pkey = d2i_PrivateKey_bio(key, NULL);
+ } else if (format == FORMAT_PEM) {
+ pkey = PEM_read_bio_PrivateKey(key, NULL, wrap_password_callback, &cb_data);
+ } else if (format == FORMAT_PKCS12) {
+ if (!load_pkcs12(key, key_descrip, wrap_password_callback, &cb_data,
+ &pkey, NULL, NULL))
+ goto end;
+#if !defined(OPENSSL_NO_RSA) && !defined(OPENSSL_NO_DSA) && !defined (OPENSSL_NO_RC4)
+ } else if (format == FORMAT_MSBLOB) {
+ pkey = b2i_PrivateKey_bio(key);
+ } else if (format == FORMAT_PVK) {
+ pkey = b2i_PVK_bio(key, wrap_password_callback, &cb_data);
+#endif
+ } else {
+ BIO_printf(bio_err, "bad input format specified for key file\n");
+ goto end;
+ }
+ end:
+ BIO_free(key);
+ if (pkey == NULL) {
+ BIO_printf(bio_err, "unable to load %s\n", key_descrip);
+ ERR_print_errors(bio_err);
+ }
+ return pkey;
+}
+
+EVP_PKEY *load_pubkey(const char *file, int format, int maybe_stdin,
+ const char *pass, ENGINE *e, const char *key_descrip)
+{
+ BIO *key = NULL;
+ EVP_PKEY *pkey = NULL;
+ PW_CB_DATA cb_data;
+
+ cb_data.password = pass;
+ cb_data.prompt_info = file;
+
+ if (file == NULL && (!maybe_stdin || format == FORMAT_ENGINE)) {
+ BIO_printf(bio_err, "no keyfile specified\n");
+ goto end;
+ }
+ if (format == FORMAT_ENGINE) {
+ if (e == NULL) {
+ BIO_printf(bio_err, "no engine specified\n");
+ } else {
+#ifndef OPENSSL_NO_ENGINE
+ pkey = ENGINE_load_public_key(e, file, (UI_METHOD *)get_ui_method(),
+ &cb_data);
+ if (pkey == NULL) {
+ BIO_printf(bio_err, "cannot load %s from engine\n", key_descrip);
+ ERR_print_errors(bio_err);
+ }
+#else
+ BIO_printf(bio_err, "engines not supported\n");
+#endif
+ }
+ goto end;
+ }
+ if (file == NULL && maybe_stdin) {
+ unbuffer(stdin);
+ key = dup_bio_in(format);
+ } else {
+ key = bio_open_default(file, 'r', format);
+ }
+ if (key == NULL)
+ goto end;
+ if (format == FORMAT_ASN1) {
+ pkey = d2i_PUBKEY_bio(key, NULL);
+ } else if (format == FORMAT_ASN1RSA) {
+#ifndef OPENSSL_NO_RSA
+ RSA *rsa;
+ rsa = d2i_RSAPublicKey_bio(key, NULL);
+ if (rsa) {
+ pkey = EVP_PKEY_new();
+ if (pkey != NULL)
+ EVP_PKEY_set1_RSA(pkey, rsa);
+ RSA_free(rsa);
+ } else
+#else
+ BIO_printf(bio_err, "RSA keys not supported\n");
+#endif
+ pkey = NULL;
+ } else if (format == FORMAT_PEMRSA) {
+#ifndef OPENSSL_NO_RSA
+ RSA *rsa;
+ rsa = PEM_read_bio_RSAPublicKey(key, NULL,
+ (pem_password_cb *)password_callback,
+ &cb_data);
+ if (rsa != NULL) {
+ pkey = EVP_PKEY_new();
+ if (pkey != NULL)
+ EVP_PKEY_set1_RSA(pkey, rsa);
+ RSA_free(rsa);
+ } else
+#else
+ BIO_printf(bio_err, "RSA keys not supported\n");
+#endif
+ pkey = NULL;
+ } else if (format == FORMAT_PEM) {
+ pkey = PEM_read_bio_PUBKEY(key, NULL,
+ (pem_password_cb *)password_callback,
+ &cb_data);
+#if !defined(OPENSSL_NO_RSA) && !defined(OPENSSL_NO_DSA)
+ } else if (format == FORMAT_MSBLOB) {
+ pkey = b2i_PublicKey_bio(key);
+#endif
+ }
+ end:
+ BIO_free(key);
+ if (pkey == NULL)
+ BIO_printf(bio_err, "unable to load %s\n", key_descrip);
+ return pkey;
+}
+
+static int load_certs_crls(const char *file, int format,
+ const char *pass, const char *desc,
+ STACK_OF(X509) **pcerts,
+ STACK_OF(X509_CRL) **pcrls)
+{
+ int i;
+ BIO *bio;
+ STACK_OF(X509_INFO) *xis = NULL;
+ X509_INFO *xi;
+ PW_CB_DATA cb_data;
+ int rv = 0;
+
+ cb_data.password = pass;
+ cb_data.prompt_info = file;
+
+ if (format != FORMAT_PEM) {
+ BIO_printf(bio_err, "bad input format specified for %s\n", desc);
+ return 0;
+ }
+
+ bio = bio_open_default(file, 'r', FORMAT_PEM);
+ if (bio == NULL)
+ return 0;
+
+ xis = PEM_X509_INFO_read_bio(bio, NULL,
+ (pem_password_cb *)password_callback,
+ &cb_data);
+
+ BIO_free(bio);
+
+ if (pcerts != NULL && *pcerts == NULL) {
+ *pcerts = sk_X509_new_null();
+ if (*pcerts == NULL)
+ goto end;
+ }
+
+ if (pcrls != NULL && *pcrls == NULL) {
+ *pcrls = sk_X509_CRL_new_null();
+ if (*pcrls == NULL)
+ goto end;
+ }
+
+ for (i = 0; i < sk_X509_INFO_num(xis); i++) {
+ xi = sk_X509_INFO_value(xis, i);
+ if (xi->x509 != NULL && pcerts != NULL) {
+ if (!sk_X509_push(*pcerts, xi->x509))
+ goto end;
+ xi->x509 = NULL;
+ }
+ if (xi->crl != NULL && pcrls != NULL) {
+ if (!sk_X509_CRL_push(*pcrls, xi->crl))
+ goto end;
+ xi->crl = NULL;
+ }
+ }
+
+ if (pcerts != NULL && sk_X509_num(*pcerts) > 0)
+ rv = 1;
+
+ if (pcrls != NULL && sk_X509_CRL_num(*pcrls) > 0)
+ rv = 1;
+
+ end:
+
+ sk_X509_INFO_pop_free(xis, X509_INFO_free);
+
+ if (rv == 0) {
+ if (pcerts != NULL) {
+ sk_X509_pop_free(*pcerts, X509_free);
+ *pcerts = NULL;
+ }
+ if (pcrls != NULL) {
+ sk_X509_CRL_pop_free(*pcrls, X509_CRL_free);
+ *pcrls = NULL;
+ }
+ BIO_printf(bio_err, "unable to load %s\n",
+ pcerts ? "certificates" : "CRLs");
+ ERR_print_errors(bio_err);
+ }
+ return rv;
+}
+
+void* app_malloc(int sz, const char *what)
+{
+ void *vp = OPENSSL_malloc(sz);
+
+ if (vp == NULL) {
+ BIO_printf(bio_err, "%s: Could not allocate %d bytes for %s\n",
+ opt_getprog(), sz, what);
+ ERR_print_errors(bio_err);
+ exit(1);
+ }
+ return vp;
+}
+
+/*
+ * Initialize or extend, if *certs != NULL, a certificate stack.
+ */
+int load_certs(const char *file, STACK_OF(X509) **certs, int format,
+ const char *pass, const char *desc)
+{
+ return load_certs_crls(file, format, pass, desc, certs, NULL);
+}
+
+/*
+ * Initialize or extend, if *crls != NULL, a certificate stack.
+ */
+int load_crls(const char *file, STACK_OF(X509_CRL) **crls, int format,
+ const char *pass, const char *desc)
+{
+ return load_certs_crls(file, format, pass, desc, NULL, crls);
+}
+
+#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_ext_copy(int *copy_type, const char *arg)
+{
+ if (strcasecmp(arg, "none") == 0)
+ *copy_type = EXT_COPY_NONE;
+ else if (strcasecmp(arg, "copy") == 0)
+ *copy_type = EXT_COPY_ADD;
+ else if (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 = NULL;
+ X509_EXTENSION *ext, *tmpext;
+ ASN1_OBJECT *obj;
+ int i, idx, ret = 0;
+ if (!x || !req || (copy_type == EXT_COPY_NONE))
+ return 1;
+ exts = X509_REQ_get_extensions(req);
+
+ for (i = 0; i < sk_X509_EXTENSION_num(exts); i++) {
+ ext = sk_X509_EXTENSION_value(exts, i);
+ obj = X509_EXTENSION_get_object(ext);
+ idx = X509_get_ext_by_OBJ(x, obj, -1);
+ /* Does extension exist? */
+ 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 {
+ tmpext = X509_get_ext(x, idx);
+ X509_delete_ext(x, idx);
+ X509_EXTENSION_free(tmpext);
+ 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 (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, X509_NAME *nm,
+ unsigned long lflags)
+{
+ char *buf;
+ char mline = 0;
+ int indent = 0;
+
+ if (title)
+ 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, const char *CApath, int noCAfile, int noCApath)
+{
+ X509_STORE *store = X509_STORE_new();
+ X509_LOOKUP *lookup;
+
+ 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) {
+ if (!X509_LOOKUP_load_file(lookup, CAfile, X509_FILETYPE_PEM)) {
+ BIO_printf(bio_err, "Error loading file %s\n", CAfile);
+ goto end;
+ }
+ } else {
+ X509_LOOKUP_load_file(lookup, NULL, X509_FILETYPE_DEFAULT);
+ }
+ }
+
+ if (CApath != NULL || !noCApath) {
+ lookup = X509_STORE_add_lookup(store, X509_LOOKUP_hash_dir());
+ if (lookup == NULL)
+ goto end;
+ if (CApath) {
+ if (!X509_LOOKUP_add_dir(lookup, CApath, X509_FILETYPE_PEM)) {
+ BIO_printf(bio_err, "Error loading directory %s\n", CApath);
+ goto end;
+ }
+ } else {
+ X509_LOOKUP_add_dir(lookup, NULL, X509_FILETYPE_DEFAULT);
+ }
+ }
+
+ ERR_clear_error();
+ return store;
+ end:
+ X509_STORE_free(store);
+ return NULL;
+}
+
+#ifndef OPENSSL_NO_ENGINE
+/* Try to load an engine in a shareable library */
+static ENGINE *try_load_engine(const char *engine)
+{
+ ENGINE *e = ENGINE_by_id("dynamic");
+ if (e) {
+ 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(const char *engine, int debug)
+{
+ ENGINE *e = NULL;
+
+#ifndef OPENSSL_NO_ENGINE
+ if (engine != NULL) {
+ if (strcmp(engine, "auto") == 0) {
+ BIO_printf(bio_err, "enabling auto ENGINE support\n");
+ ENGINE_register_all_complete();
+ return NULL;
+ }
+ if ((e = ENGINE_by_id(engine)) == NULL
+ && (e = try_load_engine(engine)) == NULL) {
+ BIO_printf(bio_err, "invalid engine \"%s\"\n", engine);
+ ERR_print_errors(bio_err);
+ return NULL;
+ }
+ if (debug) {
+ ENGINE_ctrl(e, ENGINE_CTRL_SET_LOGSTREAM, 0, bio_err, 0);
+ }
+ ENGINE_ctrl_cmd(e, "SET_USER_INTERFACE", 0, (void *)get_ui_method(),
+ 0, 1);
+ if (!ENGINE_set_default(e, ENGINE_METHOD_ALL)) {
+ BIO_printf(bio_err, "can't use that engine\n");
+ 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
+ if (e != NULL)
+ /* Free our "structural" reference. */
+ ENGINE_free(e);
+#endif
+}
+
+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 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 (in == NULL) {
+ if (!create) {
+ perror(serialfile);
+ goto err;
+ }
+ ERR_clear_error();
+ ret = BN_new();
+ if (ret == NULL || !rand_serial(ret, ai))
+ BIO_printf(bio_err, "Out of memory\n");
+ } 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 && retai) {
+ *retai = ai;
+ ai = NULL;
+ }
+ 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) {
+ ERR_print_errors(bio_err);
+ 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:
+ 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:
+ 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) {
+ ERR_print_errors(bio_err);
+ 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);
+ ERR_print_errors(bio_err);
+ 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);
+ }
+ }
+
+ retdb->dbfname = OPENSSL_strdup(dbfile);
+#ifndef OPENSSL_NO_POSIX_IO
+ retdb->dbst = dbst;
+#endif
+
+ 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);
+ return 0;
+ }
+
+ 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);
+ return 0;
+ }
+ return 1;
+}
+
+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:
+ 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:
+ 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 characters may be escaped by \
+ */
+X509_NAME *parse_name(const char *cp, long chtype, int canmulti)
+{
+ int nextismulti = 0;
+ char *work;
+ X509_NAME *n;
+
+ if (*cp++ != '/') {
+ BIO_printf(bio_err,
+ "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",
+ --cp);
+ return NULL;
+ }
+
+ n = X509_NAME_new();
+ if (n == NULL)
+ return NULL;
+ work = OPENSSL_strdup(cp);
+ if (work == NULL) {
+ BIO_printf(bio_err, "%s: Error copying name input\n", opt_getprog());
+ goto err;
+ }
+
+ while (*cp) {
+ char *bp = work;
+ char *typestr = bp;
+ unsigned char *valstr;
+ int nid;
+ int ismulti = nextismulti;
+ nextismulti = 0;
+
+ /* Collect the type */
+ while (*cp && *cp != '=')
+ *bp++ = *cp++;
+ if (*cp == '\0') {
+ BIO_printf(bio_err,
+ "%s: Hit end of string before finding the '='\n",
+ opt_getprog());
+ goto err;
+ }
+ *bp++ = '\0';
+ ++cp;
+
+ /* Collect the value. */
+ valstr = (unsigned char *)bp;
+ for (; *cp && *cp != '/'; *bp++ = *cp++) {
+ if (canmulti && *cp == '+') {
+ nextismulti = 1;
+ break;
+ }
+ if (*cp == '\\' && *++cp == '\0') {
+ BIO_printf(bio_err,
+ "%s: escape character at end of string\n",
+ opt_getprog());
+ goto err;
+ }
+ }
+ *bp++ = '\0';
+
+ /* If not at EOS (must be + or /), move forward. */
+ if (*cp)
+ ++cp;
+
+ /* Parse */
+ nid = OBJ_txt2nid(typestr);
+ if (nid == NID_undef) {
+ BIO_printf(bio_err, "%s: Skipping unknown attribute \"%s\"\n",
+ opt_getprog(), typestr);
+ continue;
+ }
+ if (*valstr == '\0') {
+ BIO_printf(bio_err,
+ "%s: No value provided for Subject Attribute %s, skipped\n",
+ opt_getprog(), typestr);
+ continue;
+ }
+ if (!X509_NAME_add_entry_by_NID(n, nid, chtype,
+ valstr, strlen((char *)valstr),
+ -1, ismulti ? -1 : 0)) {
+ BIO_printf(bio_err, "%s: Error adding name attribute \"/%s=%s\"\n",
+ opt_getprog(), 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;
+ }
+ 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;
+ char *stmp, *vtmp = NULL;
+ stmp = OPENSSL_strdup(value);
+ if (!stmp)
+ return -1;
+ vtmp = strchr(stmp, ':');
+ if (vtmp) {
+ *vtmp = 0;
+ vtmp++;
+ }
+ rv = EVP_PKEY_CTX_ctrl_str(ctx, stmp, vtmp);
+ 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;
+
+ len = strlen(in);
+ if (len >= 65535)
+ return NULL;
+
+ out = app_malloc(strlen(in) + 1, "NPN buffer");
+ for (i = 0; i <= len; ++i) {
+ if (i == len || in[i] == ',') {
+ if (i - start > 255) {
+ OPENSSL_free(out);
+ return NULL;
+ }
+ out[start] = (unsigned char)(i - start);
+ start = i + 1;
+ } else {
+ out[i + 1] = in[i];
+ }
+ }
+
+ *outlen = len + 1;
+ 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");
+ }
+}
+
+/* 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 (strncmp(uptr, "http://", 7) == 0)
+ 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)
+ return load_crl(urlptr, FORMAT_HTTP);
+ }
+ return NULL;
+}
+
+/*
+ * Example of downloading CRLs from CRLDP: not usable for real world as it
+ * always downloads, doesn't support non-blocking I/O and doesn't cache
+ * anything.
+ */
+
+static STACK_OF(X509_CRL) *crls_http_cb(X509_STORE_CTX *ctx, 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);
+}
+
+/*
+ * 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(OPENSSL_SYSTEM_VMS)
+# include <time.h>
+# include <times.h>
+
+double app_tminterval(int stop, int usertime)
+{
+ static clock_t tmstart;
+ double ret = 0;
+ clock_t now;
+# ifdef __TMS
+ struct tms rus;
+
+ now = times(&rus);
+ if (usertime)
+ now = rus.tms_utime;
+# else
+ if (usertime)
+ now = clock(); /* sum of user and kernel times */
+ else {
+ struct timeval tv;
+ gettimeofday(&tv, NULL);
+ now = (clock_t)((unsigned long long)tv.tv_sec * CLK_TCK +
+ (unsigned long long)tv.tv_usec * (1000000 / CLK_TCK)
+ );
+ }
+# endif
+ if (stop == TM_START)
+ tmstart = now;
+ else
+ ret = (now - tmstart) / (double)(CLK_TCK);
+
+ 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
+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;
+}
+#else
+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;
+
+#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(apps_bf_prefix()), b);
+ BIO_ctrl(b, PREFIX_CTRL_SET_PREFIX, 0, 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 (FMT_istext(format))
+ b = BIO_push(BIO_new(BIO_f_linebuffer()), b);
+#endif
+ return b;
+}
+
+/*
+ * Because the prefix method is created dynamically, we must also be able
+ * to destroy it.
+ */
+void destroy_prefix_method(void)
+{
+ BIO_METHOD *prefix_method = apps_bf_prefix();
+ BIO_meth_free(prefix_method);
+ prefix_method = NULL;
+}
+
+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 fd = -1, bflags, mode, textmode;
+
+ if (!private || filename == NULL || strcmp(filename, "-") == 0)
+ return bio_open_default(filename, 'w', format);
+
+ mode = O_WRONLY;
+#ifdef O_CREAT
+ mode |= O_CREAT;
+#endif
+#ifdef O_TRUNC
+ mode |= O_TRUNC;
+#endif
+ textmode = FMT_istext(format);
+ 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));
+ if (fp == NULL)
+ goto err;
+ bflags = BIO_CLOSE;
+ if (textmode)
+ bflags |= BIO_FP_TEXT;
+ b = BIO_new_fp(fp, bflags);
+ if (b)
+ 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)
+ fclose(fp);
+ else if (fd >= 0)
+ close(fd);
+ 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;
+}
+
+void make_uppercase(char *string)
+{
+ int i;
+
+ for (i = 0; string[i] != '\0'; i++)
+ string[i] = toupper((unsigned char)string[i]);
+}
+
+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;
+
+ 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)))
+ goto err;
+ OPENSSL_free(stmp);
+ }
+ params[params_n] = OSSL_PARAM_construct_end();
+ return params;
+err:
+ OPENSSL_free(stmp);
+ BIO_printf(bio_err, "Parameter error '%s'\n", 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);
+ }
+}
diff --git a/apps/lib/apps_ui.c b/apps/lib/apps_ui.c
new file mode 100644
index 0000000000..bcfe555478
--- /dev/null
+++ b/apps/lib/apps_ui.c
@@ -0,0 +1,197 @@
+/*
+ * Copyright 1995-2018 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (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_fallback_method = NULL;
+
+
+static int ui_open(UI *ui)
+{
+ int (*opener)(UI *ui) = UI_method_get_opener(ui_fallback_method);
+
+ if (opener)
+ 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 && password[0] != '\0') {
+ 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_fallback_method);
+ if (reader)
+ return reader(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 && password[0] != '\0')
+ return 1;
+ }
+ break;
+ case UIT_NONE:
+ case UIT_BOOLEAN:
+ case UIT_INFO:
+ case UIT_ERROR:
+ break;
+ }
+ }
+
+ writer = UI_method_get_writer(ui_fallback_method);
+ if (writer)
+ return writer(ui, uis);
+ return 1;
+}
+
+static int ui_close(UI *ui)
+{
+ int (*closer)(UI *ui) = UI_method_get_closer(ui_fallback_method);
+
+ if (closer)
+ return closer(ui);
+ return 1;
+}
+
+int setup_ui_method(void)
+{
+ ui_fallback_method = UI_null();
+#ifndef OPENSSL_NO_UI_CONSOLE
+ ui_fallback_method = UI_OpenSSL();
+#endif
+ ui_method = UI_create_method("OpenSSL application user interface");
+ UI_method_set_opener(ui_method, ui_open);
+ UI_method_set_reader(ui_method, ui_read);
+ UI_method_set_writer(ui_method, ui_write);
+ UI_method_set_closer(ui_method, ui_close);
+ return 0;
+}
+
+void destroy_ui_method(void)
+{
+ if (ui_method) {
+ 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/bf_prefix.c b/apps/lib/bf_prefix.c
new file mode 100644
index 0000000000..8cedca90ce
--- /dev/null
+++ b/apps/lib/bf_prefix.c
@@ -0,0 +1,177 @@
+/*
+ * Copyright 2018 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 <stdio.h>
+#include <string.h>
+#include <errno.h>
+#include <openssl/bio.h>
+#include "apps.h"
+
+static int prefix_write(BIO *b, const char *out, size_t outl,
+ size_t *numwritten);
+static int prefix_read(BIO *b, char *buf, size_t size, size_t *numread);
+static int prefix_puts(BIO *b, const char *str);
+static int prefix_gets(BIO *b, char *str, int size);
+static long prefix_ctrl(BIO *b, int cmd, long arg1, void *arg2);
+static int prefix_create(BIO *b);
+static int prefix_destroy(BIO *b);
+static long prefix_callback_ctrl(BIO *b, int cmd, BIO_info_cb *fp);
+
+static BIO_METHOD *prefix_meth = NULL;
+
+BIO_METHOD *apps_bf_prefix(void)
+{
+ if (prefix_meth == NULL) {
+ if ((prefix_meth =
+ BIO_meth_new(BIO_TYPE_FILTER, "Prefix filter")) == NULL
+ || !BIO_meth_set_create(prefix_meth, prefix_create)
+ || !BIO_meth_set_destroy(prefix_meth, prefix_destroy)
+ || !BIO_meth_set_write_ex(prefix_meth, prefix_write)
+ || !BIO_meth_set_read_ex(prefix_meth, prefix_read)
+ || !BIO_meth_set_puts(prefix_meth, prefix_puts)
+ || !BIO_meth_set_gets(prefix_meth, prefix_gets)
+ || !BIO_meth_set_ctrl(prefix_meth, prefix_ctrl)
+ || !BIO_meth_set_callback_ctrl(prefix_meth, prefix_callback_ctrl)) {
+ BIO_meth_free(prefix_meth);
+ prefix_meth = NULL;
+ }
+ }
+ return prefix_meth;
+}
+
+typedef struct prefix_ctx_st {
+ char *prefix;
+ int linestart; /* flag to indicate we're at the line start */
+} PREFIX_CTX;
+
+static int prefix_create(BIO *b)
+{
+ PREFIX_CTX *ctx = OPENSSL_zalloc(sizeof(*ctx));
+
+ if (ctx == NULL)
+ return 0;
+
+ ctx->prefix = NULL;
+ ctx->linestart = 1;
+ BIO_set_data(b, ctx);
+ BIO_set_init(b, 1);
+ return 1;
+}
+
+static int prefix_destroy(BIO *b)
+{
+ PREFIX_CTX *ctx = BIO_get_data(b);
+
+ OPENSSL_free(ctx->prefix);
+ OPENSSL_free(ctx);
+ return 1;
+}
+
+static int prefix_read(BIO *b, char *in, size_t size, size_t *numread)
+{
+ return BIO_read_ex(BIO_next(b), in, size, numread);
+}
+
+static int prefix_write(BIO *b, const char *out, size_t outl,
+ size_t *numwritten)
+{
+ PREFIX_CTX *ctx = BIO_get_data(b);
+
+ if (ctx == NULL)
+ return 0;
+
+ /* If no prefix is set or if it's empty, we've got nothing to do here */
+ if (ctx->prefix == NULL || *ctx->prefix == '\0') {
+ /* We do note if what comes next will be a new line, though */
+ if (outl > 0)
+ ctx->linestart = (out[outl-1] == '\n');
+ return BIO_write_ex(BIO_next(b), out, outl, numwritten);
+ }
+
+ *numwritten = 0;
+
+ while (outl > 0) {
+ size_t i;
+ char c;
+
+ /* If we know that we're at the start of the line, output the prefix */
+ if (ctx->linestart) {
+ size_t dontcare;
+
+ if (!BIO_write_ex(BIO_next(b), ctx->prefix, strlen(ctx->prefix),
+ &dontcare))
+ return 0;
+ ctx->linestart = 0;
+ }
+
+ /* Now, go look for the next LF, or the end of the string */
+ for (i = 0, c = '\0'; i < outl && (c = out[i]) != '\n'; i++)
+ continue;
+ if (c == '\n')
+ i++;
+
+ /* Output what we found so far */
+ while (i > 0) {
+ size_t num = 0;
+
+ if (!BIO_write_ex(BIO_next(b), out, i, &num))
+ return 0;
+ out += num;
+ outl -= num;
+ *numwritten += num;
+ i -= num;
+ }
+
+ /* If we found a LF, what follows is a new line, so take note */
+ if (c == '\n')
+ ctx->linestart = 1;
+ }
+
+ return 1;
+}
+
+static long prefix_ctrl(BIO *b, int cmd, long num, void *ptr)
+{
+ long ret = 0;
+
+ switch (cmd) {
+ case PREFIX_CTRL_SET_PREFIX:
+ {
+ PREFIX_CTX *ctx = BIO_get_data(b);
+
+ if (ctx == NULL)
+ break;
+
+ OPENSSL_free(ctx->prefix);
+ ctx->prefix = OPENSSL_strdup((const char *)ptr);
+ ret = ctx->prefix != NULL;
+ }
+ break;
+ default:
+ if (BIO_next(b) != NULL)
+ ret = BIO_ctrl(BIO_next(b), cmd, num, ptr);
+ break;
+ }
+ return ret;
+}
+
+static long prefix_callback_ctrl(BIO *b, int cmd, BIO_info_cb *fp)
+{
+ return BIO_callback_ctrl(BIO_next(b), cmd, fp);
+}
+
+static int prefix_gets(BIO *b, char *buf, int size)
+{
+ return BIO_gets(BIO_next(b), buf, size);
+}
+
+static int prefix_puts(BIO *b, const char *str)
+{
+ return BIO_write(b, str, strlen(str));
+}
diff --git a/apps/lib/build.info b/apps/lib/build.info
new file mode 100644
index 0000000000..f92d4dafc6
--- /dev/null
+++ b/apps/lib/build.info
@@ -0,0 +1,18 @@
+# Auxilliary program source
+IF[{- $config{target} =~ /^(?:VC-|mingw)/ -}]
+ # 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 \
+ bf_prefix.c columns.c app_params.c
+
+IF[{- !$disabled{apps} -}]
+ LIBS{noinst}=../libapps.a
+ SOURCE[../libapps.a]=$LIBAPPSSRC $AUXLIBAPPSSRC
+ INCLUDE[../libapps.a]=../.. ../../include ../include
+ENDIF
diff --git a/apps/lib/columns.c b/apps/lib/columns.c
new file mode 100644
index 0000000000..aa58fe1781
--- /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/fmt.c b/apps/lib/fmt.c
new file mode 100644
index 0000000000..5cb4c041a8
--- /dev/null
+++ b/apps/lib/fmt.c
@@ -0,0 +1,15 @@
+/*
+ * Copyright 2018 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (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/opt.c b/apps/lib/opt.c
new file mode 100644
index 0000000000..c2a5878ef6
--- /dev/null
+++ b/apps/lib/opt.c
@@ -0,0 +1,955 @@
+/*
+ * Copyright 2015-2018 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 "internal/nelem.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/bio.h>
+#include <openssl/x509v3.h>
+
+#define MAX_OPT_HELP_WIDTH 30
+const char OPT_HELP_STR[] = "--";
+const char OPT_MORE_STR[] = "---";
+
+/* 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)
+char *opt_progname(const char *argv0)
+{
+ size_t i, n;
+ const char *p;
+ char *q;
+
+ /* find the last '/', '\' or ':' */
+ for (p = argv0 + strlen(argv0); --p > argv0;)
+ if (*p == '/' || *p == '\\' || *p == ':') {
+ p++;
+ break;
+ }
+
+ /* 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)
+
+char *opt_progname(const char *argv0)
+{
+ const char *p, *q;
+
+ /* Find last special character sys:[foo.bar]openssl */
+ for (p = argv0 + strlen(argv0); --p > argv0;)
+ if (*p == ':' || *p == ']' || *p == '>') {
+ p++;
+ break;
+ }
+
+ q = strrchr(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
+
+char *opt_progname(const char *argv0)
+{
+ const char *p;
+
+ /* Could use strchr, but this is like the ones above. */
+ for (p = argv0 + strlen(argv0); --p > argv0;)
+ if (*p == '/') {
+ p++;
+ break;
+ }
+ strncpy(prog, p, sizeof(prog) - 1);
+ prog[sizeof(prog) - 1] = '\0';
+ return prog;
+}
+#endif
+
+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;
+ opt_progname(av[0]);
+ unknown = NULL;
+
+ for (; o->name; ++o) {
+#ifndef NDEBUG
+ const OPTIONS *next;
+ int duplicated, i;
+#endif
+
+ if (o->name == OPT_HELP_STR || o->name == OPT_MORE_STR)
+ continue;
+#ifndef NDEBUG
+ i = o->valtype;
+
+ /* Make sure options are legit. */
+ OPENSSL_assert(o->name[0] != '-');
+ OPENSSL_assert(o->retval > 0);
+ switch (i) {
+ case 0: 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':
+ 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 = strcmp(o->name, next->name) == 0;
+ 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. */
+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:
+ 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 {
+ return 0;
+ }
+ break;
+ }
+ return 1;
+}
+
+/* Parse a cipher name, put it in *EVP_CIPHER; return 0 on failure, else 1. */
+int opt_cipher(const char *name, const EVP_CIPHER **cipherp)
+{
+ *cipherp = EVP_get_cipherbyname(name);
+ if (*cipherp != NULL)
+ return 1;
+ opt_printf_stderr("%s: Unrecognized flag %s\n", prog, name);
+ return 0;
+}
+
+/*
+ * Parse message digest name, put it in *EVP_MD; return 0 on failure, else 1.
+ */
+int opt_md(const char *name, const EVP_MD **mdp)
+{
+ *mdp = EVP_get_digestbyname(name);
+ if (*mdp != NULL)
+ return 1;
+ opt_printf_stderr("%s: Unrecognized flag %s\n", prog, name);
+ return 0;
+}
+
+/* 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;
+}
+
+/* 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;
+}
+
+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_imax(const char *value, 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;
+ }
+ *result = m;
+ errno = oerrno;
+ return 1;
+}
+
+/* Parse a uintmax_t, put it into *result; return 0 on failure, else 1. */
+int opt_umax(const char *value, 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;
+ }
+ *result = m;
+ errno = oerrno;
+ return 1;
+}
+#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;
+ }
+ X509_VERIFY_PARAM_add0_policy(vpm, otmp);
+ 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_imax(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, 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':
+ /* Just a string. */
+ 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':
+ if (!opt_int(arg, &ival)
+ || (o->valtype == 'p' && ival <= 0)) {
+ opt_printf_stderr("%s: Non-positive number \"%s\" for -%s\n",
+ prog, arg, o->name);
+ return -1;
+ }
+ break;
+ case 'M':
+ if (!opt_imax(arg, &imval)) {
+ opt_printf_stderr("%s: Invalid number \"%s\" for -%s\n",
+ prog, arg, o->name);
+ return -1;
+ }
+ break;
+ case 'U':
+ if (!opt_umax(arg, &umval)) {
+ opt_printf_stderr("%s: Invalid number \"%s\" for -%s\n",
+ prog, arg, o->name);
+ return -1;
+ }
+ break;
+ case 'l':
+ if (!opt_long(arg, &lval)) {
+ opt_printf_stderr("%s: Invalid number \"%s\" for -%s\n",
+ prog, arg, o->name);
+ return -1;
+ }
+ break;
+ case 'u':
+ if (!opt_ulong(arg, &ulval)) {
+ opt_printf_stderr("%s: Invalid number \"%s\" for -%s\n",
+ prog, arg, o->name);
+ 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 -%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: Option 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. */
+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 '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 'U':
+ return "uintmax";
+ }
+ return "parm";
+}
+
+void opt_help(const OPTIONS *list)
+{
+ const OPTIONS *o;
+ int i;
+ int standard_prolog;
+ int width = 5;
+ char start[80 + 1];
+ char *p;
+ const char *help;
+
+ /* 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]\nValid options are:\n", prog);
+
+ /* Now let's print. */
+ for (o = list; o->name; o++) {
+ help = o->helpstr ? o->helpstr : "(No additional info)";
+ if (o->name == OPT_HELP_STR) {
+ opt_printf_stderr(help, prog);
+ continue;
+ }
+
+ /* 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);
+ continue;
+ }
+
+ /* Build up the "-flag [param]" part. */
+ p = start;
+ *p++ = ' ';
+ *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);
+ }
+}
+
+/* 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 0000000000..47b8afe9ef
--- /dev/null
+++ b/apps/lib/s_cb.c
@@ -0,0 +1,1521 @@
+/*
+ * Copyright 1995-2018 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/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:
+ 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:
+ 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:
+ 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},
+ {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, ":");
+ 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;
+ const char *gname;
+
+ 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 Elliptic Groups: ");
+ for (i = 0; i < ngroups; i++) {
+ if (i)
+ BIO_puts(out, ":");
+ nid = groups[i];
+ /* If unrecognised print out hex version */
+ if (nid & TLSEXT_nid_unknown) {
+ BIO_printf(out, "0x%04X", nid & 0xFFFF);
+ } else {
+ /* TODO(TLS1.3): Get group name here */
+ /* Use NIST name for curve if it exists */
+ gname = EC_curve_nid2nist(nid);
+ if (gname == NULL)
+ gname = OBJ_nid2sn(nid);
+ BIO_printf(out, "%s", gname);
+ }
+ }
+ OPENSSL_free(groups);
+ if (noshared) {
+ BIO_puts(out, "\n");
+ return 1;
+ }
+ BIO_puts(out, "\nShared Elliptic 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);
+ /* TODO(TLS1.3): Convert for DH groups */
+ gname = EC_curve_nid2nist(nid);
+ if (gname == NULL)
+ gname = OBJ_nid2sn(nid);
+ BIO_printf(out, "%s", gname);
+ }
+ 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_id(key)) {
+ case EVP_PKEY_RSA:
+ BIO_printf(out, "RSA, %d bits\n", EVP_PKEY_bits(key));
+ break;
+
+ case EVP_PKEY_DH:
+ BIO_printf(out, "DH, %d bits\n", EVP_PKEY_bits(key));
+ break;
+#ifndef OPENSSL_NO_EC
+ case EVP_PKEY_EC:
+ {
+ EC_KEY *ec = EVP_PKEY_get1_EC_KEY(key);
+ int nid;
+ const char *cname;
+ nid = EC_GROUP_get_curve_name(EC_KEY_get0_group(ec));
+ EC_KEY_free(ec);
+ cname = EC_curve_nid2nist(nid);
+ if (cname == NULL)
+ cname = OBJ_nid2sn(nid);
+ BIO_printf(out, "ECDH, %s, %d bits\n", cname, EVP_PKEY_bits(key));
+ }
+ break;
+#endif
+ default:
+ BIO_printf(out, "%s, %d bits\n", OBJ_nid2sn(EVP_PKEY_id(key)),
+ EVP_PKEY_bits(key));
+ }
+ EVP_PKEY_free(key);
+ return 1;
+}
+
+long bio_dump_callback(BIO *bio, int cmd, const char *argp,
+ int argi, long argl, long ret)
+{
+ BIO *out;
+
+ out = (BIO *)BIO_get_callback_arg(bio);
+ if (out == NULL)
+ return ret;
+
+ if (cmd == (BIO_CB_READ | BIO_CB_RETURN)) {
+ BIO_printf(out, "read from %p [%p] (%lu bytes => %ld (0x%lX))\n",
+ (void *)bio, (void *)argp, (unsigned long)argi, ret, ret);
+ BIO_dump(out, argp, (int)ret);
+ return ret;
+ } else if (cmd == (BIO_CB_WRITE | BIO_CB_RETURN)) {
+ BIO_printf(out, "write to %p [%p] (%lu bytes => %ld (0x%lX))\n",
+ (void *)bio, (void *)argp, (unsigned long)argi, ret, ret);
+ BIO_dump(out, argp, (int)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 ? ">>>" : "<<<";
+ const char *str_version = lookup(version, ssl_versions, "???");
+ const char *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) {
+ switch (content_type) {
+ case 20:
+ str_content_type = ", ChangeCipherSpec";
+ break;
+ case 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 22:
+ str_content_type = ", Handshake";
+ str_details1 = "???";
+ if (len > 0)
+ str_details1 = lookup((int)bp[0], handshakes, "???");
+ break;
+ case 23:
+ str_content_type = ", ApplicationData";
+ break;
+ }
+ }
+
+ 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 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},
+ {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_cookie_callback(SSL *ssl, unsigned char *cookie,
+ unsigned int *cookie_len)
+{
+ unsigned char *buffer;
+ size_t length = 0;
+ unsigned short port;
+ BIO_ADDR *lpeer = NULL, *peer = NULL;
+
+ /* 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");
+ 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);
+
+ /* Calculate HMAC of buffer using the secret */
+ HMAC(EVP_sha1(), cookie_secret, COOKIE_SECRET_LENGTH,
+ buffer, length, cookie, cookie_len);
+
+ OPENSSL_free(buffer);
+ BIO_ADDR_free(lpeer);
+
+ return 1;
+}
+
+int verify_cookie_callback(SSL *ssl, const unsigned char *cookie,
+ unsigned int cookie_len)
+{
+ unsigned char result[EVP_MAX_MD_SIZE];
+ unsigned int resultlength;
+
+ /* Note: we check cookie_initialized because if it's not,
+ * it cannot be valid */
+ if (cookie_initialized
+ && generate_cookie_callback(ssl, result, &resultlength)
+ && cookie_len == resultlength
+ && memcmp(result, cookie, resultlength) == 0)
+ return 1;
+
+ return 0;
+}
+
+int generate_stateless_cookie_callback(SSL *ssl, unsigned char *cookie,
+ size_t *cookie_len)
+{
+ unsigned int temp;
+ int res = generate_cookie_callback(ssl, cookie, &temp);
+ *cookie_len = temp;
+ return res;
+}
+
+int verify_stateless_cookie_callback(SSL *ssl, const unsigned char *cookie,
+ size_t cookie_len)
+{
+ return verify_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) {
+ SSL_set1_chain(ssl, exc->chain);
+ }
+ }
+ 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, &exc->chain, FORMAT_PEM, 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_PEMDER, &exc->certform))
+ return 0;
+ break;
+ case OPT_X_KEYFORM:
+ if (!opt_format(opt_arg(), OPT_FMT_PEMDER, &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_get_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");
+ }
+ X509_free(peer);
+#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) {
+ if (arg != NULL)
+ BIO_printf(bio_err, "Error with command: \"%s %s\"\n",
+ flag, arg);
+ else
+ BIO_printf(bio_err, "Error with command: \"%s\"\n", flag);
+ 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;
+ for (i = 0; i < sk_X509_CRL_num(crls); i++) {
+ crl = sk_X509_CRL_value(crls, i);
+ X509_STORE_add_crl(st, crl);
+ }
+ return 1;
+}
+
+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 *chCApath, const char *chCAfile,
+ STACK_OF(X509_CRL) *crls, int crl_download)
+{
+ X509_STORE *vfy = NULL, *ch = NULL;
+ int rv = 0;
+ if (vfyCApath != NULL || vfyCAfile != NULL) {
+ vfy = X509_STORE_new();
+ if (vfy == NULL)
+ goto err;
+ if (!X509_STORE_load_locations(vfy, vfyCAfile, vfyCApath))
+ goto err;
+ add_crls_store(vfy, crls);
+ SSL_CTX_set1_verify_cert_store(ctx, vfy);
+ if (crl_download)
+ store_setup_crl_download(vfy);
+ }
+ if (chCApath != NULL || chCAfile != NULL) {
+ ch = X509_STORE_new();
+ if (ch == NULL)
+ goto err;
+ if (!X509_STORE_load_locations(ch, chCAfile, chCApath))
+ goto err;
+ SSL_CTX_set1_chain_cert_store(ctx, ch);
+ }
+ 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
+#ifndef OPENSSL_NO_DH
+ case SSL_SECOP_OTHER_DH:
+ {
+ DH *dh = other;
+ BIO_printf(sdb->out, "%d", DH_bits(dh));
+ 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);
+ 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_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 0000000000..7dd95e9f0e
--- /dev/null
+++ b/apps/lib/s_socket.c
@@ -0,0 +1,395 @@
+/*
+ * Copyright 1995-2018 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
+
+#ifndef OPENSSL_NO_SOCK
+
+# include "apps.h"
+# include "s_apps.h"
+# include "internal/sockets.h"
+
+# 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),
+ protocol == 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",
+ BIO_ADDRINFO_family(res) == AF_INET6 ? "IPv6 " :
+ 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;
+}
+
+/*
+ * 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_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);
+ 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) {
+ 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;
+ }
+ }
+
+ asock = BIO_socket(sock_family, sock_type, sock_protocol, 0);
+ 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 (sock_port == 0) {
+ /* dynamically allocated port, report which one */
+ union BIO_sock_info_u info;
+ char *hostname = NULL;
+ char *service = NULL;
+ int success = 0;
+
+ 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
+ && BIO_printf(bio_s_out,
+ strchr(hostname, ':') == NULL
+ ? /* IPv4 */ "ACCEPT %s:%s\n"
+ : /* IPv6 */ "ACCEPT [%s]:%s\n",
+ hostname, service) > 0)
+ success = 1;
+
+ (void)BIO_flush(bio_s_out);
+ OPENSSL_free(hostname);
+ OPENSSL_free(service);
+ BIO_ADDR_free(info.addr);
+ if (!success) {
+ BIO_closesocket(asock);
+ ERR_print_errors(bio_err);
+ goto end;
+ }
+ } else {
+ (void)BIO_printf(bio_s_out, "ACCEPT\n");
+ (void)BIO_flush(bio_s_out);
+ }
+
+ 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;
+ }
+ 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 {
+ i = (*cb)(asock, type, protocol, context);
+ }
+
+ if (naccept != -1)
+ naccept--;
+ 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;
+}
+
+#endif /* OPENSSL_NO_SOCK */
diff --git a/apps/lib/vms_decc_argv.c b/apps/lib/vms_decc_argv.c
new file mode 100644
index 0000000000..932b51a837
--- /dev/null
+++ b/apps/lib/vms_decc_argv.c
@@ -0,0 +1,67 @@
+/*
+ * Copyright 2015-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 <stdlib.h>
+#include <openssl/crypto.h>
+#include "platform.h" /* for copy_argv() */
+#include "apps.h" /* for app_malloc() */
+
+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();
+
+ newargv = app_malloc(sizeof(*newargv) * (count + 1), "argv copy");
+ 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 0000000000..1b27699b9d
--- /dev/null
+++ b/apps/lib/vms_term_sock.c
@@ -0,0 +1,591 @@
+/*
+ * Copyright 2016 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 (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
+ */
+ sprintf (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
+ */
+ sprintf (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 0000000000..8836b3ba39
--- /dev/null
+++ b/apps/lib/win32_init.c
@@ -0,0 +1,307 @@
+/*
+ * Copyright 2016 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 white spaces */
+ 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