root/usr.bin/openssl/req.c
/* $OpenBSD: req.c,v 1.29 2024/04/17 01:24:43 tb Exp $ */
/* Copyright (C) 1995-1998 Eric Young (eay@cryptsoft.com)
 * All rights reserved.
 *
 * This package is an SSL implementation written
 * by Eric Young (eay@cryptsoft.com).
 * The implementation was written so as to conform with Netscapes SSL.
 *
 * This library is free for commercial and non-commercial use as long as
 * the following conditions are aheared to.  The following conditions
 * apply to all code found in this distribution, be it the RC4, RSA,
 * lhash, DES, etc., code; not just the SSL code.  The SSL documentation
 * included with this distribution is covered by the same copyright terms
 * except that the holder is Tim Hudson (tjh@cryptsoft.com).
 *
 * Copyright remains Eric Young's, and as such any Copyright notices in
 * the code are not to be removed.
 * If this package is used in a product, Eric Young should be given attribution
 * as the author of the parts of the library used.
 * This can be in the form of a textual message at program startup or
 * in documentation (online or textual) provided with the package.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 * 3. All advertising materials mentioning features or use of this software
 *    must display the following acknowledgement:
 *    "This product includes cryptographic software written by
 *     Eric Young (eay@cryptsoft.com)"
 *    The word 'cryptographic' can be left out if the rouines from the library
 *    being used are not cryptographic related :-).
 * 4. If you include any Windows specific code (or a derivative thereof) from
 *    the apps directory (application code) you must include an acknowledgement:
 *    "This product includes software written by Tim Hudson (tjh@cryptsoft.com)"
 *
 * THIS SOFTWARE IS PROVIDED BY ERIC YOUNG ``AS IS'' AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 *
 * The licence and distribution terms for any publically available version or
 * derivative of this code cannot be changed.  i.e. this code cannot simply be
 * copied and put under another distribution licence
 * [including the GNU Public Licence.]
 */

/* Until the key-gen callbacks are modified to use newer prototypes, we allow
 * deprecated functions for openssl-internal code */
#ifdef OPENSSL_NO_DEPRECATED
#undef OPENSSL_NO_DEPRECATED
#endif

#include <ctype.h>
#include <limits.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>

#include "apps.h"

#include <openssl/asn1.h>
#include <openssl/bio.h>
#include <openssl/bn.h>
#include <openssl/conf.h>
#include <openssl/err.h>
#include <openssl/evp.h>
#include <openssl/objects.h>
#include <openssl/pem.h>
#include <openssl/x509.h>
#include <openssl/x509v3.h>

#include <openssl/dsa.h>

#include <openssl/rsa.h>

#define SECTION         "req"

#define BITS            "default_bits"
#define KEYFILE         "default_keyfile"
#define PROMPT          "prompt"
#define DISTINGUISHED_NAME      "distinguished_name"
#define ATTRIBUTES      "attributes"
#define V3_EXTENSIONS   "x509_extensions"
#define REQ_EXTENSIONS  "req_extensions"
#define STRING_MASK     "string_mask"
#define UTF8_IN         "utf8"

#define DEFAULT_KEY_LENGTH      2048
#define MIN_KEY_LENGTH          384

static int make_REQ(X509_REQ * req, EVP_PKEY * pkey, char *dn, int multirdn,
    int attribs, unsigned long chtype);
static int build_subject(X509_REQ * req, char *subj, unsigned long chtype,
    int multirdn);
static int prompt_info(X509_REQ * req,
    STACK_OF(CONF_VALUE) * dn_sk, char *dn_sect,
    STACK_OF(CONF_VALUE) * attr_sk, char *attr_sect, int attribs,
    unsigned long chtype);
static int auto_info(X509_REQ * req, STACK_OF(CONF_VALUE) * sk,
    STACK_OF(CONF_VALUE) * attr, int attribs,
    unsigned long chtype);
static int add_attribute_object(X509_REQ * req, char *text, const char *def,
    char *value, int nid, int n_min,
    int n_max, unsigned long chtype);
static int add_DN_object(X509_NAME * n, char *text, const char *def, char *value,
    int nid, int n_min, int n_max, unsigned long chtype, int mval);
static int genpkey_cb(EVP_PKEY_CTX * ctx);
static int req_check_len(int len, int n_min, int n_max);
static int check_end(const char *str, const char *end);
static EVP_PKEY_CTX *set_keygen_ctx(BIO * err, const char *gstr, int *pkey_type,
    long *pkeylen, char **palgnam);
static unsigned long ext_name_hash(const OPENSSL_STRING *a);
static int ext_name_cmp(const OPENSSL_STRING *a, const OPENSSL_STRING *b);
static void exts_cleanup(OPENSSL_STRING *x);
static int duplicated(LHASH_OF(OPENSSL_STRING) *addexts, char *kv);
static CONF *req_conf = NULL;
static CONF *addext_conf = NULL;

static struct {
        LHASH_OF(OPENSSL_STRING) *addexts;
        BIO *addext_bio;
        int batch;
        unsigned long chtype;
        int days;
        const EVP_MD *digest;
        char *extensions;
        char *infile;
        int informat;
        char *keyalg;
        char *keyfile;
        int keyform;
        char *keyout;
        int modulus;
        int multirdn;
        int newhdr;
        long newkey;
        int newreq;
        unsigned long nmflag;
        int nodes;
        int noout;
        char *outfile;
        int outformat;
        char *passargin;
        char *passargout;
        STACK_OF(OPENSSL_STRING) *pkeyopts;
        int pubkey;
        char *req_exts;
        unsigned long reqflag;
        ASN1_INTEGER *serial;
        STACK_OF(OPENSSL_STRING) *sigopts;
        char *subj;
        int subject;
        char *template;
        int text;
        int verbose;
        int verify;
        int x509;
} cfg;

static int
req_opt_addext(char *arg)
{
        int i;

        if (cfg.addexts == NULL) {
                cfg.addexts = (LHASH_OF(OPENSSL_STRING) *)lh_new(
                    (LHASH_HASH_FN_TYPE)ext_name_hash,
                    (LHASH_COMP_FN_TYPE)ext_name_cmp);
                cfg.addext_bio = BIO_new(BIO_s_mem());
                if (cfg.addexts == NULL ||
                    cfg.addext_bio == NULL)
                        return (1);
        }
        i = duplicated(cfg.addexts, arg);
        if (i == 1)
                return (1);
        if (i < 0 || BIO_printf(cfg.addext_bio, "%s\n", arg) < 0)
                return (1);

        return (0);
}

static int
req_opt_days(char *arg)
{
        const char *errstr;

        cfg.days = strtonum(arg, 1, INT_MAX, &errstr);
        if (errstr != NULL) {
                BIO_printf(bio_err, "bad -days %s, using 0: %s\n",
                    arg, errstr);
                cfg.days = 30;
        }
        return (0);
}

static int
req_opt_digest(int argc, char **argv, int *argsused)
{
        char *name = argv[0];

        if (*name++ != '-')
                return (1);

        if ((cfg.digest = EVP_get_digestbyname(name)) == NULL)
                return (1);

        *argsused = 1;
        return (0);
}

static int
req_opt_newkey(char *arg)
{
        cfg.keyalg = arg;
        cfg.newreq = 1;
        return (0);
}

static int
req_opt_nameopt(char *arg)
{
        if (!set_name_ex(&cfg.nmflag, arg))
                return (1);
        return (0);
}

static int
req_opt_pkeyopt(char *arg)
{
        if (cfg.pkeyopts == NULL)
                cfg.pkeyopts = sk_OPENSSL_STRING_new_null();
        if (cfg.pkeyopts == NULL)
                return (1);
        if (!sk_OPENSSL_STRING_push(cfg.pkeyopts, arg))
                return (1);
        return (0);
}

