source: mod_gnutls/src/gnutls_config.c @ e2ba939

debian/masterdebian/stretch-backportsproxy-ticketupstream
Last change on this file since e2ba939 was e2ba939, checked in by Thomas Klute <thomas2.klute@…>, 4 years ago

Prevent memory leaks in post_conf hook

Valgrind indicated memory leaks from GnuTLS structure initializations
in mgs_load_files(). This method is called from the post_config hook,
so there was no continuous leak, but graceful restart might have been
affected. There were two main reasons for the leaks:

  • GnuTLS does not use the APR memory pool infrastructure, so its structures are not automatically deinitialized when the pool lifetime ends. Registering a cleanup hook with the pool solves this problem.
  • Memory for the X.509 certificate chain (sc->certs_x509_crt_chain) is allocated from the config pool at the beginning of mgs_load_files(), but the certificates were loaded using gnutls_x509_crt_list_import2(), which allocates memory on its own, the pointer to which was lost when the server config pool was destroyed. Switch to gnutls_x509_crt_list_import() to use the pool memory.

Additionally, all pointers to the structures released by the pool
cleanup hook must be properly initialized to NULL (and reset on
deinit) to make sure they are only deinitialized if they have been
actually been initialized. Not doing that results in interesting stack
traces, but not a working server.

Note that this commit fixes only the leaks visible in my Valgrind
analysis of a server with an X.509 identity and an anonymous
client. Other structures will be covered by follow-up commits after
additional review.

  • Property mode set to 100644
