Quellcodebibliothek Statistik Leitseite products/sources/formale Sprachen/C/Apache/modules/ssl/   (Apache Software Stiftung Version 2.4.65©)  Datei vom 25.10.2024 mit Größe 36 kB image not shown  

Quelle  ssl_engine_pphrase.c   Sprache: C

 
/* 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"

#include <openssl/ui.h>
#if MODSSL_HAVE_OPENSSL_STORE
#include <openssl/store.h>
#endif

typedef struct {
    server_rec         *s;
    apr_pool_t         *p;
    apr_array_header_t *aPassPhrase;
    int                 nPassPhraseCur;
    char               *cpPassPhraseCur;
    int                 nPassPhraseDialog;
    int                 nPassPhraseDialogCur;
    BOOL                bPassPhraseDialogOnce;
    const char         *key_id;
    const char         *pkey_file;
} pphrase_cb_arg_t;

#ifdef HAVE_ECC
static const char *key_types[] = {"RSA""DSA""ECC"};
#else
static const char *key_types[] = {"RSA""DSA"};
#endif

/*
 * Return true if the named file exists and is readable
 */


static apr_status_t exists_and_readable(const 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 const char *asn1_table_vhost_key(SSLModConfigRec *mc, apr_pool_t *p,
                                  const char *id, int i)
{
    /* 'p' pool used here is cleared on restarts (or sooner) */
    char *key = apr_psprintf(p, "%s:%d", id, i);
    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 5

static apr_file_t *writetty = NULL;
static apr_file_t *readtty = NULL;

int ssl_pphrase_Handle_CB(char *, intintvoid *);

static char *pphrase_array_get(apr_array_header_t *arr, int idx)
{
    if ((idx < 0) || (idx >= arr->nelts)) {
        return NULL;
    }

    return ((char **)arr->elts)[idx];
}

apr_status_t ssl_load_encrypted_pkey(server_rec *s, apr_pool_t *p, int idx,
                                     const char *pkey_file,
                                     apr_array_header_t **pphrases)
{
    SSLModConfigRec *mc = myModConfig(s);
    SSLSrvConfigRec *sc = mySrvConfig(s);
    const char *key_id = asn1_table_vhost_key(mc, p, sc->vhost_id, idx);
    EVP_PKEY *pPrivateKey = NULL;
    ssl_asn1_t *asn1;
    int nPassPhrase = (*pphrases)->nelts;
    int nPassPhraseRetry = 0;
    apr_time_t pkey_mtime = 0;
    apr_status_t rv;
    pphrase_cb_arg_t ppcb_arg;

    if (!pkey_file) {
         ap_log_error(APLOG_MARK, APLOG_EMERG, 0, s, APLOGNO(02573)
                      "Init: No private key specified for %s", key_id);
         return ssl_die(s);
    }
    else if ((rv = exists_and_readable(pkey_file, p, &pkey_mtime))
             != APR_SUCCESS ) {
         ap_log_error(APLOG_MARK, APLOG_EMERG, rv, s, APLOGNO(02574)
                      "Init: Can't open server private key file %s", pkey_file);
         return ssl_die(s);
    }

    ppcb_arg.s                     = s;
    ppcb_arg.p                     = p;
    ppcb_arg.aPassPhrase           = *pphrases;
    ppcb_arg.nPassPhraseCur        = 0;
    ppcb_arg.cpPassPhraseCur       = NULL;
    ppcb_arg.nPassPhraseDialog     = 0;
    ppcb_arg.nPassPhraseDialogCur  = 0;
    ppcb_arg.bPassPhraseDialogOnce = TRUE;
    ppcb_arg.key_id                = key_id;
    ppcb_arg.pkey_file             = pkey_file;

    /*
     * 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) {
        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, s, APLOGNO(02575)
                         "Reusing existing private key from %s on restart",
                         ppcb_arg.pkey_file);
            return APR_SUCCESS;
        }
    }

    ap_log_error(APLOG_MARK, APLOG_INFO, 0, s, APLOGNO(02576)
                 "Attempting to load encrypted (?) private key %s", key_id);

    for (;;) {
        /*
         * Try to read the private key file with the help of
         * the callback function which serves the pass
         * phrases to OpenSSL
         */


        ppcb_arg.cpPassPhraseCur = NULL;

        /* Ensure that the error stack is empty; some SSL
         * functions will fail spuriously if the error stack
         * is not empty. */

        ERR_clear_error();

        pPrivateKey = modssl_read_privatekey(ppcb_arg.pkey_file,
                                             ssl_pphrase_Handle_CB, &ppcb_arg);
        /* If the private key was successfully read, nothing more to
           do here. */

        if (pPrivateKey != NULL)
            break;

        /*
         * when we have more remembered pass phrases
         * try to reuse these first.
         */

        if (ppcb_arg.nPassPhraseCur < nPassPhrase) {
            ppcb_arg.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
            && ppcb_arg.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) {
            ap_log_error(APLOG_MARK, APLOG_EMERG, 0, s, APLOGNO(02577)
                         "Init: SSLPassPhraseDialog builtin is not "
                         "supported on Win32 (key file "
                         "%s)", ppcb_arg.pkey_file);
            return ssl_die(s);
        }
#endif /* WIN32 */

        /*
         * Ok, anything else now means a fatal error.
         */

        if (ppcb_arg.cpPassPhraseCur == NULL) {
            if (ppcb_arg.nPassPhraseDialogCur && pkey_mtime &&
                !isatty(fileno(stdout))) /* XXX: apr_isatty() */
            {
                ap_log_error(APLOG_MARK, APLOG_ERR, 0,
                             s, APLOGNO(02578)
                             "Init: Unable to read pass phrase "
                             "[Hint: key introduced or changed "
                             "before restart?]");
                ssl_log_ssl_error(SSLLOG_MARK, APLOG_ERR, s);
            }
            else {
                ap_log_error(APLOG_MARK, APLOG_ERR, 0,
                             s, APLOGNO(02579) "Init: Private key not found");
                ssl_log_ssl_error(SSLLOG_MARK, APLOG_ERR, s);
            }
            if (writetty) {
                apr_file_printf(writetty, "Apache:mod_ssl:Error: Private key not found.\n");
                apr_file_printf(writetty, "**Stopped\n");
            }
        }
        else {
            ap_log_error(APLOG_MARK, APLOG_EMERG, 0, s, APLOGNO(02580)
                         "Init: Pass phrase incorrect for key %s",
                         key_id);
            ssl_log_ssl_error(SSLLOG_MARK, APLOG_EMERG, s);

            if (writetty) {
                apr_file_printf(writetty, "Apache:mod_ssl:Error: Pass phrase incorrect.\n");
                apr_file_printf(writetty, "**Stopped\n");
            }
        }
        return ssl_die(s);
    }

    if (pPrivateKey == NULL) {
        ap_log_error(APLOG_MARK, APLOG_EMERG, 0, s, APLOGNO(02581)
                     "Init: Unable to read server private key from file %s",
                     ppcb_arg.pkey_file);
        ssl_log_ssl_error(SSLLOG_MARK, APLOG_EMERG, s);
        return ssl_die(s);
    }

    /*
     * Log the type of reading
     */

    if (ppcb_arg.nPassPhraseDialogCur == 0) {
        ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s, APLOGNO(02582)
                     "unencrypted %s private key - pass phrase not "
                     "required", key_id);
    }
    else {
        if (ppcb_arg.cpPassPhraseCur != NULL) {
            ap_log_error(APLOG_MARK, APLOG_DEBUG, 0,
                         s, APLOGNO(02583)
                         "encrypted %s private key - pass phrase "
                         "requested", key_id);
        }
        else {
            ap_log_error(APLOG_MARK, APLOG_DEBUG, 0,
                         s, APLOGNO(02584)
                         "encrypted %s private key - pass phrase"
                         " reused", key_id);
        }
    }

    /*
     * Ok, when we have one more pass phrase store it
     */

    if (ppcb_arg.cpPassPhraseCur != NULL) {
        *(const char **)apr_array_push(ppcb_arg.aPassPhrase) =
            ppcb_arg.cpPassPhraseCur;
        nPassPhrase++;
    }

    /* Cache the private key in the global module configuration so it
     * can be used after subsequent reloads. */

    asn1 = ssl_asn1_table_set(mc->tPrivateKey, key_id, pPrivateKey);

    if (ppcb_arg.nPassPhraseDialogCur != 0) {
        /* remember mtime of encrypted keys */
        asn1->source_mtime = pkey_mtime;
    }

    /*
     * Free the private key structure
     */

    EVP_PKEY_free(pPrivateKey);

    /*
     * Let the user know when we're successful.
     */

    if ((ppcb_arg.nPassPhraseDialog > 0) &&
        (ppcb_arg.cpPassPhraseCur != NULL)) {
        if (writetty) {
            apr_file_printf(writetty, "\n"
                            "OK: Pass Phrase Dialog successful.\n");
        }
    }

    /* Close the pipes if they were opened
     */

    if (readtty) {
        apr_file_close(readtty);
        apr_file_close(writetty);
        readtty = writetty = NULL;
    }

    return APR_SUCCESS;
}