static int
req_opt_reqopt(char *arg)
{
        if (!set_cert_ex(&cfg.reqflag, arg))
                return (1);
        return (0);
}

static int
req_opt_set_serial(char *arg)
{
        cfg.serial = s2i_ASN1_INTEGER(NULL, arg);
        if (cfg.serial == NULL)
                return (1);
        return (0);
}

static int
req_opt_sigopt(char *arg)
{
        if (cfg.sigopts == NULL)
                cfg.sigopts = sk_OPENSSL_STRING_new_null();
        if (cfg.sigopts == NULL)
                return (1);
        if (!sk_OPENSSL_STRING_push(cfg.sigopts, arg))
                return (1);
        return (0);
}

static int
req_opt_utf8(void)
{
        cfg.chtype = MBSTRING_UTF8;
        return (0);
}

static const struct option req_options[] = {
        {
                .name = "addext",
                .argname = "key=value",
                .desc = "Additional certificate extension (may be repeated)",
                .type = OPTION_ARG_FUNC,
                .opt.argfunc = req_opt_addext,
        },
        {
                .name = "batch",
                .desc = "Operate in batch mode",
                .type = OPTION_FLAG,
                .opt.flag = &cfg.batch,
        },
        {
                .name = "config",
                .argname = "file",
                .desc = "Configuration file to use as request template",
                .type = OPTION_ARG,
                .opt.arg = &cfg.template,
        },
        {
                .name = "days",
                .argname = "number",
                .desc = "Number of days generated certificate is valid for",
                .type = OPTION_ARG_FUNC,
                .opt.argfunc = req_opt_days,
        },
        {
                .name = "extensions",
                .argname = "section",
                .desc = "Config section to use for certificate extensions",
                .type = OPTION_ARG,
                .opt.arg = &cfg.extensions,
        },
        {
                .name = "in",
                .argname = "file",
                .desc = "Input file (default stdin)",
                .type = OPTION_ARG,
                .opt.arg = &cfg.infile,
        },
        {
                .name = "inform",
                .argname = "format",
                .desc = "Input format (DER or PEM (default))",
                .type = OPTION_ARG_FORMAT,
                .opt.value = &cfg.informat,
        },
        {
                .name = "key",
                .argname = "file",
                .desc = "Private key file",
                .type = OPTION_ARG,
                .opt.arg = &cfg.keyfile,
        },
        {
                .name = "keyform",
                .argname = "format",
                .desc = "Private key format (DER or PEM (default))",
                .type = OPTION_ARG_FORMAT,
                .opt.value = &cfg.keyform,
        },
        {
                .name = "keyout",
                .argname = "file",
                .desc = "Private key output file",
                .type = OPTION_ARG,
                .opt.arg = &cfg.keyout,
        },
        {
                .name = "modulus",
                .desc = "Print RSA modulus",
                .type = OPTION_FLAG,
                .opt.flag = &cfg.modulus,
        },
        {
                .name = "multivalue-rdn",
                .desc = "Enable support for multivalued RDNs",
                .type = OPTION_FLAG,
                .opt.flag = &cfg.multirdn,
        },
        {
                .name = "nameopt",
                .argname = "arg",
                .desc = "Certificate name options",
                .type = OPTION_ARG_FUNC,
                .opt.argfunc = req_opt_nameopt,
        },
        {
                .name = "new",
                .desc = "New request",
                .type = OPTION_FLAG,
                .opt.flag = &cfg.newreq,
        },
        {
                .name = "newhdr",
                .desc = "Include 'NEW' in header lines",
                .type = OPTION_FLAG,
                .opt.flag = &cfg.newhdr,
        },
        {
                .name = "newkey",
                .argname = "param",
                .desc = "Generate a new key using given parameters",
                .type = OPTION_ARG_FUNC,
                .opt.argfunc = req_opt_newkey,
        },
        {
                .name = "nodes",
                .desc = "Do not encrypt output private key",
                .type = OPTION_FLAG,
                .opt.flag = &cfg.nodes,
        },
        {
                .name = "noout",
                .desc = "Do not output request",
                .type = OPTION_FLAG,
                .opt.flag = &cfg.noout,
        },
        {
                .name = "out",
                .argname = "file",
                .desc = "Output file (default stdout)",
                .type = OPTION_ARG,
                .opt.arg = &cfg.outfile,
        },
        {
                .name = "outform",
                .argname = "format",
                .desc = "Output format (DER or PEM (default))",
                .type = OPTION_ARG_FORMAT,
                .opt.value = &cfg.outformat,
        },
        {
                .name = "passin",
                .argname = "source",
                .desc = "Private key input password source",
                .type = OPTION_ARG,
                .opt.arg = &cfg.passargin,
        },
        {
                .name = "passout",
                .argname = "source",
                .desc = "Private key output password source",
                .type = OPTION_ARG,
                .opt.arg = &cfg.passargout,
        },
        {
                .name = "pkeyopt",
                .argname = "opt:val",
                .desc = "Set the public key algorithm option opt to val",
                .type = OPTION_ARG_FUNC,
                .opt.argfunc = req_opt_pkeyopt,
        },
        {
                .name = "pubkey",
                .desc = "Output the public key",
                .type = OPTION_FLAG,
                .opt.flag = &cfg.pubkey,
        },
        {
                .name = "reqexts",
                .argname = "section",
                .desc = "Config section to use for request extensions",
                .type = OPTION_ARG,
                .opt.arg = &cfg.req_exts,
        },
        {
                .name = "reqopt",
                .argname = "option",
                .desc = "Request text options",
                .type = OPTION_ARG_FUNC,
                .opt.argfunc = req_opt_reqopt,
        },
        {
                .name = "set_serial",
                .argname = "serial",
                .desc = "Serial number to use for generated certificate",
                .type = OPTION_ARG_FUNC,
                .opt.argfunc = req_opt_set_serial,
        },
        {
                .name = "sigopt",
                .argname = "name:val",
                .desc = "Signature options",
                .type = OPTION_ARG_FUNC,
                .opt.argfunc = req_opt_sigopt,
        },
        {
                .name = "subj",
                .argname = "name",
                .desc = "Set or modify the request subject",
                .type = OPTION_ARG,
                .opt.arg = &cfg.subj,
        },
        {
                .name = "subject",
                .desc = "Output the subject of the request",
                .type = OPTION_FLAG,
                .opt.flag = &cfg.subject,
        },
        {
                .name = "text",
                .desc = "Print request in text form",
                .type = OPTION_FLAG,
                .opt.flag = &cfg.text,
        },
        {
                .name = "utf8",
                .desc = "Input characters are in UTF-8 (default ASCII)",
                .type = OPTION_FUNC,
                .opt.func = req_opt_utf8,
        },
        {
                .name = "verbose",
                .desc = "Verbose",
                .type = OPTION_FLAG,
                .opt.flag = &cfg.verbose,
        },
        {
                .name = "verify",
                .desc = "Verify signature on request",
                .type = OPTION_FLAG,
                .opt.flag = &cfg.verify,
        },
        {
                .name = "x509",
                .desc = "Output an X.509 structure instead of a certificate request",
                .type = OPTION_FLAG,
                .opt.flag = &cfg.x509,
        },
        {
                .name = NULL,
                .desc = "",
                .type = OPTION_ARGV_FUNC,
                .opt.argvfunc = req_opt_digest,
        },
        { NULL },
};

