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