2 Copyright (c) 2011, Network RADIUS SARL
5 Redistribution and use in source and binary forms, with or without
6 modification, are permitted provided that the following conditions are met:
7 * Redistributions of source code must retain the above copyright
8 notice, this list of conditions and the following disclaimer.
9 * Redistributions in binary form must reproduce the above copyright
10 notice, this list of conditions and the following disclaimer in the
11 documentation and/or other materials provided with the distribution.
12 * Neither the name of the <organization> nor the
13 names of its contributors may be used to endorse or promote products
14 derived from this software without specific prior written permission.
16 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
17 ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18 WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
19 DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY
20 DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
21 (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
22 LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
23 ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24 (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
25 SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29 * \brief Encoding and decoding packets
34 #if RS_MAX_PACKET_LEN < 64
35 #error RS_MAX_PACKET_LEN is too small. It should be at least 64.
38 #if RS_MAX_PACKET_LEN > 16384
39 #error RS_MAX_PACKET_LEN is too large. It should be smaller than 16K.
42 const char *nr_packet_codes[RS_MAX_PACKET_CODE + 1] = {
48 "Accounting-Response",
49 NULL, NULL, NULL, NULL, NULL,
51 "Status-Server", /* 12 */
52 NULL, NULL, NULL, NULL, NULL, NULL, NULL, /* 19 */
53 NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, /* 20..29 */
54 NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, /* 30..39 */
64 static uint64_t allowed_responses[RS_MAX_PACKET_CODE + 1] = {
66 (1 << PW_ACCESS_ACCEPT) | (1 << PW_ACCESS_REJECT) | (1 << PW_ACCESS_CHALLENGE),
68 1 << PW_ACCOUNTING_RESPONSE,
72 (1 << PW_ACCESS_ACCEPT) | (1 << PW_ACCESS_REJECT) | (1 << PW_ACCESS_CHALLENGE) | (1 << PW_ACCOUNTING_RESPONSE),
74 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 20..29 */
75 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 30..39 */
76 (((uint64_t) 1) << PW_DISCONNECT_ACK) | (((uint64_t) 1) << PW_DISCONNECT_NAK),
79 (((uint64_t) 1) << PW_COA_ACK) | (((uint64_t) 1) << PW_COA_NAK),
85 int nr_packet_ok_raw(const uint8_t *data, size_t sizeof_data)
88 const uint8_t *attr, *end;
90 if (!data || (sizeof_data < 20)) {
91 nr_debug_error("Invalid argument");
95 packet_len = (data[2] << 8) | data[3];
96 if (packet_len < 20) {
97 nr_debug_error("Packet length is too small");
98 return -RSE_PACKET_TOO_SMALL;
101 if (packet_len > sizeof_data) {
102 nr_debug_error("Packet length overflows received data");
103 return -RSE_PACKET_TOO_LARGE;
107 * If we receive 100 bytes, and the header says it's 20 bytes,
108 * then it's 20 bytes.
110 end = data + packet_len;
112 for (attr = data + 20; attr < end; attr += attr[1]) {
113 if ((attr + 2) > end) {
114 nr_debug_error("Attribute overflows packet");
115 return -RSE_ATTR_OVERFLOW;
119 nr_debug_error("Attribute length is too small");
120 return -RSE_ATTR_TOO_SMALL;
123 if ((attr + attr[1]) > end) {
124 nr_debug_error("Attribute length is too large");
125 return -RSE_ATTR_TOO_LARGE;
132 int nr_packet_ok(RADIUS_PACKET *packet)
136 if (!packet) return -RSE_INVAL;
138 if ((packet->flags & RS_PACKET_OK) != 0) return 0;
140 rcode = nr_packet_ok_raw(packet->data, packet->length);
141 if (rcode < 0) return rcode;
143 packet->flags |= RS_PACKET_OK;
149 * Comparison function that is time-independent. Using "memcmp"
150 * would satisfy the "comparison" part. However, it would also
151 * leak information about *which* bytes are wrong. Attackers
152 * could use that leak to create a "correct" RADIUS packet which
153 * will be accepted by the client and/or server.
155 static int digest_cmp(const uint8_t *a, const uint8_t *b, size_t length)
160 for (i = 0; i < length; i++) {
161 result |= (a[i] ^ b[i]);
168 #ifdef PW_MESSAGE_AUTHENTICATOR
169 static int msg_auth_ok(const RADIUS_PACKET *original,
171 uint8_t *data, size_t length)
173 uint8_t packet_vector[sizeof(original->vector)];
174 uint8_t msg_auth_vector[sizeof(original->vector)];
175 uint8_t calc_auth_vector[sizeof(original->vector)];
178 nr_debug_error("Message-Authenticator has invalid length");
179 return -RSE_MSG_AUTH_LEN;
182 memcpy(packet_vector, data + 4, sizeof(packet_vector));
183 memcpy(msg_auth_vector, ma + 2, sizeof(msg_auth_vector));
184 memset(ma + 2, 0, sizeof(msg_auth_vector));
190 case PW_ACCOUNTING_REQUEST:
191 case PW_ACCOUNTING_RESPONSE:
192 case PW_DISCONNECT_REQUEST:
193 case PW_DISCONNECT_ACK:
194 case PW_DISCONNECT_NAK:
198 memset(data + 4, 0, sizeof(packet_vector));
201 case PW_ACCESS_ACCEPT:
202 case PW_ACCESS_REJECT:
203 case PW_ACCESS_CHALLENGE:
205 nr_debug_error("Cannot validate response without request");
206 return -RSE_REQUEST_REQUIRED;
208 memcpy(data + 4, original->vector, sizeof(original->vector));
212 nr_hmac_md5(data, length,
213 (const uint8_t *) original->secret, original->sizeof_secret,
216 memcpy(ma + 2, msg_auth_vector, sizeof(msg_auth_vector));
217 memcpy(data + 4, packet_vector, sizeof(packet_vector));
219 if (digest_cmp(calc_auth_vector, msg_auth_vector,
220 sizeof(calc_auth_vector)) != 0) {
221 nr_debug_error("Invalid Message-Authenticator");
222 return -RSE_MSG_AUTH_WRONG;
230 * The caller ensures that the packet codes are as expected.
232 static int packet_auth_ok(const RADIUS_PACKET *original,
233 uint8_t *data, size_t length)
235 uint8_t packet_vector[sizeof(original->vector)];
236 uint8_t calc_digest[sizeof(original->vector)];
239 if ((data[0] == PW_ACCESS_REQUEST) ||
240 (data[0] == PW_STATUS_SERVER)) return 1;
242 memcpy(packet_vector, data + 4, sizeof(packet_vector));
245 memset(data + 4, 0, sizeof(packet_vector));
247 memcpy(data + 4, original->vector, sizeof(original->vector));
251 RS_MD5Update(&ctx, data, length);
252 RS_MD5Update(&ctx, original->secret, original->sizeof_secret);
253 RS_MD5Final(calc_digest, &ctx);
255 memcpy(data + 4, packet_vector, sizeof(packet_vector));
257 if (digest_cmp(calc_digest, packet_vector,
258 sizeof(packet_vector)) != 0) {
259 nr_debug_error("Invalid authentication vector");
260 return -RSE_AUTH_VECTOR_WRONG;
267 int nr_packet_verify(RADIUS_PACKET *packet, const RADIUS_PACKET *original)
271 #ifdef PW_MESSAGE_AUTHENTICATOR
275 if (!packet || !packet->data || !packet->secret) {
276 nr_debug_error("Invalid argument");
280 if ((packet->flags & RS_PACKET_VERIFIED) != 0) return 0;
283 * Packet isn't well formed. Ignore it.
285 rcode = nr_packet_ok(packet);
286 if (rcode < 0) return rcode;
289 * Get rid of improper packets as early as possible.
294 if (original->code > RS_MAX_PACKET_CODE) {
295 nr_debug_error("Invalid original code %u",
297 return -RSE_INVALID_REQUEST_CODE;
300 if (packet->data[1] != original->id) {
301 nr_debug_error("Ignoring response with wrong ID %u",
303 return -RSE_INVALID_RESPONSE_CODE;
307 mask <<= packet->data[0];
309 if ((allowed_responses[original->code] & mask) == 0) {
310 nr_debug_error("Ignoring response with wrong code %u",
312 return -RSE_INVALID_RESPONSE_CODE;
315 if ((memcmp(&packet->src, &original->dst, sizeof(packet->src)) != 0) &&
316 (evutil_sockaddr_cmp(&(packet->src), &(original->dst)) != 0)) {
317 nr_debug_error("Ignoring response from wrong IP/port");
318 return -RSE_INVALID_RESPONSE_SRC;
321 } else if (allowed_responses[packet->data[0]] != 0) {
322 nr_debug_error("Ignoring response without original");
323 return -RSE_INVALID_RESPONSE_CODE;
326 #ifdef PW_MESSAGE_AUTHENTICATOR
327 end = packet->data + packet->length;
330 * Note that the packet MUST be well-formed here.
332 for (attr = packet->data + 20; attr < end; attr += attr[1]) {
333 if (attr[0] == PW_MESSAGE_AUTHENTICATOR) {
334 rcode = msg_auth_ok(original, attr,
335 packet->data, packet->length);
336 if (rcode < 0) return rcode;
342 * Verify the packet authenticator.
344 rcode = packet_auth_ok(original, packet->data, packet->length);
345 if (rcode < 0) return rcode;
347 packet->flags |= RS_PACKET_VERIFIED;
353 int nr_packet_decode(RADIUS_PACKET *packet, const RADIUS_PACKET *original)
355 int rcode, num_attributes;
356 uint8_t *data, *attr;
358 VALUE_PAIR **tail, *vp;
360 if (!packet) return -RSE_INVAL;
362 if ((packet->flags & RS_PACKET_DECODED) != 0) return 0;
364 rcode = nr_packet_ok(packet);
365 if (rcode < 0) return rcode;
368 end = data + packet->length;
373 * Loop over the packet, converting attrs to VPs.
375 for (attr = data + 20; attr < end; attr += attr[1]) {
376 rcode = nr_attr2vp(packet, original,
377 attr, end - attr, &vp);
379 nr_vp_free(&packet->vps);
390 if (num_attributes > RS_MAX_ATTRIBUTES) {
391 nr_debug_error("Too many attributes");
392 nr_vp_free(&packet->vps);
393 return -RSE_TOO_MANY_ATTRS;
397 packet->code = data[0];
398 packet->id = data[1];
399 memcpy(packet->vector, data + 4, sizeof(packet->vector));
401 packet->flags |= RS_PACKET_DECODED;
407 int nr_packet_sign(RADIUS_PACKET *packet, const RADIUS_PACKET *original)
409 #ifdef PW_MESSAGE_AUTHENTICATOR
411 const uint8_t *attr, *end;
414 if ((packet->flags & RS_PACKET_SIGNED) != 0) return 0;
416 if ((packet->flags & RS_PACKET_ENCODED) == 0) {
419 rcode = nr_packet_encode(packet, original);
420 if (rcode < 0) return rcode;
423 if ((packet->code == PW_ACCESS_ACCEPT) ||
424 (packet->code == PW_ACCESS_CHALLENGE) ||
425 (packet->code == PW_ACCESS_REJECT)) {
426 #ifdef PW_MESSAGE_AUTHENTICATOR
428 nr_debug_error("Original packet is required to create the Message-Authenticator");
429 return -RSE_REQUEST_REQUIRED;
433 memcpy(packet->data + 4, original->vector,
434 sizeof(original->vector));
436 memcpy(packet->data + 4, packet->vector,
437 sizeof(packet->vector));
440 #ifdef PW_MESSAGE_AUTHENTICATOR
441 end = packet->data + packet->length;
443 for (attr = packet->data + 20; attr < end; attr += attr[1]) {
444 if (attr[0] == PW_MESSAGE_AUTHENTICATOR) {
445 ma = (attr - packet->data);
451 * Force all Access-Request packets to have a
452 * Message-Authenticator.
454 if (!ma && ((packet->length + 18) <= packet->sizeof_data) &&
455 ((packet->code == PW_ACCESS_REQUEST) ||
456 (packet->code == PW_STATUS_SERVER))) {
459 packet->data[ma]= PW_MESSAGE_AUTHENTICATOR;
460 packet->data[ma + 1] = 18;
461 memset(&packet->data[ma + 2], 0, 16);
462 packet->length += 18;
468 packet->data[2] = (packet->length >> 8) & 0xff;
469 packet->data[3] = packet->length & 0xff;
472 * Sign the Message-Authenticator && packet.
475 nr_hmac_md5(packet->data, packet->length,
476 (const uint8_t *) packet->secret, packet->sizeof_secret,
477 packet->data + ma + 2);
482 * Calculate the signature.
484 if (!((packet->code == PW_ACCESS_REQUEST) ||
485 (packet->code == PW_STATUS_SERVER))) {
489 RS_MD5Update(&ctx, packet->data, packet->length);
490 RS_MD5Update(&ctx, packet->secret, packet->sizeof_secret);
491 RS_MD5Final(packet->vector, &ctx);
494 memcpy(packet->data + 4, packet->vector, sizeof(packet->vector));
496 packet->attempts = 0;
497 packet->flags |= RS_PACKET_SIGNED;
503 static int can_encode_packet(RADIUS_PACKET *packet,
504 const RADIUS_PACKET *original)
506 if ((packet->code == 0) ||
507 (packet->code > RS_MAX_PACKET_CODE) ||
508 (original && (original->code > RS_MAX_PACKET_CODE))) {
509 nr_debug_error("Cannot send unknown packet code");
510 return -RSE_INVALID_REQUEST_CODE;
513 if (!nr_packet_codes[packet->code]) {
514 nr_debug_error("Cannot handle packet code %u",
516 return -RSE_INVALID_REQUEST_CODE;
521 nr_debug_error("No place to put packet");
522 return -RSE_NO_PACKET_DATA;
526 if (packet->sizeof_data < 20) {
527 nr_debug_error("The buffer is too small to encode the packet");
528 return -RSE_PACKET_TOO_SMALL;
532 * Enforce request / response correlation.
538 mask <<= packet->code;
540 if ((allowed_responses[original->code] & mask) == 0) {
541 nr_debug_error("Cannot encode response %u to packet %u",
542 packet->code, original->code);
543 return -RSE_INVALID_RESPONSE_CODE;
545 packet->id = original->id;
547 } else if (allowed_responses[packet->code] == 0) {
548 nr_debug_error("Cannot encode response %u without original",
550 return -RSE_REQUEST_REQUIRED;
556 static void encode_header(RADIUS_PACKET *packet)
558 if ((packet->flags & RS_PACKET_HEADER) != 0) return;
560 memset(packet->data, 0, 20);
561 packet->data[0] = packet->code;
562 packet->data[1] = packet->id;
564 packet->data[3] = 20;
568 * Calculate a random authentication vector.
570 if ((packet->code == PW_ACCESS_REQUEST) ||
571 (packet->code == PW_STATUS_SERVER)) {
572 nr_rand_bytes(packet->vector, sizeof(packet->vector));
574 memset(packet->vector, 0, sizeof(packet->vector));
577 memcpy(packet->data + 4, packet->vector, sizeof(packet->vector));
579 packet->flags |= RS_PACKET_HEADER;
582 int nr_packet_encode(RADIUS_PACKET *packet, const RADIUS_PACKET *original)
584 #ifdef PW_MESSAGE_AUTHENTICATOR
589 const VALUE_PAIR *vp;
592 if ((packet->flags & RS_PACKET_ENCODED) != 0) return 0;
594 rcode = can_encode_packet(packet, original);
595 if (rcode < 0) return rcode;
598 end = data + packet->sizeof_data;
600 encode_header(packet);
604 * Encode each VALUE_PAIR
608 #ifdef PW_MESSAGE_AUTHENTICATOR
609 if (vp->da->attr == PW_MESSAGE_AUTHENTICATOR) {
610 ma = (data - packet->data);
613 len = nr_vp2attr(packet, original, &vp,
615 if (len < 0) return len;
617 if (len == 0) break; /* insufficient room to encode it */
622 #ifdef PW_MESSAGE_AUTHENTICATOR
624 * Always send a Message-Authenticator.
626 * We do *not* recommend removing this code.
628 if (((packet->code == PW_ACCESS_REQUEST) ||
629 (packet->code == PW_STATUS_SERVER)) &&
631 ((data + 18) <= end)) {
632 ma = (data - packet->data);
633 data[0] = PW_MESSAGE_AUTHENTICATOR;
635 memset(data + 2, 0, 16);
640 packet->length = data - packet->data;
642 packet->data[2] = (packet->length >> 8) & 0xff;
643 packet->data[3] = packet->length & 0xff;
645 packet->flags |= RS_PACKET_ENCODED;
647 return packet->length;
652 * Ensure that the nr_data2attr_t structure is filled in
653 * appropriately. This includes filling in a fake DICT_ATTR
654 * structure, if necessary.
656 static int do_callback(void *ctx, nr_packet_walk_func_t callback,
657 int attr, int vendor,
658 const uint8_t *data, size_t sizeof_data)
666 da = nr_dict_attr_byvalue(attr, vendor);
669 * The attribute is supposed to have a particular length,
670 * but does not. It is therefore malformed.
672 if (da && (da->flags.length != 0) &&
673 da->flags.length != sizeof_data) {
678 rcode = nr_dict_attr_2struct(&myda, attr, vendor,
679 buffer, sizeof(buffer));
681 if (rcode < 0) return rcode;
685 rcode = callback(ctx, da, data, sizeof_data);
686 if (rcode < 0) return rcode;
692 int nr_packet_walk(RADIUS_PACKET *packet, void *ctx,
693 nr_packet_walk_func_t callback)
699 if (!packet || !callback) return -RSE_INVAL;
701 rcode = nr_packet_ok(packet);
702 if (rcode < 0) return rcode;
704 end = packet->data + packet->length;
706 for (attr = packet->data + 20; attr < end; attr += attr[1]) {
708 int dv_type, dv_length;
711 const DICT_VENDOR *dv = NULL;
716 if (value != PW_VENDOR_SPECIFIC) {
718 rcode = do_callback(ctx, callback,
720 attr + 2, attr[1] - 2);
721 if (rcode < 0) return rcode;
725 if (attr[1] < 6) goto raw;
726 memcpy(&vendorpec, attr + 2, 4);
727 vendorpec = ntohl(vendorpec);
729 if (dv && (dv->vendor != vendorpec)) dv = NULL;
731 if (!dv) dv = nr_dict_vendor_byvalue(vendorpec);
735 dv_length = dv->length;
742 * Malformed: it's a raw attribute.
744 if (nr_tlv_ok(attr + 6, attr[1] - 6, dv_type, dv_length) < 0) {
748 for (vsa = attr + 6; vsa < attr + attr[1]; vsa += length) {
751 value = (vsa[2] << 8) | vsa[3];
755 value = (vsa[0] << 8) | vsa[1];
763 return -RSE_INTERNAL;
768 length = attr[1] - 6 - dv_type;
773 length = vsa[dv_type + dv_length - 1];
777 return -RSE_INTERNAL;
780 rcode = do_callback(ctx, callback,
782 vsa + dv_type + dv_length,
783 length - dv_type - dv_length);
784 if (rcode < 0) return rcode;
791 int nr_packet_init(RADIUS_PACKET *packet, const RADIUS_PACKET *original,
792 const char *secret, int code,
793 void *data, size_t sizeof_data)
797 if ((code < 0) || (code > RS_MAX_PACKET_CODE)) {
798 return -RSE_INVALID_REQUEST_CODE;
801 if (!data || (sizeof_data < 20)) return -RSE_INVAL;
803 memset(packet, 0, sizeof(*packet));
804 packet->secret = secret;
805 packet->sizeof_secret = secret ? strlen(secret) : 0;
809 packet->sizeof_data = sizeof_data;
811 rcode = can_encode_packet(packet, original);
812 if (rcode < 0) return rcode;
814 encode_header(packet);
820 static int pack_eap(RADIUS_PACKET *packet,
821 const void *data, size_t data_len)
829 attr = packet->data + packet->length;
830 end = attr + packet->sizeof_data;
833 if ((attr + 255) > end) return -RSE_ATTR_OVERFLOW;
835 attr[0] = PW_EAP_MESSAGE;
837 memcpy(attr + 2, eap, 253);
843 if ((attr + (2 + left)) > end) return -RSE_ATTR_OVERFLOW;
845 attr[0] = PW_EAP_MESSAGE;
847 memcpy(attr + 2, eap, left);
849 packet->length = attr - packet->data;
854 ssize_t nr_packet_attr_append(RADIUS_PACKET *packet,
855 const RADIUS_PACKET *original,
857 const void *data, size_t data_len)
862 const VALUE_PAIR *vp;
864 if (!packet || !da || !data) {
869 if (da->type != RS_TYPE_STRING) return -RSE_ATTR_TOO_SMALL;
871 data_len = strlen(data);
874 packet->flags |= RS_PACKET_ENCODED; /* ignore any VPs */
876 attr = packet->data + packet->length;
877 end = attr + packet->sizeof_data;
879 if ((attr + 2 + data_len) > end) {
880 return -RSE_ATTR_OVERFLOW;
883 if ((da->flags.length != 0) &&
884 (data_len != da->flags.length)) {
885 return -RSE_ATTR_VALUE_MALFORMED;
888 #ifdef PW_EAP_MESSAGE
890 * automatically split EAP-Message into multiple
893 if (!da->vendor && (da->attr == PW_EAP_MESSAGE) && (data_len > 253)) {
894 return pack_eap(packet, data, data_len);
898 if (data_len > 253) return -RSE_ATTR_TOO_LARGE;
900 vp = nr_vp_init(&my_vp, da);
901 rcode = nr_vp_set_data(&my_vp, data, data_len);
902 if (rcode < 0) return rcode;
905 * Note that this function packs VSAs each into their own
906 * Vendor-Specific attribute. If this isn't what you
907 * want, use the version of the library with full support
908 * for TLVs, WiMAX, and extended attributes.
910 rcode = nr_vp2attr(packet, original, &vp, attr, end - attr);
911 if (rcode <= 0) return rcode;
913 packet->length += rcode;