static void
req_usage(void)
{
        fprintf(stderr,
            "usage: req [-addext ext] [-batch] [-config file]\n"
            "    [-days n] [-extensions section] [-in file]\n"
            "    [-inform der | pem] [-key keyfile] [-keyform der | pem]\n"
            "    [-keyout file] [-md4 | -md5 | -sha1] [-modulus]\n"
            "    [-multivalue-rdn] [-nameopt option] [-new] [-newhdr]\n"
            "    [-newkey arg] [-nodes] [-noout]\n"
            "    [-out file] [-outform der | pem] [-passin arg]\n"
            "    [-passout arg] [-pkeyopt opt:value] [-pubkey]\n"
            "    [-reqexts section] [-reqopt option] [-set_serial n]\n"
            "    [-sigopt nm:v] [-subj arg] [-subject] [-text] [-utf8]\n"
            "    [-verbose] [-verify] [-x509]\n\n");

        options_usage(req_options);
        fprintf(stderr, "\n");
}

int
req_main(int argc, char **argv)
{
        int ex = 1;
        X509 *x509ss = NULL;
        X509_REQ *req = NULL;
        EVP_PKEY_CTX *genctx = NULL;
        char *keyalgstr = NULL;
        const EVP_CIPHER *cipher = NULL;
        EVP_PKEY *pkey = NULL;
        int i = 0, pkey_type = -1;
        BIO *in = NULL, *out = NULL;
        char *passin = NULL, *passout = NULL;
        const EVP_MD *md_alg = NULL;
        char *p;

        if (pledge("stdio cpath wpath rpath tty", NULL) == -1) {
                perror("pledge");
                exit(1);
        }

        memset(&cfg, 0, sizeof(cfg));

        cfg.chtype = MBSTRING_ASC;
        cfg.days = 30;
        cfg.digest = EVP_sha256();
        cfg.newkey = -1;
        cfg.informat = FORMAT_PEM;
        cfg.keyform = FORMAT_PEM;
        cfg.outformat = FORMAT_PEM;

        if (options_parse(argc, argv, req_options, NULL, NULL) != 0) {
                req_usage();
                return (1);
        }

        req_conf = NULL;
        cipher = EVP_aes_256_cbc();

        if (!app_passwd(bio_err, cfg.passargin, cfg.passargout, &passin, &passout)) {
                BIO_printf(bio_err, "Error getting passwords\n");
                goto end;
        }
        if (cfg.template != NULL) {
                long errline = -1;

                if (cfg.verbose)
                        BIO_printf(bio_err, "Using configuration from %s\n", cfg.template);
                if ((req_conf = NCONF_new(NULL)) == NULL)
                        goto end;
                if(!NCONF_load(req_conf, cfg.template, &errline)) {
                        BIO_printf(bio_err, "error on line %ld of %s\n", errline, cfg.template);
                        goto end;
                }
        } else {
                req_conf = config;

                if (req_conf == NULL) {
                        BIO_printf(bio_err, "Unable to load config info from %s\n", default_config_file);
                        if (cfg.newreq)
                                goto end;
                } else if (cfg.verbose)
                        BIO_printf(bio_err, "Using configuration from %s\n",
                            default_config_file);
        }

        if (cfg.addext_bio != NULL) {
                long errline = -1;
                if (cfg.verbose)
                        BIO_printf(bio_err,
                            "Using additional configuration from command line\n");
                if ((addext_conf = NCONF_new(NULL)) == NULL)
                        goto end;
                if (!NCONF_load_bio(addext_conf, cfg.addext_bio, &errline)) {
                        BIO_printf(bio_err,
                            "req: Error on line %ld of config input\n",
                            errline);
                        goto end;
                }
        }

        if (req_conf != NULL) {
                if (!load_config(bio_err, req_conf))
                        goto end;
                p = NCONF_get_string(req_conf, NULL, "oid_file");
                if (p == NULL)
                        ERR_clear_error();
                if (p != NULL) {
                        BIO *oid_bio;

                        oid_bio = BIO_new_file(p, "r");
                        if (oid_bio == NULL) {
                                /*
                                BIO_printf(bio_err,"problems opening %s for extra oid's\n",p);
                                ERR_print_errors(bio_err);
                                */
                        } else {
                                OBJ_create_objects(oid_bio);
                                BIO_free(oid_bio);
                        }
                }
        }
        if (!add_oid_section(bio_err, req_conf))
                goto end;

        if (md_alg == NULL) {
                p = NCONF_get_string(req_conf, SECTION, "default_md");
                if (p == NULL)
                        ERR_clear_error();
                if (p != NULL) {
                        if ((md_alg = EVP_get_digestbyname(p)) != NULL)
                                cfg.digest = md_alg;
                }
        }
        if (!cfg.extensions) {
                cfg.extensions = NCONF_get_string(req_conf, SECTION, V3_EXTENSIONS);
                if (!cfg.extensions)
                        ERR_clear_error();
        }
        if (cfg.extensions) {
                /* Check syntax of file */
                X509V3_CTX ctx;
                X509V3_set_ctx_test(&ctx);
                X509V3_set_nconf(&ctx, req_conf);
                if (!X509V3_EXT_add_nconf(req_conf, &ctx, cfg.extensions, NULL)) {
                        BIO_printf(bio_err,
                            "Error Loading extension section %s\n", cfg.extensions);
                        goto end;
                }
        }
        if (addext_conf != NULL) {
                /* Check syntax of command line extensions */
                X509V3_CTX ctx;
                X509V3_set_ctx_test(&ctx);
                X509V3_set_nconf(&ctx, addext_conf);
                if (!X509V3_EXT_add_nconf(addext_conf, &ctx, "default", NULL)) {
                        BIO_printf(bio_err,
                            "Error Loading command line extensions\n");
                        goto end;
                }
        }
        if (!passin) {
                passin = NCONF_get_string(req_conf, SECTION, "input_password");
                if (!passin)
                        ERR_clear_error();
        }
        if (!passout) {
                passout = NCONF_get_string(req_conf, SECTION, "output_password");
                if (!passout)
                        ERR_clear_error();
        }
        p = NCONF_get_string(req_conf, SECTION, STRING_MASK);
        if (!p)
                ERR_clear_error();

        if (p && !ASN1_STRING_set_default_mask_asc(p)) {
                BIO_printf(bio_err, "Invalid global string mask setting %s\n", p);
                goto end;
        }
        if (cfg.chtype != MBSTRING_UTF8) {
                p = NCONF_get_string(req_conf, SECTION, UTF8_IN);
                if (!p)
                        ERR_clear_error();
                else if (!strcmp(p, "yes"))
                        cfg.chtype = MBSTRING_UTF8;
        }
        if (!cfg.req_exts) {
                cfg.req_exts = NCONF_get_string(req_conf, SECTION, REQ_EXTENSIONS);
                if (!cfg.req_exts)
                        ERR_clear_error();
        }
        if (cfg.req_exts) {
                /* Check syntax of file */
                X509V3_CTX ctx;
                X509V3_set_ctx_test(&ctx);
                X509V3_set_nconf(&ctx, req_conf);
                if (!X509V3_EXT_add_nconf(req_conf, &ctx, cfg.req_exts, NULL)) {
                        BIO_printf(bio_err,
                            "Error Loading request extension section %s\n",
                            cfg.req_exts);
                        goto end;
                }
        }
        in = BIO_new(BIO_s_file());
        out = BIO_new(BIO_s_file());
        if ((in == NULL) || (out == NULL))
                goto end;

        if (cfg.keyfile != NULL) {
                pkey = load_key(bio_err, cfg.keyfile, cfg.keyform, 0, passin,
                    "Private Key");
                if (!pkey) {
                        /*
                         * load_key() has already printed an appropriate
                         * message
                         */
                        goto end;
                }
        }
        if (cfg.newreq && (pkey == NULL)) {
                if (!NCONF_get_number(req_conf, SECTION, BITS, &cfg.newkey)) {
                        cfg.newkey = DEFAULT_KEY_LENGTH;
                }
                if (cfg.keyalg) {
                        genctx = set_keygen_ctx(bio_err, cfg.keyalg, &pkey_type, &cfg.newkey,
                            &keyalgstr);
                        if (!genctx)
                                goto end;
                }
                if (cfg.newkey < MIN_KEY_LENGTH && (pkey_type == EVP_PKEY_RSA || pkey_type == EVP_PKEY_DSA)) {
                        BIO_printf(bio_err, "private key length is too short,\n");
                        BIO_printf(bio_err, "it needs to be at least %d bits, not %ld\n", MIN_KEY_LENGTH, cfg.newkey);
                        goto end;
                }
                if (!genctx) {
                        genctx = set_keygen_ctx(bio_err, NULL, &pkey_type, &cfg.newkey,
                            &keyalgstr);
                        if (!genctx)
                                goto end;
                }
                if (cfg.pkeyopts) {
                        char *genopt;
                        for (i = 0; i < sk_OPENSSL_STRING_num(cfg.pkeyopts); i++) {
                                genopt = sk_OPENSSL_STRING_value(cfg.pkeyopts, i);
                                if (pkey_ctrl_string(genctx, genopt) <= 0) {
                                        BIO_printf(bio_err,
                                            "parameter error \"%s\"\n",
                                            genopt);
                                        ERR_print_errors(bio_err);
                                        goto end;
                                }
                        }
                }
                BIO_printf(bio_err, "Generating a %ld bit %s private key\n",
                    cfg.newkey, keyalgstr);

                EVP_PKEY_CTX_set_cb(genctx, genpkey_cb);
                EVP_PKEY_CTX_set_app_data(genctx, bio_err);

                if (EVP_PKEY_keygen(genctx, &pkey) <= 0) {
                        BIO_puts(bio_err, "Error Generating Key\n");
                        goto end;
                }
                EVP_PKEY_CTX_free(genctx);
                genctx = NULL;

                if (cfg.keyout == NULL) {
                        cfg.keyout = NCONF_get_string(req_conf, SECTION, KEYFILE);
                        if (cfg.keyout == NULL)
                                ERR_clear_error();
                }
                if (cfg.keyout == NULL) {
                        BIO_printf(bio_err, "writing new private key to stdout\n");
                        BIO_set_fp(out, stdout, BIO_NOCLOSE);
                } else {
                        BIO_printf(bio_err, "writing new private key to '%s'\n", cfg.keyout);
                        if (BIO_write_filename(out, cfg.keyout) <= 0) {
                                perror(cfg.keyout);
                                goto end;
                        }
                }

                p = NCONF_get_string(req_conf, SECTION, "encrypt_rsa_key");
                if (p == NULL) {
                        ERR_clear_error();
                        p = NCONF_get_string(req_conf, SECTION, "encrypt_key");
                        if (p == NULL)
                                ERR_clear_error();
                }
                if ((p != NULL) && (strcmp(p, "no") == 0))
                        cipher = NULL;
                if (cfg.nodes)
                        cipher = NULL;

                i = 0;
 loop:
                if (!PEM_write_bio_PrivateKey(out, pkey, cipher,
                        NULL, 0, NULL, passout)) {
                        if ((ERR_GET_REASON(ERR_peek_error()) ==
                                PEM_R_PROBLEMS_GETTING_PASSWORD) && (i < 3)) {
                                ERR_clear_error();
                                i++;
                                goto loop;
                        }
                        goto end;
                }
                BIO_printf(bio_err, "-----\n");
        }
        if (!cfg.newreq) {
                if (cfg.infile == NULL)
                        BIO_set_fp(in, stdin, BIO_NOCLOSE);
                else {
                        if (BIO_read_filename(in, cfg.infile) <= 0) {
                                perror(cfg.infile);
                                goto end;
                        }
                }

                if (cfg.informat == FORMAT_ASN1)
                        req = d2i_X509_REQ_bio(in, NULL);
                else if (cfg.informat == FORMAT_PEM)
                        req = PEM_read_bio_X509_REQ(in, NULL, NULL, NULL);
                else {
                        BIO_printf(bio_err, "bad input format specified for X509 request\n");
                        goto end;
                }
                if (req == NULL) {
                        BIO_printf(bio_err, "unable to load X509 request\n");
                        goto end;
                }
        }
        if (cfg.newreq || cfg.x509) {
                if (pkey == NULL) {
                        BIO_printf(bio_err, "you need to specify a private key\n");
                        goto end;
                }
                if (req == NULL) {
                        req = X509_REQ_new();
                        if (req == NULL) {
                                goto end;
                        }
                        i = make_REQ(req, pkey, cfg.subj, cfg.multirdn, !cfg.x509, cfg.chtype);
                        cfg.subj = NULL;        /* done processing '-subj' option */
                        if (!i) {
                                BIO_printf(bio_err, "problems making Certificate Request\n");
                                goto end;
                        }
                }
                if (cfg.x509) {
                        EVP_PKEY *tmppkey;

                        X509V3_CTX ext_ctx;
                        if ((x509ss = X509_new()) == NULL)
                                goto end;

                        /* Set version to V3 */
                        if ((cfg.extensions != NULL || addext_conf != NULL) &&
                            !X509_set_version(x509ss, 2))
                                goto end;
                        if (cfg.serial) {
                                if (!X509_set_serialNumber(x509ss, cfg.serial))
                                        goto end;
                        } else {
                                if (!rand_serial(NULL,
                                        X509_get_serialNumber(x509ss)))
                                        goto end;
                        }

                        if (!X509_set_issuer_name(x509ss, X509_REQ_get_subject_name(req)))
                                goto end;
                        if (!X509_gmtime_adj(X509_get_notBefore(x509ss), 0))
                                goto end;
                        if (!X509_time_adj_ex(X509_get_notAfter(x509ss), cfg.days, 0, NULL))
                                goto end;
                        if (!X509_set_subject_name(x509ss, X509_REQ_get_subject_name(req)))
                                goto end;
                        if ((tmppkey = X509_REQ_get0_pubkey(req)) == NULL)
                                goto end;
                        if (!X509_set_pubkey(x509ss, tmppkey))
                                goto end;

                        /* Set up V3 context struct */

                        X509V3_set_ctx(&ext_ctx, x509ss, x509ss, NULL, NULL, 0);
                        X509V3_set_nconf(&ext_ctx, req_conf);

                        /* Add extensions */
                        if (cfg.extensions && !X509V3_EXT_add_nconf(req_conf,
                                &ext_ctx, cfg.extensions, x509ss)) {
                                BIO_printf(bio_err,
                                    "Error Loading extension section %s\n",
                                    cfg.extensions);
                                goto end;
                        }
                        if (addext_conf != NULL &&
                            !X509V3_EXT_add_nconf(addext_conf, &ext_ctx,
                                    "default", x509ss)) {
                                BIO_printf(bio_err,
                                    "Error Loading command line extensions\n");
                                goto end;
                        }
                        i = do_X509_sign(bio_err, x509ss, pkey, cfg.digest, cfg.sigopts);
                        if (!i) {
                                ERR_print_errors(bio_err);
                                goto end;
                        }
                } else {
                        X509V3_CTX ext_ctx;

                        /* Set up V3 context struct */

                        X509V3_set_ctx(&ext_ctx, NULL, NULL, req, NULL, 0);
                        X509V3_set_nconf(&ext_ctx, req_conf);

                        /* Add extensions */
                        if (cfg.req_exts && !X509V3_EXT_REQ_add_nconf(req_conf,
                                &ext_ctx, cfg.req_exts, req)) {
                                BIO_printf(bio_err,
                                    "Error Loading extension section %s\n",
                                    cfg.req_exts);
                                goto end;
                        }
                        if (addext_conf != NULL &&
                            !X509V3_EXT_REQ_add_nconf(addext_conf, &ext_ctx,
                                    "default", req)) {
                                BIO_printf(bio_err,
                                    "Error Loading command line extensions\n");
                                goto end;
                        }
                        i = do_X509_REQ_sign(bio_err, req, pkey, cfg.digest, cfg.sigopts);
                        if (!i) {
                                ERR_print_errors(bio_err);
                                goto end;
                        }
                }
        }
        if (cfg.subj && cfg.x509) {
                BIO_printf(bio_err, "Cannot modify certificate subject\n");
                goto end;
        }
        if (cfg.subj && !cfg.x509) {
                if (cfg.verbose) {
                        BIO_printf(bio_err, "Modifying Request's Subject\n");
                        print_name(bio_err, "old subject=", X509_REQ_get_subject_name(req), cfg.nmflag);
                }
                if (build_subject(req, cfg.subj, cfg.chtype, cfg.multirdn) == 0) {
                        BIO_printf(bio_err, "ERROR: cannot modify subject\n");
                        ex = 1;
                        goto end;
                }

                if (cfg.verbose) {
                        print_name(bio_err, "new subject=", X509_REQ_get_subject_name(req), cfg.nmflag);
                }
        }
        if (cfg.verify && !cfg.x509) {
                EVP_PKEY *pubkey = pkey;

                if (pubkey == NULL)
                        pubkey = X509_REQ_get0_pubkey(req);
                if (pubkey == NULL)
                        goto end;
                i = X509_REQ_verify(req, pubkey);
                if (i < 0) {
                        goto end;
                } else if (i == 0) {
                        BIO_printf(bio_err, "verify failure\n");
                        ERR_print_errors(bio_err);
                } else          /* if (i > 0) */
                        BIO_printf(bio_err, "verify OK\n");
        }
        if (cfg.noout && !cfg.text && !cfg.modulus && !cfg.subject && !cfg.pubkey) {
                ex = 0;
                goto end;
        }
        if (cfg.outfile == NULL) {
                BIO_set_fp(out, stdout, BIO_NOCLOSE);
        } else {
                if ((cfg.keyout != NULL) && (strcmp(cfg.outfile, cfg.keyout) == 0))
                        i = (int) BIO_append_filename(out, cfg.outfile);
                else
                        i = (int) BIO_write_filename(out, cfg.outfile);
                if (!i) {
                        perror(cfg.outfile);
                        goto end;
                }
        }

        if (cfg.pubkey) {
                EVP_PKEY *tpubkey;

                if ((tpubkey = X509_REQ_get0_pubkey(req)) == NULL) {
                        BIO_printf(bio_err, "Error getting public key\n");
                        ERR_print_errors(bio_err);
                        goto end;
                }
                PEM_write_bio_PUBKEY(out, tpubkey);
        }
        if (cfg.text) {
                if (cfg.x509)
                        X509_print_ex(out, x509ss, cfg.nmflag, cfg.reqflag);
                else
                        X509_REQ_print_ex(out, req, cfg.nmflag, cfg.reqflag);
        }
        if (cfg.subject) {
                if (cfg.x509)
                        print_name(out, "subject=", X509_get_subject_name(x509ss), cfg.nmflag);
                else
                        print_name(out, "subject=", X509_REQ_get_subject_name(req), cfg.nmflag);
        }
        if (cfg.modulus) {
                EVP_PKEY *tpubkey;

                if (cfg.x509)
                        tpubkey = X509_get0_pubkey(x509ss);
                else
                        tpubkey = X509_REQ_get0_pubkey(req);
                if (tpubkey == NULL) {
                        fprintf(stdout, "Modulus=unavailable\n");
                        goto end;
                }
                fprintf(stdout, "Modulus=");
                if (EVP_PKEY_base_id(tpubkey) == EVP_PKEY_RSA) {
                        const BIGNUM *n = NULL;

                        RSA_get0_key(EVP_PKEY_get0_RSA(tpubkey), &n, NULL, NULL);

                        BN_print(out, n);
                } else
                        fprintf(stdout, "Wrong Algorithm type");
                fprintf(stdout, "\n");
        }
        if (!cfg.noout && !cfg.x509) {
                if (cfg.outformat == FORMAT_ASN1)
                        i = i2d_X509_REQ_bio(out, req);
                else if (cfg.outformat == FORMAT_PEM) {
                        if (cfg.newhdr)
                                i = PEM_write_bio_X509_REQ_NEW(out, req);
                        else
                                i = PEM_write_bio_X509_REQ(out, req);
                } else {
                        BIO_printf(bio_err, "bad output format specified for outfile\n");
                        goto end;
                }
                if (!i) {
                        BIO_printf(bio_err, "unable to write X509 request\n");
                        goto end;
                }
        }
        if (!cfg.noout && cfg.x509 && (x509ss != NULL)) {
                if (cfg.outformat == FORMAT_ASN1)
                        i = i2d_X509_bio(out, x509ss);
                else if (cfg.outformat == FORMAT_PEM)
                        i = PEM_write_bio_X509(out, x509ss);
                else {
                        BIO_printf(bio_err, "bad output format specified for outfile\n");
                        goto end;
                }
                if (!i) {
                        BIO_printf(bio_err, "unable to write X509 certificate\n");
                        goto end;
                }
        }
        ex = 0;
 end:
        if (ex) {
                ERR_print_errors(bio_err);
        }
        if ((req_conf != NULL) && (req_conf != config))
                NCONF_free(req_conf);
        NCONF_free(addext_conf);
        BIO_free(cfg.addext_bio);
        BIO_free(in);
        BIO_free_all(out);
        EVP_PKEY_free(pkey);
        if (genctx)
                EVP_PKEY_CTX_free(genctx);
        if (cfg.pkeyopts)
                sk_OPENSSL_STRING_free(cfg.pkeyopts);
        if (cfg.sigopts)
                sk_OPENSSL_STRING_free(cfg.sigopts);
        lh_OPENSSL_STRING_doall(cfg.addexts, (LHASH_DOALL_FN_TYPE)exts_cleanup);
        lh_OPENSSL_STRING_free(cfg.addexts);
        free(keyalgstr);
        X509_REQ_free(req);
        X509_free(x509ss);
        ASN1_INTEGER_free(cfg.serial);
        if (cfg.passargin && passin)
                free(passin);
        if (cfg.passargout && passout)
                free(passout);
        OBJ_cleanup();

        return (ex);
}

