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 #include "mod_session.h"
18 #include "apu_version.h"
19 #include "apr_base64.h" /* for apr_base64_decode et al */
20 #include "apr_lib.h"
21 #include "apr_strings.h"
22 #include "http_log.h"
23 #include "http_core.h"
24
25 #if APU_MAJOR_VERSION == 1 && APU_MINOR_VERSION < 4
26
27 #error session_crypto_module requires APU v1.4.0 or later
28
29 #elif APU_HAVE_CRYPTO == 0
30
31 #error Crypto support must be enabled in APR
32
33 #else
34
35 #include "apr_crypto.h" /* for apr_*_crypt et al */
36
37 #define CRYPTO_KEY "session_crypto_context"
38
39 module AP_MODULE_DECLARE_DATA session_crypto_module;
40
41 /**
42 * Structure to carry the per-dir session config.
43 */
44 typedef struct {
45 apr_array_header_t *passphrases;
46 int passphrases_set;
47 const char *cipher;
48 int cipher_set;
49 } session_crypto_dir_conf;
50
51 /**
52 * Structure to carry the server wide session config.
53 */
54 typedef struct {
55 const char *library;
56 const char *params;
57 int library_set;
58 } session_crypto_conf;
59
60 /**
61 * Initialise the encryption as per the current config.
62 *
63 * Returns APR_SUCCESS if successful.
64 */
crypt_init(request_rec * r,const apr_crypto_t * f,apr_crypto_block_key_type_e ** cipher,session_crypto_dir_conf * dconf)65 static apr_status_t crypt_init(request_rec *r,
66 const apr_crypto_t *f, apr_crypto_block_key_type_e **cipher,
67 session_crypto_dir_conf * dconf)
68 {
69 apr_status_t res;
70 apr_hash_t *ciphers;
71
72 res = apr_crypto_get_block_key_types(&ciphers, f);
73 if (APR_SUCCESS != res) {
74 ap_log_rerror(APLOG_MARK, APLOG_ERR, res, r, APLOGNO(01823)
75 "no ciphers returned by APR. "
76 "session encryption not possible");
77 return res;
78 }
79
80 *cipher = apr_hash_get(ciphers, dconf->cipher, APR_HASH_KEY_STRING);
81 if (!(*cipher)) {
82 apr_hash_index_t *hi;
83 const void *key;
84 apr_ssize_t klen;
85 int sum = 0;
86 int offset = 0;
87 char *options = NULL;
88
89 for (hi = apr_hash_first(r->pool, ciphers); hi; hi = apr_hash_next(hi)) {
90 apr_hash_this(hi, NULL, &klen, NULL);
91 sum += klen + 2;
92 }
93 for (hi = apr_hash_first(r->pool, ciphers); hi; hi = apr_hash_next(hi)) {
94 apr_hash_this(hi, &key, &klen, NULL);
95 if (!options) {
96 options = apr_palloc(r->pool, sum + 1);
97 }
98 else {
99 options[offset++] = ',';
100 options[offset++] = ' ';
101 }
102 strncpy(options + offset, key, klen);
103 offset += klen;
104 }
105 options[offset] = 0;
106
107 ap_log_rerror(APLOG_MARK, APLOG_ERR, res, r, APLOGNO(01824)
108 "cipher '%s' not recognised by crypto driver. "
109 "session encryption not possible, options: %s", dconf->cipher, options);
110
111 return APR_EGENERAL;
112 }
113
114 return APR_SUCCESS;
115 }
116
117 /**
118 * Encrypt the string given as per the current config.
119 *
120 * Returns APR_SUCCESS if successful.
121 */
encrypt_string(request_rec * r,const apr_crypto_t * f,session_crypto_dir_conf * dconf,const char * in,char ** out)122 static apr_status_t encrypt_string(request_rec * r, const apr_crypto_t *f,
123 session_crypto_dir_conf *dconf, const char *in, char **out)
124 {
125 apr_status_t res;
126 apr_crypto_key_t *key = NULL;
127 apr_size_t ivSize = 0;
128 apr_crypto_block_t *block = NULL;
129 unsigned char *encrypt = NULL;
130 unsigned char *combined = NULL;
131 apr_size_t encryptlen, tlen;
132 char *base64;
133 apr_size_t blockSize = 0;
134 const unsigned char *iv = NULL;
135 apr_uuid_t salt;
136 apr_crypto_block_key_type_e *cipher;
137 const char *passphrase;
138
139 /* by default, return an empty string */
140 *out = "";
141
142 /* don't attempt to encrypt an empty string, trying to do so causes a segfault */
143 if (!in || !*in) {
144 return APR_SUCCESS;
145 }
146
147 /* use a uuid as a salt value, and prepend it to our result */
148 apr_uuid_get(&salt);
149 res = crypt_init(r, f, &cipher, dconf);
150 if (res != APR_SUCCESS) {
151 return res;
152 }
153
154 /* encrypt using the first passphrase in the list */
155 passphrase = APR_ARRAY_IDX(dconf->passphrases, 0, char *);
156 res = apr_crypto_passphrase(&key, &ivSize, passphrase,
157 strlen(passphrase),
158 (unsigned char *) (&salt), sizeof(apr_uuid_t),
159 *cipher, APR_MODE_CBC, 1, 4096, f, r->pool);
160 if (APR_STATUS_IS_ENOKEY(res)) {
161 ap_log_rerror(APLOG_MARK, APLOG_ERR, res, r, APLOGNO(01825)
162 "the passphrase '%s' was empty", passphrase);
163 }
164 if (APR_STATUS_IS_EPADDING(res)) {
165 ap_log_rerror(APLOG_MARK, APLOG_ERR, res, r, APLOGNO(01826)
166 "padding is not supported for cipher");
167 }
168 if (APR_STATUS_IS_EKEYTYPE(res)) {
169 ap_log_rerror(APLOG_MARK, APLOG_ERR, res, r, APLOGNO(01827)
170 "the key type is not known");
171 }
172 if (APR_SUCCESS != res) {
173 ap_log_rerror(APLOG_MARK, APLOG_ERR, res, r, APLOGNO(01828)
174 "encryption could not be configured.");
175 return res;
176 }
177
178 res = apr_crypto_block_encrypt_init(&block, &iv, key, &blockSize, r->pool);
179 if (APR_SUCCESS != res) {
180 ap_log_rerror(APLOG_MARK, APLOG_ERR, res, r, APLOGNO(01829)
181 "apr_crypto_block_encrypt_init failed");
182 return res;
183 }
184
185 /* encrypt the given string */
186 res = apr_crypto_block_encrypt(&encrypt, &encryptlen, (unsigned char *)in,
187 strlen(in), block);
188 if (APR_SUCCESS != res) {
189 ap_log_rerror(APLOG_MARK, APLOG_ERR, res, r, APLOGNO(01830)
190 "apr_crypto_block_encrypt failed");
191 return res;
192 }
193 res = apr_crypto_block_encrypt_finish(encrypt + encryptlen, &tlen, block);
194 if (APR_SUCCESS != res) {
195 ap_log_rerror(APLOG_MARK, APLOG_ERR, res, r, APLOGNO(01831)
196 "apr_crypto_block_encrypt_finish failed");
197 return res;
198 }
199 encryptlen += tlen;
200
201 /* prepend the salt and the iv to the result */
202 combined = apr_palloc(r->pool, ivSize + encryptlen + sizeof(apr_uuid_t));
203 memcpy(combined, &salt, sizeof(apr_uuid_t));
204 memcpy(combined + sizeof(apr_uuid_t), iv, ivSize);
205 memcpy(combined + sizeof(apr_uuid_t) + ivSize, encrypt, encryptlen);
206
207 /* base64 encode the result */
208 base64 = apr_palloc(r->pool, apr_base64_encode_len(ivSize + encryptlen +
209 sizeof(apr_uuid_t) + 1)
210 * sizeof(char));
211 apr_base64_encode(base64, (const char *) combined,
212 ivSize + encryptlen + sizeof(apr_uuid_t));
213 *out = base64;
214
215 return res;
216
217 }
218
219 /**
220 * Decrypt the string given as per the current config.
221 *
222 * Returns APR_SUCCESS if successful.
223 */
decrypt_string(request_rec * r,const apr_crypto_t * f,session_crypto_dir_conf * dconf,const char * in,char ** out)224 static apr_status_t decrypt_string(request_rec * r, const apr_crypto_t *f,
225 session_crypto_dir_conf *dconf, const char *in, char **out)
226 {
227 apr_status_t res;
228 apr_crypto_key_t *key = NULL;
229 apr_size_t ivSize = 0;
230 apr_crypto_block_t *block = NULL;
231 unsigned char *decrypted = NULL;
232 apr_size_t decryptedlen, tlen;
233 apr_size_t decodedlen;
234 char *decoded;
235 apr_size_t blockSize = 0;
236 apr_crypto_block_key_type_e *cipher;
237 int i = 0;
238
239 /* strip base64 from the string */
240 decoded = apr_palloc(r->pool, apr_base64_decode_len(in));
241 decodedlen = apr_base64_decode(decoded, in);
242 decoded[decodedlen] = '\0';
243
244 res = crypt_init(r, f, &cipher, dconf);
245 if (res != APR_SUCCESS) {
246 return res;
247 }
248
249 /* try each passphrase in turn */
250 for (; i < dconf->passphrases->nelts; i++) {
251 const char *passphrase = APR_ARRAY_IDX(dconf->passphrases, i, char *);
252 apr_size_t len = decodedlen;
253 char *slider = decoded;
254
255 /* encrypt using the first passphrase in the list */
256 res = apr_crypto_passphrase(&key, &ivSize, passphrase,
257 strlen(passphrase),
258 (unsigned char *)decoded, sizeof(apr_uuid_t),
259 *cipher, APR_MODE_CBC, 1, 4096, f, r->pool);
260 if (APR_STATUS_IS_ENOKEY(res)) {
261 ap_log_rerror(APLOG_MARK, APLOG_DEBUG, res, r, APLOGNO(01832)
262 "the passphrase '%s' was empty", passphrase);
263 continue;
264 }
265 else if (APR_STATUS_IS_EPADDING(res)) {
266 ap_log_rerror(APLOG_MARK, APLOG_DEBUG, res, r, APLOGNO(01833)
267 "padding is not supported for cipher");
268 continue;
269 }
270 else if (APR_STATUS_IS_EKEYTYPE(res)) {
271 ap_log_rerror(APLOG_MARK, APLOG_DEBUG, res, r, APLOGNO(01834)
272 "the key type is not known");
273 continue;
274 }
275 else if (APR_SUCCESS != res) {
276 ap_log_rerror(APLOG_MARK, APLOG_DEBUG, res, r, APLOGNO(01835)
277 "encryption could not be configured.");
278 continue;
279 }
280
281 /* sanity check - decoded too short? */
282 if (decodedlen < (sizeof(apr_uuid_t) + ivSize)) {
283 ap_log_rerror(APLOG_MARK, APLOG_DEBUG, APR_SUCCESS, r, APLOGNO(01836)
284 "too short to decrypt, skipping");
285 res = APR_ECRYPT;
286 continue;
287 }
288
289 /* bypass the salt at the start of the decoded block */
290 slider += sizeof(apr_uuid_t);
291 len -= sizeof(apr_uuid_t);
292
293 res = apr_crypto_block_decrypt_init(&block, &blockSize, (unsigned char *)slider, key,
294 r->pool);
295 if (APR_SUCCESS != res) {
296 ap_log_rerror(APLOG_MARK, APLOG_DEBUG, res, r, APLOGNO(01837)
297 "apr_crypto_block_decrypt_init failed");
298 continue;
299 }
300
301 /* bypass the iv at the start of the decoded block */
302 slider += ivSize;
303 len -= ivSize;
304
305 /* decrypt the given string */
306 res = apr_crypto_block_decrypt(&decrypted, &decryptedlen,
307 (unsigned char *)slider, len, block);
308 if (res) {
309 ap_log_rerror(APLOG_MARK, APLOG_DEBUG, res, r, APLOGNO(01838)
310 "apr_crypto_block_decrypt failed");
311 continue;
312 }
313 *out = (char *) decrypted;
314
315 res = apr_crypto_block_decrypt_finish(decrypted + decryptedlen, &tlen, block);
316 if (APR_SUCCESS != res) {
317 ap_log_rerror(APLOG_MARK, APLOG_DEBUG, res, r, APLOGNO(01839)
318 "apr_crypto_block_decrypt_finish failed");
319 continue;
320 }
321 decryptedlen += tlen;
322 decrypted[decryptedlen] = 0;
323
324 break;
325 }
326
327 if (APR_SUCCESS != res) {
328 ap_log_rerror(APLOG_MARK, APLOG_INFO, res, r, APLOGNO(01840)
329 "decryption failed");
330 }
331
332 return res;
333
334 }
335
336 /**
337 * Crypto encoding for the session.
338 *
339 * @param r The request pointer.
340 * @param z A pointer to where the session will be written.
341 */
session_crypto_encode(request_rec * r,session_rec * z)342 static apr_status_t session_crypto_encode(request_rec * r, session_rec * z)
343 {
344
345 char *encoded = NULL;
346 apr_status_t res;
347 const apr_crypto_t *f = NULL;
348 session_crypto_dir_conf *dconf = ap_get_module_config(r->per_dir_config,
349 &session_crypto_module);
350
351 if (dconf->passphrases_set && z->encoded && *z->encoded) {
352 apr_pool_userdata_get((void **)&f, CRYPTO_KEY, r->server->process->pconf);
353 res = encrypt_string(r, f, dconf, z->encoded, &encoded);
354 if (res != OK) {
355 ap_log_rerror(APLOG_MARK, APLOG_DEBUG, res, r, APLOGNO(01841)
356 "encrypt session failed");
357 return res;
358 }
359 z->encoded = encoded;
360 }
361
362 return OK;
363
364 }
365
366 /**
367 * Crypto decoding for the session.
368 *
369 * @param r The request pointer.
370 * @param z A pointer to where the session will be written.
371 */
session_crypto_decode(request_rec * r,session_rec * z)372 static apr_status_t session_crypto_decode(request_rec * r,
373 session_rec * z)
374 {
375
376 char *encoded = NULL;
377 apr_status_t res;
378 const apr_crypto_t *f = NULL;
379 session_crypto_dir_conf *dconf = ap_get_module_config(r->per_dir_config,
380 &session_crypto_module);
381
382 if ((dconf->passphrases_set) && z->encoded && *z->encoded) {
383 apr_pool_userdata_get((void **)&f, CRYPTO_KEY,
384 r->server->process->pconf);
385 res = decrypt_string(r, f, dconf, z->encoded, &encoded);
386 if (res != APR_SUCCESS) {
387 ap_log_rerror(APLOG_MARK, APLOG_ERR, res, r, APLOGNO(01842)
388 "decrypt session failed, wrong passphrase?");
389 return res;
390 }
391 z->encoded = encoded;
392 }
393
394 return OK;
395
396 }
397
398 /**
399 * Initialise the SSL in the post_config hook.
400 */
session_crypto_init(apr_pool_t * p,apr_pool_t * plog,apr_pool_t * ptemp,server_rec * s)401 static int session_crypto_init(apr_pool_t *p, apr_pool_t *plog,
402 apr_pool_t *ptemp, server_rec *s)
403 {
404 const apr_crypto_driver_t *driver = NULL;
405 apr_crypto_t *f = NULL;
406
407 session_crypto_conf *conf = ap_get_module_config(s->module_config,
408 &session_crypto_module);
409
410 /* session_crypto_init() will be called twice. Don't bother
411 * going through all of the initialization on the first call
412 * because it will just be thrown away.*/
413 if (ap_state_query(AP_SQ_MAIN_STATE) == AP_SQ_MS_CREATE_PRE_CONFIG) {
414 return OK;
415 }
416
417 if (conf->library) {
418
419 const apu_err_t *err = NULL;
420 apr_status_t rv;
421
422 rv = apr_crypto_init(p);
423 if (APR_SUCCESS != rv) {
424 ap_log_error(APLOG_MARK, APLOG_ERR, rv, s, APLOGNO(01843)
425 "APR crypto could not be initialised");
426 return rv;
427 }
428
429 rv = apr_crypto_get_driver(&driver, conf->library, conf->params, &err, p);
430 if (APR_EREINIT == rv) {
431 ap_log_error(APLOG_MARK, APLOG_WARNING, rv, s, APLOGNO(01844)
432 "warning: crypto for '%s' was already initialised, "
433 "using existing configuration", conf->library);
434 rv = APR_SUCCESS;
435 }
436 if (APR_SUCCESS != rv && err) {
437 ap_log_error(APLOG_MARK, APLOG_ERR, rv, s, APLOGNO(01845)
438 "The crypto library '%s' could not be loaded: %s (%s: %d)", conf->library, err->msg, err->reason, err->rc);
439 return rv;
440 }
441 if (APR_ENOTIMPL == rv) {
442 ap_log_error(APLOG_MARK, APLOG_ERR, rv, s, APLOGNO(01846)
443 "The crypto library '%s' could not be found",
444 conf->library);
445 return rv;
446 }
447 if (APR_SUCCESS != rv || !driver) {
448 ap_log_error(APLOG_MARK, APLOG_ERR, rv, s, APLOGNO(01847)
449 "The crypto library '%s' could not be loaded",
450 conf->library);
451 return rv;
452 }
453
454 rv = apr_crypto_make(&f, driver, conf->params, p);
455 if (APR_SUCCESS != rv) {
456 ap_log_error(APLOG_MARK, APLOG_ERR, rv, s, APLOGNO(01848)
457 "The crypto library '%s' could not be initialised",
458 conf->library);
459 return rv;
460 }
461
462 ap_log_error(APLOG_MARK, APLOG_INFO, rv, s, APLOGNO(01849)
463 "The crypto library '%s' was loaded successfully",
464 conf->library);
465
466 apr_pool_userdata_set((const void *)f, CRYPTO_KEY,
467 apr_pool_cleanup_null, s->process->pconf);
468
469 }
470
471 return OK;
472 }
473
create_session_crypto_config(apr_pool_t * p,server_rec * s)474 static void *create_session_crypto_config(apr_pool_t * p, server_rec *s)
475 {
476 session_crypto_conf *new =
477 (session_crypto_conf *) apr_pcalloc(p, sizeof(session_crypto_conf));
478
479 /* if no library has been configured, set the recommended library
480 * as a sensible default.
481 */
482 #ifdef APU_CRYPTO_RECOMMENDED_DRIVER
483 new->library = APU_CRYPTO_RECOMMENDED_DRIVER;
484 #endif
485
486 return (void *) new;
487 }
488
create_session_crypto_dir_config(apr_pool_t * p,char * dummy)489 static void *create_session_crypto_dir_config(apr_pool_t * p, char *dummy)
490 {
491 session_crypto_dir_conf *new =
492 (session_crypto_dir_conf *) apr_pcalloc(p, sizeof(session_crypto_dir_conf));
493
494 new->passphrases = apr_array_make(p, 10, sizeof(char *));
495
496 /* default cipher AES256-SHA */
497 new->cipher = "aes256";
498
499 return (void *) new;
500 }
501
merge_session_crypto_dir_config(apr_pool_t * p,void * basev,void * addv)502 static void *merge_session_crypto_dir_config(apr_pool_t * p, void *basev, void *addv)
503 {
504 session_crypto_dir_conf *new = (session_crypto_dir_conf *) apr_pcalloc(p, sizeof(session_crypto_dir_conf));
505 session_crypto_dir_conf *add = (session_crypto_dir_conf *) addv;
506 session_crypto_dir_conf *base = (session_crypto_dir_conf *) basev;
507
508 new->passphrases = (add->passphrases_set == 0) ? base->passphrases : add->passphrases;
509 new->passphrases_set = add->passphrases_set || base->passphrases_set;
510 new->cipher = (add->cipher_set == 0) ? base->cipher : add->cipher;
511 new->cipher_set = add->cipher_set || base->cipher_set;
512
513 return new;
514 }
515
set_crypto_driver(cmd_parms * cmd,void * config,const char * arg)516 static const char *set_crypto_driver(cmd_parms * cmd, void *config, const char *arg)
517 {
518 session_crypto_conf *conf =
519 (session_crypto_conf *)ap_get_module_config(cmd->server->module_config,
520 &session_crypto_module);
521
522 const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY);
523
524 if (err != NULL) {
525 return err;
526 }
527
528 conf->library = ap_getword_conf(cmd->pool, &arg);
529 conf->params = arg;
530 conf->library_set = 1;
531
532 return NULL;
533 }
534
set_crypto_passphrase(cmd_parms * cmd,void * config,const char * arg)535 static const char *set_crypto_passphrase(cmd_parms * cmd, void *config, const char *arg)
536 {
537 const char **passphrase;
538 session_crypto_dir_conf *dconf = (session_crypto_dir_conf *) config;
539
540 passphrase = apr_array_push(dconf->passphrases);
541 *passphrase = arg;
542 dconf->passphrases_set = 1;
543
544 return NULL;
545 }
546
set_crypto_passphrase_file(cmd_parms * cmd,void * config,const char * filename)547 static const char *set_crypto_passphrase_file(cmd_parms *cmd, void *config,
548 const char *filename)
549 {
550 char buffer[MAX_STRING_LEN];
551 char *arg;
552 const char *args;
553 ap_configfile_t *file;
554 apr_status_t rv;
555
556 filename = ap_server_root_relative(cmd->temp_pool, filename);
557 rv = ap_pcfg_openfile(&file, cmd->temp_pool, filename);
558 if (rv != APR_SUCCESS) {
559 return apr_psprintf(cmd->pool, "%s: Could not open file %s: %pm",
560 cmd->cmd->name, filename, &rv);
561 }
562
563 while (!(ap_cfg_getline(buffer, sizeof(buffer), file))) {
564 args = buffer;
565 while (*(arg = ap_getword_conf(cmd->pool, &args)) != '\0') {
566 if (*arg == '#' || *arg == 0) {
567 break;
568 }
569 set_crypto_passphrase(cmd, config, arg);
570 }
571 }
572
573 ap_cfg_closefile(file);
574
575 return NULL;
576 }
577
set_crypto_cipher(cmd_parms * cmd,void * config,const char * cipher)578 static const char *set_crypto_cipher(cmd_parms * cmd, void *config, const char *cipher)
579 {
580 session_crypto_dir_conf *dconf = (session_crypto_dir_conf *) config;
581
582 dconf->cipher = cipher;
583 dconf->cipher_set = 1;
584
585 return NULL;
586 }
587
588 static const command_rec session_crypto_cmds[] =
589 {
590 AP_INIT_ITERATE("SessionCryptoPassphrase", set_crypto_passphrase, NULL, RSRC_CONF|OR_AUTHCFG,
591 "The passphrase(s) used to encrypt the session. First will be used for encryption, all phrases will be accepted for decryption"),
592 AP_INIT_TAKE1("SessionCryptoPassphraseFile", set_crypto_passphrase_file, NULL, RSRC_CONF|ACCESS_CONF,
593 "File containing passphrase(s) used to encrypt the session, one per line. First will be used for encryption, all phrases will be accepted for decryption"),
594 AP_INIT_TAKE1("SessionCryptoCipher", set_crypto_cipher, NULL, RSRC_CONF|OR_AUTHCFG,
595 "The underlying crypto cipher to use"),
596 AP_INIT_RAW_ARGS("SessionCryptoDriver", set_crypto_driver, NULL, RSRC_CONF,
597 "The underlying crypto library driver to use"),
598 { NULL }
599 };
600
register_hooks(apr_pool_t * p)601 static void register_hooks(apr_pool_t * p)
602 {
603 ap_hook_session_encode(session_crypto_encode, NULL, NULL, APR_HOOK_LAST);
604 ap_hook_session_decode(session_crypto_decode, NULL, NULL, APR_HOOK_FIRST);
605 ap_hook_post_config(session_crypto_init, NULL, NULL, APR_HOOK_LAST);
606 }
607
608 AP_DECLARE_MODULE(session_crypto) =
609 {
610 STANDARD20_MODULE_STUFF,
611 create_session_crypto_dir_config, /* dir config creater */
612 merge_session_crypto_dir_config, /* dir merger --- default is to override */
613 create_session_crypto_config, /* server config */
614 NULL, /* merge server config */
615 session_crypto_cmds, /* command apr_table_t */
616 register_hooks /* register hooks */
617 };
618
619 #endif
620