3d362f1903
Historically (i.e., OpenSSL 1.0.x), the openssl applications would
allow for empty subject attributes to be passed via the -subj argument,
e.g., `opensl req -subj '/CN=joe/O=/OU=local' ...`. Commit
db4c08f019
applied a badly needed rewrite
to the parse_name() helper function that parses these strings, but
in the process dropped a check that would skip attributes with no
associated value. As a result, such strings are now treated as
hard errors and the operation fails.
Restore the check to skip empty attribute values and restore
the historical behavior.
Document the behavior for empty subject attribute values in the
corresponding applications' manual pages.
Reviewed-by: Matt Caswell <matt@openssl.org>
(Merged from https://github.com/openssl/openssl/pull/7349)
2752 lines
72 KiB
C
2752 lines
72 KiB
C
/*
|
|
* 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
|
|
*/
|
|
|
|
#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 "s_apps.h"
|
|
#include "apps.h"
|
|
|
|
#ifdef _WIN32
|
|
static int WIN32_rename(const char *from, const char *to);
|
|
# define rename(from,to) WIN32_rename((from),(to))
|
|
#endif
|
|
|
|
typedef struct {
|
|
const char *name;
|
|
unsigned long flag;
|
|
unsigned long mask;
|
|
} NAME_EX_TBL;
|
|
|
|
static UI_METHOD *ui_method = NULL;
|
|
static const UI_METHOD *ui_fallback_method = NULL;
|
|
|
|
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;
|
|
}
|
|
|
|
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;
|
|
}
|
|
|
|
int password_callback(char *buf, int bufsiz, int verify, PW_CB_DATA *cb_tmp)
|
|
{
|
|
int res = 0;
|
|
UI *ui = NULL;
|
|
PW_CB_DATA *cb_data = (PW_CB_DATA *)cb_tmp;
|
|
|
|
ui = UI_new_method(ui_method);
|
|
if (ui) {
|
|
int ok = 0;
|
|
char *buff = NULL;
|
|
int ui_flags = 0;
|
|
const char *prompt_info = NULL;
|
|
char *prompt;
|
|
|
|
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) {
|
|
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 = app_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;
|
|
}
|
|
|
|
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;
|
|
if (arg2 == NULL || arg1 == NULL || strcmp(arg1, arg2))
|
|
same = 0;
|
|
else
|
|
same = 1;
|
|
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)
|
|
{
|
|
char *tmp, tpass[APP_PASS_LEN];
|
|
static BIO *pwdbio = NULL;
|
|
int i;
|
|
|
|
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, "Can't read 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 {
|
|
BIO_printf(bio_err, "Invalid password argument \"%s\"\n", 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, void *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, &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,
|
|
(pem_password_cb *)password_callback,
|
|
&cb_data);
|
|
} else if (format == FORMAT_PKCS12) {
|
|
if (!load_pkcs12(key, key_descrip,
|
|
(pem_password_cb *)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, (pem_password_cb *)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, &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, 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) {
|
|
SYSerr(SYS_F_FSTAT, errno);
|
|
ERR_add_error_data(3, "fstat('", 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(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)
|
|
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 equals.\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))
|
|
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_SYSTEM_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
|
|
}
|
|
|
|
/* app_isdir section */
|
|
#ifdef _WIN32
|
|
int app_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 app_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
|
|
|
|
/* 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 if 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 platform, such as differentiating between
|
|
* text and binary input/output on non-Unix platforms
|
|
*/
|
|
static int istext(int format)
|
|
{
|
|
return (format & B_FORMAT_TEXT) == B_FORMAT_TEXT;
|
|
}
|
|
|
|
BIO *dup_bio_in(int format)
|
|
{
|
|
return BIO_new_fp(stdin,
|
|
BIO_NOCLOSE | (istext(format) ? BIO_FP_TEXT : 0));
|
|
}
|
|
|
|
static BIO_METHOD *prefix_method = NULL;
|
|
|
|
BIO *dup_bio_out(int format)
|
|
{
|
|
BIO *b = BIO_new_fp(stdout,
|
|
BIO_NOCLOSE | (istext(format) ? BIO_FP_TEXT : 0));
|
|
void *prefix = NULL;
|
|
|
|
#ifdef OPENSSL_SYS_VMS
|
|
if (istext(format))
|
|
b = BIO_push(BIO_new(BIO_f_linebuffer()), b);
|
|
#endif
|
|
|
|
if (istext(format) && (prefix = getenv("HARNESS_OSSL_PREFIX")) != NULL) {
|
|
if (prefix_method == NULL)
|
|
prefix_method = apps_bf_prefix();
|
|
b = BIO_push(BIO_new(prefix_method), 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 | (istext(format) ? BIO_FP_TEXT : 0));
|
|
#ifdef OPENSSL_SYS_VMS
|
|
if (istext(format))
|
|
b = BIO_push(BIO_new(BIO_f_linebuffer()), b);
|
|
#endif
|
|
return b;
|
|
}
|
|
|
|
void destroy_prefix_method(void)
|
|
{
|
|
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 istext(format) ? "a" : "ab";
|
|
case 'r':
|
|
return istext(format) ? "r" : "rb";
|
|
case 'w':
|
|
return 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 = 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]);
|
|
}
|