static int
make_REQ(X509_REQ * req, EVP_PKEY * pkey, char *subj, int multirdn,
    int attribs, unsigned long chtype)
{
        int ret = 0, i;
        char no_prompt = 0;
        STACK_OF(CONF_VALUE) * dn_sk, *attr_sk = NULL;
        char *tmp, *dn_sect, *attr_sect;

        tmp = NCONF_get_string(req_conf, SECTION, PROMPT);
        if (tmp == NULL)
                ERR_clear_error();
        if ((tmp != NULL) && !strcmp(tmp, "no"))
                no_prompt = 1;

        dn_sect = NCONF_get_string(req_conf, SECTION, DISTINGUISHED_NAME);
        if (dn_sect == NULL) {
                BIO_printf(bio_err, "unable to find '%s' in config\n",
                    DISTINGUISHED_NAME);
                goto err;
        }
        dn_sk = NCONF_get_section(req_conf, dn_sect);
        if (dn_sk == NULL) {
                BIO_printf(bio_err, "unable to get '%s' section\n", dn_sect);
                goto err;
        }
        attr_sect = NCONF_get_string(req_conf, SECTION, ATTRIBUTES);
        if (attr_sect == NULL) {
                ERR_clear_error();
                attr_sk = NULL;
        } else {
                attr_sk = NCONF_get_section(req_conf, attr_sect);
                if (attr_sk == NULL) {
                        BIO_printf(bio_err, "unable to get '%s' section\n", attr_sect);
                        goto err;
                }
        }

        /* setup version number */
        if (!X509_REQ_set_version(req, 0L))
                goto err;       /* version 1 */

        if (no_prompt)
                i = auto_info(req, dn_sk, attr_sk, attribs, chtype);
        else {
                if (subj)
                        i = build_subject(req, subj, chtype, multirdn);
                else
                        i = prompt_info(req, dn_sk, dn_sect, attr_sk, attr_sect, attribs, chtype);
        }
        if (!i)
                goto err;

        if (!X509_REQ_set_pubkey(req, pkey))
                goto err;

        ret = 1;
 err:
        return (ret);
}

