~ubuntu-branches/ubuntu/feisty/apache2/feisty

« back to all changes in this revision

Viewing changes to modules/ssl/ssl_engine_pphrase.c

  • Committer: Bazaar Package Importer
  • Author(s): Andreas Barth
  • Date: 2006-12-09 21:05:45 UTC
  • mfrom: (0.6.1 upstream)
  • Revision ID: james.westby@ubuntu.com-20061209210545-h70s0xaqc2v8vqr2
Tags: 2.2.3-3.2
* Non-maintainer upload.
* 043_ajp_connection_reuse: Patch from upstream Bugzilla, fixing a critical
  issue with regard to connection reuse in mod_proxy_ajp.
  Closes: #396265

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
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
 
 
37
static 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
 */
 
76
static 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
 
 
103
static apr_file_t *writetty = NULL;
 
104
static 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
 */
 
110
static server_rec *ssl_pphrase_server_rec = NULL;
 
111
 
 
112
#ifdef SSLC_VERSION_NUMBER
 
113
int ssl_pphrase_Handle_CB(char *, int, int);
 
114
#else
 
115
int ssl_pphrase_Handle_CB(char *, int, int, void *);
 
116
#endif
 
117
 
 
118
static 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
 
 
127
static 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
 
 
135
void 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]");
 
192
            ssl_die();
 
193
        }
 
194
        algoCert = SSL_ALGO_UNKNOWN;
 
195
        algoKey  = SSL_ALGO_UNKNOWN;
 