static apr_status_t ssl_pipe_child_create(apr_pool_t *p, const char *progname)
{
    /* Child process code for 'ErrorLog "|..."';
     * may want a common framework for this, since I expect it will
     * be common for other foo-loggers to want this sort of thing...
     */

    apr_status_t rc;
    apr_procattr_t *procattr;
    apr_proc_t *procnew;

    if (((rc = apr_procattr_create(&procattr, p)) == APR_SUCCESS) &&
        ((rc = apr_procattr_io_set(procattr,
                                   APR_FULL_BLOCK,
                                   APR_FULL_BLOCK,
                                   APR_NO_PIPE)) == APR_SUCCESS)) {
        char **args;

        apr_tokenize_to_argv(progname, &args, p);
        procnew = (apr_proc_t *)apr_pcalloc(p, sizeof(*procnew));
        rc = apr_proc_create(procnew, args[0], (const char * const *)args,
                             NULL, procattr, p);
        if (rc == APR_SUCCESS) {
            /* XXX: not sure if we aught to...
             * apr_pool_note_subprocess(p, procnew, APR_KILL_AFTER_TIMEOUT);
             */

            writetty = procnew->in;
            readtty = procnew->out;
        }
    }

    return rc;
}

static int pipe_get_passwd_cb(char *buf, int length, char *prompt, int verify)
{
    apr_status_t rc;
    char *p;

    apr_file_puts(prompt, writetty);

    buf[0]='\0';
    rc = apr_file_gets(buf, length, readtty);
    apr_file_puts(APR_EOL_STR, writetty);

    if (rc != APR_SUCCESS || apr_file_eof(readtty)) {
        memset(buf, 0, length);
        return 1;  /* failure */
    }
    if ((p = strchr(buf, '\n')) != NULL) {
        *p = '\0';
    }
#ifdef WIN32
    /* XXX: apr_sometest */
    if ((p = strchr(buf, '\r')) != NULL) {
        *p = '\0';
    }
#endif
    return 0;
}