/*
 * subject is expected to be in the format /type0=value0/type1=value1/type2=...
 * where characters may be escaped by \
 */
static int
build_subject(X509_REQ * req, char *subject, unsigned long chtype, int multirdn)
{
        X509_NAME *n;

        if (!(n = parse_name(subject, chtype, multirdn)))
                return 0;

        if (!X509_REQ_set_subject_name(req, n)) {
                X509_NAME_free(n);
                return 0;
        }
        X509_NAME_free(n);
        return 1;
}


static int
prompt_info(X509_REQ * req,
    STACK_OF(CONF_VALUE) * dn_sk, char *dn_sect,
    STACK_OF(CONF_VALUE) * attr_sk, char *attr_sect, int attribs,
    unsigned long chtype)
{
        int i;
        char *p, *q;
        char buf[100];
        int nid, mval;
        long n_min, n_max;
        char *type, *value;
        const char *def;
        CONF_VALUE *v;
        X509_NAME *subj;
        subj = X509_REQ_get_subject_name(req);

        if (!cfg.batch) {
                BIO_printf(bio_err, "You are about to be asked to enter information that will be incorporated\n");
                BIO_printf(bio_err, "into your certificate request.\n");
                BIO_printf(bio_err, "What you are about to enter is what is called a Distinguished Name or a DN.\n");
                BIO_printf(bio_err, "There are quite a few fields but you can leave some blank\n");
                BIO_printf(bio_err, "For some fields there will be a default value,\n");
                BIO_printf(bio_err, "If you enter '.', the field will be left blank.\n");
                BIO_printf(bio_err, "-----\n");
        }
        if (sk_CONF_VALUE_num(dn_sk)) {
                i = -1;
 start:         for (;;) {
                        int ret;
                        i++;
                        if (sk_CONF_VALUE_num(dn_sk) <= i)
                                break;

                        v = sk_CONF_VALUE_value(dn_sk, i);
                        p = q = NULL;
                        type = v->name;
                        if (!check_end(type, "_min") || !check_end(type, "_max") ||
                            !check_end(type, "_default") ||
                            !check_end(type, "_value"))
                                continue;
                        /*
                         * Skip past any leading X. X: X, etc to allow for
                         * multiple instances
                         */
                        for (p = v->name; *p; p++)
                                if ((*p == ':') || (*p == ',') ||
                                    (*p == '.')) {
                                        p++;
                                        if (*p)
                                                type = p;
                                        break;
                                }
                        if (*type == '+') {
                                mval = -1;
                                type++;
                        } else
                                mval = 0;
                        /* If OBJ not recognised ignore it */
                        if ((nid = OBJ_txt2nid(type)) == NID_undef)
                                goto start;
                        ret = snprintf(buf, sizeof buf, "%s_default", v->name);
                        if (ret < 0 || ret >= sizeof(buf)) {
                                BIO_printf(bio_err, "Name '%s' too long for default\n",
                                    v->name);
                                return 0;
                        }
                        if ((def = NCONF_get_string(req_conf, dn_sect, buf)) == NULL) {
                                ERR_clear_error();
                                def = "";
                        }
                        ret = snprintf(buf, sizeof buf, "%s_value", v->name);
                        if (ret < 0 || ret >= sizeof(buf)) {
                                BIO_printf(bio_err, "Name '%s' too long for value\n",
                                    v->name);
                                return 0;
                        }
                        if ((value = NCONF_get_string(req_conf, dn_sect, buf)) == NULL) {
                                ERR_clear_error();
                                value = NULL;
                        }
                        ret = snprintf(buf, sizeof buf, "%s_min", v->name);
                        if (ret < 0 || ret >= sizeof(buf)) {
                                BIO_printf(bio_err, "Name '%s' too long for min\n",
                                    v->name);
                                return 0;
                        }
                        if (!NCONF_get_number(req_conf, dn_sect, buf, &n_min)) {
                                ERR_clear_error();
                                n_min = -1;
                        }
                        ret = snprintf(buf, sizeof buf, "%s_max", v->name);
                        if (ret < 0 || ret >= sizeof(buf)) {
                                BIO_printf(bio_err, "Name '%s' too long for max\n",
                                    v->name);
                                return 0;
                        }
                        if (!NCONF_get_number(req_conf, dn_sect, buf, &n_max)) {
                                ERR_clear_error();
                                n_max = -1;
                        }
                        if (!add_DN_object(subj, v->value, def, value, nid,
                                n_min, n_max, chtype, mval))
                                return 0;
                }
                if (X509_NAME_entry_count(subj) == 0) {
                        BIO_printf(bio_err, "error, no objects specified in config file\n");
                        return 0;
                }
                if (attribs) {
                        if ((attr_sk != NULL) && (sk_CONF_VALUE_num(attr_sk) > 0) &&
                            (!cfg.batch)) {
                                BIO_printf(bio_err,
                                    "\nPlease enter the following 'extra' attributes\n");
                                BIO_printf(bio_err,
                                    "to be sent with your certificate request\n");
                        }
                        i = -1;
start2:                 for (;;) {
                                int ret;
                                i++;
                                if ((attr_sk == NULL) ||
                                    (sk_CONF_VALUE_num(attr_sk) <= i))
                                        break;

                                v = sk_CONF_VALUE_value(attr_sk, i);
                                type = v->name;
                                if ((nid = OBJ_txt2nid(type)) == NID_undef)
                                        goto start2;
                                ret = snprintf(buf, sizeof buf, "%s_default", type);
                                if (ret < 0 || ret >= sizeof(buf)) {
                                        BIO_printf(bio_err, "Name '%s' too long for default\n",
                                            v->name);
                                        return 0;
                                }
                                if ((def = NCONF_get_string(req_conf, attr_sect, buf))
                                    == NULL) {
                                        ERR_clear_error();
                                        def = "";
                                }
                                ret = snprintf(buf, sizeof buf, "%s_value", type);
                                if (ret < 0 || ret >= sizeof(buf)) {
                                        BIO_printf(bio_err, "Name '%s' too long for value\n",
                                            v->name);
                                        return 0;
                                }
                                if ((value = NCONF_get_string(req_conf, attr_sect, buf))
                                    == NULL) {
                                        ERR_clear_error();
                                        value = NULL;
                                }
                                ret = snprintf(buf, sizeof buf, "%s_min", type);
                                if (ret < 0 || ret >= sizeof(buf)) {
                                        BIO_printf(bio_err, "Name '%s' too long for min\n",
                                            v->name);
                                        return 0;
                                }
                                if (!NCONF_get_number(req_conf, attr_sect, buf, &n_min)) {
                                        ERR_clear_error();
                                        n_min = -1;
                                }
                                ret = snprintf(buf, sizeof buf, "%s_max", type);
                                if (ret < 0 || ret >= sizeof(buf)) {
                                        BIO_printf(bio_err, "Name '%s' too long for max\n",
                                            v->name);
                                        return 0;
                                }
                                if (!NCONF_get_number(req_conf, attr_sect, buf, &n_max)) {
                                        ERR_clear_error();
                                        n_max = -1;
                                }
                                if (!add_attribute_object(req,
                                        v->value, def, value, nid, n_min, n_max, chtype))
                                        return 0;
                        }
                }
        } else {
                BIO_printf(bio_err, "No template, please set one up.\n");
                return 0;
        }

        return 1;

}

