diff options
-rw-r--r-- | src/libradius/radius_message.c | 62 | ||||
-rw-r--r-- | src/libradius/radius_message.h | 16 | ||||
-rw-r--r-- | src/libradius/radius_socket.c | 51 |
3 files changed, 85 insertions, 44 deletions
diff --git a/src/libradius/radius_message.c b/src/libradius/radius_message.c index e7717ff7a..dd3993704 100644 --- a/src/libradius/radius_message.c +++ b/src/libradius/radius_message.c @@ -366,6 +366,67 @@ METHOD(radius_message_t, add, void, this->msg->length = htons(ntohs(this->msg->length) + attribute->length); } +METHOD(radius_message_t, crypt, bool, + private_radius_message_t *this, chunk_t salt, chunk_t in, chunk_t out, + chunk_t secret, hasher_t *hasher) +{ + char b[HASH_SIZE_MD5]; + + /** + * From RFC2548 (encryption): + * b(1) = MD5(S + R + A) c(1) = p(1) xor b(1) C = c(1) + * b(2) = MD5(S + c(1)) c(2) = p(2) xor b(2) C = C + c(2) + * . . . + * b(i) = MD5(S + c(i-1)) c(i) = p(i) xor b(i) C = C + c(i) + * + * P/C = Plain/Crypted => in/out + * S = secret + * R = authenticator + * A = salt + */ + if (in.len != out.len) + { + return FALSE; + } + if (in.len % HASH_SIZE_MD5 || in.len < HASH_SIZE_MD5) + { + return FALSE; + } + if (out.ptr != in.ptr) + { + memcpy(out.ptr, in.ptr, in.len); + } + /* Preparse seed for first round: + * b(1) = MD5(S + R + A) */ + if (!hasher->get_hash(hasher, secret, NULL) || + !hasher->get_hash(hasher, + chunk_from_thing(this->msg->authenticator), NULL) || + !hasher->get_hash(hasher, salt, b)) + { + return FALSE; + } + while (in.len) + { + /* p(i) = b(i) xor c(1) */ + memxor(out.ptr, b, HASH_SIZE_MD5); + + out = chunk_skip(out, HASH_SIZE_MD5); + if (out.len) + { + /* Prepare seed for next round:: + * b(i) = MD5(S + c(i-1)) */ + if (!hasher->get_hash(hasher, secret, NULL) || + !hasher->get_hash(hasher, + chunk_create(in.ptr, HASH_SIZE_MD5), b)) + { + return FALSE; + } + } + in = chunk_skip(in, HASH_SIZE_MD5); + } + return TRUE; +} + METHOD(radius_message_t, sign, bool, private_radius_message_t *this, u_int8_t *req_auth, chunk_t secret, hasher_t *hasher, signer_t *signer, rng_t *rng, bool msg_auth) @@ -563,6 +624,7 @@ static private_radius_message_t *radius_message_create_empty() .get_encoding = _get_encoding, .sign = _sign, .verify = _verify, + .crypt = _crypt, .destroy = _destroy, }, ); diff --git a/src/libradius/radius_message.h b/src/libradius/radius_message.h index c49323490..4ce03a44e 100644 --- a/src/libradius/radius_message.h +++ b/src/libradius/radius_message.h @@ -285,6 +285,22 @@ struct radius_message_t { hasher_t *hasher, signer_t *signer); /** + * Perform RADIUS attribute en-/decryption. + * + * Performs en-/decryption by XOring the hash-extended secret into data, + * as specified in RFC 2865 5.2 and used by RFC 2548. + * + * @param salt salt to append to message authenticator, if any + * @param in data to en-/decrypt, multiple of HASH_SIZE_MD5 + * @param out en-/decrypted data, length equal to in + * @param secret RADIUS secret + * @param hasher MD5 hasher + * @return TRUE if en-/decryption successful + */ + bool (*crypt)(radius_message_t *this, chunk_t salt, chunk_t in, chunk_t out, + chunk_t secret, hasher_t *hasher); + + /** * Destroy the message. */ void (*destroy)(radius_message_t *this); diff --git a/src/libradius/radius_socket.c b/src/libradius/radius_socket.c index 7dab968d8..f432151c0 100644 --- a/src/libradius/radius_socket.c +++ b/src/libradius/radius_socket.c @@ -233,54 +233,17 @@ METHOD(radius_socket_t, request, radius_message_t*, static chunk_t decrypt_mppe_key(private_radius_socket_t *this, u_int16_t salt, chunk_t C, radius_message_t *request) { - chunk_t A, R, P, seed; - u_char *c, *p; + chunk_t decrypted; - /** - * From RFC2548 (encryption): - * b(1) = MD5(S + R + A) c(1) = p(1) xor b(1) C = c(1) - * b(2) = MD5(S + c(1)) c(2) = p(2) xor b(2) C = C + c(2) - * . . . - * b(i) = MD5(S + c(i-1)) c(i) = p(i) xor b(i) C = C + c(i) - */ - - if (C.len % HASH_SIZE_MD5 || C.len < HASH_SIZE_MD5) - { - return chunk_empty; - } - - A = chunk_create((u_char*)&salt, sizeof(salt)); - R = chunk_create(request->get_authenticator(request), HASH_SIZE_MD5); - P = chunk_alloca(C.len); - p = P.ptr; - c = C.ptr; - - seed = chunk_cata("cc", R, A); - - while (c < C.ptr + C.len) - { - /* b(i) = MD5(S + c(i-1)) */ - if (!this->hasher->get_hash(this->hasher, this->secret, NULL) || - !this->hasher->get_hash(this->hasher, seed, p)) - { - return chunk_empty; - } - - /* p(i) = b(i) xor c(1) */ - memxor(p, c, HASH_SIZE_MD5); - - /* prepare next round */ - seed = chunk_create(c, HASH_SIZE_MD5); - c += HASH_SIZE_MD5; - p += HASH_SIZE_MD5; - } - - /* remove truncation, first byte is key length */ - if (*P.ptr >= P.len) + decrypted = chunk_alloca(C.len); + if (!request->crypt(request, chunk_from_thing(salt), C, decrypted, + this->secret, this->hasher) || + decrypted.ptr[0] >= decrypted.len) { /* decryption failed? */ return chunk_empty; } - return chunk_clone(chunk_create(P.ptr + 1, *P.ptr)); + /* remove truncation, first byte is key length */ + return chunk_clone(chunk_create(decrypted.ptr + 1, decrypted.ptr[0])); } METHOD(radius_socket_t, decrypt_msk, chunk_t, |