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_md5.h"
22 #include "apr_strings.h"
23 #include "http_log.h"
24 #include "http_core.h"
25
26 #if APU_MAJOR_VERSION == 1 && APU_MINOR_VERSION < 4
27
28 #error session_crypto_module requires APU v1.4.0 or later
29
30 #elif APU_HAVE_CRYPTO == 0
31
32 #error Crypto support must be enabled in APR
33
34 #else
35
36 #include "apr_crypto.h" /* for apr_*_crypt et al */
37
38 #define CRYPTO_KEY "session_crypto_context"
39
40 module AP_MODULE_DECLARE_DATA session_crypto_module;
41
42 /**
43 * Structure to carry the per-dir session config.
44 */
45 typedef struct {
46 apr_array_header_t *passphrases;
47 int passphrases_set;
48 const char *cipher;
49 int cipher_set;
50 } session_crypto_dir_conf;
51
52 /**
53 * Structure to carry the server wide session config.
54 */
55 typedef struct {
56 const char *library;
57 const char *params;
58 int library_set;
59 } session_crypto_conf;
60
61 /* Wrappers around apr_siphash24() and apr_crypto_equals(),
62 * available in APU-1.6/APR-2.0 only.
63 */
64 #if APU_MAJOR_VERSION > 1 || (APU_MAJOR_VERSION == 1 && APU_MINOR_VERSION >= 6)
65
66 #include "apr_siphash.h"
67
68 #define AP_SIPHASH_DSIZE APR_SIPHASH_DSIZE
69 #define AP_SIPHASH_KSIZE APR_SIPHASH_KSIZE
70 #define ap_siphash24_auth apr_siphash24_auth
71
72 #define ap_crypto_equals apr_crypto_equals
73
74 #else
75
76 #define AP_SIPHASH_DSIZE 8
77 #define AP_SIPHASH_KSIZE 16
78
79 #define ROTL64(x, n) (((x) << (n)) | ((x) >> (64 - (n))))
80
81 #define U8TO64_LE(p) \
82 (((apr_uint64_t)((p)[0]) ) | \
83 ((apr_uint64_t)((p)[1]) << 8) | \
84 ((apr_uint64_t)((p)[2]) << 16) | \
85 ((apr_uint64_t)((p)[3]) << 24) | \
86 ((apr_uint64_t)((p)[4]) << 32) | \
87 ((apr_uint64_t)((p)[5]) << 40) | \
88 ((apr_uint64_t)((p)[6]) << 48) | \
89 ((apr_uint64_t)((p)[7]) << 56))
90
91 #define U64TO8_LE(p, v) \
92 do { \
93 (p)[0] = (unsigned char)((v) ); \
94 (p)[1] = (unsigned char)((v) >> 8); \
95 (p)[2] = (unsigned char)((v) >> 16); \
96 (p)[3] = (unsigned char)((v) >> 24); \
97 (p)[4] = (unsigned char)((v) >> 32); \
98 (p)[5] = (unsigned char)((v) >> 40); \
99 (p)[6] = (unsigned char)((v) >> 48); \
100 (p)[7] = (unsigned char)((v) >> 56); \
101 } while (0)
102
103 #define SIPROUND() \
104 do { \
105 v0 += v1; v1=ROTL64(v1,13); v1 ^= v0; v0=ROTL64(v0,32); \
106 v2 += v3; v3=ROTL64(v3,16); v3 ^= v2; \
107 v0 += v3; v3=ROTL64(v3,21); v3 ^= v0; \
108 v2 += v1; v1=ROTL64(v1,17); v1 ^= v2; v2=ROTL64(v2,32); \
109 } while(0)
110
ap_siphash24(const void * src,apr_size_t len,const unsigned char key[AP_SIPHASH_KSIZE])111 static apr_uint64_t ap_siphash24(const void *src, apr_size_t len,
112 const unsigned char key[AP_SIPHASH_KSIZE])
113 {
114 const unsigned char *ptr, *end;
115 apr_uint64_t v0, v1, v2, v3, m;
116 apr_uint64_t k0, k1;
117 unsigned int rem;
118
119 k0 = U8TO64_LE(key + 0);
120 k1 = U8TO64_LE(key + 8);
121 v3 = k1 ^ (apr_uint64_t)0x7465646279746573ULL;
122 v2 = k0 ^ (apr_uint64_t)0x6c7967656e657261ULL;
123 v1 = k1 ^ (apr_uint64_t)0x646f72616e646f6dULL;
124 v0 = k0 ^ (apr_uint64_t)0x736f6d6570736575ULL;
125
126 rem = (unsigned int)(len & 0x7);
127 for (ptr = src, end = ptr + len - rem; ptr < end; ptr += 8) {
128 m = U8TO64_LE(ptr);
129 v3 ^= m;
130 SIPROUND();
131 SIPROUND();
132 v0 ^= m;
133 }
134 m = (apr_uint64_t)(len & 0xff) << 56;
135 switch (rem) {
136 case 7: m |= (apr_uint64_t)ptr[6] << 48;
137 case 6: m |= (apr_uint64_t)ptr[5] << 40;
138 case 5: m |= (apr_uint64_t)ptr[4] << 32;
139 case 4: m |= (apr_uint64_t)ptr[3] << 24;
140 case 3: m |= (apr_uint64_t)ptr[2] << 16;
141 case 2: m |= (apr_uint64_t)ptr[1] << 8;
142 case 1: m |= (apr_uint64_t)ptr[0];
143 case 0: break;
144 }
145 v3 ^= m;
146 SIPROUND();
147 SIPROUND();
148 v0 ^= m;
149
150 v2 ^= 0xff;
151 SIPROUND();
152 SIPROUND();
153 SIPROUND();
154 SIPROUND();
155
156 return v0 ^ v1 ^ v2 ^ v3;
157 }
158
ap_siphash24_auth(unsigned char out[AP_SIPHASH_DSIZE],const void * src,apr_size_t len,const unsigned char key[AP_SIPHASH_KSIZE])159 static void ap_siphash24_auth(unsigned char out[AP_SIPHASH_DSIZE],
160 const void *src, apr_size_t len,
161 const unsigned char key[AP_SIPHASH_KSIZE])
162 {
163 apr_uint64_t h;
164 h = ap_siphash24(src, len, key);
165 U64TO8_LE(out, h);
166 }
167
ap_crypto_equals(const void * buf1,const void * buf2,apr_size_t size)168 static int ap_crypto_equals(const void *buf1, const void *buf2,
169 apr_size_t size)
170 {
171 const unsigned char *p1 = buf1;
172 const unsigned char *p2 = buf2;
173 unsigned char diff = 0;
174 apr_size_t i;
175
176 for (i = 0; i < size; ++i) {
177 diff |= p1[i] ^ p2[i];
178 }
179
180 return 1 & ((diff - 1) >> 8);
181 }
182
183 #endif
184
compute_auth(const void * src,apr_size_t len,const char * passphrase,apr_size_t passlen,unsigned char auth[AP_SIPHASH_DSIZE])185 static void compute_auth(const void *src, apr_size_t len,
186 const char *passphrase, apr_size_t passlen,
187 unsigned char auth[AP_SIPHASH_DSIZE])
188 {
189 unsigned char key[APR_MD5_DIGESTSIZE];
190
191 /* XXX: if we had a way to get the raw bytes from an apr_crypto_key_t
192 * we could use them directly (not available in APR-1.5.x).
193 * MD5 is 128bit too, so use it to get a suitable siphash key
194 * from the passphrase.
195 */
196 apr_md5(key, passphrase, passlen);
197
198 ap_siphash24_auth(auth, src, len, key);
199 }
200
201 /**
202 * Initialise the encryption as per the current config.
203 *
204 * Returns APR_SUCCESS if successful.
205 */
crypt_init(request_rec * r,const apr_crypto_t * f,apr_crypto_block_key_type_e ** cipher,session_crypto_dir_conf * dconf)206 static apr_status_t crypt_init(request_rec *r,
207 const apr_crypto_t *f, apr_crypto_block_key_type_e **cipher,
208 session_crypto_dir_conf * dconf)
209 {
210 apr_status_t res;
211 apr_hash_t *ciphers;
212
213 res = apr_crypto_get_block_key_types(&ciphers, f);
214 if (APR_SUCCESS != res) {
215 ap_log_rerror(APLOG_MARK, APLOG_ERR, res, r, APLOGNO(01823)
216 "no ciphers returned by APR. "
217 "session encryption not possible");
218 return res;
219 }
220
221 *cipher = apr_hash_get(ciphers, dconf->cipher, APR_HASH_KEY_STRING);
222 if (!(*cipher)) {
223 apr_hash_index_t *hi;
224 const void *key;
225 apr_ssize_t klen;
226 int sum = 0;
227 int offset = 0;
228 char *options = NULL;
229
230 for (hi = apr_hash_first(r->pool, ciphers); hi; hi = apr_hash_next(hi)) {
231 apr_hash_this(hi, NULL, &klen, NULL);
232 sum += klen + 2;
233 }
234 for (hi = apr_hash_first(r->pool, ciphers); hi; hi = apr_hash_next(hi)) {
235 apr_hash_this(hi, &key, &klen, NULL);
236 if (!options) {
237 options = apr_palloc(r->pool, sum + 1);
238 }
239 else {
240 options[offset++] = ',';
241 options[offset++] = ' ';
242 }
243 strncpy(options + offset, key, klen);
244 offset += klen;
245 }
246 options[offset] = 0;
247
248 ap_log_rerror(APLOG_MARK, APLOG_ERR, res, r, APLOGNO(01824)
249 "cipher '%s' not recognised by crypto driver. "
250 "session encryption not possible, options: %s", dconf->cipher, options);
251
252 return APR_EGENERAL;
253 }
254
255 return APR_SUCCESS;
256 }
257
258 /**
259 * Encrypt the string given as per the current config.
260 *
261 * Returns APR_SUCCESS if successful.
262 */
encrypt_string(request_rec * r,const apr_crypto_t * f,session_crypto_dir_conf * dconf,const char * in,char ** out)263 static apr_status_t encrypt_string(request_rec * r, const apr_crypto_t *f,
264 session_crypto_dir_conf *dconf, const char *in, char **out)
265 {
266 apr_status_t res;
267 apr_crypto_key_t *key = NULL;
268 apr_size_t ivSize = 0;
269 apr_crypto_block_t *block = NULL;
270 unsigned char *encrypt = NULL;
271 unsigned char *combined = NULL;
272 apr_size_t encryptlen, tlen, combinedlen;
273 char *base64;
274 apr_size_t blockSize = 0;
275 const unsigned char *iv = NULL;
276 apr_uuid_t salt;
277 apr_crypto_block_key_type_e *cipher;
278 const char *passphrase;
279 apr_size_t passlen;
280
281 /* use a uuid as a salt value, and prepend it to our result */
282 apr_uuid_get(&salt);
283 res = crypt_init(r, f, &cipher, dconf);
284 if (res != APR_SUCCESS) {
285 return res;
286 }
287
288 /* encrypt using the first passphrase in the list */
289 passphrase = APR_ARRAY_IDX(dconf->passphrases, 0, const char *);
290 passlen = strlen(passphrase);
291 res = apr_crypto_passphrase(&key, &ivSize, passphrase, passlen,
292 (unsigned char *) (&salt), sizeof(apr_uuid_t),
293 *cipher, APR_MODE_CBC, 1, 4096, f, r->pool);
294 if (APR_STATUS_IS_ENOKEY(res)) {
295 ap_log_rerror(APLOG_MARK, APLOG_ERR, res, r, APLOGNO(01825)
296 "failure generating key from passphrase");
297 }
298 if (APR_STATUS_IS_EPADDING(res)) {
299 ap_log_rerror(APLOG_MARK, APLOG_ERR, res, r, APLOGNO(01826)
300 "padding is not supported for cipher");
301 }
302 if (APR_STATUS_IS_EKEYTYPE(res)) {
303 ap_log_rerror(APLOG_MARK, APLOG_ERR, res, r, APLOGNO(01827)
304 "the key type is not known");
305 }
306 if (APR_SUCCESS != res) {
307 ap_log_rerror(APLOG_MARK, APLOG_ERR, res, r, APLOGNO(01828)
308 "encryption could not be configured.");
309 return res;
310 }
311
312 res = apr_crypto_block_encrypt_init(&block, &iv, key, &blockSize, r->pool);
313 if (APR_SUCCESS != res) {
314 ap_log_rerror(APLOG_MARK, APLOG_ERR, res, r, APLOGNO(01829)
315 "apr_crypto_block_encrypt_init failed");
316 return res;
317 }
318
319 /* encrypt the given string */
320 res = apr_crypto_block_encrypt(&encrypt, &encryptlen,
321 (const unsigned char *)in, strlen(in),
322 block);
323 if (APR_SUCCESS != res) {
324 ap_log_rerror(APLOG_MARK, APLOG_ERR, res, r, APLOGNO(01830)
325 "apr_crypto_block_encrypt failed");
326 return res;
327 }
328 res = apr_crypto_block_encrypt_finish(encrypt + encryptlen, &tlen, block);
329 if (APR_SUCCESS != res) {
330 ap_log_rerror(APLOG_MARK, APLOG_ERR, res, r, APLOGNO(01831)
331 "apr_crypto_block_encrypt_finish failed");
332 return res;
333 }
334 encryptlen += tlen;
335
336 /* prepend the salt and the iv to the result (keep room for the MAC) */
337 combinedlen = AP_SIPHASH_DSIZE + sizeof(apr_uuid_t) + ivSize + encryptlen;
338 combined = apr_palloc(r->pool, combinedlen);
339 memcpy(combined + AP_SIPHASH_DSIZE, &salt, sizeof(apr_uuid_t));
340 memcpy(combined + AP_SIPHASH_DSIZE + sizeof(apr_uuid_t), iv, ivSize);
341 memcpy(combined + AP_SIPHASH_DSIZE + sizeof(apr_uuid_t) + ivSize,
342 encrypt, encryptlen);
343 /* authenticate the whole salt+IV+ciphertext with a leading MAC */
344 compute_auth(combined + AP_SIPHASH_DSIZE, combinedlen - AP_SIPHASH_DSIZE,
345 passphrase, passlen, combined);
346
347 /* base64 encode the result (APR handles the trailing '\0') */
348 base64 = apr_palloc(r->pool, apr_base64_encode_len(combinedlen));
349 apr_base64_encode(base64, (const char *) combined, combinedlen);
350 *out = base64;
351
352 return res;
353
354 }
355
356 /**
357 * Decrypt the string given as per the current config.
358 *
359 * Returns APR_SUCCESS if successful.
360 */
decrypt_string(request_rec * r,const apr_crypto_t * f,session_crypto_dir_conf * dconf,const char * in,char ** out)361 static apr_status_t decrypt_string(request_rec * r, const apr_crypto_t *f,
362 session_crypto_dir_conf *dconf, const char *in, char **out)
363 {
364 apr_status_t res;
365 apr_crypto_key_t *key = NULL;
366 apr_size_t ivSize = 0;
367 apr_crypto_block_t *block = NULL;
368 unsigned char *decrypted = NULL;
369 apr_size_t decryptedlen, tlen;
370 apr_size_t decodedlen;
371 char *decoded;
372 apr_size_t blockSize = 0;
373 apr_crypto_block_key_type_e *cipher;
374 unsigned char auth[AP_SIPHASH_DSIZE];
375 int i = 0;
376
377 /* strip base64 from the string */
378 decoded = apr_palloc(r->pool, apr_base64_decode_len(in));
379 decodedlen = apr_base64_decode(decoded, in);
380 decoded[decodedlen] = '\0';
381
382 /* sanity check - decoded too short? */
383 if (decodedlen < (AP_SIPHASH_DSIZE + sizeof(apr_uuid_t))) {
384 ap_log_rerror(APLOG_MARK, APLOG_DEBUG, APR_SUCCESS, r, APLOGNO(10005)
385 "too short to decrypt, aborting");
386 return APR_ECRYPT;
387 }
388
389 res = crypt_init(r, f, &cipher, dconf);
390 if (res != APR_SUCCESS) {
391 return res;
392 }
393
394 res = APR_ECRYPT; /* in case we exhaust all passphrases */
395
396 /* try each passphrase in turn */
397 for (; i < dconf->passphrases->nelts; i++) {
398 const char *passphrase = APR_ARRAY_IDX(dconf->passphrases, i, char *);
399 apr_size_t passlen = strlen(passphrase);
400 apr_size_t len = decodedlen - AP_SIPHASH_DSIZE;
401 unsigned char *slider = (unsigned char *)decoded + AP_SIPHASH_DSIZE;
402
403 /* Verify authentication of the whole salt+IV+ciphertext by computing
404 * the MAC and comparing it (timing safe) with the one in the payload.
405 */
406 compute_auth(slider, len, passphrase, passlen, auth);
407 if (!ap_crypto_equals(auth, decoded, AP_SIPHASH_DSIZE)) {
408 ap_log_rerror(APLOG_MARK, APLOG_DEBUG, res, r, APLOGNO(10006)
409 "auth does not match, skipping");
410 continue;
411 }
412
413 /* encrypt using the first passphrase in the list */
414 res = apr_crypto_passphrase(&key, &ivSize, passphrase, passlen,
415 slider, sizeof(apr_uuid_t),
416 *cipher, APR_MODE_CBC, 1, 4096,
417 f, r->pool);
418 if (APR_STATUS_IS_ENOKEY(res)) {
419 ap_log_rerror(APLOG_MARK, APLOG_DEBUG, res, r, APLOGNO(01832)
420 "failure generating key from passphrase");
421 continue;
422 }
423 else if (APR_STATUS_IS_EPADDING(res)) {
424 ap_log_rerror(APLOG_MARK, APLOG_DEBUG, res, r, APLOGNO(01833)
425 "padding is not supported for cipher");
426 continue;
427 }
428 else if (APR_STATUS_IS_EKEYTYPE(res)) {
429 ap_log_rerror(APLOG_MARK, APLOG_DEBUG, res, r, APLOGNO(01834)
430 "the key type is not known");
431 continue;
432 }
433 else if (APR_SUCCESS != res) {
434 ap_log_rerror(APLOG_MARK, APLOG_DEBUG, res, r, APLOGNO(01835)
435 "encryption could not be configured.");
436 continue;
437 }
438
439 /* sanity check - decoded too short? */
440 if (len < (sizeof(apr_uuid_t) + ivSize)) {
441 ap_log_rerror(APLOG_MARK, APLOG_DEBUG, APR_SUCCESS, r, APLOGNO(01836)
442 "too short to decrypt, skipping");
443 res = APR_ECRYPT;
444 continue;
445 }
446
447 /* bypass the salt at the start of the decoded block */
448 slider += sizeof(apr_uuid_t);
449 len -= sizeof(apr_uuid_t);
450
451 res = apr_crypto_block_decrypt_init(&block, &blockSize, slider, key,
452 r->pool);
453 if (APR_SUCCESS != res) {
454 ap_log_rerror(APLOG_MARK, APLOG_DEBUG, res, r, APLOGNO(01837)
455 "apr_crypto_block_decrypt_init failed");
456 continue;
457 }
458
459 /* bypass the iv at the start of the decoded block */
460 slider += ivSize;
461 len -= ivSize;
462
463 /* decrypt the given string */
464 res = apr_crypto_block_decrypt(&decrypted, &decryptedlen,
465 slider, len, block);
466 if (res) {
467 ap_log_rerror(APLOG_MARK, APLOG_DEBUG, res, r, APLOGNO(01838)
468 "apr_crypto_block_decrypt failed");
469 continue;
470 }
471 *out = (char *) decrypted;
472
473 res = apr_crypto_block_decrypt_finish(decrypted + decryptedlen, &tlen, block);
474 if (APR_SUCCESS != res) {
475 ap_log_rerror(APLOG_MARK, APLOG_DEBUG, res, r, APLOGNO(01839)
476 "apr_crypto_block_decrypt_finish failed");
477 continue;
478 }
479 decryptedlen += tlen;
480 decrypted[decryptedlen] = 0;
481
482 break;
483 }
484
485 if (APR_SUCCESS != res) {
486 ap_log_rerror(APLOG_MARK, APLOG_INFO, res, r, APLOGNO(01840)
487 "decryption failed");
488 }
489
490 return res;
491
492 }
493
494 /**
495 * Crypto encoding for the session.
496 *
497 * @param r The request pointer.
498 * @param z A pointer to where the session will be written.
499 */
session_crypto_encode(request_rec * r,session_rec * z)500 static apr_status_t session_crypto_encode(request_rec * r, session_rec * z)
501 {
502
503 char *encoded = NULL;
504 apr_status_t res;
505 const apr_crypto_t *f = NULL;
506 session_crypto_dir_conf *dconf = ap_get_module_config(r->per_dir_config,
507 &session_crypto_module);
508
509 if (dconf->passphrases_set && z->encoded && *z->encoded) {
510 apr_pool_userdata_get((void **)&f, CRYPTO_KEY, r->server->process->pconf);
511 res = encrypt_string(r, f, dconf, z->encoded, &encoded);
512 if (res != OK) {
513 ap_log_rerror(APLOG_MARK, APLOG_DEBUG, res, r, APLOGNO(01841)
514 "encrypt session failed");
515 return res;
516 }
517 z->encoded = encoded;
518 }
519
520 return OK;
521
522 }
523
524 /**
525 * Crypto decoding for the session.
526 *
527 * @param r The request pointer.
528 * @param z A pointer to where the session will be written.
529 */
session_crypto_decode(request_rec * r,session_rec * z)530 static apr_status_t session_crypto_decode(request_rec * r,
531 session_rec * z)
532 {
533
534 char *encoded = NULL;
535 apr_status_t res;
536 const apr_crypto_t *f = NULL;
537 session_crypto_dir_conf *dconf = ap_get_module_config(r->per_dir_config,
538 &session_crypto_module);
539
540 if ((dconf->passphrases_set) && z->encoded && *z->encoded) {
541 apr_pool_userdata_get((void **)&f, CRYPTO_KEY,
542 r->server->process->pconf);
543 res = decrypt_string(r, f, dconf, z->encoded, &encoded);
544 if (res != APR_SUCCESS) {
545 ap_log_rerror(APLOG_MARK, APLOG_ERR, res, r, APLOGNO(01842)
546 "decrypt session failed, wrong passphrase?");
547 return res;
548 }
549 z->encoded = encoded;
550 }
551
552 return OK;
553
554 }
555
556 /**
557 * Initialise the SSL in the post_config hook.
558 */
session_crypto_init(apr_pool_t * p,apr_pool_t * plog,apr_pool_t * ptemp,server_rec * s)559 static int session_crypto_init(apr_pool_t *p, apr_pool_t *plog,
560 apr_pool_t *ptemp, server_rec *s)
561 {
562 const apr_crypto_driver_t *driver = NULL;
563 apr_crypto_t *f = NULL;
564
565 session_crypto_conf *conf = ap_get_module_config(s->module_config,
566 &session_crypto_module);
567
568 /* session_crypto_init() will be called twice. Don't bother
569 * going through all of the initialization on the first call
570 * because it will just be thrown away.*/
571 if (ap_state_query(AP_SQ_MAIN_STATE) == AP_SQ_MS_CREATE_PRE_CONFIG) {
572 return OK;
573 }
574
575 if (conf->library) {
576
577 const apu_err_t *err = NULL;
578 apr_status_t rv;
579
580 rv = apr_crypto_init(p);
581 if (APR_SUCCESS != rv) {
582 ap_log_error(APLOG_MARK, APLOG_ERR, rv, s, APLOGNO(01843)
583 "APR crypto could not be initialised");
584 return rv;
585 }
586
587 rv = apr_crypto_get_driver(&driver, conf->library, conf->params, &err, p);
588 if (APR_EREINIT == rv) {
589 ap_log_error(APLOG_MARK, APLOG_WARNING, rv, s, APLOGNO(01844)
590 "warning: crypto for '%s' was already initialised, "
591 "using existing configuration", conf->library);
592 rv = APR_SUCCESS;
593 }
594 if (APR_SUCCESS != rv && err) {
595 ap_log_error(APLOG_MARK, APLOG_ERR, rv, s, APLOGNO(01845)
596 "The crypto library '%s' could not be loaded: %s (%s: %d)", conf->library, err->msg, err->reason, err->rc);
597 return rv;
598 }
599 if (APR_ENOTIMPL == rv) {
600 ap_log_error(APLOG_MARK, APLOG_ERR, rv, s, APLOGNO(01846)
601 "The crypto library '%s' could not be found",
602 conf->library);
603 return rv;
604 }
605 if (APR_SUCCESS != rv || !driver) {
606 ap_log_error(APLOG_MARK, APLOG_ERR, rv, s, APLOGNO(01847)
607 "The crypto library '%s' could not be loaded",
608 conf->library);
609 return rv;
610 }
611
612 rv = apr_crypto_make(&f, driver, conf->params, p);
613 if (APR_SUCCESS != rv) {
614 ap_log_error(APLOG_MARK, APLOG_ERR, rv, s, APLOGNO(01848)
615 "The crypto library '%s' could not be initialised",
616 conf->library);
617 return rv;
618 }
619
620 ap_log_error(APLOG_MARK, APLOG_INFO, rv, s, APLOGNO(01849)
621 "The crypto library '%s' was loaded successfully",
622 conf->library);
623
624 apr_pool_userdata_set((const void *)f, CRYPTO_KEY,
625 apr_pool_cleanup_null, s->process->pconf);
626
627 }
628
629 return OK;
630 }
631
create_session_crypto_config(apr_pool_t * p,server_rec * s)632 static void *create_session_crypto_config(apr_pool_t * p, server_rec *s)
633 {
634 session_crypto_conf *new =
635 (session_crypto_conf *) apr_pcalloc(p, sizeof(session_crypto_conf));
636
637 /* if no library has been configured, set the recommended library
638 * as a sensible default.
639 */
640 #ifdef APU_CRYPTO_RECOMMENDED_DRIVER
641 new->library = APU_CRYPTO_RECOMMENDED_DRIVER;
642 #endif
643
644 return (void *) new;
645 }
646
create_session_crypto_dir_config(apr_pool_t * p,char * dummy)647 static void *create_session_crypto_dir_config(apr_pool_t * p, char *dummy)
648 {
649 session_crypto_dir_conf *new =
650 (session_crypto_dir_conf *) apr_pcalloc(p, sizeof(session_crypto_dir_conf));
651
652 new->passphrases = apr_array_make(p, 10, sizeof(char *));
653
654 /* default cipher AES256-SHA */
655 new->cipher = "aes256";
656
657 return (void *) new;
658 }
659
merge_session_crypto_dir_config(apr_pool_t * p,void * basev,void * addv)660 static void *merge_session_crypto_dir_config(apr_pool_t * p, void *basev, void *addv)
661 {
662 session_crypto_dir_conf *new = (session_crypto_dir_conf *) apr_pcalloc(p, sizeof(session_crypto_dir_conf));
663 session_crypto_dir_conf *add = (session_crypto_dir_conf *) addv;
664 session_crypto_dir_conf *base = (session_crypto_dir_conf *) basev;
665
666 new->passphrases = (add->passphrases_set == 0) ? base->passphrases : add->passphrases;
667 new->passphrases_set = add->passphrases_set || base->passphrases_set;
668 new->cipher = (add->cipher_set == 0) ? base->cipher : add->cipher;
669 new->cipher_set = add->cipher_set || base->cipher_set;
670
671 return new;
672 }
673
set_crypto_driver(cmd_parms * cmd,void * config,const char * arg)674 static const char *set_crypto_driver(cmd_parms * cmd, void *config, const char *arg)
675 {
676 session_crypto_conf *conf =
677 (session_crypto_conf *)ap_get_module_config(cmd->server->module_config,
678 &session_crypto_module);
679
680 const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY);
681
682 if (err != NULL) {
683 return err;
684 }
685
686 conf->library = ap_getword_conf(cmd->pool, &arg);
687 conf->params = arg;
688 conf->library_set = 1;
689
690 return NULL;
691 }
692
set_crypto_passphrase(cmd_parms * cmd,void * config,const char * arg)693 static const char *set_crypto_passphrase(cmd_parms * cmd, void *config, const char *arg)
694 {
695 int arglen = strlen(arg);
696 char **argv;
697 char *result;
698 const char **passphrase;
699 session_crypto_dir_conf *dconf = (session_crypto_dir_conf *) config;
700
701 passphrase = apr_array_push(dconf->passphrases);
702
703 if ((arglen > 5) && strncmp(arg, "exec:", 5) == 0) {
704 if (apr_tokenize_to_argv(arg+5, &argv, cmd->temp_pool) != APR_SUCCESS) {
705 return apr_pstrcat(cmd->pool,
706 "Unable to parse exec arguments from ",
707 arg+5, NULL);
708 }
709 argv[0] = ap_server_root_relative(cmd->temp_pool, argv[0]);
710
711 if (!argv[0]) {
712 return apr_pstrcat(cmd->pool,
713 "Invalid SessionCryptoPassphrase exec location:",
714 arg+5, NULL);
715 }
716 result = ap_get_exec_line(cmd->pool,
717 (const char*)argv[0], (const char * const *)argv);
718
719 if(!result) {
720 return apr_pstrcat(cmd->pool,
721 "Unable to get bind password from exec of ",
722 arg+5, NULL);
723 }
724 *passphrase = result;
725 }
726 else {
727 *passphrase = arg;
728 }
729
730 dconf->passphrases_set = 1;
731
732 return NULL;
733 }
734
set_crypto_passphrase_file(cmd_parms * cmd,void * config,const char * filename)735 static const char *set_crypto_passphrase_file(cmd_parms *cmd, void *config,
736 const char *filename)
737 {
738 char buffer[MAX_STRING_LEN];
739 char *arg;
740 const char *args;
741 ap_configfile_t *file;
742 apr_status_t rv;
743
744 filename = ap_server_root_relative(cmd->temp_pool, filename);
745 rv = ap_pcfg_openfile(&file, cmd->temp_pool, filename);
746 if (rv != APR_SUCCESS) {
747 return apr_psprintf(cmd->pool, "%s: Could not open file %s: %pm",
748 cmd->cmd->name, filename, &rv);
749 }
750
751 while (!(ap_cfg_getline(buffer, sizeof(buffer), file))) {
752 args = buffer;
753 while (*(arg = ap_getword_conf(cmd->pool, &args)) != '\0') {
754 if (*arg == '#') {
755 break;
756 }
757 set_crypto_passphrase(cmd, config, arg);
758 }
759 }
760
761 ap_cfg_closefile(file);
762
763 return NULL;
764 }
765
set_crypto_cipher(cmd_parms * cmd,void * config,const char * cipher)766 static const char *set_crypto_cipher(cmd_parms * cmd, void *config, const char *cipher)
767 {
768 session_crypto_dir_conf *dconf = (session_crypto_dir_conf *) config;
769
770 dconf->cipher = cipher;
771 dconf->cipher_set = 1;
772
773 return NULL;
774 }
775
776 static const command_rec session_crypto_cmds[] =
777 {
778 AP_INIT_ITERATE("SessionCryptoPassphrase", set_crypto_passphrase, NULL, RSRC_CONF|OR_AUTHCFG,
779 "The passphrase(s) used to encrypt the session. First will be used for encryption, all phrases will be accepted for decryption"),
780 AP_INIT_TAKE1("SessionCryptoPassphraseFile", set_crypto_passphrase_file, NULL, RSRC_CONF|ACCESS_CONF,
781 "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"),
782 AP_INIT_TAKE1("SessionCryptoCipher", set_crypto_cipher, NULL, RSRC_CONF|OR_AUTHCFG,
783 "The underlying crypto cipher to use"),
784 AP_INIT_RAW_ARGS("SessionCryptoDriver", set_crypto_driver, NULL, RSRC_CONF,
785 "The underlying crypto library driver to use"),
786 { NULL }
787 };
788
register_hooks(apr_pool_t * p)789 static void register_hooks(apr_pool_t * p)
790 {
791 ap_hook_session_encode(session_crypto_encode, NULL, NULL, APR_HOOK_LAST);
792 ap_hook_session_decode(session_crypto_decode, NULL, NULL, APR_HOOK_FIRST);
793 ap_hook_post_config(session_crypto_init, NULL, NULL, APR_HOOK_LAST);
794 }
795
796 AP_DECLARE_MODULE(session_crypto) =
797 {
798 STANDARD20_MODULE_STUFF,
799 create_session_crypto_dir_config, /* dir config creater */
800 merge_session_crypto_dir_config, /* dir merger --- default is to override */
801 create_session_crypto_config, /* server config */
802 NULL, /* merge server config */
803 session_crypto_cmds, /* command apr_table_t */
804 register_hooks /* register hooks */
805 };
806
807 #endif
808