196
        for (i = 0, j = 0; i < SSL_AIDX_MAX && sc->server->pks->cert_files[i] != NULL; i++) {
 
197
 
 
198
            apr_cpystrn(szPath, sc->server->pks->cert_files[i], sizeof(szPath));
 
199
            if ((rv = exists_and_readable(szPath, p, NULL)) != APR_SUCCESS) {
 
200
                ap_log_error(APLOG_MARK, APLOG_ERR, rv, s,
 
201
                             "Init: Can't open server certificate file %s",
 
202
                             szPath);
 
203
                ssl_die();
 
204
            }
 
205
            if ((pX509Cert = SSL_read_X509(szPath, NULL, NULL)) == NULL) {
 
206
                ap_log_error(APLOG_MARK, APLOG_ERR, 0, s,
 
207
                        "Init: Unable to read server certificate from file %s", szPath);
 
208
                ssl_log_ssl_error(APLOG_MARK, APLOG_ERR, s);
 
209
                ssl_die();
 
210
            }
 
211
 
 
212
            /*
 
213
             * check algorithm type of certificate and make
 
214
             * sure only one certificate per type is used.
 
215
             */
 
216
            at = ssl_util_algotypeof(pX509Cert, NULL);
 
217
            an = ssl_util_algotypestr(at);
 
218
            if (algoCert & at) {
 
219
                ap_log_error(APLOG_MARK, APLOG_ERR, 0, s,
 
220
                             "Init: Multiple %s server certificates not "
 
221
                             "allowed", an);
 
222
                ssl_log_ssl_error(APLOG_MARK, APLOG_ERR, s);
 
223
                ssl_die();
 
224
            }
 
225
            algoCert |= at;
 
226
 
 
227
            /*
 
228
             * Insert the certificate into global module configuration to let it
 
229
             * survive the processing between the 1st Apache API init round (where
 
230
             * we operate here) and the 2nd Apache init round (where the
 
231
             * certificate is actually used to configure mod_ssl's per-server
 
232
             * configuration structures).
 
233
             */
 
234
            cp = asn1_table_vhost_key(mc, p, cpVHostID, an);
 
235
            length = i2d_X509(pX509Cert, NULL);
 
236
            ucp = ssl_asn1_table_set(mc->tPublicCert, cp, length);
 
237
            (void)i2d_X509(pX509Cert, &ucp); /* 2nd arg increments */
 
238
 
 
239
            /*
 
240
             * Free the X509 structure
 
241
             */
 
242
            X509_free(pX509Cert);
 
243
 
 
244
            /*
 
245
             * Read in the private key: This is the non-trivial part, because the
 
246
             * key is typically encrypted, so a pass phrase dialog has to be used
 
247
             * to request it from the user (or it has to be alternatively gathered
 
248
             * from a dialog program). The important point here is that ISPs
 
249
             * usually have hundrets of virtual servers configured and a lot of
 
250
             * them use SSL, so really we have to minimize the pass phrase
 
251
             * dialogs.
 
252
             *
 
253
             * The idea is this: When N virtual hosts are configured and all of
 
254
             * them use encrypted private keys with different pass phrases, we
 
255
             * have no chance and have to pop up N pass phrase dialogs. But
 
256
             * usually the admin is clever enough and uses the same pass phrase
 
257
             * for more private key files (typically he even uses one single pass
 
258
             * phrase for all). When this is the case we can minimize the dialogs
 
259
             * by trying to re-use already known/entered pass phrases.
 
260
             */
 
261
            if (sc->server->pks->key_files[j] != NULL)
 
262
                apr_cpystrn(szPath, sc->server->pks->key_files[j++], sizeof(szPath));
 
263
 
 
264
            /*
 
265
             * Try to read the private key file with the help of
 
266
             * the callback function which serves the pass
 
267
             * phrases to OpenSSL
 
268
             */
 
269
            myCtxVarSet(mc,  1, pServ);
 
270
            myCtxVarSet(mc,  2, p);
 
271
            myCtxVarSet(mc,  3, aPassPhrase);
 
272
            myCtxVarSet(mc,  4, &nPassPhraseCur);
 
273
            myCtxVarSet(mc,  5, &cpPassPhraseCur);
 
274
            myCtxVarSet(mc,  6, cpVHostID);
 
275
            myCtxVarSet(mc,  7, an);
 
276
            myCtxVarSet(mc,  8, &nPassPhraseDialog);
 
277
            myCtxVarSet(mc,  9, &nPassPhraseDialogCur);
 
278
            myCtxVarSet(mc, 10, &bPassPhraseDialogOnce);
 
279
 
 
280
            nPassPhraseCur        = 0;
 
281
            nPassPhraseRetry      = 0;
 
282
            nPassPhraseDialogCur  = 0;
 
283
            bPassPhraseDialogOnce = TRUE;
 
284
 
 
285
            pPrivateKey = NULL;
 
286
 
 
287
            for (;;) {
 
288
                /*
 
289
                 * Try to read the private key file with the help of
 
290
                 * the callback function which serves the pass
 
291
                 * phrases to OpenSSL
 
292
                 */
 
293
                if ((rv = exists_and_readable(szPath, p,
 
294
                                              &pkey_mtime)) != APR_SUCCESS ) {
 
295
                     ap_log_error(APLOG_MARK, APLOG_ERR, rv, s,
 
296
                                  "Init: Can't open server private key file "
 
297
                                  "%s",szPath);
 
298
                     ssl_die();
 
299
                }
 
300
 
 
301
                /*
 
302
                 * if the private key is encrypted and SSLPassPhraseDialog
 
303
                 * is configured to "builtin" it isn't possible to prompt for
 
304
                 * a password after httpd has detached from the tty.
 
305
                 * in this case if we already have a private key and the
 
306
                 * file name/mtime hasn't changed, then reuse the existing key.
 
307
                 * we also reuse existing private keys that were encrypted for
 
308
                 * exec: and pipe: dialogs to minimize chances to snoop the
 
309
                 * password.  that and pipe: dialogs might prompt the user
 
310
                 * for password, which on win32 for example could happen 4
 
311
                 * times at startup.  twice for each child and twice within
 
312
                 * each since apache "restarts itself" on startup.
 
313
                 * of course this will not work for the builtin dialog if
 
314
                 * the server was started without LoadModule ssl_module
 
315
                 * configured, then restarted with it configured.
 
316
                 * but we fall through with a chance of success if the key
 
317
                 * is not encrypted or can be handled via exec or pipe dialog.
 
318
                 * and in the case of fallthrough, pkey_mtime and isatty()
 
319
                 * are used to give a better idea as to what failed.
 
320
                 */
 
321
                if (pkey_mtime) {
 
322
                    int i;
 
323
 
 
324
                    for (i=0; i < SSL_AIDX_MAX; i++) {
 
325
                        const char *key_id =
 
326
                            ssl_asn1_table_keyfmt(p, cpVHostID, i);
 
327
                        ssl_asn1_t *asn1 =
 
328
                            ssl_asn1_table_get(mc->tPrivateKey, key_id);
 
329
 
 
330
                        if (asn1 && (asn1->source_mtime == pkey_mtime)) {
 
331
                            ap_log_error(APLOG_MARK, APLOG_INFO,
 
332
                                         0, pServ,
 
333
                                         "%s reusing existing "
 
334
                                         "%s private key on restart",
 
335
                                         cpVHostID, ssl_asn1_keystr(i));
 
336
                            return;
 
337
                        }
 
338
                    }
 
339
                }
 
340
 
 
341
                cpPassPhraseCur = NULL;
 
342
                ssl_pphrase_server_rec = s; /* to make up for sslc flaw */
 
343
 
 
344
                /* Ensure that the error stack is empty; some SSL
 
345
                 * functions will fail spuriously if the error stack
 
346
                 * is not empty. */
 
347
                ERR_clear_error();
 
348
 
 
349
                bReadable = ((pPrivateKey = SSL_read_PrivateKey(szPath, NULL,
 
350
                            ssl_pphrase_Handle_CB, s)) != NULL ? TRUE : FALSE);
 
351
 
 
352
                /*
 
353
                 * when the private key file now was readable,
 
354
                 * it's fine and we go out of the loop
 
355
                 */
 
356
                if (bReadable)
 
357
                   break;
 
358
 
 
359
                /*
 
360
                 * when we have more remembered pass phrases
 
361
                 * try to reuse these first.
 
362
                 */
 
363
                if (nPassPhraseCur < nPassPhrase) {
 
364
                    nPassPhraseCur++;
 
365
                    continue;
 
366
                }
 
367
 
 
368
                /*
 
369
                 * else it's not readable and we have no more
 
370
                 * remembered pass phrases. Then this has to mean
 
371
                 * that the callback function popped up the dialog
 
372
                 * but a wrong pass phrase was entered.  We give the
 
373
                 * user (but not the dialog program) a few more
 
374
                 * chances...
 
375
                 */
 
376
#ifndef WIN32
 
377
                if ((sc->server->pphrase_dialog_type == SSL_PPTYPE_BUILTIN
 
378
                       || sc->server->pphrase_dialog_type == SSL_PPTYPE_PIPE)
 
379
#else
 
380
                if (sc->server->pphrase_dialog_type == SSL_PPTYPE_PIPE
 
381
#endif
 
382
                    && cpPassPhraseCur != NULL
 
383
                    && nPassPhraseRetry < BUILTIN_DIALOG_RETRIES ) {
 
384
                    apr_file_printf(writetty, "Apache:mod_ssl:Error: Pass phrase incorrect "
 
385
                            "(%d more retr%s permitted).\n",
 
386
                            (BUILTIN_DIALOG_RETRIES-nPassPhraseRetry),
 
387
                            (BUILTIN_DIALOG_RETRIES-nPassPhraseRetry) == 1 ? "y" : "ies");
 
388
                    nPassPhraseRetry++;
 
389
                    if (nPassPhraseRetry > BUILTIN_DIALOG_BACKOFF)
 
390
                        apr_sleep((nPassPhraseRetry-BUILTIN_DIALOG_BACKOFF)
 
391
                                    * 5 * APR_USEC_PER_SEC);
 
392
                    continue;
 
393
                }
 
394
#ifdef WIN32
 
395
                if (sc->server->pphrase_dialog_type == SSL_PPTYPE_BUILTIN) {
 
396
                    ap_log_error(APLOG_MARK, APLOG_ERR, 0, s,
 
397
                                 "Init: SSLPassPhraseDialog builtin is not "
 
398
                                 "supported on Win32 (key file "
 
399
                                 "%s)", szPath);
 
400
                    ssl_die();
 
401
                }
 
402
#endif /* WIN32 */
 
403
 
 
404
                /*
 
405
                 * Ok, anything else now means a fatal error.
 
406
                 */
 
407
                if (cpPassPhraseCur == NULL) {
 
408
                    if (nPassPhraseDialogCur && pkey_mtime &&
 
409
                        !(isterm = isatty(fileno(stdout)))) /* XXX: apr_isatty() */
 
410
                    {
 
411
                        ap_log_error(APLOG_MARK, APLOG_ERR, 0,
 
412
                                     pServ,
 
413
                                     "Init: Unable to read pass phrase "
 
414
                                     "[Hint: key introduced or changed "
 
415
                                     "before restart?]");
 
416
                        ssl_log_ssl_error(APLOG_MARK, APLOG_ERR, pServ);
 
417
                    }
 
418
                    else {
 
419
                        ap_log_error(APLOG_MARK, APLOG_ERR, 0,
 
420
                                     pServ, "Init: Private key not found");
 
421
                        ssl_log_ssl_error(APLOG_MARK, APLOG_ERR, pServ);
 
422
                    }
 
423
                    if (writetty) {
 
424
                        apr_file_printf(writetty, "Apache:mod_ssl:Error: Private key not found.\n");
 
425
                        apr_file_printf(writetty, "**Stopped\n");
 
426
                    }
 
427
                }
 
428
                else {
 
429
                    ap_log_error(APLOG_MARK, APLOG_ERR, 0,
 
430
                                 pServ, "Init: Pass phrase incorrect");
 
431
                    ssl_log_ssl_error(APLOG_MARK, APLOG_ERR, pServ);
 
432
 
 
433
                    if (writetty) {
 
434
                        apr_file_printf(writetty, "Apache:mod_ssl:Error: Pass phrase incorrect.\n");
 
435
                        apr_file_printf(writetty, "**Stopped\n");
 
436
                    }
 
437
                }
 
438
                ssl_die();
 
439
            }
 
440
 
 
441
            if (pPrivateKey == NULL) {
 
442
                ap_log_error(APLOG_MARK, APLOG_ERR, 0, s,
 
443
                            "Init: Unable to read server private key from "
 
444
                            "file %s [Hint: Perhaps it is in a separate file? "
 
445
                            "  See SSLCertificateKeyFile]", szPath);
 
446
                ssl_log_ssl_error(APLOG_MARK, APLOG_ERR, s);
 
447
                ssl_die();
 
448
            }
 
449
 
 
450
            /*
 
451
             * check algorithm type of private key and make
 
452
             * sure only one private key per type is used.
 
453
             */
 
454
            at = ssl_util_algotypeof(NULL, pPrivateKey);
 
455
            an = ssl_util_algotypestr(at);
 
456
            if (algoKey & at) {
 
457
                ap_log_error(APLOG_MARK, APLOG_ERR, 0, s,
 
458
                             "Init: Multiple %s server private keys not "
 
459
                             "allowed", an);
 
460
                ssl_log_ssl_error(APLOG_MARK, APLOG_ERR, s);
 
461
                ssl_die();
 
462
            }
 
463
            algoKey |= at;
 
464
 
 
465
            /*
 
466
             * Log the type of reading
 
467
             */
 
468
            if (nPassPhraseDialogCur == 0) {
 
469
                ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, pServ,
 
470
                             "unencrypted %s private key - pass phrase not "
 
471
                             "required", an);
 
472
            }
 
473
            else {
 
474
                if (cpPassPhraseCur != NULL) {
 
475
                    ap_log_error(APLOG_MARK, APLOG_DEBUG, 0,
 
476
                                 pServ,
 
477
                                 "encrypted %s private key - pass phrase "
 
478
                                 "requested", an);
 
479
                }
 
480
                else {
 
481
                    ap_log_error(APLOG_MARK, APLOG_DEBUG, 0,
 
482
                                 pServ,
 
483
                                 "encrypted %s private key - pass phrase"
 
484
                                 " reused", an);
 
485
                }
 
486
            }
 
487
 
 
488
            /*
 
489
             * Ok, when we have one more pass phrase store it
 
490
             */
 
491
            if (cpPassPhraseCur != NULL) {
 
492
                cpp = (char **)apr_array_push(aPassPhrase);
 
493
                *cpp = cpPassPhraseCur;
 
494
                nPassPhrase++;
 
495
            }
 
496
 
 
497
            /*
 
498
             * Insert private key into the global module configuration
 
499
             * (we convert it to a stand-alone DER byte sequence
 
500
             * because the SSL library uses static variables inside a
 
501
             * RSA structure which do not survive DSO reloads!)
 
502
             */
 
503
            cp = asn1_table_vhost_key(mc, p, cpVHostID, an);
 
504
            length = i2d_PrivateKey(pPrivateKey, NULL);
 
505
            ucp = ssl_asn1_table_set(mc->tPrivateKey, cp, length);
 
506
            (void)i2d_PrivateKey(pPrivateKey, &ucp); /* 2nd arg increments */
 
507
 
 
508
            if (nPassPhraseDialogCur != 0) {
 
509
                /* remember mtime of encrypted keys */
 
510
                asn1 = ssl_asn1_table_get(mc->tPrivateKey, cp);
 
511
                asn1->source_mtime = pkey_mtime;
 
512
            }
 
513
 
 
514
            /*
 
515
             * Free the private key structure
 
516
             */
 
517
            EVP_PKEY_free(pPrivateKey);
 
518
        }
 