File size: 35.6 KB
Line 
1/**
2 *  Copyright 2004-2005 Paul Querna
3 *  Copyright 2008, 2014 Nikos Mavrogiannopoulos
4 *  Copyright 2011 Dash Shendy
5 *  Copyright 2015-2016 Thomas Klute
6 *
7 *  Licensed under the Apache License, Version 2.0 (the "License");
8 *  you may not use this file except in compliance with the License.
9 *  You may obtain a copy of the License at
10 *
11 *      http://www.apache.org/licenses/LICENSE-2.0
12 *
13 *  Unless required by applicable law or agreed to in writing, software
14 *  distributed under the License is distributed on an "AS IS" BASIS,
15 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16 *  See the License for the specific language governing permissions and
17 *  limitations under the License.
18 *
19 */
20
21#include "mod_gnutls.h"
22#include "apr_lib.h"
23#include <gnutls/abstract.h>
24
25#define INIT_CA_SIZE 128
26/* Default OCSP response grace time in seconds */
27#define MGS_GRACE_TIME 60
28
29#ifdef APLOG_USE_MODULE
30APLOG_USE_MODULE(gnutls);
31#endif
32
33static int pin_callback(void *user, int attempt __attribute__((unused)),
34                        const char *token_url __attribute__((unused)),
35                        const char *token_label, unsigned int flags,
36                        char *pin, size_t pin_max)
37{
38    mgs_srvconf_rec *sc = user;
39
40    if (sc->pin == NULL || flags & GNUTLS_PIN_FINAL_TRY ||
41        flags & GNUTLS_PIN_WRONG) {
42        return -1;
43    }
44
45    if (token_label && strcmp(token_label, "SRK") == 0) {
46         snprintf(pin, pin_max, "%s", sc->srk_pin);
47    } else {
48         snprintf(pin, pin_max, "%s", sc->pin);
49    }
50    return 0;
51}
52
53static int load_datum_from_file(apr_pool_t * pool,
54                                const char *file, gnutls_datum_t * data)
55{
56    apr_file_t *fp;
57    apr_finfo_t finfo;
58    apr_status_t rv;
59    apr_size_t br = 0;
60
61    rv = apr_file_open(&fp, file, APR_READ | APR_BINARY,
62                       APR_OS_DEFAULT, pool);
63    if (rv != APR_SUCCESS) {
64        return rv;
65    }
66
67    rv = apr_file_info_get(&finfo, APR_FINFO_SIZE, fp);
68
69    if (rv != APR_SUCCESS) {
70        return rv;
71    }
72
73    data->data = apr_palloc(pool, finfo.size + 1);
74    rv = apr_file_read_full(fp, data->data, finfo.size, &br);
75
76    if (rv != APR_SUCCESS) {
77        return rv;
78    }
79    apr_file_close(fp);
80
81    data->data[br] = '\0';
82    data->size = br;
83
84    return 0;
85}
86
87/* 2048-bit group parameters from SRP specification */
88const char static_dh_params[] = "-----BEGIN DH PARAMETERS-----\n"
89        "MIIBBwKCAQCsa9tBMkqam/Fm3l4TiVgvr3K2ZRmH7gf8MZKUPbVgUKNzKcu0oJnt\n"
90        "gZPgdXdnoT3VIxKrSwMxDc1/SKnaBP1Q6Ag5ae23Z7DPYJUXmhY6s2YaBfvV+qro\n"
91        "KRipli8Lk7hV+XmT7Jde6qgNdArb9P90c1nQQdXDPqcdKB5EaxR3O8qXtDoj+4AW\n"
92        "dr0gekNsZIHx0rkHhxdGGludMuaI+HdIVEUjtSSw1X1ep3onddLs+gMs+9v1L7N4\n"
93        "YWAnkATleuavh05zA85TKZzMBBx7wwjYKlaY86jQw4JxrjX46dv7tpS1yAPYn3rk\n"
94        "Nd4jbVJfVHWbZeNy/NaO8g+nER+eSv9zAgEC\n"
95        "-----END DH PARAMETERS-----\n";
96
97/*
98 * Clean up the various GnuTLS data structures allocated from
99 * mgs_load_files()
100 */
101static apr_status_t mgs_pool_free_credentials(void *arg)
102{
103    mgs_srvconf_rec *sc = (mgs_srvconf_rec *) arg;
104
105    if (sc->certs)
106    {
107        gnutls_certificate_free_credentials(sc->certs);
108        sc->certs = NULL;
109    }
110
111    if (sc->anon_creds)
112    {
113        gnutls_anon_free_server_credentials(sc->anon_creds);
114        sc->anon_creds = NULL;
115    }
116
117#ifdef ENABLE_SRP
118    if (sc->srp_creds)
119    {
120        gnutls_srp_free_server_credentials(sc->srp_creds);
121        sc->srp_creds = NULL;
122    }
123#endif
124
125    if (sc->dh_params)
126    {
127        gnutls_dh_params_deinit(sc->dh_params);
128        sc->dh_params = NULL;
129    }
130
131    for (unsigned int i = 0; i < sc->certs_x509_chain_num; i++)
132    {
133        gnutls_pcert_deinit(&sc->certs_x509_chain[i]);
134        gnutls_x509_crt_deinit(sc->certs_x509_crt_chain[i]);
135    }
136
137    if (sc->privkey_x509)
138    {
139        gnutls_privkey_deinit(sc->privkey_x509);
140        sc->privkey_x509 = NULL;
141    }
142
143    if (sc->priorities)
144    {
145        gnutls_priority_deinit(sc->priorities);
146        sc->priorities = NULL;
147    }
148
149    return APR_SUCCESS;
150}
151
152int mgs_load_files(apr_pool_t * p, server_rec * s)
153{
154    apr_pool_t *spool;
155    const char *file;
156    gnutls_datum_t data;
157    int ret;
158    mgs_srvconf_rec *sc =
159        (mgs_srvconf_rec *) ap_get_module_config(s->module_config,
160                                                 &gnutls_module);
161
162    apr_pool_create(&spool, p);
163
164    sc->cert_pgp = apr_pcalloc(p, sizeof(sc->cert_pgp[0]));
165    sc->cert_crt_pgp = apr_pcalloc(p, sizeof(sc->cert_crt_pgp[0]));
166    sc->certs_x509_chain =
167        apr_pcalloc(p, MAX_CHAIN_SIZE * sizeof(sc->certs_x509_chain[0]));
168    sc->certs_x509_crt_chain =
169        apr_pcalloc(p, MAX_CHAIN_SIZE * sizeof(sc->certs_x509_crt_chain[0]));
170
171    /* Cleanup function for the GnuTLS structures allocated below */
172    apr_pool_cleanup_register(p, sc, mgs_pool_free_credentials,
173                              apr_pool_cleanup_null);
174
175    if (sc->certs == NULL)
176    {
177        ret = gnutls_certificate_allocate_credentials(&sc->certs);
178        if (ret < 0) {
179            ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, s,
180                         "GnuTLS: Failed to initialize" ": (%d) %s", ret,
181                         gnutls_strerror(ret));
182            ret = -1;
183            goto cleanup;
184        }
185    }
186
187    if (sc->anon_creds == NULL)
188    {
189        ret = gnutls_anon_allocate_server_credentials(&sc->anon_creds);
190        if (ret < 0) {
191            ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, s,
192                         "GnuTLS: Failed to initialize" ": (%d) %s", ret,
193                         gnutls_strerror(ret));
194            ret = -1;
195            goto cleanup;
196        }
197    }
198
199    /* Load SRP parameters */
200#ifdef ENABLE_SRP
201    if (sc->srp_creds == NULL)
202    {
203        ret = gnutls_srp_allocate_server_credentials(&sc->srp_creds);
204        if (ret < 0) {
205            ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, s,
206                         "GnuTLS: Failed to initialize" ": (%d) %s", ret,
207                         gnutls_strerror(ret));
208            ret = -1;
209            goto cleanup;
210        }
211    }
212
213    if (sc->srp_tpasswd_conf_file != NULL && sc->srp_tpasswd_file != NULL) {
214        ret = gnutls_srp_set_server_credentials_file
215            (sc->srp_creds, sc->srp_tpasswd_file,
216             sc->srp_tpasswd_conf_file);
217
218        if (ret < 0 && sc->enabled == GNUTLS_ENABLED_TRUE) {
219            ap_log_error(APLOG_MARK, APLOG_STARTUP, 0,
220                         s,
221                         "GnuTLS: Host '%s:%d' is missing a "
222                         "SRP password or conf File!",
223                         s->server_hostname, s->port);
224            ret = -1;
225            goto cleanup;
226        }
227    }
228#endif
229
230    if (sc->dh_params == NULL)
231    {
232        ret = gnutls_dh_params_init(&sc->dh_params);
233        if (ret < 0) {
234            ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, s,
235                         "GnuTLS: Failed to initialize"
236                         ": (%d) %s", ret, gnutls_strerror(ret));
237            ret = -1;
238            goto cleanup;
239        }
240
241    /* Load DH parameters */
242    if (sc->dh_file) {
243        if (load_datum_from_file(spool, sc->dh_file, &data) != 0) {
244            ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, s,
245                         "GnuTLS: Error Reading " "DH params '%s'", sc->dh_file);
246            ret = -1;
247            goto cleanup;
248        }
249
250        ret =
251            gnutls_dh_params_import_pkcs3(sc->dh_params, &data,
252                                          GNUTLS_X509_FMT_PEM);
253        if (ret < 0) {
254            ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, s,
255                         "GnuTLS: Failed to Import "
256                         "DH params '%s': (%d) %s", sc->dh_file, ret,
257                         gnutls_strerror(ret));
258            ret = -1;
259            goto cleanup;
260        }
261    } else {
262        gnutls_datum_t pdata = {
263            (void *) static_dh_params,
264            sizeof(static_dh_params)
265        };
266
267        ret = gnutls_dh_params_import_pkcs3(sc->dh_params, &pdata, GNUTLS_X509_FMT_PEM);
268        if (ret < 0) {
269            ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, s,
270                    "GnuTLS: Unable to generate or load DH Params: (%d) %s",
271                    ret, gnutls_strerror(ret));
272            ret = -1;
273            goto cleanup;
274        }
275    }
276    }
277
278    if (sc->x509_cert_file != NULL && sc->certs_x509_crt_chain[0] == NULL)
279    {
280        unsigned int chain_num = MAX_CHAIN_SIZE;
281        unsigned format = GNUTLS_X509_FMT_PEM;
282
283        /* Load X.509 certificate */
284        if (strncmp(sc->x509_cert_file, "pkcs11:", 7) == 0) {
285            gnutls_pkcs11_obj_t obj;
286
287            file = sc->x509_cert_file;
288
289            ret = gnutls_pkcs11_obj_init(&obj);
290            if (ret < 0) {
291                ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, s,
292                             "GnuTLS: Error Initializing PKCS #11 object");
293                ret = -1;
294                goto cleanup;
295            }
296
297            gnutls_pkcs11_obj_set_pin_function(obj, pin_callback, sc);
298
299            ret = gnutls_pkcs11_obj_import_url(obj, file, GNUTLS_PKCS11_OBJ_FLAG_LOGIN);
300            if (ret < 0) {
301                ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, s,
302                             "GnuTLS: Error Importing PKCS #11 object: '%s': %s",
303                             file, gnutls_strerror(ret));
304                ret = -1;
305                goto cleanup;
306            }
307
308            format = GNUTLS_X509_FMT_DER;
309            ret = gnutls_pkcs11_obj_export2(obj, &data);
310            if (ret < 0) {
311                ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, s,
312                             "GnuTLS: Error Exporting a PKCS #11 object: '%s': %s",
313                             file, gnutls_strerror(ret));
314                ret = -1;
315                goto cleanup;
316            }
317
318            gnutls_pkcs11_obj_deinit(obj);
319        } else {
320            file = ap_server_root_relative(spool, sc->x509_cert_file);
321
322            ret = gnutls_load_file(file, &data);
323            if (ret < 0) {
324                ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, s,
325                             "GnuTLS: Error Reading Certificate '%s': %s",
326                             file, gnutls_strerror(ret));
327                ret = -1;
328                goto cleanup;
329            }
330        }
331
332        ret = gnutls_x509_crt_list_import(sc->certs_x509_crt_chain,
333                                      &chain_num, &data, format,
334                                      GNUTLS_X509_CRT_LIST_FAIL_IF_UNSORTED);
335        gnutls_free(data.data);
336        sc->certs_x509_chain_num = chain_num;
337
338        if (ret < 0) {
339            ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, s,
340                         "GnuTLS: Failed to Import Certificate Chain '%s': (%d) %s",
341                         file, ret, gnutls_strerror(ret));
342            ret = -1;
343            goto cleanup;
344        }
345
346    for (unsigned int i = 0; i < chain_num; i++)
347    {
348            ret =
349                gnutls_pcert_import_x509(&sc->certs_x509_chain[i],
350                                         sc->certs_x509_crt_chain[i], 0);
351            if (ret < 0) {
352                ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, s,
353                             "GnuTLS: Failed to Import pCertificate '%s': (%d) %s",
354                             file, ret, gnutls_strerror(ret));
355                ret = -1;
356                goto cleanup;
357            }
358        }
359        sc->certs_x509_chain_num = chain_num;
360    }
361
362    if (sc->x509_key_file && sc->privkey_x509 == NULL)
363    {
364        ret = gnutls_privkey_init(&sc->privkey_x509);
365        if (ret < 0) {
366            ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, s,
367                         "GnuTLS: Failed to initialize: (%d) %s", ret,
368                         gnutls_strerror(ret));
369            ret = -1;
370            goto cleanup;
371        }
372
373        if (gnutls_url_is_supported(sc->x509_key_file) != 0) {
374            file = sc->x509_key_file;
375
376            gnutls_privkey_set_pin_function(sc->privkey_x509, pin_callback,
377                                            sc);
378
379            ret = gnutls_privkey_import_url(sc->privkey_x509, file, 0);
380
381            if (ret < 0) {
382                ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, s,
383                             "GnuTLS: Failed to Import Private Key URL '%s': (%d) %s",
384                             file, ret, gnutls_strerror(ret));
385                ret = -1;
386                goto cleanup;
387            }
388        } else {
389            file = ap_server_root_relative(spool, sc->x509_key_file);
390
391            if (load_datum_from_file(spool, file, &data) != 0) {
392                ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, s,
393                             "GnuTLS: Error Reading Private Key '%s'",
394                             file);
395                ret = -1;
396                goto cleanup;
397            }
398
399            ret =
400                gnutls_privkey_import_x509_raw(sc->privkey_x509, &data,
401                                               GNUTLS_X509_FMT_PEM, sc->pin,
402                                               0);
403
404            if (ret < 0) {
405                ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, s,
406                             "GnuTLS: Failed to Import Private Key '%s': (%d) %s",
407                             file, ret, gnutls_strerror(ret));
408                ret = -1;
409                goto cleanup;
410            }
411        }
412    }
413
414    /* Load the X.509 CA file */
415    if (sc->x509_ca_file) {
416        if (load_datum_from_file(spool, sc->x509_ca_file, &data) != 0) {
417            ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, s,
418                         "GnuTLS: Error Reading " "Client CA File '%s'",
419                         sc->x509_ca_file);
420            ret = -1;
421            goto cleanup;
422        }
423
424        ret = gnutls_x509_crt_list_import2(&sc->ca_list, &sc->ca_list_size,
425                                         &data, GNUTLS_X509_FMT_PEM, 0);
426        if (ret < 0) {
427            ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, s,
428                         "GnuTLS: Failed to load "
429                         "Client CA File '%s': (%d) %s", sc->x509_ca_file,
430                         ret, gnutls_strerror(ret));
431            ret = -1;
432            goto cleanup;
433        }
434    }
435
436    if (sc->pgp_cert_file) {
437        if (load_datum_from_file(spool, sc->pgp_cert_file, &data) != 0) {
438            ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, s,
439                         "GnuTLS: Error Reading " "Certificate '%s'",
440                         sc->pgp_cert_file);
441            ret = -1;
442            goto cleanup;
443        }
444
445        ret = gnutls_openpgp_crt_init(&sc->cert_crt_pgp[0]);
446        if (ret < 0) {
447            ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, s,
448                         "GnuTLS: Failed to Init "
449                         "PGP Certificate: (%d) %s", ret,
450                         gnutls_strerror(ret));
451            ret = -1;
452            goto cleanup;
453        }
454
455        ret =
456            gnutls_openpgp_crt_import(sc->cert_crt_pgp[0], &data,
457                                      GNUTLS_OPENPGP_FMT_BASE64);
458        if (ret < 0) {
459            ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, s,
460                         "GnuTLS: Failed to Import "
461                         "PGP Certificate: (%d) %s", ret,
462                         gnutls_strerror(ret));
463            ret = -1;
464            goto cleanup;
465        }
466
467        ret =
468            gnutls_pcert_import_openpgp(sc->cert_pgp, sc->cert_crt_pgp[0],
469                                        0);
470        if (ret < 0) {
471            ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, s,
472                         "GnuTLS: Failed to Import "
473                         "PGP pCertificate: (%d) %s", ret,
474                         gnutls_strerror(ret));
475            ret = -1;
476            goto cleanup;
477        }
478    }
479
480    /* Load the PGP key file */
481    if (sc->pgp_key_file) {
482        if (load_datum_from_file(spool, sc->pgp_key_file, &data) != 0) {
483            ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, s,
484                         "GnuTLS: Error Reading " "Private Key '%s'",
485                         sc->pgp_key_file);
486            ret = -1;
487            goto cleanup;
488        }
489
490        ret = gnutls_privkey_init(&sc->privkey_pgp);
491        if (ret < 0) {
492            ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, s,
493                         "GnuTLS: Failed to initialize"
494                         ": (%d) %s", ret, gnutls_strerror(ret));
495            ret = -1;
496            goto cleanup;
497        }
498
499#if GNUTLS_VERSION_NUMBER < 0x030312
500        /* GnuTLS versions before 3.3.12 contain a bug in
501         * gnutls_privkey_import_openpgp_raw which frees data that is
502         * accessed when the key is used, leading to segfault. Loading
503         * the key into a gnutls_openpgp_privkey_t and then assigning
504         * it to the gnutls_privkey_t works around the bug, hence this
505         * chain of gnutls_openpgp_privkey_init,
506         * gnutls_openpgp_privkey_import and
507         * gnutls_privkey_import_openpgp. */
508        ret = gnutls_openpgp_privkey_init(&sc->privkey_pgp_internal);
509        if (ret != 0) {
510            ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, s,
511                         "GnuTLS: Failed to initialize "
512                         "PGP Private Key '%s': (%d) %s",
513                         sc->pgp_key_file, ret, gnutls_strerror(ret));
514            ret = -1;
515            goto cleanup;
516        }
517
518        ret = gnutls_openpgp_privkey_import(sc->privkey_pgp_internal, &data,
519                                            GNUTLS_OPENPGP_FMT_BASE64, NULL, 0);
520        if (ret != 0) {
521            ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, s,
522                         "GnuTLS: Failed to Import "
523                         "PGP Private Key '%s': (%d) %s",
524                         sc->pgp_key_file, ret, gnutls_strerror(ret));
525            ret = -1;
526            goto cleanup;
527        }
528
529        ret = gnutls_privkey_import_openpgp(sc->privkey_pgp,
530                                            sc->privkey_pgp_internal, 0);
531        if (ret != 0)
532        {
533            ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, s,
534                         "GnuTLS: Failed to assign PGP Private Key '%s' "
535                         "to gnutls_privkey_t structure: (%d) %s",
536                         sc->pgp_key_file, ret, gnutls_strerror(ret));
537            ret = -1;
538            goto cleanup;
539        }
540#else
541        ret = gnutls_privkey_import_openpgp_raw(sc->privkey_pgp, &data,
542                                                GNUTLS_OPENPGP_FMT_BASE64,
543                                                NULL, NULL);
544        if (ret != 0)
545        {
546            ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, s,
547                         "GnuTLS: Failed to Import "
548                         "PGP Private Key '%s': (%d) %s",
549                         sc->pgp_key_file, ret, gnutls_strerror(ret));
550            ret = -1;
551            goto cleanup;
552        }
553#endif
554    }
555
556    /* Load the keyring file */
557    if (sc->pgp_ring_file) {
558        if (load_datum_from_file(spool, sc->pgp_ring_file, &data) != 0) {
559            ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, s,
560                         "GnuTLS: Error Reading " "Keyring File '%s'",
561                         sc->pgp_ring_file);
562            ret = -1;
563            goto cleanup;
564        }
565
566        ret = gnutls_openpgp_keyring_init(&sc->pgp_list);
567        if (ret < 0) {
568            ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, s,
569                         "GnuTLS: Failed to initialize"
570                         "keyring: (%d) %s", ret, gnutls_strerror(ret));
571            ret = -1;
572            goto cleanup;
573        }
574
575        ret = gnutls_openpgp_keyring_import(sc->pgp_list, &data,
576                                           GNUTLS_OPENPGP_FMT_BASE64);
577        if (ret < 0) {
578            ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, s,
579                         "GnuTLS: Failed to load "
580                         "Keyring File '%s': (%d) %s", sc->pgp_ring_file,
581                         ret, gnutls_strerror(ret));
582            ret = -1;
583            goto cleanup;
584        }
585    }
586
587    if (sc->priorities_str && sc->priorities == NULL)
588    {
589        const char *err;
590        ret = gnutls_priority_init(&sc->priorities, sc->priorities_str, &err);
591
592        if (ret < 0) {
593            if (ret == GNUTLS_E_INVALID_REQUEST) {
594                ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, s,
595                             "GnuTLS: Syntax error parsing priorities string at: %s",
596                             err);
597            } else {
598                ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, s,
599                             "GnuTLS: error parsing priorities string");
600
601            }
602            ret = -1;
603            goto cleanup;
604        }
605    }
606
607    ret = 0;
608  cleanup:
609    apr_pool_destroy(spool);
610
611    return ret;
612}
613
614int mgs_pkcs11_reinit(server_rec * base_server)
615{
616    int ret;
617    server_rec *s;
618    mgs_srvconf_rec *sc;
619
620    gnutls_pkcs11_reinit();
621
622    for (s = base_server; s; s = s->next) {
623        sc = (mgs_srvconf_rec *) ap_get_module_config(s->module_config, &gnutls_module);
624
625            /* gnutls caches the session in a private key, so we need to open
626             * a new one */
627            if (sc->x509_key_file && gnutls_url_is_supported(sc->x509_key_file) != 0) {
628                gnutls_privkey_deinit(sc->privkey_x509);
629
630                ret = gnutls_privkey_init(&sc->privkey_x509);
631                if (ret < 0) {
632                    ap_log_error(APLOG_MARK, APLOG_EMERG, 0, s,
633                                 "GnuTLS: Failed to initialize: (%d) %s", ret,
634                                 gnutls_strerror(ret));
635                    goto fail;
636                }
637
638                gnutls_privkey_set_pin_function(sc->privkey_x509, pin_callback, sc);
639
640                ret = gnutls_privkey_import_url(sc->privkey_x509, sc->x509_key_file, 0);
641                if (ret < 0) {
642                    ap_log_error(APLOG_MARK, APLOG_EMERG, 0, s,
643                             "GnuTLS: Failed to Re-Import Private Key URL '%s': (%d) %s",
644                             sc->x509_key_file, ret, gnutls_strerror(ret));
645                    goto fail;
646                }
647            }
648    }
649
650    return 0;
651
652 fail:
653    gnutls_privkey_deinit(sc->privkey_x509);
654    return -1;
655}
656
657const char *mgs_set_dh_file(cmd_parms * parms, void *dummy __attribute__((unused)),
658        const char *arg) {
659    mgs_srvconf_rec *sc =
660        (mgs_srvconf_rec *) ap_get_module_config(parms->server->
661                                                 module_config,
662                                                 &gnutls_module);
663
664    sc->dh_file = ap_server_root_relative(parms->pool, arg);
665
666    return NULL;
667}
668
669const char *mgs_set_cert_file(cmd_parms * parms, void *dummy __attribute__((unused)), const char *arg) {
670
671    mgs_srvconf_rec *sc =
672        (mgs_srvconf_rec *) ap_get_module_config(parms->
673                                                 server->module_config,
674                                                 &gnutls_module);
675
676    sc->x509_cert_file = apr_pstrdup(parms->pool, arg);
677
678    return NULL;
679
680}
681
682const char *mgs_set_key_file(cmd_parms * parms, void *dummy __attribute__((unused)), const char *arg) {
683
684    mgs_srvconf_rec *sc =
685        (mgs_srvconf_rec *) ap_get_module_config(parms->
686                                                 server->module_config,
687                                                 &gnutls_module);
688
689    sc->x509_key_file = apr_pstrdup(parms->pool, arg);
690
691    return NULL;
692}
693
694const char *mgs_set_pgpcert_file(cmd_parms * parms, void *dummy __attribute__((unused)),
695        const char *arg)
696{
697    mgs_srvconf_rec *sc =
698        (mgs_srvconf_rec *) ap_get_module_config(parms->server->
699                                                 module_config,
700                                                 &gnutls_module);
701
702    sc->pgp_cert_file = ap_server_root_relative(parms->pool, arg);
703
704    return NULL;
705}
706
707const char *mgs_set_pgpkey_file(cmd_parms * parms, void *dummy __attribute__((unused)),
708        const char *arg) {
709    mgs_srvconf_rec *sc =
710        (mgs_srvconf_rec *) ap_get_module_config(parms->server->
711                                                 module_config,
712                                                 &gnutls_module);
713
714    sc->pgp_key_file = ap_server_root_relative(parms->pool, arg);
715
716    return NULL;
717}
718
719const char *mgs_set_tickets(cmd_parms *parms,
720                            void *dummy __attribute__((unused)),
721                            const int arg)
722{
723    mgs_srvconf_rec *sc = (mgs_srvconf_rec *)
724        ap_get_module_config(parms->server->module_config, &gnutls_module);
725
726    if (arg)
727        sc->tickets = GNUTLS_ENABLED_TRUE;
728    else
729        sc->tickets = GNUTLS_ENABLED_FALSE;
730
731    return NULL;
732}
733
734
735#ifdef ENABLE_SRP
736
737const char *mgs_set_srp_tpasswd_file(cmd_parms * parms, void *dummy __attribute__((unused)),
738        const char *arg) {
739    mgs_srvconf_rec *sc =
740        (mgs_srvconf_rec *) ap_get_module_config(parms->server->
741                                                 module_config,
742                                                 &gnutls_module);
743
744    sc->srp_tpasswd_file = ap_server_root_relative(parms->pool, arg);
745
746    return NULL;
747}
748
749const char *mgs_set_srp_tpasswd_conf_file(cmd_parms * parms, void *dummy __attribute__((unused)),
750        const char *arg) {
751    mgs_srvconf_rec *sc =
752        (mgs_srvconf_rec *) ap_get_module_config(parms->server->
753                                                 module_config,
754                                                 &gnutls_module);
755
756    sc->srp_tpasswd_conf_file = ap_server_root_relative(parms->pool, arg);
757
758    return NULL;
759}
760
761#endif
762
763const char *mgs_set_cache(cmd_parms * parms, void *dummy __attribute__((unused)),
764        const char *type, const char *arg) {
765    const char *err;
766    mgs_srvconf_rec *sc =
767        ap_get_module_config(parms->server->module_config,
768                             &gnutls_module);
769    if ((err = ap_check_cmd_context(parms, GLOBAL_ONLY))) {
770        return err;
771    }
772
773    if (strcasecmp("none", type) == 0) {
774        sc->cache_type = mgs_cache_none;
775        sc->cache_config = NULL;
776        return NULL;
777    } else if (strcasecmp("dbm", type) == 0) {
778        sc->cache_type = mgs_cache_dbm;
779    } else if (strcasecmp("gdbm", type) == 0) {
780        sc->cache_type = mgs_cache_gdbm;
781    }
782#if HAVE_APR_MEMCACHE
783    else if (strcasecmp("memcache", type) == 0) {
784        sc->cache_type = mgs_cache_memcache;
785    }
786#endif
787    else {
788        return "Invalid Type for GnuTLSCache!";
789    }
790
791    if (arg == NULL)
792        return "Invalid argument 2 for GnuTLSCache!";
793
794    if (sc->cache_type == mgs_cache_dbm
795        || sc->cache_type == mgs_cache_gdbm) {
796        sc->cache_config = ap_server_root_relative(parms->pool, arg);
797    } else {
798        sc->cache_config = apr_pstrdup(parms->pool, arg);
799    }
800
801    return NULL;
802}
803
804const char *mgs_set_timeout(cmd_parms * parms,
805                            void *dummy __attribute__((unused)),
806                            const char *arg)
807{
808    const char *err;
809    if ((err = ap_check_cmd_context(parms, GLOBAL_ONLY)))
810        return err;
811
812    apr_int64_t argint = apr_atoi64(arg);
813    if (argint < 0)
814        return apr_psprintf(parms->pool, "%s: Invalid argument",
815                            parms->directive->directive);
816
817    mgs_srvconf_rec *sc = (mgs_srvconf_rec *)
818        ap_get_module_config(parms->server->module_config, &gnutls_module);
819
820    if (!apr_strnatcasecmp(parms->directive->directive, "GnuTLSCacheTimeout"))
821        sc->cache_timeout = apr_time_from_sec(argint);
822    else if (!apr_strnatcasecmp(parms->directive->directive,
823                                "GnuTLSOCSPGraceTime"))
824        sc->ocsp_grace_time = apr_time_from_sec(argint);
825    else
826        /* Can't happen unless there's a serious bug in mod_gnutls or Apache */
827        return apr_psprintf(parms->pool,
828                            "mod_gnutls: %s called for invalid option '%s'",
829                            __func__, parms->directive->directive);
830
831    return NULL;
832}
833
834const char *mgs_set_client_verify_method(cmd_parms * parms, void *dummy __attribute__((unused)),
835        const char *arg) {
836    mgs_srvconf_rec *sc = (mgs_srvconf_rec *)ap_get_module_config(parms->server->module_config, &gnutls_module);
837
838    if (strcasecmp("cartel", arg) == 0) {
839        sc->client_verify_method = mgs_cvm_cartel;
840    } else if (strcasecmp("msva", arg) == 0) {
841#ifdef ENABLE_MSVA
842        sc->client_verify_method = mgs_cvm_msva;
843#else
844        return "GnuTLSClientVerifyMethod: msva is not supported";
845#endif
846    } else {
847        return "GnuTLSClientVerifyMethod: Invalid argument";
848    }
849
850    return NULL;
851}
852
853const char *mgs_set_client_verify(cmd_parms * parms,
854                                  void *dirconf,
855                                  const char *arg) {
856    int mode;
857
858    if (strcasecmp("none", arg) == 0 || strcasecmp("ignore", arg) == 0) {
859        mode = GNUTLS_CERT_IGNORE;
860    } else if (strcasecmp("optional", arg) == 0
861               || strcasecmp("request", arg) == 0) {
862        mode = GNUTLS_CERT_REQUEST;
863    } else if (strcasecmp("require", arg) == 0) {
864        mode = GNUTLS_CERT_REQUIRE;
865    } else {
866        return "GnuTLSClientVerify: Invalid argument";
867    }
868
869    /* This was set from a directory context */
870    if (parms->path) {
871        mgs_dirconf_rec *dc = (mgs_dirconf_rec *) dirconf;
872        dc->client_verify_mode = mode;
873    } else {
874        mgs_srvconf_rec *sc = (mgs_srvconf_rec *)
875            ap_get_module_config(parms->server->module_config,
876                                 &gnutls_module);
877        sc->client_verify_mode = mode;
878    }
879
880    return NULL;
881}
882
883const char *mgs_set_client_ca_file(cmd_parms * parms, void *dummy __attribute__((unused)),
884        const char *arg) {
885    mgs_srvconf_rec *sc =
886        (mgs_srvconf_rec *) ap_get_module_config(parms->server->
887                                                 module_config,
888                                                 &gnutls_module);
889
890    sc->x509_ca_file = ap_server_root_relative(parms->pool, arg);
891
892    return NULL;
893}
894
895const char *mgs_set_keyring_file(cmd_parms * parms, void *dummy __attribute__((unused)),
896        const char *arg) {
897    mgs_srvconf_rec *sc =
898        (mgs_srvconf_rec *) ap_get_module_config(parms->server->
899                                                 module_config,
900                                                 &gnutls_module);
901
902    sc->pgp_ring_file = ap_server_root_relative(parms->pool, arg);
903
904    return NULL;
905}
906
907/*
908 * Enable TLS proxy operation if arg is true, disable it otherwise.
909 */
910const char *mgs_set_proxy_engine(cmd_parms *parms,
911                                 void *dummy __attribute__((unused)),
912                                 const int arg)
913{
914    mgs_srvconf_rec *sc = (mgs_srvconf_rec *)
915        ap_get_module_config(parms->server->module_config, &gnutls_module);
916
917    if (arg)
918        sc->proxy_enabled = GNUTLS_ENABLED_TRUE;
919    else
920        sc->proxy_enabled = GNUTLS_ENABLED_FALSE;
921
922    return NULL;
923}
924
925/*
926 * Enable TLS for the server/vhost if arg is true, disable it
927 * otherwise.
928 */
929const char *mgs_set_enabled(cmd_parms *parms,
930                            void *dummy __attribute__((unused)),
931                            const int arg)
932{
933    mgs_srvconf_rec *sc = (mgs_srvconf_rec *)
934        ap_get_module_config(parms->server->module_config, &gnutls_module);
935
936    if (arg)
937        sc->enabled = GNUTLS_ENABLED_TRUE;
938    else
939        sc->enabled = GNUTLS_ENABLED_FALSE;
940
941    return NULL;
942}
943
944const char *mgs_set_export_certificates_size(cmd_parms * parms, void *dummy __attribute__((unused)), const char *arg) {
945    mgs_srvconf_rec *sc = (mgs_srvconf_rec *) ap_get_module_config(parms->server->module_config, &gnutls_module);
946    if (!strcasecmp(arg, "On")) {
947        sc->export_certificates_size = 16 * 1024;
948    } else if (!strcasecmp(arg, "Off")) {
949        sc->export_certificates_size = 0;
950    } else {
951        char *endptr;
952        sc->export_certificates_size = strtol(arg, &endptr, 10);
953        while (apr_isspace(*endptr))
954            endptr++;
955        if (*endptr == '\0' || *endptr == 'b' || *endptr == 'B') {
956            ;
957        } else if (*endptr == 'k' || *endptr == 'K') {
958            sc->export_certificates_size *= 1024;
959        } else {
960            return
961                "GnuTLSExportCertificates must be set to a size (in bytes) or 'On' or 'Off'";
962        }
963    }
964
965    return NULL;
966}
967
968
969
970/*
971 * Store GnuTLS priority strings. Used for GnuTLSPriorities and
972 * GnuTLSProxyPriorities.
973 */
974const char *mgs_set_priorities(cmd_parms * parms,
975                               void *dummy __attribute__((unused)),
976                               const char *arg)
977{
978    mgs_srvconf_rec *sc = (mgs_srvconf_rec *)
979        ap_get_module_config(parms->server->module_config, &gnutls_module);
980
981    if (!strcasecmp(parms->directive->directive, "GnuTLSPriorities"))
982        sc->priorities_str = apr_pstrdup(parms->pool, arg);
983    else if (!strcasecmp(parms->directive->directive, "GnuTLSProxyPriorities"))
984        sc->proxy_priorities_str = apr_pstrdup(parms->pool, arg);
985    else
986        /* Can't happen unless there's a serious bug in mod_gnutls or Apache */
987        return apr_psprintf(parms->pool,
988                            "mod_gnutls: %s called for invalid option '%s'",
989                            __func__, parms->directive->directive);
990
991    return NULL;
992}
993
994
995
996const char *mgs_set_pin(cmd_parms * parms, void *dummy __attribute__((unused)),
997                        const char *arg)
998{
999
1000    mgs_srvconf_rec *sc = (mgs_srvconf_rec *)
1001        ap_get_module_config(parms->server->module_config, &gnutls_module);
1002
1003    sc->pin = apr_pstrdup(parms->pool, arg);
1004
1005    return NULL;
1006}
1007
1008const char *mgs_set_srk_pin(cmd_parms * parms,
1009                            void *dummy __attribute__((unused)),
1010                            const char *arg)
1011{
1012
1013    mgs_srvconf_rec *sc = (mgs_srvconf_rec *)
1014        ap_get_module_config(parms->server->module_config, &gnutls_module);
1015
1016    sc->srk_pin = apr_pstrdup(parms->pool, arg);
1017
1018    return NULL;
1019}
1020
1021
1022
1023static mgs_srvconf_rec *_mgs_config_server_create(apr_pool_t * p,
1024                                                  char **err __attribute__((unused)))
1025{
1026    mgs_srvconf_rec *sc = apr_pcalloc(p, sizeof(*sc));
1027
1028    sc->enabled = GNUTLS_ENABLED_UNSET;
1029
1030    sc->privkey_x509 = NULL;
1031    sc->privkey_pgp = NULL;
1032    sc->anon_creds = NULL;
1033#ifdef ENABLE_SRP
1034    sc->srp_creds = NULL;
1035#endif
1036    sc->certs = NULL;
1037    sc->certs_x509_chain_num = 0;
1038    sc->p11_modules = NULL;
1039    sc->pin = NULL;
1040    sc->priorities_str = NULL;
1041    sc->cache_timeout = -1;     /* -1 means "unset" */
1042    sc->cache_type = mgs_cache_unset;
1043    sc->cache_config = NULL;
1044    sc->cache = NULL;
1045    sc->tickets = GNUTLS_ENABLED_UNSET;
1046    sc->priorities = NULL;
1047    sc->dh_params = NULL;
1048    sc->proxy_enabled = GNUTLS_ENABLED_UNSET;
1049    sc->export_certificates_size = -1;
1050    sc->client_verify_method = mgs_cvm_unset;
1051
1052    sc->proxy_x509_key_file = NULL;
1053    sc->proxy_x509_cert_file = NULL;
1054    sc->proxy_x509_ca_file = NULL;
1055    sc->proxy_x509_crl_file = NULL;
1056    sc->proxy_priorities_str = NULL;
1057    sc->proxy_priorities = NULL;
1058
1059    sc->ocsp_response_file = NULL;
1060    sc->ocsp_mutex = NULL;
1061    sc->ocsp_grace_time = apr_time_from_sec(MGS_GRACE_TIME);
1062
1063/* this relies on GnuTLS never changing the gnutls_certificate_request_t enum to define -1 */
1064    sc->client_verify_mode = -1;
1065
1066    return sc;
1067}
1068
1069void *mgs_config_server_create(apr_pool_t * p,
1070                               server_rec * s __attribute__((unused))) {
1071    char *err = NULL;
1072    mgs_srvconf_rec *sc = _mgs_config_server_create(p, &err);
1073    if (sc)
1074        return sc;
1075    else
1076        return err;
1077}
1078
1079#define gnutls_srvconf_merge(t, unset) sc->t = (add->t == unset) ? base->t : add->t
1080#define gnutls_srvconf_assign(t) sc->t = add->t
1081
1082void *mgs_config_server_merge(apr_pool_t * p, void *BASE, void *ADD)
1083{
1084    int i;
1085    char *err = NULL;
1086    mgs_srvconf_rec *base = (mgs_srvconf_rec *) BASE;
1087    mgs_srvconf_rec *add = (mgs_srvconf_rec *) ADD;
1088    mgs_srvconf_rec *sc = _mgs_config_server_create(p, &err);
1089    if (NULL == sc)
1090        return err;
1091
1092    gnutls_srvconf_merge(enabled, GNUTLS_ENABLED_UNSET);
1093    gnutls_srvconf_merge(tickets, GNUTLS_ENABLED_UNSET);
1094    gnutls_srvconf_merge(proxy_enabled, GNUTLS_ENABLED_UNSET);
1095    gnutls_srvconf_merge(export_certificates_size, -1);
1096    gnutls_srvconf_merge(client_verify_method, mgs_cvm_unset);
1097    gnutls_srvconf_merge(client_verify_mode, -1);
1098    gnutls_srvconf_merge(srp_tpasswd_file, NULL);
1099    gnutls_srvconf_merge(srp_tpasswd_conf_file, NULL);
1100    gnutls_srvconf_merge(x509_cert_file, NULL);
1101
1102    gnutls_srvconf_merge(x509_key_file, NULL);
1103    gnutls_srvconf_merge(x509_ca_file, NULL);
1104    gnutls_srvconf_merge(p11_modules, NULL);
1105    gnutls_srvconf_merge(pin, NULL);
1106    gnutls_srvconf_merge(pgp_cert_file, NULL);
1107    gnutls_srvconf_merge(pgp_key_file, NULL);
1108    gnutls_srvconf_merge(pgp_ring_file, NULL);
1109    gnutls_srvconf_merge(dh_file, NULL);
1110    gnutls_srvconf_merge(priorities_str, NULL);
1111
1112    gnutls_srvconf_merge(proxy_x509_key_file, NULL);
1113    gnutls_srvconf_merge(proxy_x509_cert_file, NULL);
1114    gnutls_srvconf_merge(proxy_x509_ca_file, NULL);
1115    gnutls_srvconf_merge(proxy_x509_crl_file, NULL);
1116    gnutls_srvconf_merge(proxy_priorities_str, NULL);
1117    gnutls_srvconf_merge(proxy_priorities, NULL);
1118
1119    gnutls_srvconf_assign(ocsp_response_file);
1120    gnutls_srvconf_merge(ocsp_grace_time, apr_time_from_sec(MGS_GRACE_TIME));
1121
1122    /* FIXME: the following items are pre-allocated, and should be
1123     * properly disposed of before assigning in order to avoid leaks;
1124     * so at the moment, we can't actually have them in the config.
1125     * what happens during de-allocation? */
1126    /* TODO: mgs_load_files takes care of most of these now, verify
1127     * and clean up the following lines */
1128    gnutls_srvconf_assign(ca_list);
1129    gnutls_srvconf_assign(ca_list_size);
1130    gnutls_srvconf_assign(cert_pgp);
1131    gnutls_srvconf_assign(cert_crt_pgp);
1132    gnutls_srvconf_assign(pgp_list);
1133    gnutls_srvconf_assign(certs);
1134    gnutls_srvconf_assign(anon_creds);
1135    gnutls_srvconf_assign(srp_creds);
1136    gnutls_srvconf_assign(certs_x509_chain);
1137    gnutls_srvconf_assign(certs_x509_crt_chain);
1138    gnutls_srvconf_assign(certs_x509_chain_num);
1139
1140    /* how do these get transferred cleanly before the data from ADD
1141     * goes away? */
1142    gnutls_srvconf_assign(cert_cn);
1143    for (i = 0; i < MAX_CERT_SAN; i++)
1144        gnutls_srvconf_assign(cert_san[i]);
1145
1146    return sc;
1147}
1148
1149#undef gnutls_srvconf_merge
1150#undef gnutls_srvconf_assign
1151
1152void *mgs_config_dir_merge(apr_pool_t * p,
1153                           void *basev __attribute__((unused)),
1154                           void *addv __attribute__((unused))) {
1155    mgs_dirconf_rec *new;
1156    /*    mgs_dirconf_rec *base = (mgs_dirconf_rec *) basev; */
1157    mgs_dirconf_rec *add = (mgs_dirconf_rec *) addv;
1158
1159    new = (mgs_dirconf_rec *) apr_pcalloc(p, sizeof(mgs_dirconf_rec));
1160    new->client_verify_mode = add->client_verify_mode;
1161    return new;
1162}
1163
1164void *mgs_config_dir_create(apr_pool_t * p,
1165                            char *dir __attribute__((unused))) {
1166    mgs_dirconf_rec *dc = apr_palloc(p, sizeof (*dc));
1167    dc->client_verify_mode = -1;
1168    return dc;
1169}
1170
1171
1172
1173/*
1174 * Store paths to proxy credentials
1175 *
1176 * This function copies the paths provided in the configuration file
1177 * into the server configuration. The post configuration hook takes
1178 * care of actually loading the credentials, which means than invalid
1179 * paths or the like will be detected there.
1180 */
1181const char *mgs_store_cred_path(cmd_parms * parms,
1182                                void *dummy __attribute__((unused)),
1183                                const char *arg)
1184{
1185    mgs_srvconf_rec *sc = (mgs_srvconf_rec *)
1186        ap_get_module_config(parms->server->module_config, &gnutls_module);
1187
1188    /* parms->directive->directive contains the directive string */
1189    if (!strcasecmp(parms->directive->directive, "GnuTLSProxyKeyFile"))
1190        sc->proxy_x509_key_file = apr_pstrdup(parms->pool, arg);
1191    else if (!strcasecmp(parms->directive->directive,
1192                         "GnuTLSProxyCertificateFile"))
1193        sc->proxy_x509_cert_file = apr_pstrdup(parms->pool, arg);
1194    else if (!strcasecmp(parms->directive->directive, "GnuTLSProxyCAFile"))
1195        sc->proxy_x509_ca_file = apr_pstrdup(parms->pool, arg);
1196    else if (!strcasecmp(parms->directive->directive, "GnuTLSProxyCRLFile"))
1197        sc->proxy_x509_crl_file = apr_pstrdup(parms->pool, arg);
1198    return NULL;
1199}
1200
1201
1202
1203/*
1204 * Record PKCS #11 module to load. Note that the value is only used in
1205 * the base config, settings in virtual hosts are ignored.
1206 */
1207const char *mgs_set_p11_module(cmd_parms * parms,
1208                               void *dummy __attribute__((unused)),
1209                               const char *arg)
1210{
1211    mgs_srvconf_rec *sc = (mgs_srvconf_rec *)
1212        ap_get_module_config(parms->server->module_config, &gnutls_module);
1213    /* initialize PKCS #11 module list if necessary */
1214    if (sc->p11_modules == NULL)
1215        sc->p11_modules = apr_array_make(parms->pool, 2, sizeof(char*));
1216
1217    *(char **) apr_array_push(sc->p11_modules) = apr_pstrdup(parms->pool, arg);
1218
1219    return NULL;
1220}
Note: See TracBrowser for help on using the repository browser.