static int
auto_info(X509_REQ * req, STACK_OF(CONF_VALUE) * dn_sk,
    STACK_OF(CONF_VALUE) * attr_sk, int attribs, unsigned long chtype)
{
        int i;
        char *p, *q;
        char *type;
        CONF_VALUE *v;
        X509_NAME *subj;

        subj = X509_REQ_get_subject_name(req);

        for (i = 0; i < sk_CONF_VALUE_num(dn_sk); i++) {
                int mval;
                v = sk_CONF_VALUE_value(dn_sk, i);
                p = q = NULL;
                type = v->name;
                /*
                 * Skip past any leading X. X: X, etc to allow for multiple
                 * instances
                 */
                for (p = v->name; *p; p++)
                        if ((*p == ':') || (*p == ',') || (*p == '.')) {
                                p++;
                                if (*p)
                                        type = p;
                                break;
                        }
                if (*p == '+') {
                        p++;
                        mval = -1;
                } else
                        mval = 0;
                if (!X509_NAME_add_entry_by_txt(subj, type, chtype,
                        (unsigned char *) v->value, -1, -1, mval))
                        return 0;

        }

        if (!X509_NAME_entry_count(subj)) {
                BIO_printf(bio_err, "error, no objects specified in config file\n");
                return 0;
        }
        if (attribs) {
                for (i = 0; i < sk_CONF_VALUE_num(attr_sk); i++) {
                        v = sk_CONF_VALUE_value(attr_sk, i);
                        if (!X509_REQ_add1_attr_by_txt(req, v->name, chtype,
                                (unsigned char *) v->value, -1))
                                return 0;
                }
        }
        return 1;
}