519
    }
 
520
 
 
521
    /*
 
522
     * Let the user know when we're successful.
 
523
     */
 
524
    if (nPassPhraseDialog > 0) {
 
525
        sc = mySrvConfig(s);
 
526
        if (writetty) {
 
527
            apr_file_printf(writetty, "\n"
 
528
                            "OK: Pass Phrase Dialog successful.\n");
 
529
        }
 
530
    }
 
531
 
 
532
    /*
 
533
     * Wipe out the used memory from the
 
534
     * pass phrase array and then deallocate it
 
535
     */
 
536
    if (aPassPhrase->nelts) {
 
537
        pphrase_array_clear(aPassPhrase);
 
538
        ap_log_error(APLOG_MARK, APLOG_INFO, 0, s,
 
539
                     "Init: Wiped out the queried pass phrases from memory");
 
540
    }
 
541
 
 
542
    /* Close the pipes if they were opened
 
543
     */
 
544
    if (readtty) {
 
545
        apr_file_close(readtty);
 
546
        apr_file_close(writetty);
 
547
        readtty = writetty = NULL;
 
548
    }
 
549
    return;
 
550
}
 
551
 
 
552
static apr_status_t ssl_pipe_child_create(apr_pool_t *p, const char *progname)
 
553
{
 
554
    /* Child process code for 'ErrorLog "|..."';
 
555
     * may want a common framework for this, since I expect it will
 
556
     * be common for other foo-loggers to want this sort of thing...
 
557
     */
 
558
    apr_status_t rc;
 
559
    apr_procattr_t *procattr;
 
560
    apr_proc_t *procnew;
 
561
 
 
562
    if (((rc = apr_procattr_create(&procattr, p)) == APR_SUCCESS) &&
 
563
        ((rc = apr_procattr_io_set(procattr,
 
564
                                   APR_FULL_BLOCK,
 
565
                                   APR_FULL_BLOCK,
 
566
                                   APR_NO_PIPE)) == APR_SUCCESS)) {
 
567
        char **args;
 
568
        const char *pname;
 
569
 
 
570
        apr_tokenize_to_argv(progname, &args, p);
 
571
        pname = apr_pstrdup(p, args[0]);
 
572
        procnew = (apr_proc_t *)apr_pcalloc(p, sizeof(*procnew));
 
573
        rc = apr_proc_create(procnew, pname, (const char * const *)args,
 
574
                             NULL, procattr, p);
 
575
        if (rc == APR_SUCCESS) {
 
576
            /* XXX: not sure if we aught to...
 
577
             * apr_pool_note_subprocess(p, procnew, APR_KILL_AFTER_TIMEOUT);
 
578
             */
 
579
            writetty = procnew->in;
 
580
            readtty = procnew->out;
 
581
        }
 
582
    }
 
583
 
 
584
    return rc;
 
585
}
 