int ssl_pphrase_Handle_CB(char *buf, int bufsize, int verify, void *srv)
{
    pphrase_cb_arg_t *ppcb_arg = (pphrase_cb_arg_t *)srv;
    SSLSrvConfigRec *sc = mySrvConfig(ppcb_arg->s);
    char *cpp;
    int len = -1;

    ppcb_arg->nPassPhraseDialog++;
    ppcb_arg->nPassPhraseDialogCur++;

    /*
     * When remembered pass phrases are available use them...
     */

    if ((cpp = pphrase_array_get(ppcb_arg->aPassPhrase,
                                 ppcb_arg->nPassPhraseCur)) != NULL) {
        apr_cpystrn(buf, cpp, bufsize);
        len = strlen(buf);
        return len;
    }

    /*
     * Builtin or Pipe dialog
     */

    if (sc->server->pphrase_dialog_type == SSL_PPTYPE_BUILTIN
          || sc->server->pphrase_dialog_type == SSL_PPTYPE_PIPE) {
        char *prompt;
        int i;

        if (sc->server->pphrase_dialog_type == SSL_PPTYPE_PIPE) {
            if (!readtty) {
                ap_log_error(APLOG_MARK, APLOG_INFO, 0, ppcb_arg->s,
                             APLOGNO(01965)
                             "Init: Creating pass phrase dialog pipe child "
                             "'%s'", sc->server->pphrase_dialog_path);
                if (ssl_pipe_child_create(ppcb_arg->p,
                                          sc->server->pphrase_dialog_path)
                        != APR_SUCCESS) {
                    ap_log_error(APLOG_MARK, APLOG_ERR, 0, ppcb_arg->s,
                                 APLOGNO(01966)
                                 "Init: Failed to create pass phrase pipe '%s'",
                                 sc->server->pphrase_dialog_path);
                    PEMerr(PEM_F_PEM_DEF_CALLBACK,
                           PEM_R_PROBLEMS_GETTING_PASSWORD);
                    memset(buf, 0, (unsigned int)bufsize);
                    return (-1);
                }
            }
            ap_log_error(APLOG_MARK, APLOG_INFO, 0, ppcb_arg->s, APLOGNO(01967)
                         "Init: Requesting pass phrase via piped dialog");
        }
        else { /* sc->server->pphrase_dialog_type == SSL_PPTYPE_BUILTIN */
#ifdef WIN32
            PEMerr(PEM_F_PEM_DEF_CALLBACK, PEM_R_PROBLEMS_GETTING_PASSWORD);
            memset(buf, 0, (unsigned int)bufsize);
            return (-1);
#else
            /*
             * stderr has already been redirected to the error_log.
             * rather than attempting to temporarily rehook it to the terminal,
             * we print the prompt to stdout before EVP_read_pw_string turns
             * off tty echo
             */

            apr_file_open_stdout(&writetty, ppcb_arg->p);

            ap_log_error(APLOG_MARK, APLOG_INFO, 0, ppcb_arg->s, APLOGNO(01968)
                         "Init: Requesting pass phrase via builtin terminal "
                         "dialog");
#endif
        }

        /*
         * The first time display a header to inform the user about what
         * program he actually speaks to, which module is responsible for
         * this terminal dialog and why to the hell he has to enter
         * something...
         */

        if (ppcb_arg->nPassPhraseDialog == 1) {
            apr_file_printf(writetty, "%s mod_ssl (Pass Phrase Dialog)\n",
                            AP_SERVER_BASEVERSION);
            apr_file_printf(writetty, "Some of your private key files are encrypted for security reasons.\n");
            apr_file_printf(writetty, "In order to read them you have to provide the pass phrases.\n");
        }
        if (ppcb_arg->bPassPhraseDialogOnce) {
            ppcb_arg->bPassPhraseDialogOnce = FALSE;
            apr_file_printf(writetty, "\n");
            apr_file_printf(writetty, "Private key %s (%s)\n",
                            ppcb_arg->key_id, ppcb_arg->pkey_file);
        }

        /*
         * Emulate the OpenSSL internal pass phrase dialog
         * (see crypto/pem/pem_lib.c:def_callback() for details)
         */

        prompt = "Enter pass phrase:";

        for (;;) {
            apr_file_puts(prompt, writetty);
            if (sc->server->pphrase_dialog_type == SSL_PPTYPE_PIPE) {
                i = pipe_get_passwd_cb(buf, bufsize, ""FALSE);
            }
            else { /* sc->server->pphrase_dialog_type == SSL_PPTYPE_BUILTIN */
                i = EVP_read_pw_string(buf, bufsize, ""FALSE);
            }
            if (i != 0) {
                PEMerr(PEM_F_PEM_DEF_CALLBACK,PEM_R_PROBLEMS_GETTING_PASSWORD);
                memset(buf, 0, (unsigned int)bufsize);
                return (-1);
            }
            len = strlen(buf);
            if (len < 1)
                apr_file_printf(writetty, "Apache:mod_ssl:Error: Pass phrase empty (needs to be at least 1 character).\n");
            else
                break;
        }
    }

    /*
     * Filter program
     */

    else if (sc->server->pphrase_dialog_type == SSL_PPTYPE_FILTER) {
        const char *cmd = sc->server->pphrase_dialog_path;
        const char **argv = apr_palloc(ppcb_arg->p, sizeof(char *) * 4);
        const char *idx = ap_strrchr_c(ppcb_arg->key_id, ':') + 1;
        char *result;
        int i;

        ap_log_error(APLOG_MARK, APLOG_INFO, 0, ppcb_arg->s, APLOGNO(01969)
                     "Init: Requesting pass phrase from dialog filter "
                     "program (%s)", cmd);

        argv[0] = cmd;
        argv[1] = apr_pstrndup(ppcb_arg->p, ppcb_arg->key_id,
                               idx-1 - ppcb_arg->key_id);
        if ((i = atoi(idx)) < CERTKEYS_IDX_MAX+1) {
            /*
             * For compatibility with existing 2.4.x configurations, use
             * "RSA", "DSA" and "ECC" strings for the first two/three keys
             */

            argv[2] = key_types[i];
        } else {
            /* Four and above: use the integer index */
            argv[2] = apr_pstrdup(ppcb_arg->p, idx);
        }
        argv[3] = NULL;

        result = ssl_util_readfilter(ppcb_arg->s, ppcb_arg->p, cmd, argv);
        apr_cpystrn(buf, result, bufsize);
        len = strlen(buf);
    }

    /*
     * Ok, we now have the pass phrase, so give it back
     */

    ppcb_arg->cpPassPhraseCur = apr_pstrdup(ppcb_arg->p, buf);

    /*
     * And return its length to OpenSSL...
     */

    return (len);
}

