static apr_status_t ssl_init_server_certs()

in modules/ssl/ssl_engine_init.c [1408:1679]


static apr_status_t ssl_init_server_certs(server_rec *s,
                                          apr_pool_t *p,
                                          apr_pool_t *ptemp,
                                          modssl_ctx_t *mctx,
                                          apr_array_header_t *pphrases)
{
    SSLModConfigRec *mc = myModConfig(s);
    const char *vhost_id = mctx->sc->vhost_id, *key_id, *certfile, *keyfile;
    int i;
    EVP_PKEY *pkey;
    int custom_dh_done = 0;
#ifdef HAVE_ECC
    EC_GROUP *ecgroup = NULL;
    int curve_nid = 0;
#endif

    /* no OpenSSL default prompts for any of the SSL_CTX_use_* calls, please */
    SSL_CTX_set_default_passwd_cb(mctx->ssl_ctx, ssl_no_passwd_prompt_cb);

    /* Iterate over the SSLCertificateFile array */
    for (i = 0; (i < mctx->pks->cert_files->nelts) &&
                (certfile = APR_ARRAY_IDX(mctx->pks->cert_files, i,
                                          const char *));
         i++) {
        X509 *cert = NULL;
        const char *engine_certfile = NULL;

        key_id = apr_psprintf(ptemp, "%s:%d", vhost_id, i);

        ERR_clear_error();

        /* first the certificate (public key) */
        if (modssl_is_engine_id(certfile)) {
            engine_certfile = certfile;
        }
        else if (mctx->cert_chain) {
            if ((SSL_CTX_use_certificate_file(mctx->ssl_ctx, certfile,
                                              SSL_FILETYPE_PEM) < 1)) {
                ap_log_error(APLOG_MARK, APLOG_EMERG, 0, s, APLOGNO(02561)
                             "Failed to configure certificate %s, check %s",
                             key_id, certfile);
                ssl_log_ssl_error(SSLLOG_MARK, APLOG_EMERG, s);
                return APR_EGENERAL;
            }
        } else {
            if ((SSL_CTX_use_certificate_chain_file(mctx->ssl_ctx,
                                                    certfile) < 1)) {
                ap_log_error(APLOG_MARK, APLOG_EMERG, 0, s, APLOGNO(02562)
                             "Failed to configure certificate %s (with chain),"
                             " check %s", key_id, certfile);
                ssl_log_ssl_error(SSLLOG_MARK, APLOG_EMERG, s);
                return APR_EGENERAL;
            }
        }

        /* and second, the private key */
        if (i < mctx->pks->key_files->nelts) {
            keyfile = APR_ARRAY_IDX(mctx->pks->key_files, i, const char *);
        } else {
            keyfile = certfile;
        }

        ERR_clear_error();

        if (modssl_is_engine_id(keyfile)) {
            apr_status_t rv;

            if ((rv = modssl_load_engine_keypair(s, p, ptemp, vhost_id,
                                                 engine_certfile, keyfile,
                                                 &cert, &pkey))) {
                return rv;
            }

            if (cert) {
                if (SSL_CTX_use_certificate(mctx->ssl_ctx, cert) < 1) {
                    ap_log_error(APLOG_MARK, APLOG_EMERG, 0, s, APLOGNO(10137)
                                 "Failed to configure certificate %s from %s, check %s",
                                 key_id, mc->szCryptoDevice ?
                                             mc->szCryptoDevice : "provider",
                                 certfile);
                    ssl_log_ssl_error(SSLLOG_MARK, APLOG_EMERG, s);
                    return APR_EGENERAL;
                }

                /* SSL_CTX now owns the cert. */
                X509_free(cert);
            }                    
            
            if (SSL_CTX_use_PrivateKey(mctx->ssl_ctx, pkey) < 1) {
                ap_log_error(APLOG_MARK, APLOG_EMERG, 0, s, APLOGNO(10130)
                             "Failed to configure private key %s from %s",
                             keyfile, mc->szCryptoDevice ?
                                          mc->szCryptoDevice : "provider");
                ssl_log_ssl_error(SSLLOG_MARK, APLOG_EMERG, s);
                return APR_EGENERAL;
            }

            /* SSL_CTX now owns the key */
            EVP_PKEY_free(pkey);
        }
        else if ((SSL_CTX_use_PrivateKey_file(mctx->ssl_ctx, keyfile,
                                              SSL_FILETYPE_PEM) < 1)
                 && CHECK_PRIVKEY_ERROR(ERR_peek_last_error())) {
            ssl_asn1_t *asn1;
            const unsigned char *ptr;

            ERR_clear_error();

            /* perhaps it's an encrypted private key, so try again */
            ssl_load_encrypted_pkey(s, ptemp, i, keyfile, &pphrases);

            if (!(asn1 = ssl_asn1_table_get(mc->retained->privkeys, key_id)) ||
                !(ptr = asn1->cpData) ||
                !(pkey = d2i_AutoPrivateKey(NULL, &ptr, asn1->nData)) ||
                (SSL_CTX_use_PrivateKey(mctx->ssl_ctx, pkey) < 1)) {
                ap_log_error(APLOG_MARK, APLOG_EMERG, 0, s, APLOGNO(02564)
                             "Failed to configure encrypted (?) private key %s,"
                             " check %s", key_id, keyfile);
                ssl_log_ssl_error(SSLLOG_MARK, APLOG_EMERG, s);
                return APR_EGENERAL;
            }
        }

        if (SSL_CTX_check_private_key(mctx->ssl_ctx) < 1) {
            ap_log_error(APLOG_MARK, APLOG_EMERG, 0, s, APLOGNO(02565)
                         "Certificate and private key %s from %s and %s "
                         "do not match", key_id, certfile, keyfile);
            return APR_EGENERAL;
        }

#ifdef HAVE_SSL_CONF_CMD
        /* 
         * workaround for those OpenSSL versions where SSL_CTX_get0_certificate
         * is not yet available: create an SSL struct which we dispose of
         * as soon as we no longer need access to the cert. (Strictly speaking,
         * SSL_CTX_get0_certificate does not depend on the SSL_CONF stuff,
         * but there's no reliable way to check for its existence, so we
         * assume that if SSL_CONF is available, it's OpenSSL 1.0.2 or later,
         * and SSL_CTX_get0_certificate is implemented.)
         */
        cert = SSL_CTX_get0_certificate(mctx->ssl_ctx);
#else
        {
            SSL *ssl = SSL_new(mctx->ssl_ctx);
            if (ssl) {
                /* Workaround bug in SSL_get_certificate in OpenSSL 0.9.8y */
                SSL_set_connect_state(ssl);
                cert = SSL_get_certificate(ssl);
                SSL_free(ssl);
            }
        }
#endif
        if (!cert) {
            ap_log_error(APLOG_MARK, APLOG_ERR, 0, s, APLOGNO(02566)
                         "Unable to retrieve certificate %s", key_id);
            return APR_EGENERAL;
        }

        /* warn about potential cert issues */
        ssl_check_public_cert(s, ptemp, cert, key_id);

#if defined(HAVE_OCSP_STAPLING) && !defined(SSL_CTRL_SET_CURRENT_CERT)
        /* 
         * OpenSSL up to 1.0.1: configure stapling as we go. In 1.0.2
         * and later, there's SSL_CTX_set_current_cert, which allows
         * iterating over all certs in an SSL_CTX (including those possibly
         * loaded via SSLOpenSSLConfCmd Certificate), so for 1.0.2 and
         * later, we defer to the code in ssl_init_server_ctx.
         */
        if (!ssl_stapling_init_cert(s, p, ptemp, mctx, cert)) {
            ap_log_error(APLOG_MARK, APLOG_ERR, 0, s, APLOGNO(02567)
                         "Unable to configure certificate %s for stapling",
                         key_id);
        }
#endif

        ap_log_error(APLOG_MARK, APLOG_INFO, 0, s, APLOGNO(02568)
                     "Certificate and private key %s configured from %s and %s",
                     key_id, certfile, keyfile);
    }

    /*
     * Try to read DH parameters from the (first) SSLCertificateFile
     */
    certfile = APR_ARRAY_IDX(mctx->pks->cert_files, 0, const char *);
    if (certfile && !modssl_is_engine_id(certfile)) {
        int num_bits = 0;
#if OPENSSL_VERSION_NUMBER < 0x30000000L
        DH *dh = modssl_dh_from_file(certfile);
        if (dh) {
            num_bits = DH_bits(dh);
            SSL_CTX_set_tmp_dh(mctx->ssl_ctx, dh);
            DH_free(dh);
            custom_dh_done = 1;
        }
#else
        pkey = modssl_dh_pkey_from_file(certfile);
        if (pkey) {
            num_bits = EVP_PKEY_get_bits(pkey);
            if (!SSL_CTX_set0_tmp_dh_pkey(mctx->ssl_ctx, pkey)) {
                EVP_PKEY_free(pkey);
            }
            else {
                custom_dh_done = 1;
            }
        }
#endif
        if (custom_dh_done) {
            ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s, APLOGNO(02540)
                         "Custom DH parameters (%d bits) for %s loaded from %s",
                         num_bits, vhost_id, certfile);
        }
    }
