1/* Licensed to the Apache Software Foundation (ASF) under one or more
2 * contributor license agreements.  See the NOTICE file distributed with
3 * this work for additional information regarding copyright ownership.
4 * The ASF licenses this file to You under the Apache License, Version 2.0
5 * (the "License"); you may not use this file except in compliance with
6 * the License.  You may obtain a copy of the License at
7 *
8 *     http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17/*                      _             _
18 *  _ __ ___   ___   __| |    ___ ___| |  mod_ssl
19 * | '_ ` _ \ / _ \ / _` |   / __/ __| |  Apache Interface to OpenSSL
20 * | | | | | | (_) | (_| |   \__ \__ \ |
21 * |_| |_| |_|\___/ \__,_|___|___/___/_|
22 *                      |_____|
23 *  ssl_engine_pphrase.c
24 *  Pass Phrase Dialog
25 */
26                             /* ``Treat your password like your
27                                  toothbrush. Don't let anybody
28                                  else use it, and get a new one
29                                  every six months.''
30                                           -- Clifford Stoll     */
31#include "ssl_private.h"
32
33/*
34 * Return true if the named file exists and is readable
35 */
36
37static apr_status_t exists_and_readable(char *fname, apr_pool_t *pool, apr_time_t *mtime)
38{
39    apr_status_t stat;
40    apr_finfo_t sbuf;
41    apr_file_t *fd;
42
43    if ((stat = apr_stat(&sbuf, fname, APR_FINFO_MIN, pool)) != APR_SUCCESS)
44        return stat;
45
46    if (sbuf.filetype != APR_REG)
47        return APR_EGENERAL;
48
49    if ((stat = apr_file_open(&fd, fname, APR_READ, 0, pool)) != APR_SUCCESS)
50        return stat;
51
52    if (mtime) {
53        *mtime = sbuf.mtime;
54    }
55
56    apr_file_close(fd);
57    return APR_SUCCESS;
58}
59
60/*
61 * reuse vhost keys for asn1 tables where keys are allocated out
62 * of s->process->pool to prevent "leaking" each time we format
63 * a vhost key.  since the key is stored in a table with lifetime
64 * of s->process->pool, the key needs to have the same lifetime.
65 *
66 * XXX: probably seems silly to use a hash table with keys and values
67 * being the same, but it is easier than doing a linear search
68 * and will make it easier to remove keys if needed in the future.
69 * also have the problem with apr_array_header_t that if we
70 * underestimate the number of vhost keys when we apr_array_make(),
71 * the array will get resized when we push past the initial number
72 * of elts.  this resizing in the s->process->pool means "leaking"
73 * since apr_array_push() will apr_alloc arr->nalloc * 2 elts,
74 * leaving the original arr->elts to waste.
75 */
76static char *asn1_table_vhost_key(SSLModConfigRec *mc, apr_pool_t *p,
77                                  char *id, char *an)
78{
79    /* 'p' pool used here is cleared on restarts (or sooner) */
80    char *key = apr_psprintf(p, "%s:%s", id, an);
81    void *keyptr = apr_hash_get(mc->tVHostKeys, key,
82                                APR_HASH_KEY_STRING);
83
84    if (!keyptr) {
85        /* make a copy out of s->process->pool */
86        keyptr = apr_pstrdup(mc->pPool, key);
87        apr_hash_set(mc->tVHostKeys, keyptr,
88                     APR_HASH_KEY_STRING, keyptr);
89    }
90
91    return (char *)keyptr;
92}
93
94/*  _________________________________________________________________
95**
96**  Pass Phrase and Private Key Handling
97**  _________________________________________________________________
98*/
99
100#define BUILTIN_DIALOG_BACKOFF 2
101#define BUILTIN_DIALOG_RETRIES 5
102
103static apr_file_t *writetty = NULL;
104static apr_file_t *readtty = NULL;
105
106/*
107 * sslc has a nasty flaw where its
108 * PEM_read_bio_PrivateKey does not take a callback arg.
109 */
110static server_rec *ssl_pphrase_server_rec = NULL;
111
112#ifdef SSLC_VERSION_NUMBER
113int ssl_pphrase_Handle_CB(char *, int, int);
114#else
115int ssl_pphrase_Handle_CB(char *, int, int, void *);
116#endif
117
118static char *pphrase_array_get(apr_array_header_t *arr, int idx)
119{
120    if ((idx < 0) || (idx >= arr->nelts)) {
121        return NULL;
122    }
123
124    return ((char **)arr->elts)[idx];
125}
126
127static void pphrase_array_clear(apr_array_header_t *arr)
128{
129    if (arr->nelts > 0) {
130        memset(arr->elts, 0, arr->elt_size * arr->nelts);
131    }
132    arr->nelts = 0;
133}
134
135void ssl_pphrase_Handle(server_rec *s, apr_pool_t *p)
136{
137    SSLModConfigRec *mc = myModConfig(s);
138    SSLSrvConfigRec *sc;
139    server_rec *pServ;
140    char *cpVHostID;
141    char szPath[MAX_STRING_LEN];
142    EVP_PKEY *pPrivateKey;
143    ssl_asn1_t *asn1;
144    unsigned char *ucp;
145    long int length;
146    X509 *pX509Cert;
147    BOOL bReadable;
148    apr_array_header_t *aPassPhrase;
149    int nPassPhrase;
150    int nPassPhraseCur;
151    char *cpPassPhraseCur;
152    int nPassPhraseRetry;
153    int nPassPhraseDialog;
154    int nPassPhraseDialogCur;
155    BOOL bPassPhraseDialogOnce;
156    char **cpp;
157    int i, j;
158    ssl_algo_t algoCert, algoKey, at;
159    char *an;
160    char *cp;
161    apr_time_t pkey_mtime = 0;
162    int isterm = 1;
163    apr_status_t rv;
164    /*
165     * Start with a fresh pass phrase array
166     */
167    aPassPhrase       = apr_array_make(p, 2, sizeof(char *));
168    nPassPhrase       = 0;
169    nPassPhraseDialog = 0;
170
171    /*
172     * Walk through all configured servers
173     */
174    for (pServ = s; pServ != NULL; pServ = pServ->next) {
175        sc = mySrvConfig(pServ);
176
177        if (!sc->enabled)
178            continue;
179
180        cpVHostID = ssl_util_vhostid(p, pServ);
181        ap_log_error(APLOG_MARK, APLOG_INFO, 0, pServ,
182                     "Loading certificate & private key of SSL-aware server");
183
184        /*
185         * Read in server certificate(s): This is the easy part
186         * because this file isn't encrypted in any way.
187         */
188        if (sc->server->pks->cert_files[0] == NULL) {
189            ap_log_error(APLOG_MARK, APLOG_ERR, 0, pServ,
190                         "Server should be SSL-aware but has no certificate "
191                         "configured [Hint: SSLCertificateFile] (%s:%d)",
192                         pServ->defn_name, pServ->defn_line_number);
193            ssl_die();
194        }
195        algoCert = SSL_ALGO_UNKNOWN;
196        algoKey  = SSL_ALGO_UNKNOWN;
197        for (i = 0, j = 0; i < SSL_AIDX_MAX && sc->server->pks->cert_files[i] != NULL; i++) {
198
199            apr_cpystrn(szPath, sc->server->pks->cert_files[i], sizeof(szPath));
200            if ((rv = exists_and_readable(szPath, p, NULL)) != APR_SUCCESS) {
201                ap_log_error(APLOG_MARK, APLOG_ERR, rv, s,
202                             "Init: Can't open server certificate file %s",
203                             szPath);
204                ssl_die();
205            }
206            if ((pX509Cert = SSL_read_X509(szPath, NULL, NULL)) == NULL) {
207                ap_log_error(APLOG_MARK, APLOG_ERR, 0, s,
208                        "Init: Unable to read server certificate from file %s", szPath);
209                ssl_log_ssl_error(APLOG_MARK, APLOG_ERR, s);
210                ssl_die();
211            }
212
213            /*
214             * check algorithm type of certificate and make
215             * sure only one certificate per type is used.
216             */
217            at = ssl_util_algotypeof(pX509Cert, NULL);
218            an = ssl_util_algotypestr(at);
219            if (algoCert & at) {
220                ap_log_error(APLOG_MARK, APLOG_ERR, 0, s,
221                             "Init: Multiple %s server certificates not "
222                             "allowed", an);
223                ssl_log_ssl_error(APLOG_MARK, APLOG_ERR, s);
224                ssl_die();
225            }
226            algoCert |= at;
227
228            /*
229             * Insert the certificate into global module configuration to let it
230             * survive the processing between the 1st Apache API init round (where
231             * we operate here) and the 2nd Apache init round (where the
232             * certificate is actually used to configure mod_ssl's per-server
233             * configuration structures).
234             */
235            cp = asn1_table_vhost_key(mc, p, cpVHostID, an);
236            length = i2d_X509(pX509Cert, NULL);
237            ucp = ssl_asn1_table_set(mc->tPublicCert, cp, length);
238            (void)i2d_X509(pX509Cert, &ucp); /* 2nd arg increments */
239
240            /*
241             * Free the X509 structure
242             */
243            X509_free(pX509Cert);
244
245            /*
246             * Read in the private key: This is the non-trivial part, because the
247             * key is typically encrypted, so a pass phrase dialog has to be used
248             * to request it from the user (or it has to be alternatively gathered
249             * from a dialog program). The important point here is that ISPs
250             * usually have hundrets of virtual servers configured and a lot of
251             * them use SSL, so really we have to minimize the pass phrase
252             * dialogs.
253             *
254             * The idea is this: When N virtual hosts are configured and all of
255             * them use encrypted private keys with different pass phrases, we
256             * have no chance and have to pop up N pass phrase dialogs. But
257             * usually the admin is clever enough and uses the same pass phrase
258             * for more private key files (typically he even uses one single pass
259             * phrase for all). When this is the case we can minimize the dialogs
260             * by trying to re-use already known/entered pass phrases.
261             */
262            if (sc->server->pks->key_files[j] != NULL)
263                apr_cpystrn(szPath, sc->server->pks->key_files[j++], sizeof(szPath));
264
265            /*
266             * Try to read the private key file with the help of
267             * the callback function which serves the pass
268             * phrases to OpenSSL
269             */
270            myCtxVarSet(mc,  1, pServ);
271            myCtxVarSet(mc,  2, p);
272            myCtxVarSet(mc,  3, aPassPhrase);
273            myCtxVarSet(mc,  4, &nPassPhraseCur);
274            myCtxVarSet(mc,  5, &cpPassPhraseCur);
275            myCtxVarSet(mc,  6, cpVHostID);
276            myCtxVarSet(mc,  7, an);
277            myCtxVarSet(mc,  8, &nPassPhraseDialog);
278            myCtxVarSet(mc,  9, &nPassPhraseDialogCur);
279            myCtxVarSet(mc, 10, &bPassPhraseDialogOnce);
280
281            nPassPhraseCur        = 0;
282            nPassPhraseRetry      = 0;
283            nPassPhraseDialogCur  = 0;
284            bPassPhraseDialogOnce = TRUE;
285
286            pPrivateKey = NULL;
287
288            for (;;) {
289                /*
290                 * Try to read the private key file with the help of
291                 * the callback function which serves the pass
292                 * phrases to OpenSSL
293                 */
294                if ((rv = exists_and_readable(szPath, p,
295                                              &pkey_mtime)) != APR_SUCCESS ) {
296                     ap_log_error(APLOG_MARK, APLOG_ERR, rv, s,
297                                  "Init: Can't open server private key file "
298                                  "%s",szPath);
299                     ssl_die();
300                }
301
302                /*
303                 * if the private key is encrypted and SSLPassPhraseDialog
304                 * is configured to "builtin" it isn't possible to prompt for
305                 * a password after httpd has detached from the tty.
306                 * in this case if we already have a private key and the
307                 * file name/mtime hasn't changed, then reuse the existing key.
308                 * we also reuse existing private keys that were encrypted for
309                 * exec: and pipe: dialogs to minimize chances to snoop the
310                 * password.  that and pipe: dialogs might prompt the user
311                 * for password, which on win32 for example could happen 4
312                 * times at startup.  twice for each child and twice within
313                 * each since apache "restarts itself" on startup.
314                 * of course this will not work for the builtin dialog if
315                 * the server was started without LoadModule ssl_module
316                 * configured, then restarted with it configured.
317                 * but we fall through with a chance of success if the key
318                 * is not encrypted or can be handled via exec or pipe dialog.
319                 * and in the case of fallthrough, pkey_mtime and isatty()
320                 * are used to give a better idea as to what failed.
321                 */
322                if (pkey_mtime) {
323                    int i;
324
325                    for (i=0; i < SSL_AIDX_MAX; i++) {
326                        const char *key_id =
327                            ssl_asn1_table_keyfmt(p, cpVHostID, i);
328                        ssl_asn1_t *asn1 =
329                            ssl_asn1_table_get(mc->tPrivateKey, key_id);
330
331                        if (asn1 && (asn1->source_mtime == pkey_mtime)) {
332                            ap_log_error(APLOG_MARK, APLOG_INFO,
333                                         0, pServ,
334                                         "%s reusing existing "
335                                         "%s private key on restart",
336                                         cpVHostID, ssl_asn1_keystr(i));
337                            return;
338                        }
339                    }
340                }
341
342                cpPassPhraseCur = NULL;
343                ssl_pphrase_server_rec = s; /* to make up for sslc flaw */
344
345                /* Ensure that the error stack is empty; some SSL
346                 * functions will fail spuriously if the error stack
347                 * is not empty. */
348                ERR_clear_error();
349
350                bReadable = ((pPrivateKey = SSL_read_PrivateKey(szPath, NULL,
351                            ssl_pphrase_Handle_CB, s)) != NULL ? TRUE : FALSE);
352
353                /*
354                 * when the private key file now was readable,
355                 * it's fine and we go out of the loop
356                 */
357                if (bReadable)
358                   break;
359
360                /*
361                 * when we have more remembered pass phrases
362                 * try to reuse these first.
363                 */
364                if (nPassPhraseCur < nPassPhrase) {
365                    nPassPhraseCur++;
366                    continue;
367                }
368
369                /*
370                 * else it's not readable and we have no more
371                 * remembered pass phrases. Then this has to mean
372                 * that the callback function popped up the dialog
373                 * but a wrong pass phrase was entered.  We give the
374                 * user (but not the dialog program) a few more
375                 * chances...
376                 */
377#ifndef WIN32
378                if ((sc->server->pphrase_dialog_type == SSL_PPTYPE_BUILTIN
379                       || sc->server->pphrase_dialog_type == SSL_PPTYPE_PIPE)
380#else
381                if (sc->server->pphrase_dialog_type == SSL_PPTYPE_PIPE
382#endif
383                    && cpPassPhraseCur != NULL
384                    && nPassPhraseRetry < BUILTIN_DIALOG_RETRIES ) {
385                    apr_file_printf(writetty, "Apache:mod_ssl:Error: Pass phrase incorrect "
386                            "(%d more retr%s permitted).\n",
387                            (BUILTIN_DIALOG_RETRIES-nPassPhraseRetry),
388                            (BUILTIN_DIALOG_RETRIES-nPassPhraseRetry) == 1 ? "y" : "ies");
389                    nPassPhraseRetry++;
390                    if (nPassPhraseRetry > BUILTIN_DIALOG_BACKOFF)
391                        apr_sleep((nPassPhraseRetry-BUILTIN_DIALOG_BACKOFF)
392                                    * 5 * APR_USEC_PER_SEC);
393                    continue;
394                }
395#ifdef WIN32
396                if (sc->server->pphrase_dialog_type == SSL_PPTYPE_BUILTIN) {
397                    ap_log_error(APLOG_MARK, APLOG_ERR, 0, s,
398                                 "Init: SSLPassPhraseDialog builtin is not "
399                                 "supported on Win32 (key file "
400                                 "%s)", szPath);
401                    ssl_die();
402                }
403#endif /* WIN32 */
404
405                /*
406                 * Ok, anything else now means a fatal error.
407                 */
408                if (cpPassPhraseCur == NULL) {
409                    if (nPassPhraseDialogCur && pkey_mtime &&
410                        !(isterm = isatty(fileno(stdout)))) /* XXX: apr_isatty() */
411                    {
412                        ap_log_error(APLOG_MARK, APLOG_ERR, 0,
413                                     pServ,
414                                     "Init: Unable to read pass phrase "
415                                     "[Hint: key introduced or changed "
416                                     "before restart?]");
417                        ssl_log_ssl_error(APLOG_MARK, APLOG_ERR, pServ);
418                    }
419                    else {
420                        ap_log_error(APLOG_MARK, APLOG_ERR, 0,
421                                     pServ, "Init: Private key not found");
422                        ssl_log_ssl_error(APLOG_MARK, APLOG_ERR, pServ);
423                    }
424                    if (writetty) {
425                        apr_file_printf(writetty, "Apache:mod_ssl:Error: Private key not found.\n");
426                        apr_file_printf(writetty, "**Stopped\n");
427                    }
428                }
429                else {
430                    ap_log_error(APLOG_MARK, APLOG_ERR, 0,
431                                 pServ, "Init: Pass phrase incorrect");
432                    ssl_log_ssl_error(APLOG_MARK, APLOG_ERR, pServ);
433
434                    if (writetty) {
435                        apr_file_printf(writetty, "Apache:mod_ssl:Error: Pass phrase incorrect.\n");
436                        apr_file_printf(writetty, "**Stopped\n");
437                    }
438                }
439                ssl_die();
440            }
441
442            if (pPrivateKey == NULL) {
443                ap_log_error(APLOG_MARK, APLOG_ERR, 0, s,
444                            "Init: Unable to read server private key from "
445                            "file %s [Hint: Perhaps it is in a separate file? "
446                            "  See SSLCertificateKeyFile]", szPath);
447                ssl_log_ssl_error(APLOG_MARK, APLOG_ERR, s);
448                ssl_die();
449            }
450
451            /*
452             * check algorithm type of private key and make
453             * sure only one private key per type is used.
454             */
455            at = ssl_util_algotypeof(NULL, pPrivateKey);
456            an = ssl_util_algotypestr(at);
457            if (algoKey & at) {
458                ap_log_error(APLOG_MARK, APLOG_ERR, 0, s,
459                             "Init: Multiple %s server private keys not "
460                             "allowed", an);
461                ssl_log_ssl_error(APLOG_MARK, APLOG_ERR, s);
462                ssl_die();
463            }
464            algoKey |= at;
465
466            /*
467             * Log the type of reading
468             */
469            if (nPassPhraseDialogCur == 0) {
470                ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, pServ,
471                             "unencrypted %s private key - pass phrase not "
472                             "required", an);
473            }
474            else {
475                if (cpPassPhraseCur != NULL) {
476                    ap_log_error(APLOG_MARK, APLOG_DEBUG, 0,
477                                 pServ,
478                                 "encrypted %s private key - pass phrase "
479                                 "requested", an);
480                }
481                else {
482                    ap_log_error(APLOG_MARK, APLOG_DEBUG, 0,
483                                 pServ,
484                                 "encrypted %s private key - pass phrase"
485                                 " reused", an);
486                }
487            }
488
489            /*
490             * Ok, when we have one more pass phrase store it
491             */
492            if (cpPassPhraseCur != NULL) {
493                cpp = (char **)apr_array_push(aPassPhrase);
494                *cpp = cpPassPhraseCur;
495                nPassPhrase++;
496            }
497
498            /*
499             * Insert private key into the global module configuration
500             * (we convert it to a stand-alone DER byte sequence
501             * because the SSL library uses static variables inside a
502             * RSA structure which do not survive DSO reloads!)
503             */
504            cp = asn1_table_vhost_key(mc, p, cpVHostID, an);
505            length = i2d_PrivateKey(pPrivateKey, NULL);
506            ucp = ssl_asn1_table_set(mc->tPrivateKey, cp, length);
507            (void)i2d_PrivateKey(pPrivateKey, &ucp); /* 2nd arg increments */
508
509            if (nPassPhraseDialogCur != 0) {
510                /* remember mtime of encrypted keys */
511                asn1 = ssl_asn1_table_get(mc->tPrivateKey, cp);
512                asn1->source_mtime = pkey_mtime;
513            }
514
515            /*
516             * Free the private key structure
517             */
518            EVP_PKEY_free(pPrivateKey);
519        }
520    }
521
522    /*
523     * Let the user know when we're successful.
524     */
525    if (nPassPhraseDialog > 0) {
526        sc = mySrvConfig(s);
527        if (writetty) {
528            apr_file_printf(writetty, "\n"
529                            "OK: Pass Phrase Dialog successful.\n");
530        }
531    }
532
533    /*
534     * Wipe out the used memory from the
535     * pass phrase array and then deallocate it
536     */
537    if (aPassPhrase->nelts) {
538        pphrase_array_clear(aPassPhrase);
539        ap_log_error(APLOG_MARK, APLOG_INFO, 0, s,
540                     "Init: Wiped out the queried pass phrases from memory");
541    }
542
543    /* Close the pipes if they were opened
544     */
545    if (readtty) {
546        apr_file_close(readtty);
547        apr_file_close(writetty);
548        readtty = writetty = NULL;
549    }
550    return;
551}
552
553static apr_status_t ssl_pipe_child_create(apr_pool_t *p, const char *progname)
554{
555    /* Child process code for 'ErrorLog "|..."';
556     * may want a common framework for this, since I expect it will
557     * be common for other foo-loggers to want this sort of thing...
558     */
559    apr_status_t rc;
560    apr_procattr_t *procattr;
561    apr_proc_t *procnew;
562
563    if (((rc = apr_procattr_create(&procattr, p)) == APR_SUCCESS) &&
564        ((rc = apr_procattr_io_set(procattr,
565                                   APR_FULL_BLOCK,
566                                   APR_FULL_BLOCK,
567                                   APR_NO_PIPE)) == APR_SUCCESS)) {
568        char **args;
569        const char *pname;
570
571        apr_tokenize_to_argv(progname, &args, p);
572        pname = apr_pstrdup(p, args[0]);
573        procnew = (apr_proc_t *)apr_pcalloc(p, sizeof(*procnew));
574        rc = apr_proc_create(procnew, pname, (const char * const *)args,
575                             NULL, procattr, p);
576        if (rc == APR_SUCCESS) {
577            /* XXX: not sure if we aught to...
578             * apr_pool_note_subprocess(p, procnew, APR_KILL_AFTER_TIMEOUT);
579             */
580            writetty = procnew->in;
581            readtty = procnew->out;
582        }
583    }
584
585    return rc;
586}
587
588static int pipe_get_passwd_cb(char *buf, int length, char *prompt, int verify)
589{
590    apr_status_t rc;
591    char *p;
592
593    apr_file_puts(prompt, writetty);
594
595    buf[0]='\0';
596    rc = apr_file_gets(buf, length, readtty);
597    apr_file_puts(APR_EOL_STR, writetty);
598
599    if (rc != APR_SUCCESS || apr_file_eof(readtty)) {
600        memset(buf, 0, length);
601        return 1;  /* failure */
602    }
603    if ((p = strchr(buf, '\n')) != NULL) {
604        *p = '\0';
605    }
606#ifdef WIN32
607    /* XXX: apr_sometest */
608    if ((p = strchr(buf, '\r')) != NULL) {
609        *p = '\0';
610    }
611#endif
612    return 0;
613}
614
615#ifdef SSLC_VERSION_NUMBER
616int ssl_pphrase_Handle_CB(char *buf, int bufsize, int verify)
617{
618    void *srv = ssl_pphrase_server_rec;
619#else
620int ssl_pphrase_Handle_CB(char *buf, int bufsize, int verify, void *srv)
621{
622#endif
623    SSLModConfigRec *mc;
624    server_rec *s;
625    apr_pool_t *p;
626    apr_array_header_t *aPassPhrase;
627    SSLSrvConfigRec *sc;
628    int *pnPassPhraseCur;
629    char **cppPassPhraseCur;
630    char *cpVHostID;
631    char *cpAlgoType;
632    int *pnPassPhraseDialog;
633    int *pnPassPhraseDialogCur;
634    BOOL *pbPassPhraseDialogOnce;
635    char *cpp;
636    int len = -1;
637
638    mc = myModConfig((server_rec *)srv);
639
640    /*
641     * Reconnect to the context of ssl_phrase_Handle()
642     */
643    s                      = myCtxVarGet(mc,  1, server_rec *);
644    p                      = myCtxVarGet(mc,  2, apr_pool_t *);
645    aPassPhrase            = myCtxVarGet(mc,  3, apr_array_header_t *);
646    pnPassPhraseCur        = myCtxVarGet(mc,  4, int *);
647    cppPassPhraseCur       = myCtxVarGet(mc,  5, char **);
648    cpVHostID              = myCtxVarGet(mc,  6, char *);
649    cpAlgoType             = myCtxVarGet(mc,  7, char *);
650    pnPassPhraseDialog     = myCtxVarGet(mc,  8, int *);
651    pnPassPhraseDialogCur  = myCtxVarGet(mc,  9, int *);
652    pbPassPhraseDialogOnce = myCtxVarGet(mc, 10, BOOL *);
653    sc                     = mySrvConfig(s);
654
655    (*pnPassPhraseDialog)++;
656    (*pnPassPhraseDialogCur)++;
657
658    /*
659     * When remembered pass phrases are available use them...
660     */
661    if ((cpp = pphrase_array_get(aPassPhrase, *pnPassPhraseCur)) != NULL) {
662        apr_cpystrn(buf, cpp, bufsize);
663        len = strlen(buf);
664        return len;
665    }
666
667    /*
668     * Builtin or Pipe dialog
669     */
670    if (sc->server->pphrase_dialog_type == SSL_PPTYPE_BUILTIN
671          || sc->server->pphrase_dialog_type == SSL_PPTYPE_PIPE) {
672        char *prompt;
673        int i;
674
675        if (sc->server->pphrase_dialog_type == SSL_PPTYPE_PIPE) {
676            if (!readtty) {
677                ap_log_error(APLOG_MARK, APLOG_INFO, 0, s,
678                             "Init: Creating pass phrase dialog pipe child "
679                             "'%s'", sc->server->pphrase_dialog_path);
680                if (ssl_pipe_child_create(p, sc->server->pphrase_dialog_path)
681                        != APR_SUCCESS) {
682                    ap_log_error(APLOG_MARK, APLOG_ERR, 0, s,
683                                 "Init: Failed to create pass phrase pipe '%s'",
684                                 sc->server->pphrase_dialog_path);
685                    PEMerr(PEM_F_DEF_CALLBACK,PEM_R_PROBLEMS_GETTING_PASSWORD);
686                    memset(buf, 0, (unsigned int)bufsize);
687                    return (-1);
688                }
689            }
690            ap_log_error(APLOG_MARK, APLOG_INFO, 0, s,
691                         "Init: Requesting pass phrase via piped dialog");
692        }
693        else { /* sc->server->pphrase_dialog_type == SSL_PPTYPE_BUILTIN */
694#ifdef WIN32
695            PEMerr(PEM_F_DEF_CALLBACK,PEM_R_PROBLEMS_GETTING_PASSWORD);
696            memset(buf, 0, (unsigned int)bufsize);
697            return (-1);
698#else
699            /*
700             * stderr has already been redirected to the error_log.
701             * rather than attempting to temporarily rehook it to the terminal,
702             * we print the prompt to stdout before EVP_read_pw_string turns
703             * off tty echo
704             */
705            apr_file_open_stdout(&writetty, p);
706
707            ap_log_error(APLOG_MARK, APLOG_INFO, 0, s,
708                         "Init: Requesting pass phrase via builtin terminal "
709                         "dialog");
710#endif
711        }
712
713        /*
714         * The first time display a header to inform the user about what
715         * program he actually speaks to, which module is responsible for
716         * this terminal dialog and why to the hell he has to enter
717         * something...
718         */
719        if (*pnPassPhraseDialog == 1) {
720            apr_file_printf(writetty, "%s mod_ssl/%s (Pass Phrase Dialog)\n",
721                            AP_SERVER_BASEVERSION, MOD_SSL_VERSION);
722            apr_file_printf(writetty, "Some of your private key files are encrypted for security reasons.\n");
723            apr_file_printf(writetty, "In order to read them you have to provide the pass phrases.\n");
724        }
725        if (*pbPassPhraseDialogOnce) {
726            *pbPassPhraseDialogOnce = FALSE;
727            apr_file_printf(writetty, "\n");
728            apr_file_printf(writetty, "Server %s (%s)\n", cpVHostID, cpAlgoType);
729        }
730
731        /*
732         * Emulate the OpenSSL internal pass phrase dialog
733         * (see crypto/pem/pem_lib.c:def_callback() for details)
734         */
735        prompt = "Enter pass phrase:";
736
737        for (;;) {
738            apr_file_puts(prompt, writetty);
739            if (sc->server->pphrase_dialog_type == SSL_PPTYPE_PIPE) {
740                i = pipe_get_passwd_cb(buf, bufsize, "", FALSE);
741            }
742            else { /* sc->server->pphrase_dialog_type == SSL_PPTYPE_BUILTIN */
743                i = EVP_read_pw_string(buf, bufsize, "", FALSE);
744            }
745            if (i != 0) {
746                PEMerr(PEM_F_DEF_CALLBACK,PEM_R_PROBLEMS_GETTING_PASSWORD);
747                memset(buf, 0, (unsigned int)bufsize);
748                return (-1);
749            }
750            len = strlen(buf);
751            if (len < 1)
752                apr_file_printf(writetty, "Apache:mod_ssl:Error: Pass phrase empty (needs to be at least 1 character).\n");
753            else
754                break;
755        }
756    }
757
758    /*
759     * Filter program
760     */
761    else if (sc->server->pphrase_dialog_type == SSL_PPTYPE_FILTER) {
762        const char *cmd = sc->server->pphrase_dialog_path;
763        const char **argv = apr_palloc(p, sizeof(char *) * 4);
764        char *result;
765
766        ap_log_error(APLOG_MARK, APLOG_INFO, 0, s,
767                     "Init: Requesting pass phrase from dialog filter "
768                     "program (%s)", cmd);
769
770        argv[0] = cmd;
771        argv[1] = cpVHostID;
772        argv[2] = cpAlgoType;
773        argv[3] = NULL;
774
775        result = ssl_util_readfilter(s, p, cmd, argv);
776        apr_cpystrn(buf, result, bufsize);
777        len = strlen(buf);
778    }
779
780    /*
781     * Ok, we now have the pass phrase, so give it back
782     */
783    *cppPassPhraseCur = apr_pstrdup(p, buf);
784
785    /*
786     * And return it's length to OpenSSL...
787     */
788    return (len);
789}
790
791