#if MODSSL_HAVE_ENGINE_API || MODSSL_HAVE_OPENSSL_STORE

/* OpenSSL UI implementation for passphrase entry; largely duplicated
 * from ssl_pphrase_Handle_CB but adjusted for UI API. TODO: Might be
 * worth trying to shift pphrase handling over to the UI API
 * completely. */

static int passphrase_ui_open(UI *ui)
{
    pphrase_cb_arg_t *ppcb = UI_get0_user_data(ui);
    SSLSrvConfigRec *sc = mySrvConfig(ppcb->s);

    ppcb->nPassPhraseDialog++;
    ppcb->nPassPhraseDialogCur++;

    /*
     * Builtin or Pipe dialog
     */

    if (sc->server->pphrase_dialog_type == SSL_PPTYPE_BUILTIN
        || sc->server->pphrase_dialog_type == SSL_PPTYPE_PIPE) {
        if (sc->server->pphrase_dialog_type == SSL_PPTYPE_PIPE) {
            if (!readtty) {
                ap_log_error(APLOG_MARK, APLOG_INFO, 0, ppcb->s,
                             APLOGNO(10143)
                             "Init: Creating pass phrase dialog pipe child "
                             "'%s'", sc->server->pphrase_dialog_path);
                if (ssl_pipe_child_create(ppcb->p,
                            sc->server->pphrase_dialog_path)
                        != APR_SUCCESS) {
                    ap_log_error(APLOG_MARK, APLOG_ERR, 0, ppcb->s,
                                 APLOGNO(10144)
                                 "Init: Failed to create pass phrase pipe '%s'",
                                 sc->server->pphrase_dialog_path);
                    return 0;
                }
            }
            ap_log_error(APLOG_MARK, APLOG_INFO, 0, ppcb->s, APLOGNO(10145)
                         "Init: Requesting pass phrase via piped dialog");
        }
        else { /* sc->server->pphrase_dialog_type == SSL_PPTYPE_BUILTIN */
#ifdef WIN32
            ap_log_error(APLOG_MARK, APLOG_ERR, 0, ppcb->s, APLOGNO(10146)
                         "Init: Failed to create pass phrase pipe '%s'",
                         sc->server->pphrase_dialog_path);
            return 0;
#else
            /*
             * stderr has already been redirected to the error_log.
             * rather than attempting to temporarily rehook it to the terminal,
             * we print the prompt to stdout before EVP_read_pw_string turns
             * off tty echo
             */

            apr_file_open_stdout(&writetty, ppcb->p);

            ap_log_error(APLOG_MARK, APLOG_INFO, 0, ppcb->s, APLOGNO(10147)
                         "Init: Requesting pass phrase via builtin terminal "
                         "dialog");
#endif
        }

        /*
         * The first time display a header to inform the user about what
         * program he actually speaks to, which module is responsible for
         * this terminal dialog and why to the hell he has to enter
         * something...
         */

        if (ppcb->nPassPhraseDialog == 1) {
            apr_file_printf(writetty, "%s mod_ssl (Pass Phrase Dialog)\n",
                            AP_SERVER_BASEVERSION);
            apr_file_printf(writetty,
                            "A pass phrase is required to access the private key.\n");
        }
        if (ppcb->bPassPhraseDialogOnce) {
            ppcb->bPassPhraseDialogOnce = FALSE;
            apr_file_printf(writetty, "\n");
            apr_file_printf(writetty, "Private key %s (%s)\n",
                            ppcb->key_id, ppcb->pkey_file);
        }
    }

    return 1;
}

