⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 ssl_engine_pphrase.c

📁 linux网络服务器工具
💻 C
📖 第 1 页 / 共 2 页
字号:
/* Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements.  See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License.  You may obtain a copy of the License at * *     http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. *//*                      _             _ *  _ __ ___   ___   __| |    ___ ___| |  mod_ssl * | '_ ` _ \ / _ \ / _` |   / __/ __| |  Apache Interface to OpenSSL * | | | | | | (_) | (_| |   \__ \__ \ | * |_| |_| |_|\___/ \__,_|___|___/___/_| *                      |_____| *  ssl_engine_pphrase.c *  Pass Phrase Dialog */                             /* ``Treat your password like your                                  toothbrush. Don't let anybody                                  else use it, and get a new one                                  every six months.''                                           -- Clifford Stoll     */#include "ssl_private.h"/* * Return true if the named file exists and is readable */static apr_status_t exists_and_readable(char *fname, apr_pool_t *pool, apr_time_t *mtime){    apr_status_t stat;    apr_finfo_t sbuf;    apr_file_t *fd;    if ((stat = apr_stat(&sbuf, fname, APR_FINFO_MIN, pool)) != APR_SUCCESS)        return stat;    if (sbuf.filetype != APR_REG)        return APR_EGENERAL;    if ((stat = apr_file_open(&fd, fname, APR_READ, 0, pool)) != APR_SUCCESS)        return stat;    if (mtime) {        *mtime = sbuf.mtime;    }    apr_file_close(fd);    return APR_SUCCESS;}/* * reuse vhost keys for asn1 tables where keys are allocated out * of s->process->pool to prevent "leaking" each time we format * a vhost key.  since the key is stored in a table with lifetime * of s->process->pool, the key needs to have the same lifetime. * * XXX: probably seems silly to use a hash table with keys and values * being the same, but it is easier than doing a linear search * and will make it easier to remove keys if needed in the future. * also have the problem with apr_array_header_t that if we * underestimate the number of vhost keys when we apr_array_make(), * the array will get resized when we push past the initial number * of elts.  this resizing in the s->process->pool means "leaking" * since apr_array_push() will apr_alloc arr->nalloc * 2 elts, * leaving the original arr->elts to waste. */static char *asn1_table_vhost_key(SSLModConfigRec *mc, apr_pool_t *p,                                  char *id, char *an){    /* 'p' pool used here is cleared on restarts (or sooner) */    char *key = apr_psprintf(p, "%s:%s", id, an);    void *keyptr = apr_hash_get(mc->tVHostKeys, key,                                APR_HASH_KEY_STRING);    if (!keyptr) {        /* make a copy out of s->process->pool */        keyptr = apr_pstrdup(mc->pPool, key);        apr_hash_set(mc->tVHostKeys, keyptr,                     APR_HASH_KEY_STRING, keyptr);    }    return (char *)keyptr;}/*  _________________________________________________________________****  Pass Phrase and Private Key Handling**  _________________________________________________________________*/#define BUILTIN_DIALOG_BACKOFF 2#define BUILTIN_DIALOG_RETRIES 5static apr_file_t *writetty = NULL;static apr_file_t *readtty = NULL;/* * sslc has a nasty flaw where its * PEM_read_bio_PrivateKey does not take a callback arg. */static server_rec *ssl_pphrase_server_rec = NULL;#ifdef SSLC_VERSION_NUMBERint ssl_pphrase_Handle_CB(char *, int, int);#elseint ssl_pphrase_Handle_CB(char *, int, int, void *);#endifstatic char *pphrase_array_get(apr_array_header_t *arr, int idx){    if ((idx < 0) || (idx >= arr->nelts)) {        return NULL;    }    return ((char **)arr->elts)[idx];}static void pphrase_array_clear(apr_array_header_t *arr){    if (arr->nelts > 0) {        memset(arr->elts, 0, arr->elt_size * arr->nelts);    }    arr->nelts = 0;}void ssl_pphrase_Handle(server_rec *s, apr_pool_t *p){    SSLModConfigRec *mc = myModConfig(s);    SSLSrvConfigRec *sc;    server_rec *pServ;    char *cpVHostID;    char szPath[MAX_STRING_LEN];    EVP_PKEY *pPrivateKey;    ssl_asn1_t *asn1;    unsigned char *ucp;    long int length;    X509 *pX509Cert;    BOOL bReadable;    apr_array_header_t *aPassPhrase;    int nPassPhrase;    int nPassPhraseCur;    char *cpPassPhraseCur;    int nPassPhraseRetry;    int nPassPhraseDialog;    int nPassPhraseDialogCur;    BOOL bPassPhraseDialogOnce;    char **cpp;    int i, j;    ssl_algo_t algoCert, algoKey, at;    char *an;    char *cp;    apr_time_t pkey_mtime = 0;    int isterm = 1;    apr_status_t rv;    /*     * Start with a fresh pass phrase array     */    aPassPhrase       = apr_array_make(p, 2, sizeof(char *));    nPassPhrase       = 0;    nPassPhraseDialog = 0;    /*     * Walk through all configured servers     */    for (pServ = s; pServ != NULL; pServ = pServ->next) {        sc = mySrvConfig(pServ);        if (!sc->enabled)            continue;        cpVHostID = ssl_util_vhostid(p, pServ);        ap_log_error(APLOG_MARK, APLOG_INFO, 0, pServ,                     "Loading certificate & private key of SSL-aware server");        /*         * Read in server certificate(s): This is the easy part         * because this file isn't encrypted in any way.         */        if (sc->server->pks->cert_files[0] == NULL) {            ap_log_error(APLOG_MARK, APLOG_ERR, 0, pServ,                         "Server should be SSL-aware but has no certificate "                         "configured [Hint: SSLCertificateFile]");            ssl_die();        }        algoCert = SSL_ALGO_UNKNOWN;        algoKey  = SSL_ALGO_UNKNOWN;        for (i = 0, j = 0; i < SSL_AIDX_MAX && sc->server->pks->cert_files[i] != NULL; i++) {            apr_cpystrn(szPath, sc->server->pks->cert_files[i], sizeof(szPath));            if ((rv = exists_and_readable(szPath, p, NULL)) != APR_SUCCESS) {                ap_log_error(APLOG_MARK, APLOG_ERR, rv, s,                             "Init: Can't open server certificate file %s",                             szPath);                ssl_die();            }            if ((pX509Cert = SSL_read_X509(szPath, NULL, NULL)) == NULL) {                ap_log_error(APLOG_MARK, APLOG_ERR, 0, s,                        "Init: Unable to read server certificate from file %s", szPath);                ssl_log_ssl_error(APLOG_MARK, APLOG_ERR, s);                ssl_die();            }            /*             * check algorithm type of certificate and make             * sure only one certificate per type is used.             */            at = ssl_util_algotypeof(pX509Cert, NULL);            an = ssl_util_algotypestr(at);            if (algoCert & at) {                ap_log_error(APLOG_MARK, APLOG_ERR, 0, s,                             "Init: Multiple %s server certificates not "                             "allowed", an);                ssl_log_ssl_error(APLOG_MARK, APLOG_ERR, s);                ssl_die();            }            algoCert |= at;            /*             * Insert the certificate into global module configuration to let it             * survive the processing between the 1st Apache API init round (where             * we operate here) and the 2nd Apache init round (where the             * certificate is actually used to configure mod_ssl's per-server             * configuration structures).             */            cp = asn1_table_vhost_key(mc, p, cpVHostID, an);            length = i2d_X509(pX509Cert, NULL);            ucp = ssl_asn1_table_set(mc->tPublicCert, cp, length);            (void)i2d_X509(pX509Cert, &ucp); /* 2nd arg increments */            /*             * Free the X509 structure             */            X509_free(pX509Cert);            /*             * Read in the private key: This is the non-trivial part, because the             * key is typically encrypted, so a pass phrase dialog has to be used             * to request it from the user (or it has to be alternatively gathered             * from a dialog program). The important point here is that ISPs             * usually have hundrets of virtual servers configured and a lot of             * them use SSL, so really we have to minimize the pass phrase             * dialogs.             *             * The idea is this: When N virtual hosts are configured and all of             * them use encrypted private keys with different pass phrases, we             * have no chance and have to pop up N pass phrase dialogs. But             * usually the admin is clever enough and uses the same pass phrase             * for more private key files (typically he even uses one single pass             * phrase for all). When this is the case we can minimize the dialogs             * by trying to re-use already known/entered pass phrases.             */            if (sc->server->pks->key_files[j] != NULL)                apr_cpystrn(szPath, sc->server->pks->key_files[j++], sizeof(szPath));            /*             * Try to read the private key file with the help of             * the callback function which serves the pass             * phrases to OpenSSL             */            myCtxVarSet(mc,  1, pServ);            myCtxVarSet(mc,  2, p);            myCtxVarSet(mc,  3, aPassPhrase);            myCtxVarSet(mc,  4, &nPassPhraseCur);            myCtxVarSet(mc,  5, &cpPassPhraseCur);            myCtxVarSet(mc,  6, cpVHostID);            myCtxVarSet(mc,  7, an);            myCtxVarSet(mc,  8, &nPassPhraseDialog);            myCtxVarSet(mc,  9, &nPassPhraseDialogCur);            myCtxVarSet(mc, 10, &bPassPhraseDialogOnce);            nPassPhraseCur        = 0;            nPassPhraseRetry      = 0;            nPassPhraseDialogCur  = 0;            bPassPhraseDialogOnce = TRUE;            pPrivateKey = NULL;            for (;;) {                /*                 * Try to read the private key file with the help of                 * the callback function which serves the pass                 * phrases to OpenSSL                 */                if ((rv = exists_and_readable(szPath, p,                                              &pkey_mtime)) != APR_SUCCESS ) {                     ap_log_error(APLOG_MARK, APLOG_ERR, rv, s,                                  "Init: Can't open server private key file "                                  "%s",szPath);                     ssl_die();                }                /*                 * if the private key is encrypted and SSLPassPhraseDialog                 * is configured to "builtin" it isn't possible to prompt for                 * a password after httpd has detached from the tty.                 * in this case if we already have a private key and the                 * file name/mtime hasn't changed, then reuse the existing key.                 * we also reuse existing private keys that were encrypted for                 * exec: and pipe: dialogs to minimize chances to snoop the                 * password.  that and pipe: dialogs might prompt the user                 * for password, which on win32 for example could happen 4                 * times at startup.  twice for each child and twice within                 * each since apache "restarts itself" on startup.                 * of course this will not work for the builtin dialog if                 * the server was started without LoadModule ssl_module                 * configured, then restarted with it configured.                 * but we fall through with a chance of success if the key                 * is not encrypted or can be handled via exec or pipe dialog.                 * and in the case of fallthrough, pkey_mtime and isatty()                 * are used to give a better idea as to what failed.                 */                if (pkey_mtime) {                    int i;                    for (i=0; i < SSL_AIDX_MAX; i++) {                        const char *key_id =                            ssl_asn1_table_keyfmt(p, cpVHostID, i);                        ssl_asn1_t *asn1 =                            ssl_asn1_table_get(mc->tPrivateKey, key_id);                        if (asn1 && (asn1->source_mtime == pkey_mtime)) {                            ap_log_error(APLOG_MARK, APLOG_INFO,                                         0, pServ,                                         "%s reusing existing "                                         "%s private key on restart",                                         cpVHostID, ssl_asn1_keystr(i));                            return;                        }                    }                }                cpPassPhraseCur = NULL;                ssl_pphrase_server_rec = s; /* to make up for sslc flaw */                /* Ensure that the error stack is empty; some SSL                 * functions will fail spuriously if the error stack                 * is not empty. */                ERR_clear_error();                bReadable = ((pPrivateKey = SSL_read_PrivateKey(szPath, NULL,                            ssl_pphrase_Handle_CB, s)) != NULL ? TRUE : FALSE);                /*                 * when the private key file now was readable,                 * it's fine and we go out of the loop                 */                if (bReadable)                   break;                /*                 * when we have more remembered pass phrases                 * try to reuse these first.                 */                if (nPassPhraseCur < nPassPhrase) {                    nPassPhraseCur++;                    continue;                }                /*                 * else it's not readable and we have no more                 * remembered pass phrases. Then this has to mean                 * that the callback function popped up the dialog                 * but a wrong pass phrase was entered.  We give the                 * user (but not the dialog program) a few more                 * chances...                 */#ifndef WIN32                if ((sc->server->pphrase_dialog_type == SSL_PPTYPE_BUILTIN                       || sc->server->pphrase_dialog_type == SSL_PPTYPE_PIPE)#else                if (sc->server->pphrase_dialog_type == SSL_PPTYPE_PIPE#endif                    && cpPassPhraseCur != NULL                    && nPassPhraseRetry < BUILTIN_DIALOG_RETRIES ) {                    apr_file_printf(writetty, "Apache:mod_ssl:Error: Pass phrase incorrect "                            "(%d more retr%s permitted).\n",                            (BUILTIN_DIALOG_RETRIES-nPassPhraseRetry),                            (BUILTIN_DIALOG_RETRIES-nPassPhraseRetry) == 1 ? "y" : "ies");                    nPassPhraseRetry++;                    if (nPassPhraseRetry > BUILTIN_DIALOG_BACKOFF)                        apr_sleep((nPassPhraseRetry-BUILTIN_DIALOG_BACKOFF)                                    * 5 * APR_USEC_PER_SEC);                    continue;                }#ifdef WIN32                if (sc->server->pphrase_dialog_type == SSL_PPTYPE_BUILTIN) {

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -