Latest update.
This commit is contained in:
@@ -0,0 +1,96 @@
|
||||
/*
|
||||
* Copyright 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 "apps.h"
|
||||
#include "app_params.h"
|
||||
|
||||
static int describe_param_type(char *buf, size_t bufsz, const OSSL_PARAM *param)
|
||||
{
|
||||
const char *type_mod = "";
|
||||
const char *type = NULL;
|
||||
int show_type_number = 0;
|
||||
int printed_len;
|
||||
|
||||
switch (param->data_type) {
|
||||
case OSSL_PARAM_UNSIGNED_INTEGER:
|
||||
type_mod = "unsigned ";
|
||||
/* FALLTHRU */
|
||||
case OSSL_PARAM_INTEGER:
|
||||
type = "integer";
|
||||
break;
|
||||
case OSSL_PARAM_UTF8_PTR:
|
||||
type_mod = "pointer to a ";
|
||||
/* FALLTHRU */
|
||||
case OSSL_PARAM_UTF8_STRING:
|
||||
type = "UTF8 encoded string";
|
||||
break;
|
||||
case OSSL_PARAM_OCTET_PTR:
|
||||
type_mod = "pointer to an ";
|
||||
/* FALLTHRU */
|
||||
case OSSL_PARAM_OCTET_STRING:
|
||||
type = "octet string";
|
||||
break;
|
||||
default:
|
||||
type = "unknown type";
|
||||
show_type_number = 1;
|
||||
break;
|
||||
}
|
||||
|
||||
printed_len = BIO_snprintf(buf, bufsz, "%s: ", param->key);
|
||||
if (printed_len > 0) {
|
||||
buf += printed_len;
|
||||
bufsz -= printed_len;
|
||||
}
|
||||
printed_len = BIO_snprintf(buf, bufsz, "%s%s", type_mod, type);
|
||||
if (printed_len > 0) {
|
||||
buf += printed_len;
|
||||
bufsz -= printed_len;
|
||||
}
|
||||
if (show_type_number) {
|
||||
printed_len = BIO_snprintf(buf, bufsz, " [%d]", param->data_type);
|
||||
if (printed_len > 0) {
|
||||
buf += printed_len;
|
||||
bufsz -= printed_len;
|
||||
}
|
||||
}
|
||||
if (param->data_size == 0)
|
||||
printed_len = BIO_snprintf(buf, bufsz, " (arbitrary size)");
|
||||
else
|
||||
printed_len = BIO_snprintf(buf, bufsz, " (max %zu bytes large)",
|
||||
param->data_size);
|
||||
if (printed_len > 0) {
|
||||
buf += printed_len;
|
||||
bufsz -= printed_len;
|
||||
}
|
||||
*buf = '\0';
|
||||
return 1;
|
||||
}
|
||||
|
||||
int print_param_types(const char *thing, const OSSL_PARAM *pdefs, int indent)
|
||||
{
|
||||
if (pdefs == NULL) {
|
||||
BIO_printf(bio_out, "%*sNo declared %s\n", indent, "", thing);
|
||||
} else if (pdefs->key == NULL) {
|
||||
/*
|
||||
* An empty list? This shouldn't happen, but let's just make sure to
|
||||
* say something if there's a badly written provider...
|
||||
*/
|
||||
BIO_printf(bio_out, "%*sEmpty list of %s (!!!)\n", indent, "", thing);
|
||||
} else {
|
||||
BIO_printf(bio_out, "%*s%s:\n", indent, "", thing);
|
||||
for (; pdefs->key != NULL; pdefs++) {
|
||||
char buf[200]; /* This should be ample space */
|
||||
|
||||
describe_param_type(buf, sizeof(buf), pdefs);
|
||||
BIO_printf(bio_out, "%*s %s\n", indent, "", buf);
|
||||
}
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
+2618
@@ -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, >ype);
|
||||
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(¶ms[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);
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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));
|
||||
}
|
||||
@@ -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
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
+967
@@ -0,0 +1,967 @@
|
||||
/*
|
||||
* 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[] = "---";
|
||||
const char OPT_SECTION_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 ||
|
||||
o->name == OPT_SECTION_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_print(const OPTIONS *o, int width)
|
||||
{
|
||||
const char* help;
|
||||
char start[80 + 1];
|
||||
char *p;
|
||||
|
||||
help = o->helpstr ? o->helpstr : "(No additional info)";
|
||||
if (o->name == OPT_HELP_STR || o->name == OPT_SECTION_STR) {
|
||||
opt_printf_stderr(help, prog);
|
||||
return;
|
||||
}
|
||||
|
||||
/* Pad out prefix */
|
||||
memset(start, ' ', sizeof(start) - 1);
|
||||
start[sizeof(start) - 1] = '\0';
|
||||
|
||||
if (o->name == OPT_MORE_STR) {
|
||||
/* Continuation of previous line; pad and print. */
|
||||
start[width] = '\0';
|
||||
opt_printf_stderr("%s %s\n", start, help);
|
||||
return;
|
||||
}
|
||||
|
||||
/* Build up the "-flag [param]" part. */
|
||||
p = start;
|
||||
*p++ = ' ';
|
||||
*p++ = '-';
|
||||
if (o->name[0])
|
||||
p += strlen(strcpy(p, o->name));
|
||||
else
|
||||
*p++ = '*';
|
||||
if (o->valtype != '-') {
|
||||
*p++ = ' ';
|
||||
p += strlen(strcpy(p, valtype2param(o)));
|
||||
}
|
||||
*p = ' ';
|
||||
if ((int)(p - start) >= MAX_OPT_HELP_WIDTH) {
|
||||
*p = '\0';
|
||||
opt_printf_stderr("%s\n", start);
|
||||
memset(start, ' ', sizeof(start));
|
||||
}
|
||||
start[width] = '\0';
|
||||
opt_printf_stderr("%s %s\n", start, help);
|
||||
}
|
||||
|
||||
void opt_help(const OPTIONS *list)
|
||||
{
|
||||
const OPTIONS *o;
|
||||
int i;
|
||||
int standard_prolog;
|
||||
int width = 5;
|
||||
char start[80 + 1];
|
||||
|
||||
/* Starts with its own help message? */
|
||||
standard_prolog = list[0].name != OPT_HELP_STR;
|
||||
|
||||
/* Find the widest help. */
|
||||
for (o = list; o->name; o++) {
|
||||
if (o->name == OPT_MORE_STR)
|
||||
continue;
|
||||
i = 2 + (int)strlen(o->name);
|
||||
if (o->valtype != '-')
|
||||
i += 1 + strlen(valtype2param(o));
|
||||
if (i < MAX_OPT_HELP_WIDTH && i > width)
|
||||
width = i;
|
||||
OPENSSL_assert(i < (int)sizeof(start));
|
||||
}
|
||||
|
||||
if (standard_prolog) {
|
||||
opt_printf_stderr("Usage: %s [options]\n", prog);
|
||||
if (list[0].name != OPT_SECTION_STR)
|
||||
opt_printf_stderr("Valid options are:\n", prog);
|
||||
}
|
||||
|
||||
/* Now let's print. */
|
||||
for (o = list; o->name; o++) {
|
||||
opt_print(o, width);
|
||||
}
|
||||
}
|
||||
|
||||
/* opt_isdir section */
|
||||
#ifdef _WIN32
|
||||
# include <windows.h>
|
||||
int opt_isdir(const char *name)
|
||||
{
|
||||
DWORD attr;
|
||||
# if defined(UNICODE) || defined(_UNICODE)
|
||||
size_t i, len_0 = strlen(name) + 1;
|
||||
WCHAR tempname[MAX_PATH];
|
||||
|
||||
if (len_0 > MAX_PATH)
|
||||
return -1;
|
||||
|
||||
# if !defined(_WIN32_WCE) || _WIN32_WCE>=101
|
||||
if (!MultiByteToWideChar(CP_ACP, 0, name, len_0, tempname, MAX_PATH))
|
||||
# endif
|
||||
for (i = 0; i < len_0; i++)
|
||||
tempname[i] = (WCHAR)name[i];
|
||||
|
||||
attr = GetFileAttributes(tempname);
|
||||
# else
|
||||
attr = GetFileAttributes(name);
|
||||
# endif
|
||||
if (attr == INVALID_FILE_ATTRIBUTES)
|
||||
return -1;
|
||||
return ((attr & FILE_ATTRIBUTE_DIRECTORY) != 0);
|
||||
}
|
||||
#else
|
||||
# include <sys/stat.h>
|
||||
# ifndef S_ISDIR
|
||||
# if defined(_S_IFMT) && defined(_S_IFDIR)
|
||||
# define S_ISDIR(a) (((a) & _S_IFMT) == _S_IFDIR)
|
||||
# else
|
||||
# define S_ISDIR(a) (((a) & S_IFMT) == S_IFDIR)
|
||||
# endif
|
||||
# endif
|
||||
|
||||
int opt_isdir(const char *name)
|
||||
{
|
||||
# if defined(S_ISDIR)
|
||||
struct stat st;
|
||||
|
||||
if (stat(name, &st) == 0)
|
||||
return S_ISDIR(st.st_mode);
|
||||
else
|
||||
return -1;
|
||||
# else
|
||||
return -1;
|
||||
# endif
|
||||
}
|
||||
#endif
|
||||
+1521
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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 */
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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
|
||||
@@ -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
|
||||
Reference in New Issue
Block a user