static int passphrase_ui_read(UI *ui, UI_STRING *uis)
{
    pphrase_cb_arg_t *ppcb = UI_get0_user_data(ui);
    SSLSrvConfigRec *sc = mySrvConfig(ppcb->s);
    const char *prompt;
    int i;
    int bufsize;
    int len;
    char *buf;

    prompt = UI_get0_output_string(uis);
    if (prompt == NULL) {
        prompt = "Enter pass phrase:";
    }

    /*
     * Get the maximum expected size and allocate the buffer
     */

    bufsize = UI_get_result_maxsize(uis);
    buf = apr_pcalloc(ppcb->p, bufsize);

    if (sc->server->pphrase_dialog_type == SSL_PPTYPE_BUILTIN
        || sc->server->pphrase_dialog_type == SSL_PPTYPE_PIPE) {
        /*
         * Get the pass phrase through a callback.
         * Empty input is not accepted.
         */

        for (;;) {
            if (sc->server->pphrase_dialog_type == SSL_PPTYPE_PIPE) {
                i = pipe_get_passwd_cb(buf, bufsize, ""FALSE);
            }
            else { /* sc->server->pphrase_dialog_type == SSL_PPTYPE_BUILTIN */
                i = EVP_read_pw_string(buf, bufsize, ""FALSE);
            }
            if (i != 0) {
                OPENSSL_cleanse(buf, bufsize);
                return 0;
            }
            len = strlen(buf);
            if (len < 1){
                apr_file_printf(writetty, "Apache:mod_ssl:Error: Pass phrase"
                                "empty (needs to be at least 1 character).\n");
                apr_file_puts(prompt, writetty);
            }
            else {
                break;
            }
        }
    }
    /*
     * Filter program
     */

    else if (sc->server->pphrase_dialog_type == SSL_PPTYPE_FILTER) {
        const char *cmd = sc->server->pphrase_dialog_path;
        const char **argv = apr_palloc(ppcb->p, sizeof(char *) * 3);
        char *result;

        ap_log_error(APLOG_MARK, APLOG_INFO, 0, ppcb->s, APLOGNO(10148)
                     "Init: Requesting pass phrase from dialog filter "
                     "program (%s)", cmd);

        argv[0] = cmd;
        argv[1] = ppcb->key_id;
        argv[2] = NULL;

        result = ssl_util_readfilter(ppcb->s, ppcb->p, cmd, argv);
        apr_cpystrn(buf, result, bufsize);
        len = strlen(buf);
    }

    /*
     * Ok, we now have the pass phrase, so give it back
     */

    ppcb->cpPassPhraseCur = apr_pstrdup(ppcb->p, buf);
    UI_set_result(ui, uis, buf);

    /* Clear sensitive data. */
    OPENSSL_cleanse(buf, bufsize);
    return 1;
}

static int passphrase_ui_write(UI *ui, UI_STRING *uis)
{
    pphrase_cb_arg_t *ppcb = UI_get0_user_data(ui);
    SSLSrvConfigRec *sc;
    const char *prompt;

    sc = mySrvConfig(ppcb->s);

    if (sc->server->pphrase_dialog_type == SSL_PPTYPE_BUILTIN
        || sc->server->pphrase_dialog_type == SSL_PPTYPE_PIPE) {
        prompt = UI_get0_output_string(uis);
        apr_file_puts(prompt, writetty);
    }

    return 1;
}

static int passphrase_ui_close(UI *ui)
{
    /*
     * Close the pipes if they were opened
     */

    if (readtty) {
        apr_file_close(readtty);
        apr_file_close(writetty);
        readtty = writetty = NULL;
    }
    return 1;
}

static apr_status_t pp_ui_method_cleanup(void *uip)
{
    UI_METHOD *uim = uip;
    
    UI_destroy_method(uim);

    return APR_SUCCESS;
}

static UI_METHOD *get_passphrase_ui(apr_pool_t *p)
{
    UI_METHOD *ui_method = UI_create_method("Passphrase UI");

    UI_method_set_opener(ui_method, passphrase_ui_open);
    UI_method_set_reader(ui_method, passphrase_ui_read);
    UI_method_set_writer(ui_method, passphrase_ui_write);
    UI_method_set_closer(ui_method, passphrase_ui_close);

    apr_pool_cleanup_register(p, ui_method, pp_ui_method_cleanup,
                              pp_ui_method_cleanup);
    
    return ui_method;
}
#endif

#if MODSSL_HAVE_ENGINE_API
static apr_status_t modssl_engine_cleanup(void *engine)
{
    ENGINE *e = engine;

    ENGINE_finish(e);

    return APR_SUCCESS;
}

/* Tries to load the key and optionally certificate via the ENGINE
 * API. Returns APR_ENOTIMPL if an ENGINE could not be identified
 * loaded from the key name. */

