123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710 |
- #include "radius_message.h"
- #include <utils/debug.h>
- #include <bio/bio_reader.h>
- #include <crypto/hashers/hasher.h>
- typedef struct private_radius_message_t private_radius_message_t;
- typedef struct rmsg_t rmsg_t;
- typedef struct rattr_t rattr_t;
- struct rmsg_t {
-
- uint8_t code;
-
- uint8_t identifier;
-
- uint16_t length;
-
- uint8_t authenticator[HASH_SIZE_MD5];
-
- uint8_t attributes[];
- } __attribute__((packed));
- struct rattr_t {
-
- uint8_t type;
-
- uint8_t length;
-
- uint8_t value[];
- } __attribute__((packed));
- struct private_radius_message_t {
-
- radius_message_t public;
-
- rmsg_t *msg;
-
- chunk_t password;
- };
- void libradius_init(void)
- {
-
- }
- ENUM_BEGIN(radius_message_code_names, RMC_ACCESS_REQUEST, RMC_ACCOUNTING_RESPONSE,
- "Access-Request",
- "Access-Accept",
- "Access-Reject",
- "Accounting-Request",
- "Accounting-Response");
- ENUM_NEXT(radius_message_code_names, RMC_ACCESS_CHALLENGE, RMC_ACCESS_CHALLENGE, RMC_ACCOUNTING_RESPONSE,
- "Access-Challenge");
- ENUM_NEXT(radius_message_code_names, RMC_DISCONNECT_REQUEST, RMC_COA_NAK, RMC_ACCESS_CHALLENGE,
- "Disconnect-Request",
- "Disconnect-ACK",
- "Disconnect-NAK",
- "CoA-Request",
- "CoA-ACK",
- "CoA-NAK");
- ENUM_END(radius_message_code_names, RMC_COA_NAK);
- ENUM_BEGIN(radius_attribute_type_names, RAT_USER_NAME, RAT_MIP6_HOME_LINK_PREFIX,
- "User-Name",
- "User-Password",
- "CHAP-Password",
- "NAS-IP-Address",
- "NAS-Port",
- "Service-Type",
- "Framed-Protocol",
- "Framed-IP-Address",
- "Framed-IP-Netmask",
- "Framed-Routing",
- "Filter-Id",
- "Framed-MTU",
- "Framed-Compression",
- "Login-IP-Host",
- "Login-Service",
- "Login-TCP-Port",
- "Unassigned",
- "Reply-Message",
- "Callback-Number",
- "Callback-Id",
- "Unassigned",
- "Framed-Route",
- "Framed-IPX-Network",
- "State",
- "Class",
- "Vendor-Specific",
- "Session-Timeout",
- "Idle-Timeout",
- "Termination-Action",
- "Called-Station-Id",
- "Calling-Station-Id",
- "NAS-Identifier",
- "Proxy-State",
- "Login-LAT-Service",
- "Login-LAT-Node",
- "Login-LAT-Group",
- "Framed-AppleTalk-Link",
- "Framed-AppleTalk-Network",
- "Framed-AppleTalk-Zone",
- "Acct-Status-Type",
- "Acct-Delay-Time",
- "Acct-Input-Octets",
- "Acct-Output-Octets",
- "Acct-Session-Id",
- "Acct-Authentic",
- "Acct-Session-Time",
- "Acct-Input-Packets",
- "Acct-Output-Packets",
- "Acct-Terminate-Cause",
- "Acct-Multi-Session-Id",
- "Acct-Link-Count",
- "Acct-Input-Gigawords",
- "Acct-Output-Gigawords",
- "Unassigned",
- "Event-Timestamp",
- "Egress-VLANID",
- "Ingress-Filters",
- "Egress-VLAN-Name",
- "User-Priority-Table",
- "CHAP-Challenge",
- "NAS-Port-Type",
- "Port-Limit",
- "Login-LAT-Port",
- "Tunnel-Type",
- "Tunnel-Medium-Type",
- "Tunnel-Client-Endpoint",
- "Tunnel-Server-Endpoint",
- "Acct-Tunnel-Connection",
- "Tunnel-Password",
- "ARAP-Password",
- "ARAP-Features",
- "ARAP-Zone-Access",
- "ARAP-Security",
- "ARAP-Security-Data",
- "Password-Retry",
- "Prompt",
- "Connect-Info",
- "Configuration-Token",
- "EAP-Message",
- "Message-Authenticator",
- "Tunnel-Private-Group-ID",
- "Tunnel-Assignment-ID",
- "Tunnel-Preference",
- "ARAP-Challenge-Response",
- "Acct-Interim-Interval",
- "Acct-Tunnel-Packets-Lost",
- "NAS-Port-Id",
- "Framed-Pool",
- "CUI",
- "Tunnel-Client-Auth-ID",
- "Tunnel-Server-Auth-ID",
- "NAS-Filter-Rule",
- "Unassigned",
- "Originating-Line-Info",
- "NAS-IPv6-Address",
- "Framed-Interface-Id",
- "Framed-IPv6-Prefix",
- "Login-IPv6-Host",
- "Framed-IPv6-Route",
- "Framed-IPv6-Pool",
- "Error-Cause",
- "EAP-Key-Name",
- "Digest-Response",
- "Digest-Realm",
- "Digest-Nonce",
- "Digest-Response-Auth",
- "Digest-Nextnonce",
- "Digest-Method",
- "Digest-URI",
- "Digest-Qop",
- "Digest-Algorithm",
- "Digest-Entity-Body-Hash",
- "Digest-CNonce",
- "Digest-Nonce-Count",
- "Digest-Username",
- "Digest-Opaque",
- "Digest-Auth-Param",
- "Digest-AKA-Auts",
- "Digest-Domain",
- "Digest-Stale",
- "Digest-HA1",
- "SIP-AOR",
- "Delegated-IPv6-Prefix",
- "MIP6-Feature-Vector",
- "MIP6-Home-Link-Prefix");
- ENUM_NEXT(radius_attribute_type_names, RAT_FRAMED_IPV6_ADDRESS, RAT_STATEFUL_IPV6_ADDRESS_POOL, RAT_MIP6_HOME_LINK_PREFIX,
- "Framed-IPv6-Address",
- "DNS-Server-IPv6-Address",
- "Route-IPv6-Information",
- "Delegated-IPv6-Prefix-Pool",
- "Stateful-IPv6-Address-Pool");
- ENUM_END(radius_attribute_type_names, RAT_STATEFUL_IPV6_ADDRESS_POOL);
- typedef struct {
-
- enumerator_t public;
-
- rattr_t *next;
-
- int left;
- } attribute_enumerator_t;
- METHOD(enumerator_t, attribute_enumerate, bool,
- attribute_enumerator_t *this, va_list args)
- {
- chunk_t *data;
- int *type;
- VA_ARGS_VGET(args, type, data);
- if (this->left == 0)
- {
- return FALSE;
- }
- if (this->left < sizeof(rattr_t) ||
- this->left < this->next->length)
- {
- DBG1(DBG_IKE, "RADIUS message truncated");
- return FALSE;
- }
- *type = this->next->type;
- data->ptr = this->next->value;
- data->len = this->next->length - sizeof(rattr_t);
- this->left -= this->next->length;
- this->next = ((void*)this->next) + this->next->length;
- return TRUE;
- }
- METHOD(radius_message_t, create_enumerator, enumerator_t*,
- private_radius_message_t *this)
- {
- attribute_enumerator_t *e;
- if (ntohs(this->msg->length) < sizeof(rmsg_t) + sizeof(rattr_t))
- {
- return enumerator_create_empty();
- }
- INIT(e,
- .public = {
- .enumerate = enumerator_enumerate_default,
- .venumerate = _attribute_enumerate,
- .destroy = (void*)free,
- },
- .next = (rattr_t*)this->msg->attributes,
- .left = ntohs(this->msg->length) - sizeof(rmsg_t),
- );
- return &e->public;
- }
- typedef struct {
-
- enumerator_t public;
-
- enumerator_t *inner;
-
- uint32_t vendor;
-
- bio_reader_t *reader;
- } vendor_enumerator_t;
- METHOD(enumerator_t, vendor_enumerate, bool,
- vendor_enumerator_t *this, va_list args)
- {
- chunk_t inner_data, *data;
- int inner_type, *vendor, *type;
- uint8_t type8, len;
- VA_ARGS_VGET(args, vendor, type, data);
- while (TRUE)
- {
- if (this->reader)
- {
- if (this->reader->remaining(this->reader) >= 2 &&
- this->reader->read_uint8(this->reader, &type8) &&
- this->reader->read_uint8(this->reader, &len) && len >= 2 &&
- this->reader->read_data(this->reader, len - 2, data))
- {
- *vendor = this->vendor;
- *type = type8;
- return TRUE;
- }
- this->reader->destroy(this->reader);
- this->reader = NULL;
- }
- if (this->inner->enumerate(this->inner, &inner_type, &inner_data))
- {
- if (inner_type == RAT_VENDOR_SPECIFIC)
- {
- this->reader = bio_reader_create(inner_data);
- if (!this->reader->read_uint32(this->reader, &this->vendor))
- {
- this->reader->destroy(this->reader);
- this->reader = NULL;
- }
- }
- }
- else
- {
- return FALSE;
- }
- }
- }
- METHOD(enumerator_t, vendor_destroy, void,
- vendor_enumerator_t *this)
- {
- DESTROY_IF(this->reader);
- this->inner->destroy(this->inner);
- free(this);
- }
- METHOD(radius_message_t, create_vendor_enumerator, enumerator_t*,
- private_radius_message_t *this)
- {
- vendor_enumerator_t *e;
- INIT(e,
- .public = {
- .enumerate = enumerator_enumerate_default,
- .venumerate = _vendor_enumerate,
- .destroy = _vendor_destroy,
- },
- .inner = create_enumerator(this),
- );
- return &e->public;
- }
- METHOD(radius_message_t, add, void,
- private_radius_message_t *this, radius_attribute_type_t type, chunk_t data)
- {
- rattr_t *attribute;
- if (type == RAT_USER_PASSWORD && !this->password.len)
- {
-
- this->password = chunk_alloc(round_up(data.len, HASH_SIZE_MD5));
- memset(this->password.ptr + data.len, 0, this->password.len - data.len);
- memcpy(this->password.ptr, data.ptr, data.len);
- return;
- }
- data.len = min(data.len, MAX_RADIUS_ATTRIBUTE_SIZE);
- this->msg = realloc(this->msg,
- ntohs(this->msg->length) + sizeof(rattr_t) + data.len);
- attribute = ((void*)this->msg) + ntohs(this->msg->length);
- attribute->type = type;
- attribute->length = data.len + sizeof(rattr_t);
- memcpy(attribute->value, data.ptr, data.len);
- 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];
-
- 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);
- }
-
- 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)
- {
-
- memxor(out.ptr, b, HASH_SIZE_MD5);
- out = chunk_skip(out, HASH_SIZE_MD5);
- if (out.len)
- {
-
- 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, uint8_t *req_auth, chunk_t secret,
- hasher_t *hasher, signer_t *signer, rng_t *rng, bool msg_auth)
- {
- if (rng)
- {
-
- if (!rng->get_bytes(rng, HASH_SIZE_MD5, this->msg->authenticator))
- {
- return FALSE;
- }
- }
- else
- {
-
- if (req_auth)
- {
- memcpy(this->msg->authenticator, req_auth, HASH_SIZE_MD5);
- }
- else
- {
- memset(this->msg->authenticator, 0, sizeof(this->msg->authenticator));
- }
- }
- if (this->password.len)
- {
-
- if (!crypt(this, chunk_empty, this->password, this->password,
- secret, hasher))
- {
- return FALSE;
- }
- add(this, RAT_USER_PASSWORD, this->password);
- chunk_clear(&this->password);
- }
- if (msg_auth)
- {
- char buf[HASH_SIZE_MD5];
-
- memset(buf, 0, sizeof(buf));
- add(this, RAT_MESSAGE_AUTHENTICATOR, chunk_create(buf, sizeof(buf)));
- if (!signer->get_signature(signer,
- chunk_create((u_char*)this->msg, ntohs(this->msg->length)),
- ((u_char*)this->msg) + ntohs(this->msg->length) - HASH_SIZE_MD5))
- {
- return FALSE;
- }
- }
- if (!rng)
- {
- chunk_t msg;
-
- msg = chunk_create((u_char*)this->msg, ntohs(this->msg->length));
- if (!hasher->get_hash(hasher, msg, NULL) ||
- !hasher->get_hash(hasher, secret, this->msg->authenticator))
- {
- return FALSE;
- }
- }
- return TRUE;
- }
- METHOD(radius_message_t, verify, bool,
- private_radius_message_t *this, uint8_t *req_auth, chunk_t secret,
- hasher_t *hasher, signer_t *signer)
- {
- char buf[HASH_SIZE_MD5], res_auth[HASH_SIZE_MD5];
- enumerator_t *enumerator;
- int type;
- chunk_t data, msg;
- bool has_eap = FALSE, has_auth = FALSE;
- msg = chunk_create((u_char*)this->msg, ntohs(this->msg->length));
- if (this->msg->code != RMC_ACCESS_REQUEST)
- {
-
- memcpy(res_auth, this->msg->authenticator, HASH_SIZE_MD5);
- if (req_auth)
- {
- memcpy(this->msg->authenticator, req_auth, HASH_SIZE_MD5);
- }
- else
- {
- memset(this->msg->authenticator, 0, HASH_SIZE_MD5);
- }
-
- if (!hasher->get_hash(hasher, msg, NULL) ||
- !hasher->get_hash(hasher, secret, buf) ||
- !memeq_const(buf, res_auth, HASH_SIZE_MD5))
- {
- DBG1(DBG_CFG, "RADIUS Response-Authenticator verification failed");
- return FALSE;
- }
- }
-
- enumerator = create_enumerator(this);
- while (enumerator->enumerate(enumerator, &type, &data))
- {
- if (type == RAT_MESSAGE_AUTHENTICATOR)
- {
- if (data.len != HASH_SIZE_MD5)
- {
- DBG1(DBG_CFG, "RADIUS Message-Authenticator invalid length");
- enumerator->destroy(enumerator);
- return FALSE;
- }
- memcpy(buf, data.ptr, data.len);
- memset(data.ptr, 0, data.len);
- if (signer->verify_signature(signer, msg,
- chunk_create(buf, sizeof(buf))))
- {
-
- memcpy(data.ptr, buf, data.len);
- has_auth = TRUE;
- break;
- }
- else
- {
- DBG1(DBG_CFG, "RADIUS Message-Authenticator verification failed");
- enumerator->destroy(enumerator);
- return FALSE;
- }
- }
- else if (type == RAT_EAP_MESSAGE)
- {
- has_eap = TRUE;
- }
- }
- enumerator->destroy(enumerator);
- if (this->msg->code != RMC_ACCESS_REQUEST)
- {
-
- memcpy(this->msg->authenticator, res_auth, HASH_SIZE_MD5);
- }
- if (has_eap && !has_auth)
- {
- DBG1(DBG_CFG, "RADIUS Message-Authenticator attribute missing");
- return FALSE;
- }
- return TRUE;
- }
- METHOD(radius_message_t, get_code, radius_message_code_t,
- private_radius_message_t *this)
- {
- return this->msg->code;
- }
- METHOD(radius_message_t, get_identifier, uint8_t,
- private_radius_message_t *this)
- {
- return this->msg->identifier;
- }
- METHOD(radius_message_t, set_identifier, void,
- private_radius_message_t *this, uint8_t identifier)
- {
- this->msg->identifier = identifier;
- }
- METHOD(radius_message_t, get_authenticator, uint8_t*,
- private_radius_message_t *this)
- {
- return this->msg->authenticator;
- }
- METHOD(radius_message_t, get_encoding, chunk_t,
- private_radius_message_t *this)
- {
- return chunk_create((u_char*)this->msg, ntohs(this->msg->length));
- }
- METHOD(radius_message_t, destroy, void,
- private_radius_message_t *this)
- {
- chunk_clear(&this->password);
- free(this->msg);
- free(this);
- }
- static private_radius_message_t *radius_message_create_empty()
- {
- private_radius_message_t *this;
- INIT(this,
- .public = {
- .create_enumerator = _create_enumerator,
- .create_vendor_enumerator = _create_vendor_enumerator,
- .add = _add,
- .get_code = _get_code,
- .get_identifier = _get_identifier,
- .set_identifier = _set_identifier,
- .get_authenticator = _get_authenticator,
- .get_encoding = _get_encoding,
- .sign = _sign,
- .verify = _verify,
- .crypt = _crypt,
- .destroy = _destroy,
- },
- );
- return this;
- }
- radius_message_t *radius_message_create(radius_message_code_t code)
- {
- private_radius_message_t *this = radius_message_create_empty();
- INIT(this->msg,
- .code = code,
- .identifier = 0,
- .length = htons(sizeof(rmsg_t)),
- );
- return &this->public;
- }
- radius_message_t *radius_message_parse(chunk_t data)
- {
- private_radius_message_t *this = radius_message_create_empty();
- this->msg = malloc(data.len);
- memcpy(this->msg, data.ptr, data.len);
- if (data.len < sizeof(rmsg_t) ||
- ntohs(this->msg->length) != data.len)
- {
- DBG1(DBG_IKE, "RADIUS message has invalid length");
- destroy(this);
- return NULL;
- }
- return &this->public;
- }
|