X-Git-Url: http://www.project-moonshot.org/gitweb/?a=blobdiff_plain;f=src%2Flib%2Fradius.c;h=62751dfa469c4bc3c8074da8b9f9e8bedd358bd7;hb=3fe7a409cbbc70bf0bee41c46a38d6fdfa77c697;hp=ea1f025b9c1f1e7cd1ae82fbb85784d388010a35;hpb=5f3e8b84cd5dc5a7959c5c395d1d7a1e0c1415c0;p=freeradius.git diff --git a/src/lib/radius.c b/src/lib/radius.c index ea1f025..62751df 100644 --- a/src/lib/radius.c +++ b/src/lib/radius.c @@ -53,7 +53,8 @@ RCSID("$Id$") * is unsigned, and the attacker can use resources on the server, * even if the end request is rejected. */ -int librad_max_attributes = 0; +int fr_max_attributes = 0; +FILE *fr_log_fp = NULL; typedef struct radius_packet_t { uint8_t code; @@ -63,13 +64,11 @@ typedef struct radius_packet_t { uint8_t data[1]; } radius_packet_t; -static lrad_randctx lrad_rand_pool; /* across multiple calls */ -static int lrad_rand_initialized = 0; +static fr_randctx fr_rand_pool; /* across multiple calls */ +static int fr_rand_initialized = 0; static unsigned int salt_offset = 0; - -#define MAX_PACKET_CODE (52) -static const char *packet_codes[] = { +const char *fr_packet_codes[FR_MAX_PACKET_CODE] = { "", "Access-Request", "Access-Accept", @@ -125,72 +124,107 @@ static const char *packet_codes[] = { }; -/* - * Wrapper for sendto which handles sendfromto, IPv6, and all - * possible combinations. - */ -static int rad_sendto(int sockfd, void *data, size_t data_len, int flags, - lrad_ipaddr_t *src_ipaddr, lrad_ipaddr_t *dst_ipaddr, - int dst_port) +void fr_printf_log(const char *fmt, ...) { - struct sockaddr_storage dst; - socklen_t sizeof_dst = sizeof(dst); + va_list ap; -#ifdef WITH_UDPFROMTO - struct sockaddr_storage src; - socklen_t sizeof_src = sizeof(src); + va_start(ap, fmt); + if ((fr_debug_flag == 0) || !fr_log_fp) { + va_end(ap); + return; + } - memset(&src, 0, sizeof(src)); -#endif - memset(&dst, 0, sizeof(dst)); + vfprintf(fr_log_fp, fmt, ap); + va_end(ap); - /* - * IPv4 is supported. - */ - if (dst_ipaddr->af == AF_INET) { - struct sockaddr_in *s4; + return; +} - s4 = (struct sockaddr_in *)&dst; - sizeof_dst = sizeof(struct sockaddr_in); +static void print_hex(RADIUS_PACKET *packet) +{ + int i; - s4->sin_family = AF_INET; - s4->sin_addr = dst_ipaddr->ipaddr.ip4addr; - s4->sin_port = htons(dst_port); + if (!packet->data) return; -#ifdef WITH_UDPFROMTO - s4 = (struct sockaddr_in *)&src; - sizeof_src = sizeof(struct sockaddr_in); + printf(" Code:\t\t%u\n", packet->data[0]); + printf(" Id:\t\t%u\n", packet->data[1]); + printf(" Length:\t%u\n", ((packet->data[2] << 8) | + (packet->data[3]))); + printf(" Vector:\t"); + for (i = 4; i < 20; i++) { + printf("%02x", packet->data[i]); + } + printf("\n"); - s4->sin_family = AF_INET; - s4->sin_addr = src_ipaddr->ipaddr.ip4addr; -#endif + if (packet->data_len > 20) { + int total; + const uint8_t *ptr; + printf(" Data:"); - /* - * IPv6 MAY be supported. - */ -#ifdef HAVE_STRUCT_SOCKADDR_IN6 - } else if (dst_ipaddr->af == AF_INET6) { - struct sockaddr_in6 *s6; + total = packet->data_len - 20; + ptr = packet->data + 20; - s6 = (struct sockaddr_in6 *)&dst; - sizeof_dst = sizeof(struct sockaddr_in6); + while (total > 0) { + int attrlen; - s6->sin6_family = AF_INET6; - s6->sin6_addr = dst_ipaddr->ipaddr.ip6addr; - s6->sin6_port = htons(dst_port); + printf("\t\t"); + if (total < 2) { /* too short */ + printf("%02x\n", *ptr); + break; + } + + if (ptr[1] > total) { /* too long */ + for (i = 0; i < total; i++) { + printf("%02x ", ptr[i]); + } + break; + } + + printf("%02x %02x ", ptr[0], ptr[1]); + attrlen = ptr[1] - 2; + ptr += 2; + total -= 2; + + for (i = 0; i < attrlen; i++) { + if ((i > 0) && ((i & 0x0f) == 0x00)) + printf("\t\t\t"); + printf("%02x ", ptr[i]); + if ((i & 0x0f) == 0x0f) printf("\n"); + } + + if ((attrlen & 0x0f) != 0x00) printf("\n"); + + ptr += attrlen; + total -= attrlen; + } + } + fflush(stdout); +} + + +/* + * Wrapper for sendto which handles sendfromto, IPv6, and all + * possible combinations. + */ +static int rad_sendto(int sockfd, void *data, size_t data_len, int flags, + fr_ipaddr_t *src_ipaddr, int src_port, + fr_ipaddr_t *dst_ipaddr, int dst_port) +{ + struct sockaddr_storage dst; + socklen_t sizeof_dst; #ifdef WITH_UDPFROMTO - return -1; /* UDPFROMTO && IPv6 are not supported */ -#if 0 - s6 = (struct sockaddr_in6 *)&src; - sizeof_src = sizeof(struct sockaddr_in6); - - s6->sin6_family = AF_INET6; - s6->sin6_addr = src_ipaddr->ipaddr.ip6addr; -#endif /* #if 0 */ -#endif /* WITH_UDPFROMTO */ -#endif /* HAVE_STRUCT_SOCKADDR_IN6 */ - } else return -1; /* Unknown address family, Die Die Die! */ + struct sockaddr_storage src; + socklen_t sizeof_src; + + fr_ipaddr2sockaddr(src_ipaddr, src_port, &src, &sizeof_src); +#else + src_port = src_port; /* -Wunused */ +#endif + + if (!fr_ipaddr2sockaddr(dst_ipaddr, dst_port, &dst, &sizeof_dst)) { + return -1; + } #ifdef WITH_UDPFROMTO /* @@ -213,7 +247,7 @@ static int rad_sendto(int sockfd, void *data, size_t data_len, int flags, * No udpfromto, OR an IPv6 socket, fail gracefully. */ return sendto(sockfd, data, data_len, flags, - (struct sockaddr *)&dst, sizeof_dst); + (struct sockaddr *) &dst, sizeof_dst); } @@ -228,7 +262,7 @@ void rad_recv_discard(int sockfd) } -ssize_t rad_recv_header(int sockfd, lrad_ipaddr_t *src_ipaddr, int *src_port, +ssize_t rad_recv_header(int sockfd, fr_ipaddr_t *src_ipaddr, int *src_port, int *code) { ssize_t data_len, packet_len; @@ -238,7 +272,10 @@ ssize_t rad_recv_header(int sockfd, lrad_ipaddr_t *src_ipaddr, int *src_port, data_len = recvfrom(sockfd, header, sizeof(header), MSG_PEEK, (struct sockaddr *)&src, &sizeof_src); - if (data_len < 0) return -1; + if (data_len < 0) { + if ((errno == EAGAIN) || (errno == EINTR)) return 0; + return -1; + } /* * Too little data is available, discard the packet. @@ -274,25 +311,10 @@ ssize_t rad_recv_header(int sockfd, lrad_ipaddr_t *src_ipaddr, int *src_port, } } - if (src.ss_family == AF_INET) { - struct sockaddr_in *s4; - - s4 = (struct sockaddr_in *)&src; - src_ipaddr->af = AF_INET; - src_ipaddr->ipaddr.ip4addr = s4->sin_addr; - *src_port = ntohs(s4->sin_port); - -#ifdef HAVE_STRUCT_SOCKADDR_IN6 - } else if (src.ss_family == AF_INET6) { - struct sockaddr_in6 *s6; - - s6 = (struct sockaddr_in6 *)&src; - src_ipaddr->af = AF_INET6; - src_ipaddr->ipaddr.ip6addr = s6->sin6_addr; - *src_port = ntohs(s6->sin6_port); - -#endif - } else { + /* + * Convert AF. If unknown, discard packet. + */ + if (!fr_sockaddr2ipaddr(&src, sizeof_src, src_ipaddr, src_port)) { recvfrom(sockfd, header, sizeof(header), 0, (struct sockaddr *)&src, &sizeof_src); return 1; @@ -313,8 +335,8 @@ ssize_t rad_recv_header(int sockfd, lrad_ipaddr_t *src_ipaddr, int *src_port, * possible combinations. */ static ssize_t rad_recvfrom(int sockfd, uint8_t **pbuf, int flags, - lrad_ipaddr_t *src_ipaddr, uint16_t *src_port, - lrad_ipaddr_t *dst_ipaddr, uint16_t *dst_port) + fr_ipaddr_t *src_ipaddr, uint16_t *src_port, + fr_ipaddr_t *dst_ipaddr, uint16_t *dst_port) { struct sockaddr_storage src; struct sockaddr_storage dst; @@ -324,6 +346,7 @@ static ssize_t rad_recvfrom(int sockfd, uint8_t **pbuf, int flags, uint8_t header[4]; void *buf; size_t len; + int port; memset(&src, 0, sizeof_src); memset(&dst, 0, sizeof_dst); @@ -345,7 +368,10 @@ static ssize_t rad_recvfrom(int sockfd, uint8_t **pbuf, int flags, */ data_len = recvfrom(sockfd, header, sizeof(header), MSG_PEEK, (struct sockaddr *)&src, &sizeof_src); - if (data_len < 0) return -1; + if (data_len < 0) { + if ((errno == EAGAIN) || (errno == EINTR)) return 0; + return -1; + } /* * Too little data is available, discard the packet. @@ -405,40 +431,14 @@ static ssize_t rad_recvfrom(int sockfd, uint8_t **pbuf, int flags, return data_len; } - /* - * Check address families, and update src/dst ports, etc. - */ - if (src.ss_family == AF_INET) { - struct sockaddr_in *s4; - - s4 = (struct sockaddr_in *)&src; - src_ipaddr->af = AF_INET; - src_ipaddr->ipaddr.ip4addr = s4->sin_addr; - *src_port = ntohs(s4->sin_port); - - s4 = (struct sockaddr_in *)&dst; - dst_ipaddr->af = AF_INET; - dst_ipaddr->ipaddr.ip4addr = s4->sin_addr; - *dst_port = ntohs(s4->sin_port); - -#ifdef HAVE_STRUCT_SOCKADDR_IN6 - } else if (src.ss_family == AF_INET6) { - struct sockaddr_in6 *s6; - - s6 = (struct sockaddr_in6 *)&src; - src_ipaddr->af = AF_INET6; - src_ipaddr->ipaddr.ip6addr = s6->sin6_addr; - *src_port = ntohs(s6->sin6_port); - - s6 = (struct sockaddr_in6 *)&dst; - dst_ipaddr->af = AF_INET6; - dst_ipaddr->ipaddr.ip6addr = s6->sin6_addr; - *dst_port = ntohs(s6->sin6_port); -#endif - } else { + if (!fr_sockaddr2ipaddr(&src, sizeof_src, src_ipaddr, &port)) { free(buf); return -1; /* Unknown address family, Die Die Die! */ } + *src_port = port; + + fr_sockaddr2ipaddr(&dst, sizeof_dst, dst_ipaddr, &port); + *dst_port = port; /* * Different address families should never happen. @@ -472,13 +472,13 @@ static ssize_t rad_recvfrom(int sockfd, uint8_t **pbuf, int flags, static void make_secret(uint8_t *digest, const uint8_t *vector, const char *secret, const uint8_t *value) { - lrad_MD5_CTX context; + FR_MD5_CTX context; int i; - lrad_MD5Init(&context); - lrad_MD5Update(&context, vector, AUTH_VECTOR_LEN); - lrad_MD5Update(&context, secret, strlen(secret)); - lrad_MD5Final(digest, &context); + fr_MD5Init(&context); + fr_MD5Update(&context, vector, AUTH_VECTOR_LEN); + fr_MD5Update(&context, (const uint8_t *) secret, strlen(secret)); + fr_MD5Final(digest, &context); for ( i = 0; i < AUTH_VECTOR_LEN; i++ ) { digest[i] ^= value[i]; @@ -486,11 +486,11 @@ static void make_secret(uint8_t *digest, const uint8_t *vector, } #define MAX_PASS_LEN (128) -static void make_passwd(uint8_t *output, int *outlen, - const uint8_t *input, int inlen, +static void make_passwd(uint8_t *output, size_t *outlen, + const uint8_t *input, size_t inlen, const char *secret, const uint8_t *vector) { - lrad_MD5_CTX context, old; + FR_MD5_CTX context, old; uint8_t digest[AUTH_VECTOR_LEN]; uint8_t passwd[MAX_PASS_LEN]; int i, n; @@ -500,10 +500,15 @@ static void make_passwd(uint8_t *output, int *outlen, * If the length is zero, round it up. */ len = inlen; + + if (len > MAX_PASS_LEN) len = MAX_PASS_LEN; + + memcpy(passwd, input, len); + memset(passwd + len, 0, sizeof(passwd) - len); + if (len == 0) { len = AUTH_PASS_LEN; } - else if (len > MAX_PASS_LEN) len = MAX_PASS_LEN; else if ((len & 0x0f) != 0) { len += 0x0f; @@ -511,27 +516,24 @@ static void make_passwd(uint8_t *output, int *outlen, } *outlen = len; - memcpy(passwd, input, len); - memset(passwd + len, 0, sizeof(passwd) - len); - - lrad_MD5Init(&context); - lrad_MD5Update(&context, secret, strlen(secret)); + fr_MD5Init(&context); + fr_MD5Update(&context, (const uint8_t *) secret, strlen(secret)); old = context; /* * Do first pass. */ - lrad_MD5Update(&context, vector, AUTH_PASS_LEN); + fr_MD5Update(&context, vector, AUTH_PASS_LEN); for (n = 0; n < len; n += AUTH_PASS_LEN) { if (n > 0) { context = old; - lrad_MD5Update(&context, + fr_MD5Update(&context, passwd + n - AUTH_PASS_LEN, AUTH_PASS_LEN); } - lrad_MD5Final(digest, &context); + fr_MD5Final(digest, &context); for (i = 0; i < AUTH_PASS_LEN; i++) { passwd[i + n] ^= digest[i]; } @@ -540,11 +542,11 @@ static void make_passwd(uint8_t *output, int *outlen, memcpy(output, passwd, len); } -static void make_tunnel_passwd(uint8_t *output, int *outlen, - const uint8_t *input, int inlen, int room, +static void make_tunnel_passwd(uint8_t *output, size_t *outlen, + const uint8_t *input, size_t inlen, size_t room, const char *secret, const uint8_t *vector) { - lrad_MD5_CTX context, old; + FR_MD5_CTX context, old; uint8_t digest[AUTH_VECTOR_LEN]; uint8_t passwd[MAX_STRING_LEN + AUTH_VECTOR_LEN]; int i, n; @@ -604,26 +606,27 @@ static void make_tunnel_passwd(uint8_t *output, int *outlen, * add in some CSPRNG data. should be OK.. */ passwd[0] = (0x80 | ( ((salt_offset++) & 0x0f) << 3) | - (lrad_rand() & 0x07)); - passwd[1] = lrad_rand(); + (fr_rand() & 0x07)); + passwd[1] = fr_rand(); passwd[2] = inlen; /* length of the password string */ - lrad_MD5Init(&context); - lrad_MD5Update(&context, secret, strlen(secret)); + fr_MD5Init(&context); + fr_MD5Update(&context, (const uint8_t *) secret, strlen(secret)); old = context; - lrad_MD5Update(&context, vector, AUTH_VECTOR_LEN); - lrad_MD5Update(&context, &passwd[0], 2); + fr_MD5Update(&context, vector, AUTH_VECTOR_LEN); + fr_MD5Update(&context, &passwd[0], 2); for (n = 0; n < len; n += AUTH_PASS_LEN) { if (n > 0) { context = old; - lrad_MD5Update(&context, + fr_MD5Update(&context, passwd + 2 + n - AUTH_PASS_LEN, AUTH_PASS_LEN); } - lrad_MD5Final(digest, &context); + fr_MD5Final(digest, &context); + for (i = 0; i < AUTH_PASS_LEN; i++) { passwd[i + 2 + n] ^= digest[i]; } @@ -631,122 +634,18 @@ static void make_tunnel_passwd(uint8_t *output, int *outlen, memcpy(output, passwd, len + 2); } - /* - * Parse a data structure into a RADIUS attribute. + * Returns the end of the data. */ -int rad_vp2attr(const RADIUS_PACKET *packet, const RADIUS_PACKET *original, - const char *secret, const VALUE_PAIR *vp, uint8_t *ptr) +static int vp2data(const RADIUS_PACKET *packet, const RADIUS_PACKET *original, + const char *secret, const VALUE_PAIR *vp, uint8_t *start, + size_t room) { - int vendorcode; - int offset, len, total_length; - uint32_t lvalue; - uint8_t *length_ptr, *vsa_length_ptr; - const uint8_t *data = NULL; - uint8_t array[4]; - - vendorcode = total_length = 0; - length_ptr = vsa_length_ptr = NULL; - - /* - * For interoperability, always put vendor attributes - * into their own VSA. - */ - if ((vendorcode = VENDOR(vp->attribute)) == 0) { - *(ptr++) = vp->attribute & 0xFF; - length_ptr = ptr; - *(ptr++) = 2; - total_length += 2; - - } else { - int vsa_tlen = 1; - int vsa_llen = 1; - DICT_VENDOR *dv = dict_vendorbyvalue(vendorcode); - - /* - * This must be an RFC-format attribute. If it - * wasn't, then the "decode" function would have - * made a Vendor-Specific attribute (i.e. type - * 26), and we would have "vendorcode == 0" here. - */ - if (dv) { - vsa_tlen = dv->type; - vsa_llen = dv->length; - } - - /* - * Build a VSA header. - */ - *ptr++ = PW_VENDOR_SPECIFIC; - vsa_length_ptr = ptr; - *ptr++ = 6; - lvalue = htonl(vendorcode); - memcpy(ptr, &lvalue, 4); - ptr += 4; - total_length += 6; - - switch (vsa_tlen) { - case 1: - ptr[0] = (vp->attribute & 0xFF); - break; - - case 2: - ptr[0] = ((vp->attribute >> 8) & 0xFF); - ptr[1] = (vp->attribute & 0xFF); - break; - - case 4: - ptr[0] = 0; - ptr[1] = 0; - ptr[2] = ((vp->attribute >> 8) & 0xFF); - ptr[3] = (vp->attribute & 0xFF); - break; - - default: - return 0; /* silently discard it */ - } - ptr += vsa_tlen; - - switch (vsa_llen) { - case 0: - length_ptr = vsa_length_ptr; - vsa_length_ptr = NULL; - break; - case 1: - ptr[0] = 0; - length_ptr = ptr; - break; - case 2: - ptr[0] = 0; - ptr[1] = 0; - length_ptr = ptr + 1; - break; - - default: - return 0; /* silently discard it */ - } - ptr += vsa_llen; - - total_length += vsa_tlen + vsa_llen; - if (vsa_length_ptr) *vsa_length_ptr += vsa_tlen + vsa_llen; - *length_ptr += vsa_tlen + vsa_llen; - } - - offset = 0; - if (vp->flags.has_tag) { - if (TAG_VALID(vp->flags.tag)) { - ptr[0] = vp->flags.tag & 0xff; - offset = 1; - - } else if (vp->flags.encrypt == FLAG_ENCRYPT_TUNNEL_PASSWORD) { - /* - * Tunnel passwords REQUIRE a tag, even - * if don't have a valid tag. - */ - ptr[0] = 0; - offset = 1; - } /* else don't write a tag */ - } /* else the attribute doesn't have a tag */ + uint32_t lvalue; + size_t len; + const uint8_t *data; + uint8_t *ptr = start; + uint8_t array[4]; /* * Set up the default sources for the data. @@ -768,28 +667,20 @@ int rad_vp2attr(const RADIUS_PACKET *packet, const RADIUS_PACKET *original, len = 1; /* just in case */ array[0] = vp->vp_integer & 0xff; data = array; - offset = 0; break; - case PW_TYPE_SHORT: len = 2; /* just in case */ array[0] = (vp->vp_integer >> 8) & 0xff; array[1] = vp->vp_integer & 0xff; data = array; - offset = 0; break; case PW_TYPE_INTEGER: len = 4; /* just in case */ lvalue = htonl(vp->vp_integer); memcpy(array, &lvalue, sizeof(lvalue)); - - /* - * Perhaps discard the first octet. - */ - data = &array[offset]; - len -= offset; + data = array; break; case PW_TYPE_IPADDR: @@ -806,17 +697,34 @@ int rad_vp2attr(const RADIUS_PACKET *packet, const RADIUS_PACKET *original, len = 4; /* just in case */ break; + case PW_TYPE_SIGNED: + { + int32_t slvalue; + + len = 4; /* just in case */ + slvalue = htonl(vp->vp_signed); + memcpy(array, &slvalue, sizeof(slvalue)); + break; + } + + case PW_TYPE_TLV: + data = vp->vp_tlv; + if (!data) { + fr_strerror_printf("ERROR: Cannot encode NULL TLV"); + return -1; + } + if (vp->length > room) return 0; /* can't chop TLVs to fit */ + break; + default: /* unknown type: ignore it */ - librad_log("ERROR: Unknown attribute type %d", vp->type); + fr_strerror_printf("ERROR: Unknown attribute type %d", vp->type); return -1; } /* - * Bound the data to 255 bytes. + * Bound the data to the calling size */ - if (len + offset + total_length > 255) { - len = 255 - offset - total_length; - } + if (len > room) len = room; /* * Encrypt the various password styles @@ -826,31 +734,46 @@ int rad_vp2attr(const RADIUS_PACKET *packet, const RADIUS_PACKET *original, */ switch (vp->flags.encrypt) { case FLAG_ENCRYPT_USER_PASSWORD: - make_passwd(ptr + offset, &len, - data, len, + make_passwd(ptr, &len, data, len, secret, packet->vector); break; case FLAG_ENCRYPT_TUNNEL_PASSWORD: - if (!original) { - librad_log("ERROR: No request packet, cannot encrypt %s attribute in the vp.", vp->name); - return -1; - } + lvalue = 0; + if (vp->flags.has_tag) lvalue = 1; /* - * Check if 255 - offset - total_length is less - * than 18. If so, we can't fit the data into - * the available space, and we discard the - * attribute. + * Check if there's enough room. If there isn't, + * we discard the attribute. * * This is ONLY a problem if we have multiple VSA's * in one Vendor-Specific, though. */ - if ((255 - offset - total_length) < 18) return 0; + if (room < (18 + lvalue)) return 0; + + switch (packet->code) { + case PW_AUTHENTICATION_ACK: + case PW_AUTHENTICATION_REJECT: + case PW_ACCESS_CHALLENGE: + default: + if (!original) { + fr_strerror_printf("ERROR: No request packet, cannot encrypt %s attribute in the vp.", vp->name); + return -1; + } - make_tunnel_passwd(ptr + offset, &len, - data, len, 255 - offset - total_length, - secret, original->vector); + if (lvalue) ptr[0] = vp->flags.tag; + make_tunnel_passwd(ptr + lvalue, &len, data, len, + room - lvalue, + secret, original->vector); + break; + case PW_ACCOUNTING_REQUEST: + case PW_DISCONNECT_REQUEST: + case PW_COA_REQUEST: + ptr[0] = vp->flags.tag; + make_tunnel_passwd(ptr + 1, &len, data, len - 1, room, + secret, packet->vector); + break; + } break; /* @@ -858,40 +781,398 @@ int rad_vp2attr(const RADIUS_PACKET *packet, const RADIUS_PACKET *original, * always fits. */ case FLAG_ENCRYPT_ASCEND_SECRET: - make_secret(ptr + offset, packet->vector, - secret, data); + make_secret(ptr, packet->vector, secret, data); len = AUTH_VECTOR_LEN; break; default: - /* - * Just copy the data over - */ - memcpy(ptr + offset, data, len); + if (vp->flags.has_tag && TAG_VALID(vp->flags.tag)) { + if (vp->type == PW_TYPE_STRING) { + if (len > (room - 1)) len = room - 1; + ptr[0] = vp->flags.tag; + ptr++; + } else if (vp->type == PW_TYPE_INTEGER) { + array[0] = vp->flags.tag; + } /* else it can't be any other type */ + } + memcpy(ptr, data, len); break; } /* switch over encryption flags */ + return len + (ptr - start);; +} + + +static int rad_vp2rfc(const RADIUS_PACKET *packet, + const RADIUS_PACKET *original, + const char *secret, const VALUE_PAIR *vp, + unsigned int attribute, uint8_t *ptr, size_t room) +{ + int len; + + if (room < 2) return 0; + + ptr[0] = attribute & 0xff; /* NOT vp->attribute */ + ptr[1] = 2; + + if (room > (255 - ptr[1])) room = 255 - ptr[1]; + len = vp2data(packet, original, secret, vp, ptr + 2, room); + if (len < 0) return len; + + ptr[1] += len; + + return ptr[1]; +} + +extern int fr_wimax_max_tlv; +extern int fr_wimax_shift[]; +extern int fr_wimax_mask[]; + +static int tlv2data(const RADIUS_PACKET *packet, + const RADIUS_PACKET *original, + const char *secret, const VALUE_PAIR *vp, + uint8_t *ptr, size_t room, int nest) +{ + int len; + + if (nest > fr_wimax_max_tlv) return -1; + + if (room < 2) return 0; + room -= 2; + + ptr[0] = (vp->attribute >> fr_wimax_shift[nest]) & fr_wimax_mask[nest]; + ptr[1] = 2; + + /* + * No more nested TLVs: pack the data. + */ + if ((nest == fr_wimax_max_tlv) || + ((vp->attribute >> fr_wimax_shift[nest + 1]) == 0)) { + len = vp2data(packet, original, secret, vp, ptr + 2, room); + } else { + len = tlv2data(packet, original, secret, vp, ptr + 2, room, + nest + 1); + } + if (len <= 0) return len; + + ptr[1] += len; + + return ptr[1]; +} + +static int wimax2data(const RADIUS_PACKET *packet, + const RADIUS_PACKET *original, + const char *secret, const VALUE_PAIR *vp, + uint8_t *start, size_t room, uint8_t *ptr) +{ + int len; + + /* + * Offsets to Vendor-Specific length, and to length of + * WiMAX attribute. + */ +#define VS_OFF (1) +#define WM_OFF (7) + + if (room < 1) return 0; + room--; + + /* + * Account for continuation bytes. The caller has + * already accounting for the continuation byte in the + * Vendor-Specific "length" field. + */ + start[WM_OFF]++; + *(ptr++) = 0; + + /* + * Chop everything to fit in one attribute. + */ + if (room > (255 - 9)) room = (255 - 9); + + /* + * The attribute contains TLVs that we have NOT decoded + * properly, OR it contains TLV that the user has encoded + * manually. If it has no data, OR it's too long, + * discard it. We're not going to walk through its + * contents trying to figure out how to chop it across + * multiple continuations. + */ + if (vp->flags.has_tlv && (!vp->vp_tlv || (vp->length > room))) { + return 0; + } + /* - * Account for the tag (if any). + * The attribute is a top-level integer, ipaddr, etc. + * Encode it. */ - len += offset; + if (!vp->flags.is_tlv) { + len = vp2data(packet, original, secret, vp, ptr, room); + } else { + len = tlv2data(packet, original, secret, vp, ptr, room, 1); + } + + if (len <= 0) return len; + + start[VS_OFF] += len; + start[WM_OFF] += len; + + return start[VS_OFF]; +} + + +static int rad_vp2extended(const RADIUS_PACKET *packet, + const RADIUS_PACKET *original, + const char *secret, const VALUE_PAIR *vp, + unsigned int attribute, uint8_t *ptr, size_t room) +{ + int len = 2; + + if (room < 3) return 0; + + ptr[0] = attribute & 0xff; /* NOT vp->attribute */ + ptr[1] = 3; + + if (vp->flags.extended) { + ptr[2] = (attribute & 0xff00) >> 8; + len++; + + } else if (vp->flags.extended_flags) { + if (room < 4) return 0; + + ptr[1] = 4; + ptr[2] = (attribute & 0xff00) >> 8; + ptr[3] = 0; + + len += 2; + } /* - * RFC 2865 section 5 says that zero-length attributes - * MUST NOT be sent. + * For now, no extended attribute can be longer than the + * encapsulating attribute. Once we add support for the + * "M" bit, this restriction will be relaxed. */ - if (len == 0) return 0; + if (room > (255 - ptr[1])) room = 255 - ptr[1]; + + if (!vp->flags.is_tlv) { + len = vp2data(packet, original, secret, vp, ptr + ptr[1], room); + } else { + len = tlv2data(packet, original, secret, vp, ptr + ptr[1], room, 2); + } + + if (len < 0) return len; + + ptr[1] += len; + + return ptr[1]; +} + +/* + * Parse a data structure into a RADIUS attribute. + */ +int rad_vp2attr(const RADIUS_PACKET *packet, const RADIUS_PACKET *original, + const char *secret, const VALUE_PAIR *vp, uint8_t *start, + size_t room) +{ + int len; + uint32_t lvalue; + uint8_t *ptr; + DICT_VENDOR *dv; /* - * Update the various lengths. + * RFC format attributes take the fast path. */ - *length_ptr += len; - if (vsa_length_ptr) *vsa_length_ptr += len; + if (vp->vendor == 0) { + return rad_vp2rfc(packet, original, secret, vp, + vp->attribute, start, room); + } + + if (vp->vendor == VENDORPEC_EXTENDED) { + return rad_vp2extended(packet, original, secret, vp, + vp->attribute, start, room); + } + + /* + * Not enough room for: + * attr, len, vendor-id, vsa, vsalen + */ + if (room < 8) return 0; + + /* + * Build the Vendor-Specific header + */ + ptr = start; + *ptr++ = PW_VENDOR_SPECIFIC; + *ptr++ = 6; + room -= 6; + lvalue = htonl(vp->vendor); + memcpy(ptr, &lvalue, 4); + ptr += 4; + + /* + * Unknown vendors, and type=1,length=1,no-continuation + * are RFC format attributes. + */ + dv = dict_vendorbyvalue(vp->vendor); + if (!dv || + ((dv->type == 1) && (dv->length = 1) && !dv->flags)) { + len = rad_vp2rfc(packet, original, secret, vp, + vp->attribute, ptr, room); + if (len <= 0) return len; + + start[1] += len; + return start[1]; + } + + if (room < (dv->type + dv->length + dv->flags)) return 0; + room -= (dv->type + dv->length + dv->flags); + start[1] += (dv->type + dv->length + dv->flags); + + switch (dv->type) { + case 1: + ptr[0] = (vp->attribute & 0xFF); + break; + + case 2: + ptr[0] = ((vp->attribute >> 8) & 0xFF); + ptr[1] = (vp->attribute & 0xFF); + break; + + case 4: + ptr[0] = 0; + ptr[1] = ((vp->attribute >> 16) & 0xFF); + ptr[2] = ((vp->attribute >> 8) & 0xFF); + ptr[3] = (vp->attribute & 0xFF); + break; + + default: + return 0; /* silently discard it */ + } + ptr += dv->type; + + switch (dv->length) { + case 0: + break; + case 1: + ptr[0] = dv->type + 1; + break; + case 2: + ptr[0] = 0; + ptr[1] = dv->type + 2; + break; + + default: + return 0; /* silently discard it */ + } + ptr += dv->length; + + /* + * WiMAX attributes take their own path through the + * system. + */ + if (dv->flags) return wimax2data(packet, original, secret, vp, + start, room, ptr); + + len = vp2data(packet, original, secret, vp, ptr, room); + if (len <= 0) return len; + + if (dv->length != 0) ptr[-1] += len; + + start[1] += len; + + return start[1]; +} + +/* + * Swap 123a -> 0321 + */ +#define REORDER(x) ((x & 0xff00) << 8) | ((x & 0xff0000) >> 8) | ((x & 0xff000000 >> 24)) + + +/* + * Encode a WiMAX sub-TLV. It must NOT be called for WiMAX + * attributes that are of type integer, string, etc. + */ +static int rad_encode_wimax(const RADIUS_PACKET *packet, + const RADIUS_PACKET *original, + const char *secret, VALUE_PAIR *reply, + uint8_t *start, size_t room) +{ + int len, redo; + uint32_t lvalue; + uint8_t *ptr = start, *vsa = start; + uint32_t maxattr; + VALUE_PAIR *vp = reply; + + /* + * Swap the order of the WiMAX hacks, to make later + * comparisons easier. + */ + maxattr = REORDER(vp->attribute); + + /* + * Build the Vendor-Specific header + */ + ptr = start; + redo = 0; + +redo_vsa: + vsa = ptr; + + if (room < 9) return 0; + *ptr++ = PW_VENDOR_SPECIFIC; + *ptr++ = 9; + room -= 9; + lvalue = htonl(vp->vendor); + memcpy(ptr, &lvalue, 4); + ptr += 4; + *(ptr++) = vp->attribute & 0xff; + *(ptr++) = 3; + *(ptr++) = 0; /* continuation */ + room -= 9; + +redo_tlv: + len = tlv2data(packet, original, secret, vp, ptr, room, 1); + if (len < 0) return len; + + /* + * Not enough room. Do a continuation. + */ + if ((len == 0) || ((vsa[VS_OFF] + len) > 255)) { + if (redo) return (start - vsa); + + vsa[8] = 0x80; + redo = 1; + goto redo_vsa; + } + redo = 0; + ptr += len; - total_length += len; + vsa[VS_OFF] += len; + vsa[WM_OFF] += len; + + vp->flags.encoded = 1; + vp = vp->next; + + /* + * Look at the NEXT tlv. Ensure that we encode + * attributes into a common VSA *only* if they are for + * the same WiMAX VSA, AND if the TLVs are in numerically + * increasing order. + */ + if (vp && vp->flags.is_tlv && (reply->vendor == vp->vendor) && + ((reply->attribute & 0xff) == (vp->attribute & 0xff))) { + uint32_t attr; + + attr = REORDER(vp->attribute); + if (attr >= maxattr) { + maxattr = attr; + goto redo_tlv; + } + } - return total_length; /* of attribute */ + return ptr - start; } @@ -910,16 +1191,12 @@ int rad_encode(RADIUS_PACKET *packet, const RADIUS_PACKET *original, char ip_buffer[128]; /* - * For simplicity in the following logic, we allow - * the attributes to "overflow" the 4k maximum - * RADIUS packet size, by one attribute. - * - * It's uint32_t, for alignment purposes. + * A 4K packet, aligned on 64-bits. */ - uint32_t data[(MAX_PACKET_LEN + 256) / 4]; + uint64_t data[MAX_PACKET_LEN / sizeof(uint64_t)]; - if ((packet->code > 0) && (packet->code < MAX_PACKET_CODE)) { - what = packet_codes[packet->code]; + if ((packet->code > 0) && (packet->code < FR_MAX_PACKET_CODE)) { + what = fr_packet_codes[packet->code]; } else { what = "Reply"; } @@ -939,7 +1216,7 @@ int rad_encode(RADIUS_PACKET *packet, const RADIUS_PACKET *original, case PW_AUTHENTICATION_REJECT: case PW_ACCESS_CHALLENGE: if (!original) { - librad_log("ERROR: Cannot sign response packet without a request packet."); + fr_strerror_printf("ERROR: Cannot sign response packet without a request packet."); return -1; } break; @@ -993,10 +1270,23 @@ int rad_encode(RADIUS_PACKET *packet, const RADIUS_PACKET *original, */ for (reply = packet->vps; reply; reply = reply->next) { /* - * Ignore non-wire attributes + * Ignore non-wire attributes, but allow extended + * attributes. */ - if ((VENDOR(reply->attribute) == 0) && - ((reply->attribute & 0xFFFF) > 0xff)) { + if ((reply->vendor == 0) && + ((reply->attribute & 0xFFFF) >= 256) && + !reply->flags.extended && !reply->flags.extended_flags) { +#ifndef NDEBUG + /* + * Permit the admin to send BADLY formatted + * attributes with a debug build. + */ + if (reply->attribute == PW_RAW_ATTRIBUTE) { + memcpy(ptr, reply->vp_octets, reply->length); + len = reply->length; + goto next; + } +#endif continue; } @@ -1022,22 +1312,34 @@ int rad_encode(RADIUS_PACKET *packet, const RADIUS_PACKET *original, */ debug_pair(reply); - len = rad_vp2attr(packet, original, secret, reply, ptr); + /* + * Skip attributes that are encoded. + */ + if (reply->flags.encoded) continue; + + if ((reply->vendor == VENDORPEC_WIMAX) && reply->flags.is_tlv) { + len = rad_encode_wimax(packet, original, secret, + reply, ptr, + ((uint8_t *) data) + sizeof(data) - ptr); + } else { + + len = rad_vp2attr(packet, original, secret, reply, ptr, + ((uint8_t *) data) + sizeof(data) - ptr); + } if (len < 0) return -1; /* - * Check that the packet is no more than 4k in - * size, AFTER writing the attribute past the 4k - * boundary, but BEFORE deciding to increase the - * size of the packet. Note that the 'data' - * buffer, above, is one attribute longer than - * necessary, in order to permit this overflow. + * Failed to encode the attribute, likely because + * the packet is full. */ - if ((total_length + len) > MAX_PACKET_LEN) { + if ((len == 0) && + (total_length > (sizeof(data) - 2 - reply->length))) { + DEBUG("WARNING: Attributes are too long for packet. Discarding data past %d bytes", total_length); break; } + next: ptr += len; total_length += len; } /* done looping over all attributes */ @@ -1053,11 +1355,11 @@ int rad_encode(RADIUS_PACKET *packet, const RADIUS_PACKET *original, packet->data_len = total_length; packet->data = (uint8_t *) malloc(packet->data_len); if (!packet->data) { - librad_log("Out of memory"); + fr_strerror_printf("Out of memory"); return -1; } - memcpy(packet->data, data, packet->data_len); + memcpy(packet->data, hdr, packet->data_len); hdr = (radius_packet_t *) packet->data; total_length = htons(total_length); @@ -1079,13 +1381,13 @@ int rad_sign(RADIUS_PACKET *packet, const RADIUS_PACKET *original, * It wasn't assigned an Id, this is bad! */ if (packet->id < 0) { - librad_log("ERROR: RADIUS packets must be assigned an Id."); + fr_strerror_printf("ERROR: RADIUS packets must be assigned an Id."); return -1; } if (!packet->data || (packet->data_len < AUTH_HDR_LEN) || (packet->offset < 0)) { - librad_log("ERROR: You must call rad_encode() before rad_sign()"); + fr_strerror_printf("ERROR: You must call rad_encode() before rad_sign()"); return -1; } @@ -1112,7 +1414,7 @@ int rad_sign(RADIUS_PACKET *packet, const RADIUS_PACKET *original, case PW_AUTHENTICATION_REJECT: case PW_ACCESS_CHALLENGE: if (!original) { - librad_log("ERROR: Cannot sign response packet without a request packet."); + fr_strerror_printf("ERROR: Cannot sign response packet without a request packet."); return -1; } memcpy(hdr->vector, original->vector, @@ -1130,9 +1432,9 @@ int rad_sign(RADIUS_PACKET *packet, const RADIUS_PACKET *original, * into the Message-Authenticator * attribute. */ - lrad_hmac_md5(packet->data, packet->data_len, - secret, strlen(secret), - calc_auth_vector); + fr_hmac_md5(packet->data, packet->data_len, + (const uint8_t *) secret, strlen(secret), + calc_auth_vector); memcpy(packet->data + packet->offset + 2, calc_auth_vector, AUTH_VECTOR_LEN); @@ -1164,11 +1466,12 @@ int rad_sign(RADIUS_PACKET *packet, const RADIUS_PACKET *original, { uint8_t digest[16]; - MD5_CTX context; - MD5Init(&context); - MD5Update(&context, packet->data, packet->data_len); - MD5Update(&context, secret, strlen(secret)); - MD5Final(digest, &context); + FR_MD5_CTX context; + fr_MD5Init(&context); + fr_MD5Update(&context, packet->data, packet->data_len); + fr_MD5Update(&context, (const uint8_t *) secret, + strlen(secret)); + fr_MD5Final(digest, &context); memcpy(hdr->vector, digest, AUTH_VECTOR_LEN); memcpy(packet->vector, digest, AUTH_VECTOR_LEN); @@ -1197,8 +1500,8 @@ int rad_send(RADIUS_PACKET *packet, const RADIUS_PACKET *original, return 0; } - if ((packet->code > 0) && (packet->code < MAX_PACKET_CODE)) { - what = packet_codes[packet->code]; + if ((packet->code > 0) && (packet->code < FR_MAX_PACKET_CODE)) { + what = fr_packet_codes[packet->code]; } else { what = "Reply"; } @@ -1226,7 +1529,7 @@ int rad_send(RADIUS_PACKET *packet, const RADIUS_PACKET *original, * If packet->data points to data, then we print out * the VP list again only for debugging. */ - } else if (librad_debug) { + } else if (fr_debug_flag) { DEBUG("Sending %s of id %d to %s port %d\n", what, packet->id, inet_ntop(packet->dst_ipaddr.af, &packet->dst_ipaddr.ipaddr, @@ -1234,7 +1537,8 @@ int rad_send(RADIUS_PACKET *packet, const RADIUS_PACKET *original, packet->dst_port); for (reply = packet->vps; reply; reply = reply->next) { - /* FIXME: ignore attributes > 0xff */ + if ((reply->vendor == 0) && + ((reply->attribute & 0xFFFF) > 0xff)) continue; debug_pair(reply); } } @@ -1243,8 +1547,28 @@ int rad_send(RADIUS_PACKET *packet, const RADIUS_PACKET *original, * And send it on it's way. */ return rad_sendto(packet->sockfd, packet->data, packet->data_len, 0, - &packet->src_ipaddr, &packet->dst_ipaddr, - packet->dst_port); + &packet->src_ipaddr, packet->src_port, + &packet->dst_ipaddr, packet->dst_port); +} + +/* + * Do a comparison of two authentication digests by comparing + * the FULL digest. Otehrwise, the server can be subject to + * timing attacks that allow attackers find a valid message + * authenticator. + * + * http://www.cs.rice.edu/~dwallach/pub/crosby-timing2009.pdf + */ +static int digest_cmp(const uint8_t *a, const uint8_t *b, size_t length) +{ + int result = 0; + size_t i; + + for (i = 0; i < length; i++) { + result |= a[i] ^ b[i]; + } + + return result; /* 0 is OK, !0 is !OK, just like memcmp */ } @@ -1255,7 +1579,7 @@ int rad_send(RADIUS_PACKET *packet, const RADIUS_PACKET *original, static int calc_acctdigest(RADIUS_PACKET *packet, const char *secret) { uint8_t digest[AUTH_VECTOR_LEN]; - MD5_CTX context; + FR_MD5_CTX context; /* * Zero out the auth_vector in the received packet. @@ -1268,15 +1592,15 @@ static int calc_acctdigest(RADIUS_PACKET *packet, const char *secret) /* * MD5(packet + secret); */ - MD5Init(&context); - MD5Update(&context, packet->data, packet->data_len); - MD5Update(&context, secret, strlen(secret)); - MD5Final(digest, &context); + fr_MD5Init(&context); + fr_MD5Update(&context, packet->data, packet->data_len); + fr_MD5Update(&context, (const uint8_t *) secret, strlen(secret)); + fr_MD5Final(digest, &context); /* * Return 0 if OK, 2 if not OK. */ - if (memcmp(digest, packet->vector, AUTH_VECTOR_LEN) != 0) return 2; + if (digest_cmp(digest, packet->vector, AUTH_VECTOR_LEN) != 0) return 2; return 0; } @@ -1289,7 +1613,7 @@ static int calc_replydigest(RADIUS_PACKET *packet, RADIUS_PACKET *original, const char *secret) { uint8_t calc_digest[AUTH_VECTOR_LEN]; - MD5_CTX context; + FR_MD5_CTX context; /* * Very bad! @@ -1306,10 +1630,10 @@ static int calc_replydigest(RADIUS_PACKET *packet, RADIUS_PACKET *original, /* * MD5(packet + secret); */ - MD5Init(&context); - MD5Update(&context, packet->data, packet->data_len); - MD5Update(&context, secret, strlen(secret)); - MD5Final(calc_digest, &context); + fr_MD5Init(&context); + fr_MD5Update(&context, packet->data, packet->data_len); + fr_MD5Update(&context, (const uint8_t *) secret, strlen(secret)); + fr_MD5Final(calc_digest, &context); /* * Copy the packet's vector back to the packet. @@ -1319,7 +1643,7 @@ static int calc_replydigest(RADIUS_PACKET *packet, RADIUS_PACKET *original, /* * Return 0 if OK, 2 if not OK. */ - if (memcmp(packet->vector, calc_digest, AUTH_VECTOR_LEN) != 0) return 2; + if (digest_cmp(packet->vector, calc_digest, AUTH_VECTOR_LEN) != 0) return 2; return 0; } @@ -1330,7 +1654,7 @@ static int calc_replydigest(RADIUS_PACKET *packet, RADIUS_PACKET *original, * packet is not 'const * const' because we may update data_len, * if there's more data in the UDP packet than in the RADIUS packet. */ -int rad_packet_ok(RADIUS_PACKET *packet) +int rad_packet_ok(RADIUS_PACKET *packet, int flags) { uint8_t *attr; int totallen; @@ -1349,11 +1673,11 @@ int rad_packet_ok(RADIUS_PACKET *packet) * "The minimum length is 20 ..." */ if (packet->data_len < AUTH_HDR_LEN) { - librad_log("WARNING: Malformed RADIUS packet from host %s: too short (received %d < minimum %d)", + fr_strerror_printf("WARNING: Malformed RADIUS packet from host %s: too short (received %d < minimum %d)", inet_ntop(packet->src_ipaddr.af, &packet->src_ipaddr.ipaddr, host_ipaddr, sizeof(host_ipaddr)), - packet->data_len, AUTH_HDR_LEN); + (int) packet->data_len, AUTH_HDR_LEN); return 0; } @@ -1363,11 +1687,11 @@ int rad_packet_ok(RADIUS_PACKET *packet) * " ... and maximum length is 4096." */ if (packet->data_len > MAX_PACKET_LEN) { - librad_log("WARNING: Malformed RADIUS packet from host %s: too long (received %d > maximum %d)", + fr_strerror_printf("WARNING: Malformed RADIUS packet from host %s: too long (received %d > maximum %d)", inet_ntop(packet->src_ipaddr.af, &packet->src_ipaddr.ipaddr, host_ipaddr, sizeof(host_ipaddr)), - packet->data_len, MAX_PACKET_LEN); + (int) packet->data_len, MAX_PACKET_LEN); return 0; } @@ -1384,8 +1708,8 @@ int rad_packet_ok(RADIUS_PACKET *packet) * Code of 16 or greate is not understood. */ if ((hdr->code == 0) || - (hdr->code >= MAX_PACKET_CODE)) { - librad_log("WARNING: Bad RADIUS packet from host %s: unknown packet code %d", + (hdr->code >= FR_MAX_PACKET_CODE)) { + fr_strerror_printf("WARNING: Bad RADIUS packet from host %s: unknown packet code%d ", inet_ntop(packet->src_ipaddr.af, &packet->src_ipaddr.ipaddr, host_ipaddr, sizeof(host_ipaddr)), @@ -1400,6 +1724,11 @@ int rad_packet_ok(RADIUS_PACKET *packet) if (hdr->code == PW_STATUS_SERVER) require_ma = 1; /* + * It's also required if the caller asks for it. + */ + if (flags) require_ma = 1; + + /* * Repeat the length checks. This time, instead of * looking at the data we received, look at the value * of the 'length' field inside of the packet. @@ -1411,7 +1740,7 @@ int rad_packet_ok(RADIUS_PACKET *packet) * "The minimum length is 20 ..." */ if (totallen < AUTH_HDR_LEN) { - librad_log("WARNING: Malformed RADIUS packet from host %s: too short (length %d < minimum %d)", + fr_strerror_printf("WARNING: Malformed RADIUS packet from host %s: too short (length %d < minimum %d)", inet_ntop(packet->src_ipaddr.af, &packet->src_ipaddr.ipaddr, host_ipaddr, sizeof(host_ipaddr)), @@ -1427,7 +1756,7 @@ int rad_packet_ok(RADIUS_PACKET *packet) * " ... and maximum length is 4096." */ if (totallen > MAX_PACKET_LEN) { - librad_log("WARNING: Malformed RADIUS packet from host %s: too long (length %d > maximum %d)", + fr_strerror_printf("WARNING: Malformed RADIUS packet from host %s: too long (length %d > maximum %d)", inet_ntop(packet->src_ipaddr.af, &packet->src_ipaddr.ipaddr, host_ipaddr, sizeof(host_ipaddr)), @@ -1444,11 +1773,11 @@ int rad_packet_ok(RADIUS_PACKET *packet) * i.e. No response to the NAS. */ if (packet->data_len < totallen) { - librad_log("WARNING: Malformed RADIUS packet from host %s: received %d octets, packet length says %d", + fr_strerror_printf("WARNING: Malformed RADIUS packet from host %s: received %d octets, packet length says %d", inet_ntop(packet->src_ipaddr.af, &packet->src_ipaddr.ipaddr, host_ipaddr, sizeof(host_ipaddr)), - packet->data_len, totallen); + (int) packet->data_len, totallen); return 0; } @@ -1485,10 +1814,22 @@ int rad_packet_ok(RADIUS_PACKET *packet) while (count > 0) { /* + * We need at least 2 bytes to check the + * attribute header. + */ + if (count < 2) { + fr_strerror_printf("WARNING: Malformed RADIUS packet from host %s: attribute header overflows the packet", + inet_ntop(packet->src_ipaddr.af, + &packet->src_ipaddr.ipaddr, + host_ipaddr, sizeof(host_ipaddr))); + return 0; + } + + /* * Attribute number zero is NOT defined. */ if (attr[0] == 0) { - librad_log("WARNING: Malformed RADIUS packet from host %s: Invalid attribute 0", + fr_strerror_printf("WARNING: Malformed RADIUS packet from host %s: Invalid attribute 0", inet_ntop(packet->src_ipaddr.af, &packet->src_ipaddr.ipaddr, host_ipaddr, sizeof(host_ipaddr))); @@ -1500,7 +1841,7 @@ int rad_packet_ok(RADIUS_PACKET *packet) * fields. Anything shorter is an invalid attribute. */ if (attr[1] < 2) { - librad_log("WARNING: Malformed RADIUS packet from host %s: attribute %d too short", + fr_strerror_printf("WARNING: Malformed RADIUS packet from host %s: attribute %u too short", inet_ntop(packet->src_ipaddr.af, &packet->src_ipaddr.ipaddr, host_ipaddr, sizeof(host_ipaddr)), @@ -1509,6 +1850,19 @@ int rad_packet_ok(RADIUS_PACKET *packet) } /* + * If there are fewer bytes in the packet than in the + * attribute, it's a bad packet. + */ + if (count < attr[1]) { + fr_strerror_printf("WARNING: Malformed RADIUS packet from host %s: attribute %u data overflows the packet", + inet_ntop(packet->src_ipaddr.af, + &packet->src_ipaddr.ipaddr, + host_ipaddr, sizeof(host_ipaddr)), + attr[0]); + return 0; + } + + /* * Sanity check the attributes for length. */ switch (attr[0]) { @@ -1525,7 +1879,7 @@ int rad_packet_ok(RADIUS_PACKET *packet) case PW_MESSAGE_AUTHENTICATOR: if (attr[1] != 2 + AUTH_VECTOR_LEN) { - librad_log("WARNING: Malformed RADIUS packet from host %s: Message-Authenticator has invalid length %d", + fr_strerror_printf("WARNING: Malformed RADIUS packet from host %s: Message-Authenticator has invalid length %d", inet_ntop(packet->src_ipaddr.af, &packet->src_ipaddr.ipaddr, host_ipaddr, sizeof(host_ipaddr)), @@ -1553,7 +1907,7 @@ int rad_packet_ok(RADIUS_PACKET *packet) * If not, we complain, and throw the packet away. */ if (count != 0) { - librad_log("WARNING: Malformed RADIUS packet from host %s: packet attributes do NOT exactly fill the packet", + fr_strerror_printf("WARNING: Malformed RADIUS packet from host %s: packet attributes do NOT exactly fill the packet", inet_ntop(packet->src_ipaddr.af, &packet->src_ipaddr.ipaddr, host_ipaddr, sizeof(host_ipaddr))); @@ -1565,13 +1919,13 @@ int rad_packet_ok(RADIUS_PACKET *packet) * attributes, and we've seen more than that maximum, * then throw the packet away, as a possible DoS. */ - if ((librad_max_attributes > 0) && - (num_attributes > librad_max_attributes)) { - librad_log("WARNING: Possible DoS attack from host %s: Too many attributes in request (received %d, max %d are allowed).", + if ((fr_max_attributes > 0) && + (num_attributes > fr_max_attributes)) { + fr_strerror_printf("WARNING: Possible DoS attack from host %s: Too many attributes in request (received %d, max %d are allowed).", inet_ntop(packet->src_ipaddr.af, &packet->src_ipaddr.ipaddr, host_ipaddr, sizeof(host_ipaddr)), - num_attributes, librad_max_attributes); + num_attributes, fr_max_attributes); return 0; } @@ -1587,7 +1941,7 @@ int rad_packet_ok(RADIUS_PACKET *packet) * Message-Authenticator attributes. */ if (require_ma && ! seen_ma) { - librad_log("WARNING: Insecure packet from host %s: Packet does not contain required Message-Authenticator attribute", + fr_strerror_printf("WARNING: Insecure packet from host %s: Packet does not contain required Message-Authenticator attribute", inet_ntop(packet->src_ipaddr.af, &packet->src_ipaddr.ipaddr, host_ipaddr, sizeof(host_ipaddr))); @@ -1609,20 +1963,26 @@ int rad_packet_ok(RADIUS_PACKET *packet) * Receive UDP client requests, and fill in * the basics of a RADIUS_PACKET structure. */ -RADIUS_PACKET *rad_recv(int fd) +RADIUS_PACKET *rad_recv(int fd, int flags) { + int sock_flags = 0; RADIUS_PACKET *packet; /* * Allocate the new request data structure */ if ((packet = malloc(sizeof(*packet))) == NULL) { - librad_log("out of memory"); + fr_strerror_printf("out of memory"); return NULL; } memset(packet, 0, sizeof(*packet)); - packet->data_len = rad_recvfrom(fd, &packet->data, 0, + if (flags & 0x02) { + sock_flags = MSG_PEEK; + flags &= ~0x02; + } + + packet->data_len = rad_recvfrom(fd, &packet->data, sock_flags, &packet->src_ipaddr, &packet->src_port, &packet->dst_ipaddr, &packet->dst_port); @@ -1630,7 +1990,7 @@ RADIUS_PACKET *rad_recv(int fd) * Check for socket errors. */ if (packet->data_len < 0) { - librad_log("Error receiving packet: %s", strerror(errno)); + fr_strerror_printf("Error receiving packet: %s", strerror(errno)); /* packet->data is NULL */ free(packet); return NULL; @@ -1642,7 +2002,7 @@ RADIUS_PACKET *rad_recv(int fd) * packet. */ if (packet->data_len > MAX_PACKET_LEN) { - librad_log("Discarding packet: Larger than RFC limitation of 4096 bytes."); + fr_strerror_printf("Discarding packet: Larger than RFC limitation of 4096 bytes."); /* packet->data is NULL */ free(packet); return NULL; @@ -1655,7 +2015,7 @@ RADIUS_PACKET *rad_recv(int fd) * packet->data == NULL */ if ((packet->data_len == 0) || !packet->data) { - librad_log("No data."); + fr_strerror_printf("Empty packet: Socket is not ready."); free(packet); return NULL; } @@ -1663,7 +2023,7 @@ RADIUS_PACKET *rad_recv(int fd) /* * See if it's a well-formed RADIUS packet. */ - if (!rad_packet_ok(packet)) { + if (!rad_packet_ok(packet, flags)) { rad_free(&packet); return NULL; } @@ -1684,25 +2044,26 @@ RADIUS_PACKET *rad_recv(int fd) */ packet->vps = NULL; - if (librad_debug) { + if (fr_debug_flag) { char host_ipaddr[128]; - if ((packet->code > 0) && (packet->code < MAX_PACKET_CODE)) { - printf("rad_recv: %s packet from host %s port %d", - packet_codes[packet->code], - inet_ntop(packet->src_ipaddr.af, - &packet->src_ipaddr.ipaddr, - host_ipaddr, sizeof(host_ipaddr)), - packet->src_port); + if ((packet->code > 0) && (packet->code < FR_MAX_PACKET_CODE)) { + DEBUG("rad_recv: %s packet from host %s port %d", + fr_packet_codes[packet->code], + inet_ntop(packet->src_ipaddr.af, + &packet->src_ipaddr.ipaddr, + host_ipaddr, sizeof(host_ipaddr)), + packet->src_port); } else { - printf("rad_recv: Packet from host %s port %d code=%d", - inet_ntop(packet->src_ipaddr.af, - &packet->src_ipaddr.ipaddr, - host_ipaddr, sizeof(host_ipaddr)), - packet->src_port, - packet->code); + DEBUG("rad_recv: Packet from host %s port %d code=%d", + inet_ntop(packet->src_ipaddr.af, + &packet->src_ipaddr.ipaddr, + host_ipaddr, sizeof(host_ipaddr)), + packet->src_port, + packet->code); } - printf(", id=%d, length=%d\n", packet->id, packet->data_len); + DEBUG(", id=%d, length=%d\n", + packet->id, (int) packet->data_len); } return packet; @@ -1764,19 +2125,20 @@ int rad_verify(RADIUS_PACKET *packet, RADIUS_PACKET *original, case PW_AUTHENTICATION_REJECT: case PW_ACCESS_CHALLENGE: if (!original) { - librad_log("ERROR: Cannot validate Message-Authenticator in response packet without a request packet."); + fr_strerror_printf("ERROR: Cannot validate Message-Authenticator in response packet without a request packet."); return -1; } memcpy(packet->data + 4, original->vector, AUTH_VECTOR_LEN); break; } - lrad_hmac_md5(packet->data, packet->data_len, - secret, strlen(secret), calc_auth_vector); - if (memcmp(calc_auth_vector, msg_auth_vector, + fr_hmac_md5(packet->data, packet->data_len, + (const uint8_t *) secret, strlen(secret), + calc_auth_vector); + if (digest_cmp(calc_auth_vector, msg_auth_vector, sizeof(calc_auth_vector)) != 0) { char buffer[32]; - librad_log("Received packet from %s with invalid Message-Authenticator! (Shared secret is incorrect.)", + fr_strerror_printf("Received packet from %s with invalid Message-Authenticator! (Shared secret is incorrect.)", inet_ntop(packet->src_ipaddr.af, &packet->src_ipaddr.ipaddr, buffer, sizeof(buffer))); @@ -1800,10 +2162,10 @@ int rad_verify(RADIUS_PACKET *packet, RADIUS_PACKET *original, * It looks like a RADIUS packet, but we can't validate * the signature. */ - if ((packet->code == 0) || packet->code >= MAX_PACKET_CODE) { + if ((packet->code == 0) || (packet->code >= FR_MAX_PACKET_CODE)) { char buffer[32]; - librad_log("Received Unknown packet code %d" - "from client %s port %d: Cannot validate signature", + fr_strerror_printf("Received Unknown packet code %d " + "from client %s port %d: Cannot validate signature.", packet->code, inet_ntop(packet->src_ipaddr.af, &packet->src_ipaddr.ipaddr, @@ -1821,17 +2183,19 @@ int rad_verify(RADIUS_PACKET *packet, RADIUS_PACKET *original, case PW_AUTHENTICATION_REQUEST: case PW_STATUS_SERVER: - case PW_DISCONNECT_REQUEST: /* * The authentication vector is random * nonsense, invented by the client. */ break; + case PW_COA_REQUEST: + case PW_DISCONNECT_REQUEST: case PW_ACCOUNTING_REQUEST: if (calc_acctdigest(packet, secret) > 1) { - librad_log("Received Accounting-Request packet " - "from %s with invalid signature! (Shared secret is incorrect.)", + fr_strerror_printf("Received %s packet " + "from client %s with invalid signature! (Shared secret is incorrect.)", + fr_packet_codes[packet->code], inet_ntop(packet->src_ipaddr.af, &packet->src_ipaddr.ipaddr, buffer, sizeof(buffer))); @@ -1850,20 +2214,19 @@ int rad_verify(RADIUS_PACKET *packet, RADIUS_PACKET *original, case PW_COA_NAK: rcode = calc_replydigest(packet, original, secret); if (rcode > 1) { - librad_log("Received %s packet " - "from client %s port %d with invalid signature (err=%d)! (Shared secret is incorrect.)", - packet_codes[packet->code], + fr_strerror_printf("Received %s packet " + "from home server %s port %d with invalid signature! (Shared secret is incorrect.)", + fr_packet_codes[packet->code], inet_ntop(packet->src_ipaddr.af, &packet->src_ipaddr.ipaddr, buffer, sizeof(buffer)), - packet->src_port, - rcode); + packet->src_port); return -1; } break; default: - librad_log("Received Unknown packet code %d" + fr_strerror_printf("Received Unknown packet code %d " "from client %s port %d: Cannot validate signature", packet->code, inet_ntop(packet->src_ipaddr.af, @@ -1877,19 +2240,12 @@ int rad_verify(RADIUS_PACKET *packet, RADIUS_PACKET *original, } -/* - * Parse a RADIUS attribute into a data structure. - */ -VALUE_PAIR *rad_attr2vp(const RADIUS_PACKET *packet, const RADIUS_PACKET *original, - const char *secret, int attribute, int length, - const uint8_t *data) +static VALUE_PAIR *data2vp(const RADIUS_PACKET *packet, + const RADIUS_PACKET *original, + const char *secret, size_t length, + const uint8_t *data, VALUE_PAIR *vp) { int offset = 0; - VALUE_PAIR *vp; - - if ((vp = paircreate(attribute, PW_TYPE_OCTETS)) == NULL) { - return NULL; - } /* * If length is greater than 253, something is SERIOUSLY @@ -1902,6 +2258,15 @@ VALUE_PAIR *rad_attr2vp(const RADIUS_PACKET *packet, const RADIUS_PACKET *origin vp->next = NULL; /* + * It's supposed to be a fixed length, but we found + * a different length instead. Make it type "octets", + * and do no more processing on it. + */ + if ((vp->flags.length > 0) && (vp->flags.length != length)) { + goto raw; + } + + /* * Handle tags. */ if (vp->flags.has_tag) { @@ -1914,7 +2279,10 @@ VALUE_PAIR *rad_attr2vp(const RADIUS_PACKET *packet, const RADIUS_PACKET *origin vp->flags.tag = data[0]; if ((vp->type == PW_TYPE_STRING) || - (vp->type == PW_TYPE_OCTETS)) offset = 1; + (vp->type == PW_TYPE_OCTETS)) { + if (length == 0) goto raw; + offset = 1; + } } } @@ -2016,7 +2384,7 @@ VALUE_PAIR *rad_attr2vp(const RADIUS_PACKET *packet, const RADIUS_PACKET *origin */ { DICT_VALUE *dval; - dval = dict_valbyattr(vp->attribute, + dval = dict_valbyattr(vp->attribute, vp->vendor, vp->vp_integer); if (dval) { strlcpy(vp->vp_strvalue, @@ -2079,23 +2447,483 @@ VALUE_PAIR *rad_attr2vp(const RADIUS_PACKET *packet, const RADIUS_PACKET *origin } break; - default: - raw: - vp->type = PW_TYPE_OCTETS; + case PW_TYPE_SIGNED: + if (vp->length != 4) goto raw; + + /* + * Overload vp_integer for ntohl, which takes + * uint32_t, not int32_t + */ + memcpy(&vp->vp_integer, vp->vp_octets, 4); + vp->vp_integer = ntohl(vp->vp_integer); + memcpy(&vp->vp_signed, &vp->vp_integer, 4); + break; + + case PW_TYPE_TLV: vp->length = length; - memcpy(vp->vp_octets, data, length); + vp->vp_tlv = malloc(length); + if (!vp->vp_tlv) { + pairfree(&vp); + fr_strerror_printf("No memory"); + return NULL; + } + memcpy(vp->vp_tlv, data, length); + break; + case PW_TYPE_COMBO_IP: + if (vp->length == 4) { + vp->type = PW_TYPE_IPADDR; + memcpy(&vp->vp_ipaddr, vp->vp_octets, 4); + break; + + } else if (vp->length == 16) { + vp->type = PW_TYPE_IPV6ADDR; + /* vp->vp_ipv6addr == vp->vp_octets */ + break; + + } + /* FALL-THROUGH */ + default: + raw: /* - * Ensure there's no encryption or tag stuff, - * we just pass the attribute as-is. + * Change the name to show the user that the + * attribute is not of the correct format. */ - memset(&vp->flags, 0, sizeof(vp->flags)); + { + int attr = vp->attribute; + int vendor = vp->vendor; + VALUE_PAIR *vp2; + + vp2 = pairalloc(NULL); + if (!vp2) { + pairfree(&vp); + return NULL; + } + pairfree(&vp); + vp = vp2; + + /* + * This sets "vp->flags" appropriately, + * and vp->type. + */ + if (!paircreate_raw(attr, vendor, PW_TYPE_OCTETS, vp)) { + return NULL; + } + + vp->length = length; + memcpy(vp->vp_octets, data, length); + } + break; } return vp; } +static void rad_sortvp(VALUE_PAIR **head) +{ + int swapped; + VALUE_PAIR *vp, **tail; + + /* + * Walk over the VP's, sorting them in order. Did I + * mention that I hate WiMAX continuations? + * + * And bubble sort! WTF is up with that? + */ + do { + swapped = 0; + tail = head; + while (*tail) { + vp = *tail; + if (!vp->next) break; + + if (vp->attribute > vp->next->attribute) { + *tail = vp->next; + vp->next = (*tail)->next; + (*tail)->next = vp; + swapped = 1; + } + tail = &(vp->next); + } + } while (swapped); +} + + +/* + * Walk the packet, looking for continuations of this attribute. + * + * This is (worst-case) O(N^2) in the number of RADIUS + * attributes. That happens only when perverse clients create + * continued attributes, AND separate the fragmented portions + * with a lot of other attributes. + * + * Sane clients should put the fragments next to each other, in + * which case this is O(N), in the number of fragments. + */ +static uint8_t *rad_coalesce(unsigned int attribute, int vendor, + size_t length, uint8_t *data, + size_t packet_length, size_t *ptlv_length) + +{ + uint32_t lvalue; + size_t tlv_length = length; + uint8_t *ptr, *tlv, *tlv_data; + + for (ptr = data + length; + ptr != (data + packet_length); + ptr += ptr[1]) { + /* FIXME: Check that there are 6 bytes of data here... */ + if ((ptr[0] != PW_VENDOR_SPECIFIC) || + (ptr[1] < (2 + 4 + 3)) || /* WiMAX VSA with continuation */ + (ptr[2] != 0) || (ptr[3] != 0) || /* our requirement */ + (ptr[4] != ((vendor >> 8) & 0xff)) || + (ptr[5] != (vendor & 0xff))) { + continue; + } + + memcpy(&lvalue, ptr + 2, 4); /* Vendor Id */ + lvalue = ntohl(lvalue); + lvalue <<= 16; + lvalue |= ptr[2 + 4]; /* add in VSA number */ + if (lvalue != attribute) continue; + + /* + * If the vendor-length is too small, it's badly + * formed, so we stop. + */ + if ((ptr[2 + 4 + 1]) < 3) break; + + tlv_length += ptr[2 + 4 + 1] - 3; + if ((ptr[2 + 4 + 1 + 1] & 0x80) == 0) break; + } + + tlv = tlv_data = malloc(tlv_length); + if (!tlv_data) return NULL; + + memcpy(tlv, data, length); + tlv += length; + + /* + * Now we walk the list again, copying the data over to + * our newly created memory. + */ + for (ptr = data + length; + ptr != (data + packet_length); + ptr += ptr[1]) { + int this_length; + + if ((ptr[0] != PW_VENDOR_SPECIFIC) || + (ptr[1] < (2 + 4 + 3)) || /* WiMAX VSA with continuation */ + (ptr[2] != 0) || (ptr[3] != 0)) { /* our requirement */ + continue; + } + + memcpy(&lvalue, ptr + 2, 4); + lvalue = ntohl(lvalue); + lvalue <<= 16; + lvalue |= ptr[2 + 4]; + if (lvalue != attribute) continue; + + /* + * If the vendor-length is too small, it's badly + * formed, so we stop. + */ + if ((ptr[2 + 4 + 1]) < 3) break; + + this_length = ptr[2 + 4 + 1] - 3; + memcpy(tlv, ptr + 2 + 4 + 3, this_length); + tlv += this_length; + + ptr[2 + 4] = 0; /* What a hack! */ + if ((ptr[2 + 4 + 1 + 1] & 0x80) == 0) break; + } + + *ptlv_length = tlv_length; + return tlv_data; +} + + +/* + * Walk over Evil WIMAX TLVs, creating attributes. + */ +static VALUE_PAIR *tlv2wimax(const RADIUS_PACKET *packet, + const RADIUS_PACKET *original, + const char *secret, + int attribute, int vendor, + uint8_t *ptr, size_t len, int nest) +{ + VALUE_PAIR *head = NULL; + VALUE_PAIR **tail = &head; + VALUE_PAIR *vp; + uint8_t *y; /* why do I need to do this? */ + + if (nest > fr_wimax_max_tlv) return NULL; + + /* + * Sanity check the attribute. + */ + for (y = ptr; y < (ptr + len); y += y[1]) { + if ((y[0] == 0) || ((y + 2) > (ptr + len)) || + (y[1] < 2) || ((y + y[1]) > (ptr + len))) { + return NULL; + } + + /* + * Attribute number is too large for us to + * represent it in our horrible internal + * representation. + */ + if ((ptr[0] & ~fr_wimax_mask[nest]) != 0) { + return NULL; + } + } + + for (y = ptr; y < (ptr + len); y += y[1]) { + DICT_ATTR *da; + + da = dict_attrbyvalue(attribute | (ptr[0] << fr_wimax_shift[nest]), vendor); + if (da && (da->type == PW_TYPE_TLV)) { + vp = tlv2wimax(packet, original, secret, + attribute | (ptr[0] << fr_wimax_shift[nest]), + vendor, ptr + 2, ptr[1] - 2, + nest + 1); + if (!vp) goto error; + } else { + vp = paircreate(attribute | (ptr[0] << fr_wimax_shift[nest]), vendor, + PW_TYPE_OCTETS); + if (!vp) { + error: + pairfree(&head); + return NULL; + } + + if (!data2vp(packet, original, secret, + y[1] - 2, y + 2, vp)) { + goto error; + } + } + + *tail = vp; + while (*tail) tail = &((*tail)->next); + } + + return head; +} + +/* + * Start at the *data* portion of a continued attribute. search + * through the rest of the attributes to find a matching one, and + * add it's contents to our contents. + */ +static VALUE_PAIR *rad_continuation2vp(const RADIUS_PACKET *packet, + const RADIUS_PACKET *original, + const char *secret, int attribute, + int vendor, + int length, /* CANNOT be zero */ + uint8_t *data, size_t packet_length, + int flag, DICT_ATTR *da) +{ + size_t tlv_length, left; + uint8_t *ptr; + uint8_t *tlv_data; + VALUE_PAIR *vp, *head, **tail; + DICT_ATTR *tlv_da; + + /* + * Ensure we have data that hasn't been split across + * multiple attributes. + */ + if (flag) { + tlv_data = rad_coalesce(attribute, vendor, length, + data, packet_length, &tlv_length); + if (!tlv_data) return NULL; + } else { + tlv_data = data; + tlv_length = length; + } + + /* + * Non-TLV types cannot be continued across multiple + * attributes. This is true even of keys that are + * encrypted with the tunnel-password method. The spec + * says that they can be continued... but also that the + * keys are 160 bits, which means that they CANNOT be + * continued. + * + * Note that we don't check "flag" here. The calling + * code ensures that + */ + if (!da || (da->type != PW_TYPE_TLV)) { + not_well_formed: + if (tlv_data == data) { /* true if we had 'goto' */ + tlv_data = malloc(tlv_length); + if (!tlv_data) return NULL; + memcpy(tlv_data, data, tlv_length); + } + + vp = paircreate(attribute, vendor, PW_TYPE_OCTETS); + if (!vp) return NULL; + + vp->type = PW_TYPE_TLV; + vp->flags.encrypt = FLAG_ENCRYPT_NONE; + vp->flags.has_tag = 0; + vp->flags.is_tlv = 0; + vp->vp_tlv = tlv_data; + vp->length = tlv_length; + return vp; + } /* else it WAS a TLV, go decode the sub-tlv's */ + + /* + * Now (sigh) we walk over the TLV, seeing if it is + * well-formed. + */ + left = tlv_length; + for (ptr = tlv_data; + ptr != (tlv_data + tlv_length); + ptr += ptr[1]) { + if ((left < 2) || + (ptr[1] < 2) || + (ptr[1] > left)) { + goto not_well_formed; + } + + left -= ptr[1]; + } + + /* + * Now we walk over the TLV *again*, creating sub-tlv's. + */ + head = NULL; + tail = &head; + + for (ptr = tlv_data; + ptr != (tlv_data + tlv_length); + ptr += ptr[1]) { + + tlv_da = dict_attrbyvalue(attribute | (ptr[0] << fr_wimax_shift[1]), vendor); + if (tlv_da && (tlv_da->type == PW_TYPE_TLV)) { + vp = tlv2wimax(packet, original, secret, + attribute | (ptr[0] << 8), + vendor, ptr + 2, ptr[1] - 2, 2); + + if (!vp) goto error; + } else { + vp = paircreate(attribute | (ptr[0] << fr_wimax_shift[1]), vendor, + PW_TYPE_OCTETS); + if (!vp) { + error: + pairfree(&head); + goto not_well_formed; + } + + if (!data2vp(packet, original, secret, + ptr[1] - 2, ptr + 2, vp)) { + goto error; + } + } + + *tail = vp; + + while (*tail) tail = &((*tail)->next); + } + + /* + * TLV's MAY be continued, but sometimes they're not. + */ + if (tlv_data != data) free(tlv_data); + + if (head->next) rad_sortvp(&head); + + return head; +} + + +/* + * Extended attribute TLV to VP. + */ +static VALUE_PAIR *tlv2vp(const RADIUS_PACKET *packet, + const RADIUS_PACKET *original, + const char *secret, int attribute, + int length, const uint8_t *data) +{ + VALUE_PAIR *vp; + + if ((length < 2) || (data[1] < 2)) return NULL; + + /* + * For now, only one TLV is allowed. + */ + if (data[1] != length) return NULL; + + attribute |= (data[0] << fr_wimax_shift[2]); + + vp = paircreate(attribute, VENDORPEC_EXTENDED, PW_TYPE_OCTETS); + if (!vp) return NULL; + + return data2vp(packet, original, secret, length - 2, data + 2, vp); +} + +/* + * Parse a RADIUS attribute into a data structure. + */ +VALUE_PAIR *rad_attr2vp(const RADIUS_PACKET *packet, + const RADIUS_PACKET *original, + const char *secret, int attribute, int vendor, + int length, const uint8_t *data) +{ + VALUE_PAIR *vp; + + /* + * Hard-coded values are bad... + */ + if ((vendor == 0) && (attribute >= 241) && (attribute <= 246)) { + DICT_ATTR *da; + + da = dict_attrbyvalue(attribute, VENDORPEC_EXTENDED); + if (da && (da->flags.extended || da->flags.extended_flags)) { + + if (length == 0) return NULL; + + attribute |= (data[0] << fr_wimax_shift[1]); + vendor = VENDORPEC_EXTENDED; + + data++; + length--; + + /* + * There may be a flag octet. + */ + if (da->flags.extended_flags) { + if (length == 0) return NULL; + + /* + * If there's a flag, we can't + * handle it. + */ + if (data[0] != 0) return NULL; + data++; + length--; + } + + /* + * Now look up the extended attribute, to + * see if it's a TLV carrying more data. + * + */ + da = dict_attrbyvalue(attribute, VENDORPEC_EXTENDED); + if (da && da->flags.has_tlv) { + return tlv2vp(packet, original, secret, + attribute, length, data); + } + } + } + vp = paircreate(attribute, vendor, PW_TYPE_OCTETS); + if (!vp) return NULL; + + return data2vp(packet, original, secret, length, data, vp); +} + /* * Calculate/check digest, and decode radius attributes. @@ -2110,13 +2938,13 @@ int rad_decode(RADIUS_PACKET *packet, RADIUS_PACKET *original, uint32_t vendorcode; VALUE_PAIR **tail; VALUE_PAIR *pair; - uint8_t *ptr; + uint8_t *ptr, *vsa_ptr; int packet_length; int attribute; int attrlen; int vendorlen; radius_packet_t *hdr; - int vsa_tlen, vsa_llen; + int vsa_tlen, vsa_llen, vsa_offset; DICT_VENDOR *dv = NULL; int num_attributes = 0; @@ -2138,6 +2966,7 @@ int rad_decode(RADIUS_PACKET *packet, RADIUS_PACKET *original, vendorcode = 0; vendorlen = 0; vsa_tlen = vsa_llen = 1; + vsa_offset = 0; /* * We have to read at least two bytes. @@ -2198,19 +3027,18 @@ int rad_decode(RADIUS_PACKET *packet, RADIUS_PACKET *original, if (myvendor == 0) goto create_pair; /* - * This is an implementation issue. - * We currently pack vendor into the upper - * 16 bits of a 32-bit attribute number, - * so we can't handle vendor numbers larger - * than 16 bits. + * Allow vendors up to 2^24. Past that, + * get confused. */ - if (myvendor > 65535) goto create_pair; + if (myvendor > FR_MAX_VENDOR) goto create_pair; vsa_tlen = vsa_llen = 1; + vsa_offset = 0; dv = dict_vendorbyvalue(myvendor); if (dv) { vsa_tlen = dv->type; vsa_llen = dv->length; + if (dv->flags) vsa_offset = 1; } /* @@ -2230,9 +3058,10 @@ int rad_decode(RADIUS_PACKET *packet, RADIUS_PACKET *original, int myattr = 0; /* - * Don't have a type, it's bad. + * Not enough room for one more + * attribute. Die! */ - if (sublen < vsa_tlen) goto create_pair; + if (sublen < (vsa_tlen + vsa_llen + vsa_offset)) goto create_pair; /* * Ensure that the attribute number @@ -2261,25 +3090,30 @@ int rad_decode(RADIUS_PACKET *packet, RADIUS_PACKET *original, goto create_pair; } - /* - * Not enough room for one more - * attribute. Die! - */ - if (sublen < vsa_tlen + vsa_llen) goto create_pair; switch (vsa_llen) { case 0: - attribute = (myvendor << 16) | myattr; + attribute = myattr; ptr += 4 + vsa_tlen; attrlen -= (4 + vsa_tlen); packet_length -= 4 + vsa_tlen; goto create_pair; case 1: - if (subptr[vsa_tlen] < (vsa_tlen + vsa_llen)) + if (subptr[vsa_tlen] < (vsa_tlen + vsa_llen + vsa_offset)) goto create_pair; if (subptr[vsa_tlen] > sublen) goto create_pair; + + /* + * WiMAX: 0bCrrrrrrr + * Reserved bits MUST be + * zero. + */ + if (vsa_offset && + ((subptr[vsa_tlen + vsa_llen] & 0x7f) != 0)) + goto create_pair; + sublen -= subptr[vsa_tlen]; subptr += subptr[vsa_tlen]; break; @@ -2327,12 +3161,12 @@ int rad_decode(RADIUS_PACKET *packet, RADIUS_PACKET *original, default: /* can't hit this. */ return -1; } - attribute |= (vendorcode << 16); + vsa_ptr = ptr; ptr += vsa_tlen; switch (vsa_llen) { case 1: - attrlen = ptr[0] - (vsa_tlen + vsa_llen); + attrlen = ptr[0] - (vsa_tlen + vsa_llen + vsa_offset); break; case 2: @@ -2342,26 +3176,74 @@ int rad_decode(RADIUS_PACKET *packet, RADIUS_PACKET *original, default: /* can't hit this. */ return -1; } - ptr += vsa_llen; - vendorlen -= vsa_tlen + vsa_llen + attrlen; - if (vendorlen == 0) vendorcode = 0; - packet_length -= (vsa_tlen + vsa_llen); + + ptr += vsa_llen + vsa_offset; + vendorlen -= vsa_tlen + vsa_llen + vsa_offset + attrlen; + packet_length -= (vsa_tlen + vsa_llen + vsa_offset); + + /* + * Ignore VSAs that have no data. + */ + if (attrlen == 0) goto next; + + /* + * WiMAX attributes of type 0 are ignored. They + * are a secret flag to us that the attribute has + * already been dealt with. + */ + if ((vendorcode == VENDORPEC_WIMAX) && (attribute == 0)) { + goto next; + } + + if (vsa_offset) { + DICT_ATTR *da; + + da = dict_attrbyvalue(attribute, vendorcode); + + /* + * If it's NOT continued, AND we know + * about it, AND it's not a TLV, we can + * create a normal pair. + */ + if (((vsa_ptr[2] & 0x80) == 0) && + da && (da->type != PW_TYPE_TLV)) goto create_pair; + + /* + * Else it IS continued, or it's a TLV. + * Go do a lot of work to find the stuff. + */ + pair = rad_continuation2vp(packet, original, secret, + attribute, vendorcode, + attrlen, ptr, + packet_length, + ((vsa_ptr[2] & 0x80) != 0), + da); + goto created_pair; + } /* * Create the attribute, setting the default type * to 'octets'. If the type in the dictionary * is different, then the dictionary type will * over-ride this one. + * + * If the attribute has no data, then discard it. + * + * Unless it's CUI. Damn you, CUI! */ create_pair: + if (!attrlen && + (attribute != PW_CHARGEABLE_USER_IDENTITY)) goto next; + pair = rad_attr2vp(packet, original, secret, - attribute, attrlen, ptr); + attribute, vendorcode, attrlen, ptr); if (!pair) { pairfree(&packet->vps); - librad_log("out of memory"); + fr_strerror_printf("out of memory"); return -1; } + created_pair: *tail = pair; while (pair) { num_attributes++; @@ -2376,19 +3258,21 @@ int rad_decode(RADIUS_PACKET *packet, RADIUS_PACKET *original, * then without using the dictionary. We * therefore enforce the limits here, too. */ - if ((librad_max_attributes > 0) && - (num_attributes > librad_max_attributes)) { + if ((fr_max_attributes > 0) && + (num_attributes > fr_max_attributes)) { char host_ipaddr[128]; pairfree(&packet->vps); - librad_log("WARNING: Possible DoS attack from host %s: Too many attributes in request (received %d, max %d are allowed).", + fr_strerror_printf("WARNING: Possible DoS attack from host %s: Too many attributes in request (received %d, max %d are allowed).", inet_ntop(packet->src_ipaddr.af, &packet->src_ipaddr.ipaddr, host_ipaddr, sizeof(host_ipaddr)), - num_attributes, librad_max_attributes); + num_attributes, fr_max_attributes); return -1; } + next: + if (vendorlen == 0) vendorcode = 0; ptr += attrlen; packet_length -= attrlen; } @@ -2397,7 +3281,7 @@ int rad_decode(RADIUS_PACKET *packet, RADIUS_PACKET *original, * Merge information from the outside world into our * random pool. */ - lrad_rand_seed(packet->data, AUTH_HDR_LEN); + fr_rand_seed(packet->data, AUTH_HDR_LEN); return 0; } @@ -2414,10 +3298,10 @@ int rad_decode(RADIUS_PACKET *packet, RADIUS_PACKET *original, * int *pwlen is updated to the new length of the encrypted * password - a multiple of 16 bytes. */ -int rad_pwencode(char *passwd, int *pwlen, const char *secret, +int rad_pwencode(char *passwd, size_t *pwlen, const char *secret, const uint8_t *vector) { - lrad_MD5_CTX context, old; + FR_MD5_CTX context, old; uint8_t digest[AUTH_VECTOR_LEN]; int i, n, secretlen; int len; @@ -2448,8 +3332,8 @@ int rad_pwencode(char *passwd, int *pwlen, const char *secret, */ secretlen = strlen(secret); - lrad_MD5Init(&context); - lrad_MD5Update(&context, secret, secretlen); + fr_MD5Init(&context); + fr_MD5Update(&context, (const uint8_t *) secret, secretlen); old = context; /* save intermediate work */ /* @@ -2458,14 +3342,14 @@ int rad_pwencode(char *passwd, int *pwlen, const char *secret, */ for (n = 0; n < len; n += AUTH_PASS_LEN) { if (n == 0) { - lrad_MD5Update(&context, vector, AUTH_PASS_LEN); - lrad_MD5Final(digest, &context); + fr_MD5Update(&context, vector, AUTH_PASS_LEN); + fr_MD5Final(digest, &context); } else { context = old; - lrad_MD5Update(&context, - passwd + n - AUTH_PASS_LEN, - AUTH_PASS_LEN); - lrad_MD5Final(digest, &context); + fr_MD5Update(&context, + (uint8_t *) passwd + n - AUTH_PASS_LEN, + AUTH_PASS_LEN); + fr_MD5Final(digest, &context); } for (i = 0; i < AUTH_PASS_LEN; i++) { @@ -2479,12 +3363,13 @@ int rad_pwencode(char *passwd, int *pwlen, const char *secret, /* * Decode password. */ -int rad_pwdecode(char *passwd, int pwlen, const char *secret, +int rad_pwdecode(char *passwd, size_t pwlen, const char *secret, const uint8_t *vector) { - lrad_MD5_CTX context, old; + FR_MD5_CTX context, old; uint8_t digest[AUTH_VECTOR_LEN]; - int i, n, secretlen; + int i; + size_t n, secretlen; /* * The RFC's say that the maximum is 128. @@ -2503,8 +3388,8 @@ int rad_pwdecode(char *passwd, int pwlen, const char *secret, */ secretlen = strlen(secret); - lrad_MD5Init(&context); - lrad_MD5Update(&context, secret, secretlen); + fr_MD5Init(&context); + fr_MD5Update(&context, (const uint8_t *) secret, secretlen); old = context; /* save intermediate work */ /* @@ -2512,16 +3397,22 @@ int rad_pwdecode(char *passwd, int pwlen, const char *secret, */ for (n = 0; n < pwlen; n += AUTH_PASS_LEN) { if (n == 0) { - lrad_MD5Update(&context, vector, AUTH_VECTOR_LEN); - lrad_MD5Final(digest, &context); + fr_MD5Update(&context, vector, AUTH_VECTOR_LEN); + fr_MD5Final(digest, &context); context = old; - if (pwlen > AUTH_PASS_LEN) lrad_MD5Update(&context, passwd, AUTH_PASS_LEN); + if (pwlen > AUTH_PASS_LEN) { + fr_MD5Update(&context, (uint8_t *) passwd, + AUTH_PASS_LEN); + } } else { - lrad_MD5Final(digest, &context); + fr_MD5Final(digest, &context); context = old; - if (pwlen > (n + AUTH_PASS_LEN)) lrad_MD5Update(&context, passwd + n, AUTH_PASS_LEN); + if (pwlen > (n + AUTH_PASS_LEN)) { + fr_MD5Update(&context, (uint8_t *) passwd + n, + AUTH_PASS_LEN); + } } for (i = 0; i < AUTH_PASS_LEN; i++) { @@ -2544,7 +3435,7 @@ int rad_pwdecode(char *passwd, int pwlen, const char *secret, * This is per RFC-2868 which adds a two char SALT to the initial intermediate * value MD5 hash. */ -int rad_tunnel_pwencode(char *passwd, int *pwlen, const char *secret, +int rad_tunnel_pwencode(char *passwd, size_t *pwlen, const char *secret, const uint8_t *vector) { uint8_t buffer[AUTH_VECTOR_LEN + MAX_STRING_LEN + 3]; @@ -2581,8 +3472,8 @@ int rad_tunnel_pwencode(char *passwd, int *pwlen, const char *secret, * add in some CSPRNG data. should be OK.. */ salt[0] = (0x80 | ( ((salt_offset++) & 0x0f) << 3) | - (lrad_rand() & 0x07)); - salt[1] = lrad_rand(); + (fr_rand() & 0x07)); + salt[1] = fr_rand(); /* * Padd password to multiple of AUTH_PASS_LEN bytes. @@ -2606,10 +3497,10 @@ int rad_tunnel_pwencode(char *passwd, int *pwlen, const char *secret, if (!n2) { memcpy(buffer + secretlen, vector, AUTH_VECTOR_LEN); memcpy(buffer + secretlen + AUTH_VECTOR_LEN, salt, 2); - librad_md5_calc(digest, buffer, secretlen + AUTH_VECTOR_LEN + 2); + fr_md5_calc(digest, buffer, secretlen + AUTH_VECTOR_LEN + 2); } else { memcpy(buffer + secretlen, passwd + n2 - AUTH_PASS_LEN, AUTH_PASS_LEN); - librad_md5_calc(digest, buffer, secretlen + AUTH_PASS_LEN); + fr_md5_calc(digest, buffer, secretlen + AUTH_PASS_LEN); } for (i = 0; i < AUTH_PASS_LEN; i++) { @@ -2627,10 +3518,10 @@ int rad_tunnel_pwencode(char *passwd, int *pwlen, const char *secret, * initial intermediate value, to differentiate it from the * above. */ -int rad_tunnel_pwdecode(uint8_t *passwd, int *pwlen, const char *secret, +int rad_tunnel_pwdecode(uint8_t *passwd, size_t *pwlen, const char *secret, const uint8_t *vector) { - lrad_MD5_CTX context, old; + FR_MD5_CTX context, old; uint8_t digest[AUTH_VECTOR_LEN]; int secretlen; unsigned i, n, len, reallen; @@ -2641,7 +3532,7 @@ int rad_tunnel_pwdecode(uint8_t *passwd, int *pwlen, const char *secret, * We need at least a salt. */ if (len < 2) { - librad_log("tunnel password is too short"); + fr_strerror_printf("tunnel password is too short"); return -1; } @@ -2668,8 +3559,8 @@ int rad_tunnel_pwdecode(uint8_t *passwd, int *pwlen, const char *secret, */ secretlen = strlen(secret); - lrad_MD5Init(&context); - lrad_MD5Update(&context, secret, secretlen); + fr_MD5Init(&context); + fr_MD5Update(&context, (const uint8_t *) secret, secretlen); old = context; /* save intermediate work */ /* @@ -2677,15 +3568,15 @@ int rad_tunnel_pwdecode(uint8_t *passwd, int *pwlen, const char *secret, * * b(1) = MD5(secret + vector + salt) */ - lrad_MD5Update(&context, vector, AUTH_VECTOR_LEN); - lrad_MD5Update(&context, passwd, 2); + fr_MD5Update(&context, vector, AUTH_VECTOR_LEN); + fr_MD5Update(&context, passwd, 2); reallen = 0; for (n = 0; n < len; n += AUTH_PASS_LEN) { int base = 0; if (n == 0) { - lrad_MD5Final(digest, &context); + fr_MD5Final(digest, &context); context = old; @@ -2696,18 +3587,18 @@ int rad_tunnel_pwdecode(uint8_t *passwd, int *pwlen, const char *secret, */ reallen = passwd[2] ^ digest[0]; if (reallen >= len) { - librad_log("tunnel password is too long for the attribute"); + fr_strerror_printf("tunnel password is too long for the attribute"); return -1; } - lrad_MD5Update(&context, passwd + 2, AUTH_PASS_LEN); + fr_MD5Update(&context, passwd + 2, AUTH_PASS_LEN); base = 1; } else { - lrad_MD5Final(digest, &context); + fr_MD5Final(digest, &context); context = old; - lrad_MD5Update(&context, passwd + n + 2, AUTH_PASS_LEN); + fr_MD5Update(&context, passwd + n + 2, AUTH_PASS_LEN); } for (i = base; i < AUTH_PASS_LEN; i++) { @@ -2737,7 +3628,7 @@ int rad_chap_encode(RADIUS_PACKET *packet, uint8_t *output, int id, VALUE_PAIR *password) { int i; - char *ptr; + uint8_t *ptr; uint8_t string[MAX_STRING_LEN * 2 + 1]; VALUE_PAIR *challenge; @@ -2768,7 +3659,7 @@ int rad_chap_encode(RADIUS_PACKET *packet, uint8_t *output, int id, * Use Chap-Challenge pair if present, * Request-Authenticator otherwise. */ - challenge = pairfind(packet->vps, PW_CHAP_CHALLENGE); + challenge = pairfind(packet->vps, PW_CHAP_CHALLENGE, 0); if (challenge) { memcpy(ptr, challenge->vp_strvalue, challenge->length); i += challenge->length; @@ -2778,7 +3669,7 @@ int rad_chap_encode(RADIUS_PACKET *packet, uint8_t *output, int id, } *output = id; - librad_md5_calc((uint8_t *)output + 1, (uint8_t *)string, i); + fr_md5_calc((uint8_t *)output + 1, (uint8_t *)string, i); return 0; } @@ -2789,17 +3680,17 @@ int rad_chap_encode(RADIUS_PACKET *packet, uint8_t *output, int id, * * May be called any number of times. */ -void lrad_rand_seed(const void *data, size_t size) +void fr_rand_seed(const void *data, size_t size) { uint32_t hash; /* * Ensure that the pool is initialized. */ - if (!lrad_rand_initialized) { + if (!fr_rand_initialized) { int fd; - memset(&lrad_rand_pool, 0, sizeof(lrad_rand_pool)); + memset(&fr_rand_pool, 0, sizeof(fr_rand_pool)); fd = open("/dev/urandom", O_RDONLY); if (fd >= 0) { @@ -2807,22 +3698,22 @@ void lrad_rand_seed(const void *data, size_t size) ssize_t this; total = this = 0; - while (total < sizeof(lrad_rand_pool.randrsl)) { - this = read(fd, lrad_rand_pool.randrsl, - sizeof(lrad_rand_pool.randrsl) - total); + while (total < sizeof(fr_rand_pool.randrsl)) { + this = read(fd, fr_rand_pool.randrsl, + sizeof(fr_rand_pool.randrsl) - total); if ((this < 0) && (errno != EINTR)) break; if (this > 0) total += this; } close(fd); } else { - lrad_rand_pool.randrsl[0] = fd; - lrad_rand_pool.randrsl[1] = time(NULL); - lrad_rand_pool.randrsl[2] = errno; + fr_rand_pool.randrsl[0] = fd; + fr_rand_pool.randrsl[1] = time(NULL); + fr_rand_pool.randrsl[2] = errno; } - lrad_randinit(&lrad_rand_pool, 1); - lrad_rand_pool.randcnt = 0; - lrad_rand_initialized = 1; + fr_randinit(&fr_rand_pool, 1); + fr_rand_pool.randcnt = 0; + fr_rand_initialized = 1; } if (!data) return; @@ -2830,32 +3721,32 @@ void lrad_rand_seed(const void *data, size_t size) /* * Hash the user data */ - hash = lrad_rand(); - if (!hash) hash = lrad_rand(); - hash = lrad_hash_update(data, size, hash); + hash = fr_rand(); + if (!hash) hash = fr_rand(); + hash = fr_hash_update(data, size, hash); - lrad_rand_pool.randmem[lrad_rand_pool.randcnt] ^= hash; + fr_rand_pool.randmem[fr_rand_pool.randcnt] ^= hash; } /* * Return a 32-bit random number. */ -uint32_t lrad_rand(void) +uint32_t fr_rand(void) { uint32_t num; /* * Ensure that the pool is initialized. */ - if (!lrad_rand_initialized) { - lrad_rand_seed(NULL, 0); + if (!fr_rand_initialized) { + fr_rand_seed(NULL, 0); } - num = lrad_rand_pool.randrsl[lrad_rand_pool.randcnt++]; - if (lrad_rand_pool.randcnt == 256) { - lrad_isaac(&lrad_rand_pool); - lrad_rand_pool.randcnt = 0; + num = fr_rand_pool.randrsl[fr_rand_pool.randcnt++]; + if (fr_rand_pool.randcnt >= 256) { + fr_rand_pool.randcnt = 0; + fr_isaac(&fr_rand_pool); } return num; @@ -2870,7 +3761,7 @@ RADIUS_PACKET *rad_alloc(int newvector) RADIUS_PACKET *rp; if ((rp = malloc(sizeof(RADIUS_PACKET))) == NULL) { - librad_log("out of memory"); + fr_strerror_printf("out of memory"); return NULL; } memset(rp, 0, sizeof(*rp)); @@ -2885,17 +3776,46 @@ RADIUS_PACKET *rad_alloc(int newvector) * Don't expose the actual contents of the random * pool. */ - base = lrad_rand(); + base = fr_rand(); for (i = 0; i < AUTH_VECTOR_LEN; i += sizeof(uint32_t)) { - hash = lrad_rand() ^ base; + hash = fr_rand() ^ base; memcpy(rp->vector + i, &hash, sizeof(hash)); } } - lrad_rand(); /* stir the pool again */ + fr_rand(); /* stir the pool again */ return rp; } +RADIUS_PACKET *rad_alloc_reply(RADIUS_PACKET *packet) +{ + RADIUS_PACKET *reply; + + if (!packet) return NULL; + + reply = rad_alloc(0); + if (!reply) return NULL; + + /* + * Initialize the fields from the request. + */ + reply->sockfd = packet->sockfd; + reply->dst_ipaddr = packet->src_ipaddr; + reply->src_ipaddr = packet->dst_ipaddr; + reply->dst_port = packet->src_port; + reply->src_port = packet->dst_port; + reply->id = packet->id; + reply->code = 0; /* UNKNOWN code */ + memcpy(reply->vector, packet->vector, + sizeof(reply->vector)); + reply->vps = NULL; + reply->data = NULL; + reply->data_len = 0; + + return reply; +} + + /* * Free a RADIUS_PACKET */