static apr_status_t modssl_load_keypair_engine(server_rec *s, apr_pool_t *pconf,
                                               apr_pool_t *ptemp,
                                               const char *vhostid,
                                               const char *certid,
                                               const char *keyid,
                                               X509 **pubkey,
                                               EVP_PKEY **privkey)
{
    const char *c, *scheme;
    ENGINE *e;
    UI_METHOD *ui_method = get_passphrase_ui(ptemp);
    pphrase_cb_arg_t ppcb;

    memset(&ppcb, 0, sizeof ppcb);
    ppcb.s = s;
    ppcb.p = ptemp;
    ppcb.bPassPhraseDialogOnce = TRUE;
    ppcb.key_id = vhostid;
    ppcb.pkey_file = keyid;

    c = ap_strchr_c(keyid, ':');
    if (!c || c == keyid) {
        ap_log_error(APLOG_MARK, APLOG_NOTICE, 0, s, APLOGNO(10131)
                     "Init: Unrecognized private key identifier `%s'",
                     keyid);
        return APR_ENOTIMPL;
    }

    scheme = apr_pstrmemdup(ptemp, keyid, c - keyid);
    if (!(e = ENGINE_by_id(scheme))) {
        ap_log_error(APLOG_MARK, APLOG_NOTICE, 0, s, APLOGNO(10132)
                     "Init: Failed to load engine for private key %s",
                     keyid);
        ssl_log_ssl_error(SSLLOG_MARK, APLOG_NOTICE, s);
        return APR_ENOTIMPL;
    }

    if (!ENGINE_init(e)) {
        ap_log_error(APLOG_MARK, APLOG_EMERG, 0, s, APLOGNO(10149)
                     "Init: Failed to initialize engine %s for private key %s",
                     scheme, keyid);
        ssl_log_ssl_error(SSLLOG_MARK, APLOG_EMERG, s);
        return ssl_die(s);
    }

    ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s, 
                 "Init: Initialized engine %s for private key %s",
                 scheme, keyid);

    if (APLOGdebug(s)) {
        ENGINE_ctrl_cmd_string(e, "VERBOSE", NULL, 0);
    }

    if (certid) {
        struct {
            const char *cert_id;
            X509 *cert;
        } params = { certid, NULL };

        if (!ENGINE_ctrl_cmd(e, "LOAD_CERT_CTRL", 0, ¶ms, NULL, 1)) {
            ap_log_error(APLOG_MARK, APLOG_EMERG, 0, s, APLOGNO(10136)
                         "Init: Unable to get the certificate");
            ssl_log_ssl_error(SSLLOG_MARK, APLOG_EMERG, s);
            return ssl_die(s);
        }

        *pubkey = params.cert;
    }

    *privkey = ENGINE_load_private_key(e, keyid, ui_method, &ppcb);
    if (*privkey == NULL) {
        ap_log_error(APLOG_MARK, APLOG_EMERG, 0, s, APLOGNO(10133)
                     "Init: Unable to get the private key");
        ssl_log_ssl_error(SSLLOG_MARK, APLOG_EMERG, s);
        return ssl_die(s);
    }

    /* Release the functional reference obtained by ENGINE_init() only
     * when after the ENGINE is no longer used. */

    apr_pool_cleanup_register(pconf, e, modssl_engine_cleanup, modssl_engine_cleanup);

    /* Release the structural reference obtained by ENGINE_by_id()
     * immediately. */

    ENGINE_free(e);

    return APR_SUCCESS;
}
#endif

#if MODSSL_HAVE_OPENSSL_STORE
static OSSL_STORE_INFO *modssl_load_store_uri(server_rec *s, apr_pool_t *p,
                                              const char *vhostid,
                                              const char *uri, int info_type)
{
    OSSL_STORE_CTX *sctx;
    UI_METHOD *ui_method = get_passphrase_ui(p);
    pphrase_cb_arg_t ppcb;
    OSSL_STORE_INFO *info = NULL;

    memset(&ppcb, 0, sizeof ppcb);
    ppcb.s = s;
    ppcb.p = p;
    ppcb.bPassPhraseDialogOnce = TRUE;
    ppcb.key_id = vhostid;
    ppcb.pkey_file = uri;

    sctx = OSSL_STORE_open(uri, ui_method, &ppcb, NULL, NULL);
    if (!sctx) {
        ap_log_error(APLOG_MARK, APLOG_ERR, 0, s, APLOGNO(10491)
                     "Init: OSSL_STORE_open failed for PKCS#11 URI `%s'",
                     uri);
        return NULL;
    }

    while (!OSSL_STORE_eof(sctx)) {
        info = OSSL_STORE_load(sctx);
        if (!info)
            break;

        if (OSSL_STORE_INFO_get_type(info) == info_type)
            break;

        OSSL_STORE_INFO_free(info);
        info = NULL;
    }

    OSSL_STORE_close(sctx);

    return info;
}