static int
add_DN_object(X509_NAME * n, char *text, const char *def, char *value,
    int nid, int n_min, int n_max, unsigned long chtype, int mval)
{
        int i, ret = 0;
        char buf[1024];
 start:
        if (!cfg.batch)
                BIO_printf(bio_err, "%s [%s]:", text, def);
        (void) BIO_flush(bio_err);
        if (value != NULL) {
                strlcpy(buf, value, sizeof buf);
                strlcat(buf, "\n", sizeof buf);
                BIO_printf(bio_err, "%s\n", value);
        } else {
                buf[0] = '\0';
                if (!cfg.batch) {
                        if (!fgets(buf, sizeof buf, stdin))
                                return 0;
                } else {
                        buf[0] = '\n';
                        buf[1] = '\0';
                }
        }

        if (buf[0] == '\0')
                return (0);
        else if (buf[0] == '\n') {
                if ((def == NULL) || (def[0] == '\0'))
                        return (1);
                strlcpy(buf, def, sizeof buf);
                strlcat(buf, "\n", sizeof buf);
        } else if ((buf[0] == '.') && (buf[1] == '\n'))
                return (1);

        i = strlen(buf);
        if (buf[i - 1] != '\n') {
                BIO_printf(bio_err, "weird input :-(\n");
                return (0);
        }
        buf[--i] = '\0';
        if (!req_check_len(i, n_min, n_max))
                goto start;
        if (!X509_NAME_add_entry_by_NID(n, nid, chtype,
                (unsigned char *) buf, -1, -1, mval))
                goto err;
        ret = 1;
 err:
        return (ret);
}

static int
add_attribute_object(X509_REQ * req, char *text, const char *def,
    char *value, int nid, int n_min,
    int n_max, unsigned long chtype)
{
        int i;
        static char buf[1024];

 start:
        if (!cfg.batch)
                BIO_printf(bio_err, "%s [%s]:", text, def);
        (void) BIO_flush(bio_err);
        if (value != NULL) {
                strlcpy(buf, value, sizeof buf);
                strlcat(buf, "\n", sizeof buf);
                BIO_printf(bio_err, "%s\n", value);
        } else {
                buf[0] = '\0';
                if (!cfg.batch) {
                        if (!fgets(buf, sizeof buf, stdin))
                                return 0;
                } else {
                        buf[0] = '\n';
                        buf[1] = '\0';
                }
        }

        if (buf[0] == '\0')
                return (0);
        else if (buf[0] == '\n') {
                if ((def == NULL) || (def[0] == '\0'))
                        return (1);
                strlcpy(buf, def, sizeof buf);
                strlcat(buf, "\n", sizeof buf);
        } else if ((buf[0] == '.') && (buf[1] == '\n'))
                return (1);

        i = strlen(buf);
        if (buf[i - 1] != '\n') {
                BIO_printf(bio_err, "weird input :-(\n");
                return (0);
        }
        buf[--i] = '\0';
        if (!req_check_len(i, n_min, n_max))
                goto start;

        if (!X509_REQ_add1_attr_by_NID(req, nid, chtype,
                (unsigned char *) buf, -1)) {
                BIO_printf(bio_err, "Error adding attribute\n");
                ERR_print_errors(bio_err);
                goto err;
        }
        return (1);
 err:
        return (0);
}

static int
req_check_len(int len, int n_min, int n_max)
{
        if ((n_min > 0) && (len < n_min)) {
                BIO_printf(bio_err, "string is too short, it needs to be at least %d bytes long\n", n_min);
                return (0);
        }
        if ((n_max >= 0) && (len > n_max)) {
                BIO_printf(bio_err, "string is too long, it needs to be less than  %d bytes long\n", n_max);
                return (0);
        }
        return (1);
}

/* Check if the end of a string matches 'end' */
static int
check_end(const char *str, const char *end)
{
        int elen, slen;
        const char *tmp;
        elen = strlen(end);
        slen = strlen(str);
        if (elen > slen)
                return 1;
        tmp = str + slen - elen;
        return strcmp(tmp, end);
}