586
 
 
587
static int pipe_get_passwd_cb(char *buf, int length, char *prompt, int verify)
 
588
{
 
589
    apr_status_t rc;
 
590
    char *p;
 
591
 
 
592
    apr_file_puts(prompt, writetty);
 
593
 
 
594
    buf[0]='\0';
 
595
    rc = apr_file_gets(buf, length, readtty);
 
596
    apr_file_puts(APR_EOL_STR, writetty);
 
597
 
 
598
    if (rc != APR_SUCCESS || apr_file_eof(readtty)) {
 
599
        memset(buf, 0, length);
 
600
        return 1;  /* failure */
 
601
    }
 
602
    if ((p = strchr(buf, '\n')) != NULL) {
 
603
        *p = '\0';
 
604
    }
 
605
#ifdef WIN32
 
606
    /* XXX: apr_sometest */
 
607
    if ((p = strchr(buf, '\r')) != NULL) {
 
608
        *p = '\0';
 
609
    }
 
610
#endif
 
611
    return 0;
 
612
}
 
613
 
 
614
#ifdef SSLC_VERSION_NUMBER
 
615
int ssl_pphrase_Handle_CB(char *buf, int bufsize, int verify)
 
616
{
 
617
    void *srv = ssl_pphrase_server_rec;
 
618
#else
 
619
int ssl_pphrase_Handle_CB(char *buf, int bufsize, int verify, void *srv)
 
620
{
 
621
#endif
 
622
    SSLModConfigRec *mc;
 
623
    server_rec *s;
 
624
    apr_pool_t *p;
 
625
    apr_array_header_t *aPassPhrase;
 
626
    SSLSrvConfigRec *sc;
 
627
    int *pnPassPhraseCur;
 
628
    char **cppPassPhraseCur;
 
629
    char *cpVHostID;
 
630
    char *cpAlgoType;
 
631
    int *pnPassPhraseDialog;
 
632
    int *pnPassPhraseDialogCur;
 
633
    BOOL *pbPassPhraseDialogOnce;
 
634
    char *cpp;
 
635
    int len = -1;
 
636
 
 
637
    mc = myModConfig((server_rec *)srv);
 
638
 
 
639
    /*
 
640
     * Reconnect to the context of ssl_phrase_Handle()
 
641
     */
 
642
    s                      = myCtxVarGet(mc,  1, server_rec *);
 
643
    p                      = myCtxVarGet(mc,  2, apr_pool_t *);
 
644
    aPassPhrase            = myCtxVarGet(mc,  3, apr_array_header_t *);
 
645
    pnPassPhraseCur        = myCtxVarGet(mc,  4, int *);
 
646
    cppPassPhraseCur       = myCtxVarGet(mc,  5, char **);
 
647
    cpVHostID              = myCtxVarGet(mc,  6, char *);
 
648
    cpAlgoType             = myCtxVarGet(mc,  7, char *);
 
649
    pnPassPhraseDialog     = myCtxVarGet(mc,  8, int *);
 
650
    pnPassPhraseDialogCur  = myCtxVarGet(mc,  9, int *);
 
651
    pbPassPhraseDialogOnce = myCtxVarGet(mc, 10, BOOL *);
 
652
    sc                     = mySrvConfig(s);
 
653
 
 
654
    (*pnPassPhraseDialog)++;
 
655
    (*pnPassPhraseDialogCur)++;
 
656
 
 
657
    /*
 
658
     * When remembered pass phrases are available use them...
 
659
     */
 
660
    if ((cpp = pphrase_array_get(aPassPhrase, *pnPassPhraseCur)) != NULL) {
 
661
        apr_cpystrn(buf, cpp, bufsize);
 
662
        len = strlen(buf);
 
663
        return len;
 
664
    }
 
665
 
 
666
    /*
 
667
     * Builtin or Pipe dialog
 
668
     */
 
669
    if (sc->server->pphrase_dialog_type == SSL_PPTYPE_BUILTIN
 
670
          || sc->server->pphrase_dialog_type == SSL_PPTYPE_PIPE) {
 
671
        char *prompt;
 
672
        int i;
 
673
 
 
674
        if (sc->server->pphrase_dialog_type == SSL_PPTYPE_PIPE) {
 
675
            if (!readtty) {
 
676
                ap_log_error(APLOG_MARK, APLOG_INFO, 0, s,
 
677
                             "Init: Creating pass phrase dialog pipe child "
 
678
                             "'%s'", sc->server->pphrase_dialog_path);
 
679
                if (ssl_pipe_child_create(p, sc->server->pphrase_dialog_path)
 
680
                        != APR_SUCCESS) {
 
681
                    ap_log_error(APLOG_MARK, APLOG_ERR, 0, s,
 
682
                                 "Init: Failed to create pass phrase pipe '%s'",
 
683
                                 sc->server->pphrase_dialog_path);
 
684
                    PEMerr(PEM_F_DEF_CALLBACK,PEM_R_PROBLEMS_GETTING_PASSWORD);
 
685
                    memset(buf, 0, (unsigned int)bufsize);
 
686
                    return (-1);
 
687
                }
 
688
            }
 
689
            ap_log_error(APLOG_MARK, APLOG_INFO, 0, s,
 
690
                         "Init: Requesting pass phrase via piped dialog");
 
691
        }
 
692
        else { /* sc->server->pphrase_dialog_type == SSL_PPTYPE_BUILTIN */
 
693
#ifdef WIN32
 
694
            PEMerr(PEM_F_DEF_CALLBACK,PEM_R_PROBLEMS_GETTING_PASSWORD);
 
695
            memset(buf, 0, (unsigned int)bufsize);
 
696
            return (-1);
 
697
#else
 
698
            /*
 
699
             * stderr has already been redirected to the error_log.
 
700
             * rather than attempting to temporarily rehook it to the terminal,
 
701
             * we print the prompt to stdout before EVP_read_pw_string turns
 
702
             * off tty echo
 
703
             */
 
704
            apr_file_open_stdout(&writetty, p);
 
705
 
 
706
            ap_log_error(APLOG_MARK, APLOG_INFO, 0, s,
 
707
                         "Init: Requesting pass phrase via builtin terminal "
 
708
                         "dialog");
 
709
#endif
 
710
        }
 
711
 
 
712
        /*
 
713
         * The first time display a header to inform the user about what
 
714
         * program he actually speaks to, which module is responsible for
 
715
         * this terminal dialog and why to the hell he has to enter
 
716
         * something...
 
717
         */
 
718
        if (*pnPassPhraseDialog == 1) {
 
719
            apr_file_printf(writetty, "%s mod_ssl/%s (Pass Phrase Dialog)\n",
 
720
                            AP_SERVER_BASEVERSION, MOD_SSL_VERSION);
 
721
            apr_file_printf(writetty, "Some of your private key files are encrypted for security reasons.\n");
 
722
            apr_file_printf(writetty, "In order to read them you have to provide the pass phrases.\n");
 
723
        }
 
724
        if (*pbPassPhraseDialogOnce) {
 
725
            *pbPassPhraseDialogOnce = FALSE;
 
726
            apr_file_printf(writetty, "\n");
 
727
            apr_file_printf(writetty, "Server %s (%s)\n", cpVHostID, cpAlgoType);
 
728
        }
 
729
 
 
730
        /*
 
731
         * Emulate the OpenSSL internal pass phrase dialog
 
732
         * (see crypto/pem/pem_lib.c:def_callback() for details)
 
733
         */
 
734
        prompt = "Enter pass phrase:";
 
735
 
 
736
        for (;;) {
 
737
            apr_file_puts(prompt, writetty);
 
738
            if (sc->server->pphrase_dialog_type == SSL_PPTYPE_PIPE) {
 
739
                i = pipe_get_passwd_cb(buf, bufsize, "", FALSE);
 
740
            }
 
741
            else { /* sc->server->pphrase_dialog_type == SSL_PPTYPE_BUILTIN */
 
742
                i = EVP_read_pw_string(buf, bufsize, "", FALSE);
 
743
            }
 
744
            if (i != 0) {
 
745
                PEMerr(PEM_F_DEF_CALLBACK,PEM_R_PROBLEMS_GETTING_PASSWORD);
 
746
                memset(buf, 0, (unsigned int)bufsize);
 
747
                return (-1);
 
748
            }
 
749
            len = strlen(buf);
 
750
            if (len < 1)
 
751
                apr_file_printf(writetty, "Apache:mod_ssl:Error: Pass phrase empty (needs to be at least 1 character).\n");
 
752
            else
 
753
                break;
 
754
        }
 
755
    }
 
756
 
 
757
    /*
 
758
     * Filter program
 
759
     */
 
760
    else if (sc->server->pphrase_dialog_type == SSL_PPTYPE_FILTER) {
 
761
        const char *cmd = sc->server->pphrase_dialog_path;
 
762
        const char **argv = apr_palloc(p, sizeof(char *) * 4);
 
763
        char *result;
 
764
 
 
765
        ap_log_error(APLOG_MARK, APLOG_INFO, 0, s,
 
766
                     "Init: Requesting pass phrase from dialog filter "
 
767
                     "program (%s)", cmd);
 
768
 
 
769
        argv[0] = cmd;
 
770
        argv[1] = cpVHostID;
 
771
        argv[2] = cpAlgoType;
 
772
        argv[3] = NULL;
 
773
 
 
774
        result = ssl_util_readfilter(s, p, cmd, argv);
 
775
        apr_cpystrn(buf, result, bufsize);
 
776
        len = strlen(buf);
 
777
    }
 
778
 
 
779
    /*
 
780
     * Ok, we now have the pass phrase, so give it back
 
781
     */
 
782
    *cppPassPhraseCur = apr_pstrdup(p, buf);
 
783
 
 
784
    /*
 
785
     * And return it's length to OpenSSL...
 
786
     */
 
787
    return (len);
 
788
}
 
789