static apr_status_t modssl_load_keypair_store(server_rec *s, apr_pool_t *p,
                                              const char *vhostid,
                                              const char *certid,
                                              const char *keyid,
                                              X509 **pubkey,
                                              EVP_PKEY **privkey)
{
    OSSL_STORE_INFO *info = NULL;

    *privkey = NULL;
    *pubkey = NULL;

    info = modssl_load_store_uri(s, p, vhostid, keyid, OSSL_STORE_INFO_PKEY);
    if (!info) {
        ap_log_error(APLOG_MARK, APLOG_EMERG, 0, s, APLOGNO(10492)
                     "Init: OSSL_STORE_INFO_PKEY lookup failed for private key identifier `%s'",
                     keyid);
        return ssl_die(s);
    }

    *privkey = OSSL_STORE_INFO_get1_PKEY(info);
    OSSL_STORE_INFO_free(info);
    if (!*privkey) {
        ap_log_error(APLOG_MARK, APLOG_EMERG, 0, s, APLOGNO(10493)
                     "Init: OSSL_STORE_INFO_PKEY lookup failed for private key identifier `%s'",
                     keyid);
        return ssl_die(s);
    }

    if (certid) {
        info = modssl_load_store_uri(s, p, vhostid, certid, OSSL_STORE_INFO_CERT);
        if (!info) {
            ap_log_error(APLOG_MARK, APLOG_EMERG, 0, s, APLOGNO(10494)
                         "Init: OSSL_STORE_INFO_CERT lookup failed for certificate identifier `%s'",
                         keyid);
            return ssl_die(s);
        }

        *pubkey = OSSL_STORE_INFO_get1_CERT(info);
        OSSL_STORE_INFO_free(info);
        if (!*pubkey) {
            ap_log_error(APLOG_MARK, APLOG_EMERG, 0, s, APLOGNO(10495)
                     "Init: OSSL_STORE_INFO_CERT lookup failed for certificate identifier `%s'",
                     certid);
            return ssl_die(s);
        }
    }

    return APR_SUCCESS;
}
#endif

apr_status_t modssl_load_engine_keypair(server_rec *s,
                                        apr_pool_t *pconf, apr_pool_t *ptemp,
                                        const char *vhostid,
                                        const char *certid, const char *keyid,
                                        X509 **pubkey, EVP_PKEY **privkey)
{
#if MODSSL_HAVE_ENGINE_API 
    apr_status_t rv;

    rv = modssl_load_keypair_engine(s, pconf, ptemp,
                                    vhostid, certid, keyid,
                                    pubkey, privkey);
    if (rv == APR_SUCCESS) {
        return rv;
    }
    /* If STORE support is not present, all errors are fatal here; if
     * STORE is present and the ENGINE could not be loaded, ignore the
     * error and fall through to try loading via the STORE API. */

    else if (!MODSSL_HAVE_OPENSSL_STORE || rv != APR_ENOTIMPL) {
        return ssl_die(s);
    }

#endif
#if MODSSL_HAVE_OPENSSL_STORE
    return modssl_load_keypair_store(s, ptemp, vhostid, certid, keyid,
                                     pubkey, privkey);
#else
    ap_log_error(APLOG_MARK, APLOG_EMERG, 0, s, APLOGNO(10496)
                 "Init: no method for loading keypair for %s (%s | %s)",
                 vhostid, certid ? certid : "no cert", keyid);
    return APR_ENOTIMPL;
#endif
}

98%


¤ Dauer der Verarbeitung: 0.10 Sekunden  (vorverarbeitet)  ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

Beweissystem der NASA

Beweissystem Isabelle

NIST Cobol Testsuite

Cephes Mathematical Library

Wiener Entwicklungsmethode

Haftungshinweis

Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.

Bemerkung:

Die farbliche Syntaxdarstellung ist noch experimentell.