#if !MODSSL_USE_OPENSSL_PRE_1_1_API
    if (!custom_dh_done) {
        /* If no parameter is manually configured, enable auto
         * selection. */
        SSL_CTX_set_dh_auto(mctx->ssl_ctx, 1);
    }
#endif

#ifdef HAVE_ECC
    /*
     * Similarly, try to read the ECDH curve name from SSLCertificateFile...
     */
    if (certfile && !modssl_is_engine_id(certfile)
        && (ecgroup = modssl_ec_group_from_file(certfile))
        && (curve_nid = EC_GROUP_get_curve_name(ecgroup))) {
#if OPENSSL_VERSION_NUMBER < 0x30000000L
        EC_KEY *eckey = EC_KEY_new_by_curve_name(curve_nid);
        if (eckey) {
            SSL_CTX_set_tmp_ecdh(mctx->ssl_ctx, eckey);
            EC_KEY_free(eckey);
        }
        else {
            curve_nid = 0;
        }
#else
        if (!SSL_CTX_set1_curves(mctx->ssl_ctx, &curve_nid, 1)) {
            curve_nid = 0;
        }
#endif
        if (curve_nid) {
            ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s, APLOGNO(02541)
                         "ECDH curve %s for %s specified in %s",
                         OBJ_nid2sn(curve_nid), vhost_id, certfile);
        }
    }
    /*
     * ...otherwise, enable auto curve selection (OpenSSL 1.0.2)
     * or configure NIST P-256 (required to enable ECDHE for earlier versions)
     * ECDH is always enabled in 1.1.0 unless excluded from SSLCipherList
     */
#if MODSSL_USE_OPENSSL_PRE_1_1_API
    if (!curve_nid) {
#if defined(SSL_CTX_set_ecdh_auto)
        SSL_CTX_set_ecdh_auto(mctx->ssl_ctx, 1);
#else
        EC_KEY *eckey = EC_KEY_new_by_curve_name(NID_X9_62_prime256v1);
        if (eckey) {
            SSL_CTX_set_tmp_ecdh(mctx->ssl_ctx, eckey);
            EC_KEY_free(eckey);
        }
#endif
    }
#endif
    /* OpenSSL assures us that _free() is NULL-safe */
    EC_GROUP_free(ecgroup);
#endif

    return APR_SUCCESS;
}