/* Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License.
*/
#include"mod_session.h" #include"apu_version.h" #include"apr_base64.h"/* for apr_base64_decode et al */ #include"apr_lib.h" #include"apr_md5.h" #include"apr_strings.h" #include"http_log.h" #include"http_core.h"
rem = (unsignedint)(len & 0x7); for (ptr = src, end = ptr + len - rem; ptr < end; ptr += 8) {
m = U8TO64_LE(ptr);
v3 ^= m;
SIPROUND();
SIPROUND();
v0 ^= m;
}
m = (apr_uint64_t)(len & 0xff) << 56; switch (rem) { case 7: m |= (apr_uint64_t)ptr[6] << 48; case 6: m |= (apr_uint64_t)ptr[5] << 40; case 5: m |= (apr_uint64_t)ptr[4] << 32; case 4: m |= (apr_uint64_t)ptr[3] << 24; case 3: m |= (apr_uint64_t)ptr[2] << 16; case 2: m |= (apr_uint64_t)ptr[1] << 8; case 1: m |= (apr_uint64_t)ptr[0]; case 0: break;
}
v3 ^= m;
SIPROUND();
SIPROUND();
v0 ^= m;
/* XXX: if we had a way to get the raw bytes from an apr_crypto_key_t * we could use them directly (not available in APR-1.5.x). * MD5 is 128bit too, so use it to get a suitable siphash key * from the passphrase.
*/
apr_md5(key, passphrase, passlen);
ap_siphash24_auth(auth, src, len, key);
}
/** * Initialise the encryption as per the current config. * * Returns APR_SUCCESS if successful.
*/ static apr_status_t crypt_init(request_rec *r, const apr_crypto_t *f, apr_crypto_block_key_type_e **cipher,
session_crypto_dir_conf * dconf)
{
apr_status_t res;
apr_hash_t *ciphers;
res = apr_crypto_get_block_key_types(&ciphers, f); if (APR_SUCCESS != res) {
ap_log_rerror(APLOG_MARK, APLOG_ERR, res, r, APLOGNO(01823) "no ciphers returned by APR. " "session encryption not possible"); return res;
}
*cipher = apr_hash_get(ciphers, dconf->cipher, APR_HASH_KEY_STRING); if (!(*cipher)) {
apr_hash_index_t *hi; constvoid *key;
apr_ssize_t klen; int sum = 0; int offset = 0; char *options = NULL;
for (hi = apr_hash_first(r->pool, ciphers); hi; hi = apr_hash_next(hi)) {
apr_hash_this(hi, NULL, &klen, NULL);
sum += klen + 2;
} for (hi = apr_hash_first(r->pool, ciphers); hi; hi = apr_hash_next(hi)) {
apr_hash_this(hi, &key, &klen, NULL); if (!options) {
options = apr_palloc(r->pool, sum + 1);
} else {
options[offset++] = ',';
options[offset++] = ' ';
}
strncpy(options + offset, key, klen);
offset += klen;
}
options[offset] = 0;
ap_log_rerror(APLOG_MARK, APLOG_ERR, res, r, APLOGNO(01824) "cipher '%s' not recognised by crypto driver. " "session encryption not possible, options: %s", dconf->cipher, options);
/* use a uuid as a salt value, and prepend it to our result */
apr_uuid_get(&salt);
res = crypt_init(r, f, &cipher, dconf); if (res != APR_SUCCESS) { return res;
}
/* encrypt using the first passphrase in the list */
passphrase = APR_ARRAY_IDX(dconf->passphrases, 0, constchar *);
passlen = strlen(passphrase);
res = apr_crypto_passphrase(&key, &ivSize, passphrase, passlen,
(unsignedchar *) (&salt), sizeof(apr_uuid_t),
*cipher, APR_MODE_CBC, 1, 4096, f, r->pool); if (APR_STATUS_IS_ENOKEY(res)) {
ap_log_rerror(APLOG_MARK, APLOG_ERR, res, r, APLOGNO(01825) "failure generating key from passphrase");
} if (APR_STATUS_IS_EPADDING(res)) {
ap_log_rerror(APLOG_MARK, APLOG_ERR, res, r, APLOGNO(01826) "padding is not supported for cipher");
} if (APR_STATUS_IS_EKEYTYPE(res)) {
ap_log_rerror(APLOG_MARK, APLOG_ERR, res, r, APLOGNO(01827) "the key type is not known");
} if (APR_SUCCESS != res) {
ap_log_rerror(APLOG_MARK, APLOG_ERR, res, r, APLOGNO(01828) "encryption could not be configured."); return res;
}
/* encrypt the given string */
res = apr_crypto_block_encrypt(&encrypt, &encryptlen,
(constunsignedchar *)in, strlen(in),
block); if (APR_SUCCESS != res) {
ap_log_rerror(APLOG_MARK, APLOG_ERR, res, r, APLOGNO(01830) "apr_crypto_block_encrypt failed"); return res;
}
res = apr_crypto_block_encrypt_finish(encrypt + encryptlen, &tlen, block); if (APR_SUCCESS != res) {
ap_log_rerror(APLOG_MARK, APLOG_ERR, res, r, APLOGNO(01831) "apr_crypto_block_encrypt_finish failed"); return res;
}
encryptlen += tlen;
/* prepend the salt and the iv to the result (keep room for the MAC) */
combinedlen = AP_SIPHASH_DSIZE + sizeof(apr_uuid_t) + ivSize + encryptlen;
combined = apr_palloc(r->pool, combinedlen);
memcpy(combined + AP_SIPHASH_DSIZE, &salt, sizeof(apr_uuid_t));
memcpy(combined + AP_SIPHASH_DSIZE + sizeof(apr_uuid_t), iv, ivSize);
memcpy(combined + AP_SIPHASH_DSIZE + sizeof(apr_uuid_t) + ivSize,
encrypt, encryptlen); /* authenticate the whole salt+IV+ciphertext with a leading MAC */
compute_auth(combined + AP_SIPHASH_DSIZE, combinedlen - AP_SIPHASH_DSIZE,
passphrase, passlen, combined);
/* base64 encode the result (APR handles the trailing '\0') */
base64 = apr_palloc(r->pool, apr_base64_encode_len(combinedlen));
apr_base64_encode(base64, (constchar *) combined, combinedlen);
*out = base64;
return res;
}
/** * Decrypt the string given as per the current config. * * Returns APR_SUCCESS if successful.
*/ static apr_status_t decrypt_string(request_rec * r, const apr_crypto_t *f,
session_crypto_dir_conf *dconf, constchar *in, char **out)
{
apr_status_t res;
apr_crypto_key_t *key = NULL;
apr_size_t ivSize = 0;
apr_crypto_block_t *block = NULL; unsignedchar *decrypted = NULL;
apr_size_t decryptedlen, tlen;
apr_size_t decodedlen; char *decoded;
apr_size_t blockSize = 0;
apr_crypto_block_key_type_e *cipher; unsignedchar auth[AP_SIPHASH_DSIZE]; int i = 0;
/* strip base64 from the string */
decoded = apr_palloc(r->pool, apr_base64_decode_len(in));
decodedlen = apr_base64_decode(decoded, in);
decoded[decodedlen] = '\0';
/* sanity check - decoded too short? */ if (decodedlen < (AP_SIPHASH_DSIZE + sizeof(apr_uuid_t))) {
ap_log_rerror(APLOG_MARK, APLOG_DEBUG, APR_SUCCESS, r, APLOGNO(10005) "too short to decrypt, aborting"); return APR_ECRYPT;
}
res = crypt_init(r, f, &cipher, dconf); if (res != APR_SUCCESS) { return res;
}
res = APR_ECRYPT; /* in case we exhaust all passphrases */
/* try each passphrase in turn */ for (; i < dconf->passphrases->nelts; i++) { constchar *passphrase = APR_ARRAY_IDX(dconf->passphrases, i, char *);
apr_size_t passlen = strlen(passphrase);
apr_size_t len = decodedlen - AP_SIPHASH_DSIZE; unsignedchar *slider = (unsignedchar *)decoded + AP_SIPHASH_DSIZE;
/* Verify authentication of the whole salt+IV+ciphertext by computing * the MAC and comparing it (timing safe) with the one in the payload.
*/
compute_auth(slider, len, passphrase, passlen, auth); if (!ap_crypto_equals(auth, decoded, AP_SIPHASH_DSIZE)) {
ap_log_rerror(APLOG_MARK, APLOG_DEBUG, res, r, APLOGNO(10006) "auth does not match, skipping"); continue;
}
/* encrypt using the first passphrase in the list */
res = apr_crypto_passphrase(&key, &ivSize, passphrase, passlen,
slider, sizeof(apr_uuid_t),
*cipher, APR_MODE_CBC, 1, 4096,
f, r->pool); if (APR_STATUS_IS_ENOKEY(res)) {
ap_log_rerror(APLOG_MARK, APLOG_DEBUG, res, r, APLOGNO(01832) "failure generating key from passphrase"); continue;
} elseif (APR_STATUS_IS_EPADDING(res)) {
ap_log_rerror(APLOG_MARK, APLOG_DEBUG, res, r, APLOGNO(01833) "padding is not supported for cipher"); continue;
} elseif (APR_STATUS_IS_EKEYTYPE(res)) {
ap_log_rerror(APLOG_MARK, APLOG_DEBUG, res, r, APLOGNO(01834) "the key type is not known"); continue;
} elseif (APR_SUCCESS != res) {
ap_log_rerror(APLOG_MARK, APLOG_DEBUG, res, r, APLOGNO(01835) "encryption could not be configured."); continue;
}
/* sanity check - decoded too short? */ if (len < (sizeof(apr_uuid_t) + ivSize)) {
ap_log_rerror(APLOG_MARK, APLOG_DEBUG, APR_SUCCESS, r, APLOGNO(01836) "too short to decrypt, skipping");
res = APR_ECRYPT; continue;
}
/* bypass the salt at the start of the decoded block */
slider += sizeof(apr_uuid_t);
len -= sizeof(apr_uuid_t);
/** * Crypto encoding for the session. * * @param r The request pointer. * @param z A pointer to where the session will be written.
*/ static apr_status_t session_crypto_encode(request_rec * r, session_rec * z)
{
/** * Crypto decoding for the session. * * @param r The request pointer. * @param z A pointer to where the session will be written.
*/ static apr_status_t session_crypto_decode(request_rec * r,
session_rec * z)
{
/* session_crypto_init() will be called twice. Don't bother * going through all of the initialization on the first call
* because it will just be thrown away.*/ if (ap_state_query(AP_SQ_MAIN_STATE) == AP_SQ_MS_CREATE_PRE_CONFIG) { return OK;
}
if (conf->library) {
const apu_err_t *err = NULL;
apr_status_t rv;
rv = apr_crypto_init(p); if (APR_SUCCESS != rv) {
ap_log_error(APLOG_MARK, APLOG_ERR, rv, s, APLOGNO(01843) "APR crypto could not be initialised"); return rv;
}
rv = apr_crypto_get_driver(&driver, conf->library, conf->params, &err, p); if (APR_EREINIT == rv) {
ap_log_error(APLOG_MARK, APLOG_WARNING, rv, s, APLOGNO(01844) "warning: crypto for '%s' was already initialised, " "using existing configuration", conf->library);
rv = APR_SUCCESS;
} if (APR_SUCCESS != rv && err) {
ap_log_error(APLOG_MARK, APLOG_ERR, rv, s, APLOGNO(01845) "The crypto library '%s' could not be loaded: %s (%s: %d)", conf->library, err->msg, err->reason, err->rc); return rv;
} if (APR_ENOTIMPL == rv) {
ap_log_error(APLOG_MARK, APLOG_ERR, rv, s, APLOGNO(01846) "The crypto library '%s' could not be found",
conf->library); return rv;
} if (APR_SUCCESS != rv || !driver) {
ap_log_error(APLOG_MARK, APLOG_ERR, rv, s, APLOGNO(01847) "The crypto library '%s' could not be loaded",
conf->library); return rv;
}
rv = apr_crypto_make(&f, driver, conf->params, p); if (APR_SUCCESS != rv) {
ap_log_error(APLOG_MARK, APLOG_ERR, rv, s, APLOGNO(01848) "The crypto library '%s' could not be initialised",
conf->library); return rv;
}
ap_log_error(APLOG_MARK, APLOG_INFO, rv, s, APLOGNO(01849) "The crypto library '%s' was loaded successfully",
conf->library);
/* if no library has been configured, set the recommended library * as a sensible default.
*/ #ifdef APU_CRYPTO_RECOMMENDED_DRIVER
new->library = APU_CRYPTO_RECOMMENDED_DRIVER; #endif
staticconst command_rec session_crypto_cmds[] =
{
AP_INIT_ITERATE("SessionCryptoPassphrase", set_crypto_passphrase, NULL, RSRC_CONF|OR_AUTHCFG, "The passphrase(s) used to encrypt the session. First will be used for encryption, all phrases will be accepted for decryption"),
AP_INIT_TAKE1("SessionCryptoPassphraseFile", set_crypto_passphrase_file, NULL, RSRC_CONF|ACCESS_CONF, "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"),
AP_INIT_TAKE1("SessionCryptoCipher", set_crypto_cipher, NULL, RSRC_CONF|OR_AUTHCFG, "The underlying crypto cipher to use"),
AP_INIT_RAW_ARGS("SessionCryptoDriver", set_crypto_driver, NULL, RSRC_CONF, "The underlying crypto library driver to use"),
{ NULL }
};
AP_DECLARE_MODULE(session_crypto) =
{
STANDARD20_MODULE_STUFF,
create_session_crypto_dir_config, /* dir config creater */
merge_session_crypto_dir_config, /* dir merger --- default is to override */
create_session_crypto_config, /* server config */
NULL, /* merge server config */
session_crypto_cmds, /* command apr_table_t */
register_hooks /* register hooks */
};
#endif
¤ Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.0.21Bemerkung:
(vorverarbeitet)
¤
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung ist noch experimentell.