static EVP_PKEY_CTX *
set_keygen_ctx(BIO * err, const char *gstr, int *pkey_type,
    long *pkeylen, char **palgnam)
{
        EVP_PKEY_CTX *gctx = NULL;
        EVP_PKEY *param = NULL;
        long keylen = -1;
        BIO *pbio = NULL;
        const char *paramfile = NULL;
        const char *errstr;

        if (gstr == NULL) {
                *pkey_type = EVP_PKEY_RSA;
                keylen = *pkeylen;
        } else if (gstr[0] >= '0' && gstr[0] <= '9') {
                *pkey_type = EVP_PKEY_RSA;
                keylen = strtonum(gstr, 0, LONG_MAX, &errstr);
                if (errstr) {
                        BIO_printf(err, "bad algorithm %s: %s\n", gstr, errstr);
                        return NULL;
                }
                *pkeylen = keylen;
        } else if (!strncmp(gstr, "param:", 6))
                paramfile = gstr + 6;
        else {
                const char *p = strchr(gstr, ':');
                int len;
                const EVP_PKEY_ASN1_METHOD *ameth;

                if (p)
                        len = p - gstr;
                else
                        len = strlen(gstr);

                ameth = EVP_PKEY_asn1_find_str(NULL, gstr, len);

                if (!ameth) {
                        BIO_printf(err, "Unknown algorithm %.*s\n", len, gstr);
                        return NULL;
                }
                EVP_PKEY_asn1_get0_info(NULL, pkey_type, NULL, NULL, NULL,
                    ameth);
                if (*pkey_type == EVP_PKEY_RSA) {
                        if (p) {
                                keylen = strtonum(p + 1, 0, LONG_MAX, &errstr);
                                if (errstr) {
                                        BIO_printf(err, "bad algorithm %s: %s\n",
                                            p + 1, errstr);
                                        return NULL;
                                }
                                *pkeylen = keylen;
                        } else
                                keylen = *pkeylen;
                } else if (p)
                        paramfile = p + 1;
        }

        if (paramfile) {
                pbio = BIO_new_file(paramfile, "r");
                if (!pbio) {
                        BIO_printf(err, "Can't open parameter file %s\n",
                            paramfile);
                        return NULL;
                }
                param = PEM_read_bio_Parameters(pbio, NULL);

                if (!param) {
                        X509 *x;
                        (void) BIO_reset(pbio);
                        x = PEM_read_bio_X509(pbio, NULL, NULL, NULL);
                        if (x) {
                                param = X509_get_pubkey(x);
                                X509_free(x);
                        }
                }
                BIO_free(pbio);

                if (!param) {
                        BIO_printf(err, "Error reading parameter file %s\n",
                            paramfile);
                        return NULL;
                }
                if (*pkey_type == -1)
                        *pkey_type = EVP_PKEY_id(param);
                else if (*pkey_type != EVP_PKEY_base_id(param)) {
                        BIO_printf(err, "Key Type does not match parameters\n");
                        EVP_PKEY_free(param);
                        return NULL;
                }
        }
        if (palgnam) {
                const EVP_PKEY_ASN1_METHOD *ameth;
                const char *anam;
                ameth = EVP_PKEY_asn1_find(NULL, *pkey_type);
                if (!ameth) {
                        BIO_puts(err, "Internal error: can't find key algorithm\n");
                        return NULL;
                }
                EVP_PKEY_asn1_get0_info(NULL, NULL, NULL, NULL, &anam, ameth);
                *palgnam = strdup(anam);
        }
        if (param) {
                gctx = EVP_PKEY_CTX_new(param, NULL);
                *pkeylen = EVP_PKEY_bits(param);
                EVP_PKEY_free(param);
        } else
                gctx = EVP_PKEY_CTX_new_id(*pkey_type, NULL);

        if (!gctx) {
                BIO_puts(err, "Error allocating keygen context\n");
                ERR_print_errors(err);
                return NULL;
        }
        if (EVP_PKEY_keygen_init(gctx) <= 0) {
                BIO_puts(err, "Error initializing keygen context\n");
                ERR_print_errors(err);
                EVP_PKEY_CTX_free(gctx);
                return NULL;
        }
        if ((*pkey_type == EVP_PKEY_RSA) && (keylen != -1)) {
                if (EVP_PKEY_CTX_set_rsa_keygen_bits(gctx, keylen) <= 0) {
                        BIO_puts(err, "Error setting RSA keysize\n");
                        ERR_print_errors(err);
                        EVP_PKEY_CTX_free(gctx);
                        return NULL;
                }
        }

        return gctx;
}

static int
genpkey_cb(EVP_PKEY_CTX * ctx)
{
        char c = '*';
        BIO *b = EVP_PKEY_CTX_get_app_data(ctx);
        int p;
        p = EVP_PKEY_CTX_get_keygen_info(ctx, 0);
        if (p == 0)
                c = '.';
        if (p == 1)
                c = '+';
        if (p == 2)
                c = '*';
        if (p == 3)
                c = '\n';
        BIO_write(b, &c, 1);
        (void) BIO_flush(b);
        return 1;
}

static int
do_sign_init(BIO * err, EVP_MD_CTX * ctx, EVP_PKEY * pkey,
    const EVP_MD * md, STACK_OF(OPENSSL_STRING) * sigopts)
{
        EVP_PKEY_CTX *pkctx = NULL;
        int default_nid;
        int i;

        if (EVP_PKEY_get_default_digest_nid(pkey, &default_nid) == 2 &&
            default_nid == NID_undef) {
                /* The digest is required to be EVP_md_null() (EdDSA). */
                md = EVP_md_null();
        }

        if (!EVP_DigestSignInit(ctx, &pkctx, md, NULL, pkey))
                return 0;
        for (i = 0; i < sk_OPENSSL_STRING_num(sigopts); i++) {
                char *sigopt = sk_OPENSSL_STRING_value(sigopts, i);
                if (pkey_ctrl_string(pkctx, sigopt) <= 0) {
                        BIO_printf(err, "parameter error \"%s\"\n", sigopt);
                        ERR_print_errors(bio_err);
                        return 0;
                }
        }
        return 1;
}

int
do_X509_sign(BIO * err, X509 * x, EVP_PKEY * pkey, const EVP_MD * md,
    STACK_OF(OPENSSL_STRING) * sigopts)
{
        EVP_MD_CTX *mctx;
        int rv;

        if ((mctx = EVP_MD_CTX_new()) == NULL)
                return 0;

        rv = do_sign_init(err, mctx, pkey, md, sigopts);
        if (rv > 0)
                rv = X509_sign_ctx(x, mctx);

        EVP_MD_CTX_free(mctx);

        return rv > 0;
}


int
do_X509_REQ_sign(BIO * err, X509_REQ * x, EVP_PKEY * pkey, const EVP_MD * md,
    STACK_OF(OPENSSL_STRING) * sigopts)
{
        EVP_MD_CTX *mctx;
        int rv;

        if ((mctx = EVP_MD_CTX_new()) == NULL)
                return 0;

        rv = do_sign_init(err, mctx, pkey, md, sigopts);
        if (rv > 0)
                rv = X509_REQ_sign_ctx(x, mctx);

        EVP_MD_CTX_free(mctx);

        return rv > 0;
}



int
do_X509_CRL_sign(BIO * err, X509_CRL * x, EVP_PKEY * pkey, const EVP_MD * md,
    STACK_OF(OPENSSL_STRING) * sigopts)
{
        int rv;
        EVP_MD_CTX *mctx;

        if ((mctx = EVP_MD_CTX_new()) == NULL)
                return 0;

        rv = do_sign_init(err, mctx, pkey, md, sigopts);
        if (rv > 0)
                rv = X509_CRL_sign_ctx(x, mctx);

        EVP_MD_CTX_free(mctx);

        return rv > 0;
}

static unsigned long
ext_name_hash(const OPENSSL_STRING *a)
{
        return lh_strhash((const char *)a);
}

static int
ext_name_cmp(const OPENSSL_STRING *a, const OPENSSL_STRING *b)
{
        return strcmp((const char *)a, (const char *)b);
}

static void
exts_cleanup(OPENSSL_STRING *x)
{
        free((char *)x);
}

/*
 * Is the |kv| key already duplicated ? This is remarkably tricky to get right.
 * Return 0 if unique, -1 on runtime error; 1 if found or a syntax error.
 */
static int
duplicated(LHASH_OF(OPENSSL_STRING) *addexts, char *kv)
{
        char *p;
        size_t off;

        /* Check syntax. */
        /* Skip leading whitespace, make a copy. */
        while (*kv && isspace(*kv))
                if (*++kv == '\0')
                        return 1;
        if ((p = strchr(kv, '=')) == NULL)
                return 1;
        off = p - kv;
        if ((kv = strdup(kv)) == NULL)
                return -1;

        /* Skip trailing space before the equal sign. */
        for (p = kv + off; p > kv; --p)
                if (!isspace(p[-1]))
                        break;
        if (p == kv) {
                free(kv);
                return 1;
        }
        *p = '\0';

        /* See if "key" is there by attempting to add it. */
        if ((p = (char *)lh_OPENSSL_STRING_insert(addexts, (OPENSSL_STRING*)kv))
            != NULL || lh_OPENSSL_STRING_error(addexts)) {
                free(p != NULL ? p : kv);
                return -1;
        }

        return 0;
}