int ca_main()

in lib/openssl/apps/ca.c [224:1253]


int ca_main(int argc, char **argv)
{
    CONF *conf = NULL;
    ENGINE *e = NULL;
    BIGNUM *crlnumber = NULL, *serial = NULL;
    EVP_PKEY *pkey = NULL;
    BIO *in = NULL, *out = NULL, *Sout = NULL;
    ASN1_INTEGER *tmpser;
    ASN1_TIME *tmptm;
    CA_DB *db = NULL;
    DB_ATTR db_attr;
    STACK_OF(CONF_VALUE) *attribs = NULL;
    STACK_OF(OPENSSL_STRING) *sigopts = NULL;
    STACK_OF(X509) *cert_sk = NULL;
    X509_CRL *crl = NULL;
    const EVP_MD *dgst = NULL;
    char *configfile = default_config_file, *section = NULL;
    char *md = NULL, *policy = NULL, *keyfile = NULL;
    char *certfile = NULL, *crl_ext = NULL, *crlnumberfile = NULL, *key = NULL;
    const char *infile = NULL, *spkac_file = NULL, *ss_cert_file = NULL;
    const char *extensions = NULL, *extfile = NULL, *passinarg = NULL;
    char *outdir = NULL, *outfile = NULL, *rev_arg = NULL, *ser_status = NULL;
    const char *serialfile = NULL, *subj = NULL;
    char *prog, *startdate = NULL, *enddate = NULL;
    char *dbfile = NULL, *f;
    char new_cert[PATH_MAX];
    char tmp[10 + 1] = "\0";
    char *const *pp;
    const char *p;
    size_t outdirlen = 0;
    int create_ser = 0, free_key = 0, total = 0, total_done = 0;
    int batch = 0, default_op = 1, doupdatedb = 0, ext_copy = EXT_COPY_NONE;
    int keyformat = FORMAT_PEM, multirdn = 0, notext = 0, output_der = 0;
    int ret = 1, email_dn = 1, req = 0, verbose = 0, gencrl = 0, dorevoke = 0;
    int rand_ser = 0, i, j, selfsign = 0, def_nid, def_ret;
    long crldays = 0, crlhours = 0, crlsec = 0, days = 0;
    unsigned long chtype = MBSTRING_ASC, certopt = 0;
    X509 *x509 = NULL, *x509p = NULL, *x = NULL;
    REVINFO_TYPE rev_type = REV_NONE;
    X509_REVOKED *r = NULL;
    OPTION_CHOICE o;

    prog = opt_init(argc, argv, ca_options);
    while ((o = opt_next()) != OPT_EOF) {
        switch (o) {
        case OPT_EOF:
        case OPT_ERR:
opthelp:
            BIO_printf(bio_err, "%s: Use -help for summary.\n", prog);
            goto end;
        case OPT_HELP:
            opt_help(ca_options);
            ret = 0;
            goto end;
        case OPT_IN:
            req = 1;
            infile = opt_arg();
            break;
        case OPT_OUT:
            outfile = opt_arg();
            break;
        case OPT_VERBOSE:
            verbose = 1;
            break;
        case OPT_CONFIG:
            configfile = opt_arg();
            break;
        case OPT_NAME:
            section = opt_arg();
            break;
        case OPT_SUBJ:
            subj = opt_arg();
            /* preserve=1; */
            break;
        case OPT_UTF8:
            chtype = MBSTRING_UTF8;
            break;
        case OPT_RAND_SERIAL:
            rand_ser = 1;
            break;
        case OPT_CREATE_SERIAL:
            create_ser = 1;
            break;
        case OPT_MULTIVALUE_RDN:
            multirdn = 1;
            break;
        case OPT_STARTDATE:
            startdate = opt_arg();
            break;
        case OPT_ENDDATE:
            enddate = opt_arg();
            break;
        case OPT_DAYS:
            days = atoi(opt_arg());
            break;
        case OPT_MD:
            md = opt_arg();
            break;
        case OPT_POLICY:
            policy = opt_arg();
            break;
        case OPT_KEYFILE:
            keyfile = opt_arg();
            break;
        case OPT_KEYFORM:
            if (!opt_format(opt_arg(), OPT_FMT_ANY, &keyformat))
                goto opthelp;
            break;
        case OPT_PASSIN:
            passinarg = opt_arg();
            break;
        case OPT_R_CASES:
            if (!opt_rand(o))
                goto end;
            break;
        case OPT_KEY:
            key = opt_arg();
            break;
        case OPT_CERT:
            certfile = opt_arg();
            break;
        case OPT_SELFSIGN:
            selfsign = 1;
            break;
        case OPT_OUTDIR:
            outdir = opt_arg();
            break;
        case OPT_SIGOPT:
            if (sigopts == NULL)
                sigopts = sk_OPENSSL_STRING_new_null();
            if (sigopts == NULL || !sk_OPENSSL_STRING_push(sigopts, opt_arg()))
                goto end;
            break;
        case OPT_NOTEXT:
            notext = 1;
            break;
        case OPT_BATCH:
            batch = 1;
            break;
        case OPT_PRESERVEDN:
            preserve = 1;
            break;
        case OPT_NOEMAILDN:
            email_dn = 0;
            break;
        case OPT_GENCRL:
            gencrl = 1;
            break;
        case OPT_MSIE_HACK:
            msie_hack = 1;
            break;
        case OPT_CRLDAYS:
            crldays = atol(opt_arg());
            break;
        case OPT_CRLHOURS:
            crlhours = atol(opt_arg());
            break;
        case OPT_CRLSEC:
            crlsec = atol(opt_arg());
            break;
        case OPT_INFILES:
            req = 1;
            goto end_of_options;
        case OPT_SS_CERT:
            ss_cert_file = opt_arg();
            req = 1;
            break;
        case OPT_SPKAC:
            spkac_file = opt_arg();
            req = 1;
            break;
        case OPT_REVOKE:
            infile = opt_arg();
            dorevoke = 1;
            break;
        case OPT_VALID:
            infile = opt_arg();
            dorevoke = 2;
            break;
        case OPT_EXTENSIONS:
            extensions = opt_arg();
            break;
        case OPT_EXTFILE:
            extfile = opt_arg();
            break;
        case OPT_STATUS:
            ser_status = opt_arg();
            break;
        case OPT_UPDATEDB:
            doupdatedb = 1;
            break;
        case OPT_CRLEXTS:
            crl_ext = opt_arg();
            break;
        case OPT_CRL_REASON:   /* := REV_CRL_REASON */
        case OPT_CRL_HOLD:
        case OPT_CRL_COMPROMISE:
        case OPT_CRL_CA_COMPROMISE:
            rev_arg = opt_arg();
            rev_type = (o - OPT_CRL_REASON) + REV_CRL_REASON;
            break;
        case OPT_ENGINE:
            e = setup_engine(opt_arg(), 0);
            break;
        }
    }
end_of_options:
    argc = opt_num_rest();
    argv = opt_rest();

    BIO_printf(bio_err, "Using configuration from %s\n", configfile);

    if ((conf = app_load_config(configfile)) == NULL)
        goto end;
    if (configfile != default_config_file && !app_load_modules(conf))
        goto end;

    /* Lets get the config section we are using */
    if (section == NULL
        && (section = lookup_conf(conf, BASE_SECTION, ENV_DEFAULT_CA)) == NULL)
        goto end;

    p = NCONF_get_string(conf, NULL, "oid_file");
    if (p == NULL)
        ERR_clear_error();
    if (p != NULL) {
        BIO *oid_bio = BIO_new_file(p, "r");

        if (oid_bio == NULL) {
            ERR_clear_error();
        } else {
            OBJ_create_objects(oid_bio);
            BIO_free(oid_bio);
        }
    }
    if (!add_oid_section(conf)) {
        ERR_print_errors(bio_err);
        goto end;
    }

    app_RAND_load_conf(conf, BASE_SECTION);

    f = NCONF_get_string(conf, section, STRING_MASK);
    if (f == NULL)
        ERR_clear_error();

    if (f != NULL && !ASN1_STRING_set_default_mask_asc(f)) {
        BIO_printf(bio_err, "Invalid global string mask setting %s\n", f);
        goto end;
    }

    if (chtype != MBSTRING_UTF8) {
        f = NCONF_get_string(conf, section, UTF8_IN);
        if (f == NULL)
            ERR_clear_error();
        else if (strcmp(f, "yes") == 0)
            chtype = MBSTRING_UTF8;
    }

    db_attr.unique_subject = 1;
    p = NCONF_get_string(conf, section, ENV_UNIQUE_SUBJECT);
    if (p != NULL)
        db_attr.unique_subject = parse_yesno(p, 1);
    else
        ERR_clear_error();

    /*****************************************************************/
    /* report status of cert with serial number given on command line */
    if (ser_status) {
        dbfile = lookup_conf(conf, section, ENV_DATABASE);
        if (dbfile == NULL)
            goto end;

        db = load_index(dbfile, &db_attr);
        if (db == NULL)
            goto end;

        if (index_index(db) <= 0)
            goto end;

        if (get_certificate_status(ser_status, db) != 1)
            BIO_printf(bio_err, "Error verifying serial %s!\n", ser_status);
        goto end;
    }

    /*****************************************************************/
    /* we definitely need a private key, so let's get it */

    if (keyfile == NULL
        && (keyfile = lookup_conf(conf, section, ENV_PRIVATE_KEY)) == NULL)
        goto end;

    if (key == NULL) {
        free_key = 1;
        if (!app_passwd(passinarg, NULL, &key, NULL)) {
            BIO_printf(bio_err, "Error getting password\n");
            goto end;
        }
    }
    pkey = load_key(keyfile, keyformat, 0, key, e, "CA private key");
    if (key != NULL)
        OPENSSL_cleanse(key, strlen(key));
    if (pkey == NULL)
        /* load_key() has already printed an appropriate message */
        goto end;

    /*****************************************************************/
    /* we need a certificate */
    if (!selfsign || spkac_file || ss_cert_file || gencrl) {
        if (certfile == NULL
            && (certfile = lookup_conf(conf, section, ENV_CERTIFICATE)) == NULL)
            goto end;

        x509 = load_cert(certfile, FORMAT_PEM, "CA certificate");
        if (x509 == NULL)
            goto end;

        if (!X509_check_private_key(x509, pkey)) {
            BIO_printf(bio_err,
                       "CA certificate and CA private key do not match\n");
            goto end;
        }
    }
    if (!selfsign)
        x509p = x509;

    f = NCONF_get_string(conf, BASE_SECTION, ENV_PRESERVE);
    if (f == NULL)
        ERR_clear_error();
    if ((f != NULL) && ((*f == 'y') || (*f == 'Y')))
        preserve = 1;
    f = NCONF_get_string(conf, BASE_SECTION, ENV_MSIE_HACK);
    if (f == NULL)
        ERR_clear_error();
    if ((f != NULL) && ((*f == 'y') || (*f == 'Y')))
        msie_hack = 1;

    f = NCONF_get_string(conf, section, ENV_NAMEOPT);

    if (f != NULL) {
        if (!set_nameopt(f)) {
            BIO_printf(bio_err, "Invalid name options: \"%s\"\n", f);
            goto end;
        }
        default_op = 0;
    }

    f = NCONF_get_string(conf, section, ENV_CERTOPT);

    if (f != NULL) {
        if (!set_cert_ex(&certopt, f)) {
            BIO_printf(bio_err, "Invalid certificate options: \"%s\"\n", f);
            goto end;
        }
        default_op = 0;
    } else {
        ERR_clear_error();
    }

    f = NCONF_get_string(conf, section, ENV_EXTCOPY);

    if (f != NULL) {
        if (!set_ext_copy(&ext_copy, f)) {
            BIO_printf(bio_err, "Invalid extension copy option: \"%s\"\n", f);
            goto end;
        }
    } else {
        ERR_clear_error();
    }

    /*****************************************************************/
    /* lookup where to write new certificates */
    if ((outdir == NULL) && (req)) {

        outdir = NCONF_get_string(conf, section, ENV_NEW_CERTS_DIR);
        if (outdir == NULL) {
            BIO_printf(bio_err,
                       "there needs to be defined a directory for new certificate to be placed in\n");
            goto end;
        }
#ifndef OPENSSL_SYS_VMS
        /*
         * outdir is a directory spec, but access() for VMS demands a
         * filename.  We could use the DEC C routine to convert the
         * directory syntax to Unix, and give that to app_isdir,
         * but for now the fopen will catch the error if it's not a
         * directory
         */
        if (app_isdir(outdir) <= 0) {
            BIO_printf(bio_err, "%s: %s is not a directory\n", prog, outdir);
            perror(outdir);
            goto end;
        }
#endif
    }

    /*****************************************************************/
    /* we need to load the database file */
    dbfile = lookup_conf(conf, section, ENV_DATABASE);
    if (dbfile == NULL)
        goto end;

    db = load_index(dbfile, &db_attr);
    if (db == NULL)
        goto end;

    /* Lets check some fields */
    for (i = 0; i < sk_OPENSSL_PSTRING_num(db->db->data); i++) {
        pp = sk_OPENSSL_PSTRING_value(db->db->data, i);
        if ((pp[DB_type][0] != DB_TYPE_REV) && (pp[DB_rev_date][0] != '\0')) {
            BIO_printf(bio_err,
                       "entry %d: not revoked yet, but has a revocation date\n",
                       i + 1);
            goto end;
        }
        if ((pp[DB_type][0] == DB_TYPE_REV) &&
            !make_revoked(NULL, pp[DB_rev_date])) {
            BIO_printf(bio_err, " in entry %d\n", i + 1);
            goto end;
        }
        if (!check_time_format((char *)pp[DB_exp_date])) {
            BIO_printf(bio_err, "entry %d: invalid expiry date\n", i + 1);
            goto end;
        }
        p = pp[DB_serial];
        j = strlen(p);
        if (*p == '-') {
            p++;
            j--;
        }
        if ((j & 1) || (j < 2)) {
            BIO_printf(bio_err, "entry %d: bad serial number length (%d)\n",
                       i + 1, j);
            goto end;
        }
        for ( ; *p; p++) {
            if (!isxdigit(_UC(*p))) {
                BIO_printf(bio_err,
                           "entry %d: bad char 0%o '%c' in serial number\n",
                           i + 1, *p, *p);
                goto end;
            }
        }
    }
    if (verbose) {
        TXT_DB_write(bio_out, db->db);
        BIO_printf(bio_err, "%d entries loaded from the database\n",
                   sk_OPENSSL_PSTRING_num(db->db->data));
        BIO_printf(bio_err, "generating index\n");
    }

    if (index_index(db) <= 0)
        goto end;

    /*****************************************************************/
    /* Update the db file for expired certificates */
    if (doupdatedb) {
        if (verbose)
            BIO_printf(bio_err, "Updating %s ...\n", dbfile);

        i = do_updatedb(db);
        if (i == -1) {
            BIO_printf(bio_err, "Malloc failure\n");
            goto end;
        } else if (i == 0) {
            if (verbose)
                BIO_printf(bio_err, "No entries found to mark expired\n");
        } else {
            if (!save_index(dbfile, "new", db))
                goto end;

            if (!rotate_index(dbfile, "new", "old"))
                goto end;

            if (verbose)
                BIO_printf(bio_err, "Done. %d entries marked as expired\n", i);
        }
    }

    /*****************************************************************/
    /* Read extensions config file                                   */
    if (extfile) {
        if ((extconf = app_load_config(extfile)) == NULL) {
            ret = 1;
            goto end;
        }

        if (verbose)
            BIO_printf(bio_err, "Successfully loaded extensions file %s\n",
                       extfile);

        /* We can have sections in the ext file */
        if (extensions == NULL) {
            extensions = NCONF_get_string(extconf, "default", "extensions");
            if (extensions == NULL)
                extensions = "default";
        }
    }

    /*****************************************************************/
    if (req || gencrl) {
        if (spkac_file != NULL && outfile != NULL) {
            output_der = 1;
            batch = 1;
        }
    }

    def_ret = EVP_PKEY_get_default_digest_nid(pkey, &def_nid);
    /*
     * EVP_PKEY_get_default_digest_nid() returns 2 if the digest is
     * mandatory for this algorithm.
     */
    if (def_ret == 2 && def_nid == NID_undef) {
        /* The signing algorithm requires there to be no digest */
        dgst = EVP_md_null();
    } else if (md == NULL
               && (md = lookup_conf(conf, section, ENV_DEFAULT_MD)) == NULL) {
        goto end;
    } else {
        if (strcmp(md, "default") == 0) {
            if (def_ret <= 0) {
                BIO_puts(bio_err, "no default digest\n");
                goto end;
            }
            md = (char *)OBJ_nid2sn(def_nid);
        }

        if (!opt_md(md, &dgst))
            goto end;
    }

    if (req) {
        if (email_dn == 1) {
            char *tmp_email_dn = NULL;

            tmp_email_dn = NCONF_get_string(conf, section, ENV_DEFAULT_EMAIL_DN);
            if (tmp_email_dn != NULL && strcmp(tmp_email_dn, "no") == 0)
                email_dn = 0;
        }
        if (verbose)
            BIO_printf(bio_err, "message digest is %s\n",
                       OBJ_nid2ln(EVP_MD_type(dgst)));
        if (policy == NULL
            && (policy = lookup_conf(conf, section, ENV_POLICY)) == NULL)
            goto end;

        if (verbose)
            BIO_printf(bio_err, "policy is %s\n", policy);

        if (NCONF_get_string(conf, section, ENV_RAND_SERIAL) != NULL) {
            rand_ser = 1;
        } else {
            serialfile = lookup_conf(conf, section, ENV_SERIAL);
            if (serialfile == NULL)
                goto end;
        }

        if (extconf == NULL) {
            /*
             * no '-extfile' option, so we look for extensions in the main
             * configuration file
             */
            if (extensions == NULL) {
                extensions = NCONF_get_string(conf, section, ENV_EXTENSIONS);
                if (extensions == NULL)
                    ERR_clear_error();
            }
            if (extensions != NULL) {
                /* Check syntax of file */
                X509V3_CTX ctx;
                X509V3_set_ctx_test(&ctx);
                X509V3_set_nconf(&ctx, conf);
                if (!X509V3_EXT_add_nconf(conf, &ctx, extensions, NULL)) {
                    BIO_printf(bio_err,
                               "Error Loading extension section %s\n",
                               extensions);
                    ret = 1;
                    goto end;
                }
            }
        }

        if (startdate == NULL) {
            startdate = NCONF_get_string(conf, section, ENV_DEFAULT_STARTDATE);
            if (startdate == NULL)
                ERR_clear_error();
        }
        if (startdate != NULL && !ASN1_TIME_set_string_X509(NULL, startdate)) {
            BIO_printf(bio_err,
                       "start date is invalid, it should be YYMMDDHHMMSSZ or YYYYMMDDHHMMSSZ\n");
            goto end;
        }
        if (startdate == NULL)
            startdate = "today";

        if (enddate == NULL) {
            enddate = NCONF_get_string(conf, section, ENV_DEFAULT_ENDDATE);
            if (enddate == NULL)
                ERR_clear_error();
        }
        if (enddate != NULL && !ASN1_TIME_set_string_X509(NULL, enddate)) {
            BIO_printf(bio_err,
                       "end date is invalid, it should be YYMMDDHHMMSSZ or YYYYMMDDHHMMSSZ\n");
            goto end;
        }

        if (days == 0) {
            if (!NCONF_get_number(conf, section, ENV_DEFAULT_DAYS, &days))
                days = 0;
        }
        if (enddate == NULL && days == 0) {
            BIO_printf(bio_err, "cannot lookup how many days to certify for\n");
            goto end;
        }

        if (rand_ser) {
            if ((serial = BN_new()) == NULL || !rand_serial(serial, NULL)) {
                BIO_printf(bio_err, "error generating serial number\n");
                goto end;
            }
        } else {
            if ((serial = load_serial(serialfile, create_ser, NULL)) == NULL) {
                BIO_printf(bio_err, "error while loading serial number\n");
                goto end;
            }
            if (verbose) {
                if (BN_is_zero(serial)) {
                    BIO_printf(bio_err, "next serial number is 00\n");
                } else {
                    if ((f = BN_bn2hex(serial)) == NULL)
                        goto end;
                    BIO_printf(bio_err, "next serial number is %s\n", f);
                    OPENSSL_free(f);
                }
            }
        }

        if ((attribs = NCONF_get_section(conf, policy)) == NULL) {
            BIO_printf(bio_err, "unable to find 'section' for %s\n", policy);
            goto end;
        }

        if ((cert_sk = sk_X509_new_null()) == NULL) {
            BIO_printf(bio_err, "Memory allocation failure\n");
            goto end;
        }
        if (spkac_file != NULL) {
            total++;
            j = certify_spkac(&x, spkac_file, pkey, x509, dgst, sigopts,
                              attribs, db, serial, subj, chtype, multirdn,
                              email_dn, startdate, enddate, days, extensions,
                              conf, verbose, certopt, get_nameopt(), default_op,
                              ext_copy);
            if (j < 0)
                goto end;
            if (j > 0) {
                total_done++;
                BIO_printf(bio_err, "\n");
                if (!BN_add_word(serial, 1))
                    goto end;
                if (!sk_X509_push(cert_sk, x)) {
                    BIO_printf(bio_err, "Memory allocation failure\n");
                    goto end;
                }
            }
        }
        if (ss_cert_file != NULL) {
            total++;
            j = certify_cert(&x, ss_cert_file, pkey, x509, dgst, sigopts,
                             attribs,
                             db, serial, subj, chtype, multirdn, email_dn,
                             startdate, enddate, days, batch, extensions,
                             conf, verbose, certopt, get_nameopt(), default_op,
                             ext_copy);
            if (j < 0)
                goto end;
            if (j > 0) {
                total_done++;
                BIO_printf(bio_err, "\n");
                if (!BN_add_word(serial, 1))
                    goto end;
                if (!sk_X509_push(cert_sk, x)) {
                    BIO_printf(bio_err, "Memory allocation failure\n");
                    goto end;
                }
            }
        }
        if (infile != NULL) {
            total++;
            j = certify(&x, infile, pkey, x509p, dgst, sigopts, attribs, db,
                        serial, subj, chtype, multirdn, email_dn, startdate,
                        enddate, days, batch, extensions, conf, verbose,
                        certopt, get_nameopt(), default_op, ext_copy, selfsign);
            if (j < 0)
                goto end;
            if (j > 0) {
                total_done++;
                BIO_printf(bio_err, "\n");
                if (!BN_add_word(serial, 1))
                    goto end;
                if (!sk_X509_push(cert_sk, x)) {
                    BIO_printf(bio_err, "Memory allocation failure\n");
                    goto end;
                }
            }
        }
        for (i = 0; i < argc; i++) {
            total++;
            j = certify(&x, argv[i], pkey, x509p, dgst, sigopts, attribs, db,
                        serial, subj, chtype, multirdn, email_dn, startdate,
                        enddate, days, batch, extensions, conf, verbose,
                        certopt, get_nameopt(), default_op, ext_copy, selfsign);
            if (j < 0)
                goto end;
            if (j > 0) {
                total_done++;
                BIO_printf(bio_err, "\n");
                if (!BN_add_word(serial, 1)) {
                    X509_free(x);
                    goto end;
                }
                if (!sk_X509_push(cert_sk, x)) {
                    BIO_printf(bio_err, "Memory allocation failure\n");
                    X509_free(x);
                    goto end;
                }
            }
        }
        /*
         * we have a stack of newly certified certificates and a data base
         * and serial number that need updating
         */

        if (sk_X509_num(cert_sk) > 0) {
            if (!batch) {
                BIO_printf(bio_err,
                           "\n%d out of %d certificate requests certified, commit? [y/n]",
                           total_done, total);
                (void)BIO_flush(bio_err);
                tmp[0] = '\0';
                if (fgets(tmp, sizeof(tmp), stdin) == NULL) {
                    BIO_printf(bio_err, "CERTIFICATION CANCELED: I/O error\n");
                    ret = 0;
                    goto end;
                }
                if (tmp[0] != 'y' && tmp[0] != 'Y') {
                    BIO_printf(bio_err, "CERTIFICATION CANCELED\n");
                    ret = 0;
                    goto end;
                }
            }

            BIO_printf(bio_err, "Write out database with %d new entries\n",
                       sk_X509_num(cert_sk));

            if (serialfile != NULL
                    && !save_serial(serialfile, "new", serial, NULL))
                goto end;

            if (!save_index(dbfile, "new", db))
                goto end;
        }

        outdirlen = OPENSSL_strlcpy(new_cert, outdir, sizeof(new_cert));
#ifndef OPENSSL_SYS_VMS
        outdirlen = OPENSSL_strlcat(new_cert, "/", sizeof(new_cert));
#endif

        if (verbose)
            BIO_printf(bio_err, "writing new certificates\n");

        for (i = 0; i < sk_X509_num(cert_sk); i++) {
            BIO *Cout = NULL;
            X509 *xi = sk_X509_value(cert_sk, i);
            ASN1_INTEGER *serialNumber = X509_get_serialNumber(xi);
            const unsigned char *psn = ASN1_STRING_get0_data(serialNumber);
            const int snl = ASN1_STRING_length(serialNumber);
            const int filen_len = 2 * (snl > 0 ? snl : 1) + sizeof(".pem");
            char *n = new_cert + outdirlen;

            if (outdirlen + filen_len > PATH_MAX) {
                BIO_printf(bio_err, "certificate file name too long\n");
                goto end;
            }

            if (snl > 0) {
                static const char HEX_DIGITS[] = "0123456789ABCDEF";

                for (j = 0; j < snl; j++, psn++) {
                    *n++ = HEX_DIGITS[*psn >> 4];
                    *n++ = HEX_DIGITS[*psn & 0x0F];
                }
            } else {
                *(n++) = '0';
                *(n++) = '0';
            }
            *(n++) = '.';
            *(n++) = 'p';
            *(n++) = 'e';
            *(n++) = 'm';
            *n = '\0';          /* closing new_cert */
            if (verbose)
                BIO_printf(bio_err, "writing %s\n", new_cert);

            Sout = bio_open_default(outfile, 'w',
                                    output_der ? FORMAT_ASN1 : FORMAT_TEXT);
            if (Sout == NULL)
                goto end;

            Cout = BIO_new_file(new_cert, "w");
            if (Cout == NULL) {
                perror(new_cert);
                goto end;
            }
            write_new_certificate(Cout, xi, 0, notext);
            write_new_certificate(Sout, xi, output_der, notext);
            BIO_free_all(Cout);
            BIO_free_all(Sout);
            Sout = NULL;
        }

        if (sk_X509_num(cert_sk)) {
            /* Rename the database and the serial file */
            if (serialfile != NULL
                    && !rotate_serial(serialfile, "new", "old"))
                goto end;

            if (!rotate_index(dbfile, "new", "old"))
                goto end;

            BIO_printf(bio_err, "Data Base Updated\n");
        }
    }

    /*****************************************************************/
    if (gencrl) {
        int crl_v2 = 0;
        if (crl_ext == NULL) {
            crl_ext = NCONF_get_string(conf, section, ENV_CRLEXT);
            if (crl_ext == NULL)
                ERR_clear_error();
        }
        if (crl_ext != NULL) {
            /* Check syntax of file */
            X509V3_CTX ctx;
            X509V3_set_ctx_test(&ctx);
            X509V3_set_nconf(&ctx, conf);
            if (!X509V3_EXT_add_nconf(conf, &ctx, crl_ext, NULL)) {
                BIO_printf(bio_err,
                           "Error Loading CRL extension section %s\n", crl_ext);
                ret = 1;
                goto end;
            }
        }

        if ((crlnumberfile = NCONF_get_string(conf, section, ENV_CRLNUMBER))
            != NULL)
            if ((crlnumber = load_serial(crlnumberfile, 0, NULL)) == NULL) {
                BIO_printf(bio_err, "error while loading CRL number\n");
                goto end;
            }

        if (!crldays && !crlhours && !crlsec) {
            if (!NCONF_get_number(conf, section,
                                  ENV_DEFAULT_CRL_DAYS, &crldays))
                crldays = 0;
            if (!NCONF_get_number(conf, section,
                                  ENV_DEFAULT_CRL_HOURS, &crlhours))
                crlhours = 0;
            ERR_clear_error();
        }
        if ((crldays == 0) && (crlhours == 0) && (crlsec == 0)) {
            BIO_printf(bio_err,
                       "cannot lookup how long until the next CRL is issued\n");
            goto end;
        }

        if (verbose)
            BIO_printf(bio_err, "making CRL\n");
        if ((crl = X509_CRL_new()) == NULL)
            goto end;
        if (!X509_CRL_set_issuer_name(crl, X509_get_subject_name(x509)))
            goto end;

        tmptm = ASN1_TIME_new();
        if (tmptm == NULL
                || X509_gmtime_adj(tmptm, 0) == NULL
                || !X509_CRL_set1_lastUpdate(crl, tmptm)
                || X509_time_adj_ex(tmptm, crldays, crlhours * 60 * 60 + crlsec,
                                    NULL) == NULL) {
            BIO_puts(bio_err, "error setting CRL nextUpdate\n");
            ASN1_TIME_free(tmptm);
            goto end;
        }
        X509_CRL_set1_nextUpdate(crl, tmptm);

        ASN1_TIME_free(tmptm);

        for (i = 0; i < sk_OPENSSL_PSTRING_num(db->db->data); i++) {
            pp = sk_OPENSSL_PSTRING_value(db->db->data, i);
            if (pp[DB_type][0] == DB_TYPE_REV) {
                if ((r = X509_REVOKED_new()) == NULL)
                    goto end;
                j = make_revoked(r, pp[DB_rev_date]);
                if (!j)
                    goto end;
                if (j == 2)
                    crl_v2 = 1;
                if (!BN_hex2bn(&serial, pp[DB_serial]))
                    goto end;
                tmpser = BN_to_ASN1_INTEGER(serial, NULL);
                BN_free(serial);
                serial = NULL;
                if (!tmpser)
                    goto end;
                X509_REVOKED_set_serialNumber(r, tmpser);
                ASN1_INTEGER_free(tmpser);
                X509_CRL_add0_revoked(crl, r);
            }
        }

        /*
         * sort the data so it will be written in serial number order
         */
        X509_CRL_sort(crl);

        /* we now have a CRL */
        if (verbose)
            BIO_printf(bio_err, "signing CRL\n");

        /* Add any extensions asked for */

        if (crl_ext != NULL || crlnumberfile != NULL) {
            X509V3_CTX crlctx;
            X509V3_set_ctx(&crlctx, x509, NULL, NULL, crl, 0);
            X509V3_set_nconf(&crlctx, conf);

            if (crl_ext != NULL)
                if (!X509V3_EXT_CRL_add_nconf(conf, &crlctx, crl_ext, crl))
                    goto end;
            if (crlnumberfile != NULL) {
                tmpser = BN_to_ASN1_INTEGER(crlnumber, NULL);
                if (!tmpser)
                    goto end;
                X509_CRL_add1_ext_i2d(crl, NID_crl_number, tmpser, 0, 0);
                ASN1_INTEGER_free(tmpser);
                crl_v2 = 1;
                if (!BN_add_word(crlnumber, 1))
                    goto end;
            }
        }
        if (crl_ext != NULL || crl_v2) {
            if (!X509_CRL_set_version(crl, 1))
                goto end;       /* version 2 CRL */
        }

        /* we have a CRL number that need updating */
        if (crlnumberfile != NULL
                && !save_serial(crlnumberfile, "new", crlnumber, NULL))
            goto end;

        BN_free(crlnumber);
        crlnumber = NULL;

        if (!do_X509_CRL_sign(crl, pkey, dgst, sigopts))
            goto end;

        Sout = bio_open_default(outfile, 'w',
                                output_der ? FORMAT_ASN1 : FORMAT_TEXT);
        if (Sout == NULL)
            goto end;

        PEM_write_bio_X509_CRL(Sout, crl);

        /* Rename the crlnumber file */
        if (crlnumberfile != NULL
                && !rotate_serial(crlnumberfile, "new", "old"))
            goto end;

    }
    /*****************************************************************/
    if (dorevoke) {
        if (infile == NULL) {
            BIO_printf(bio_err, "no input files\n");
            goto end;
        } else {
            X509 *revcert;
            revcert = load_cert(infile, FORMAT_PEM, infile);
            if (revcert == NULL)
                goto end;
            if (dorevoke == 2)
                rev_type = REV_VALID;
            j = do_revoke(revcert, db, rev_type, rev_arg);
            if (j <= 0)
                goto end;
            X509_free(revcert);

            if (!save_index(dbfile, "new", db))
                goto end;

            if (!rotate_index(dbfile, "new", "old"))
                goto end;

            BIO_printf(bio_err, "Data Base Updated\n");
        }
    }
    ret = 0;

 end:
    if (ret)
        ERR_print_errors(bio_err);
    BIO_free_all(Sout);
    BIO_free_all(out);
    BIO_free_all(in);
    sk_X509_pop_free(cert_sk, X509_free);

    if (free_key)
        OPENSSL_free(key);
    BN_free(serial);
    BN_free(crlnumber);
    free_index(db);
    sk_OPENSSL_STRING_free(sigopts);
    EVP_PKEY_free(pkey);
    X509_free(x509);
    X509_CRL_free(crl);
    NCONF_free(conf);
    NCONF_free(extconf);
    release_engine(e);
    return ret;
}