X-Git-Url: http://www.project-moonshot.org/gitweb/?a=blobdiff_plain;f=src%2Flib%2Fradius.c;h=439feece62feb61080bcd1ec921eec3d4a294b6a;hb=94dc4bb60ec649ce899c1d5e32b575d9523a48f2;hp=a6f9da7991b1974333ebb2bb0445ce2d7e340e4f;hpb=11a42e7d103641d9db023b70625bfc8684ec0c96;p=freeradius.git diff --git a/src/lib/radius.c b/src/lib/radius.c index a6f9da7..439feec 100644 --- a/src/lib/radius.c +++ b/src/lib/radius.c @@ -53,7 +53,7 @@ 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 { @@ -68,9 +68,7 @@ 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", @@ -131,7 +129,7 @@ void fr_printf_log(const char *fmt, ...) va_list ap; va_start(ap, fmt); - if ((librad_debug == 0) || !fr_log_fp) { + if ((fr_debug_flag == 0) || !fr_log_fp) { va_end(ap); return; } @@ -142,6 +140,68 @@ void fr_printf_log(const char *fmt, ...) return; } +static void print_hex(RADIUS_PACKET *packet) +{ + int i; + + if (!packet->data) return; + + 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"); + + if (packet->data_len > 20) { + int total; + const uint8_t *ptr; + printf(" Data:"); + + total = packet->data_len - 20; + ptr = packet->data + 20; + + while (total > 0) { + int attrlen; + + 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. @@ -151,66 +211,20 @@ static int rad_sendto(int sockfd, void *data, size_t data_len, int flags, fr_ipaddr_t *dst_ipaddr, int dst_port) { struct sockaddr_storage dst; - socklen_t sizeof_dst = sizeof(dst); + socklen_t sizeof_dst; #ifdef WITH_UDPFROMTO struct sockaddr_storage src; - socklen_t sizeof_src = sizeof(src); - - memset(&src, 0, sizeof(src)); -#endif - memset(&dst, 0, sizeof(dst)); - - /* - * IPv4 is supported. - */ - if (dst_ipaddr->af == AF_INET) { - struct sockaddr_in *s4; - - s4 = (struct sockaddr_in *)&dst; - sizeof_dst = sizeof(struct sockaddr_in); - - s4->sin_family = AF_INET; - s4->sin_addr = dst_ipaddr->ipaddr.ip4addr; - s4->sin_port = htons(dst_port); - -#ifdef WITH_UDPFROMTO - s4 = (struct sockaddr_in *)&src; - sizeof_src = sizeof(struct sockaddr_in); + socklen_t sizeof_src; - s4->sin_family = AF_INET; - s4->sin_addr = src_ipaddr->ipaddr.ip4addr; - s4->sin_port = htons(src_port); + fr_ipaddr2sockaddr(src_ipaddr, src_port, &src, &sizeof_src); #else - src_port = src_port; /* -Wunused */ + src_port = src_port; /* -Wunused */ #endif - /* - * IPv6 MAY be supported. - */ -#ifdef HAVE_STRUCT_SOCKADDR_IN6 - } else if (dst_ipaddr->af == AF_INET6) { - struct sockaddr_in6 *s6; - - s6 = (struct sockaddr_in6 *)&dst; - sizeof_dst = sizeof(struct sockaddr_in6); - - s6->sin6_family = AF_INET6; - s6->sin6_addr = dst_ipaddr->ipaddr.ip6addr; - s6->sin6_port = htons(dst_port); - -#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! */ + if (!fr_ipaddr2sockaddr(dst_ipaddr, dst_port, &dst, &sizeof_dst)) { + return -1; + } #ifdef WITH_UDPFROMTO /* @@ -219,7 +233,7 @@ static int rad_sendto(int sockfd, void *data, size_t data_len, int flags, * And if they don't specify a source IP address, don't * use udpfromto. */ - if ((dst_ipaddr->af == AF_INET) || + if ((dst_ipaddr->af == AF_INET) && (src_ipaddr->af != AF_UNSPEC)) { return sendfromto(sockfd, data, data_len, flags, (struct sockaddr *)&src, sizeof_src, @@ -233,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); } @@ -297,25 +311,10 @@ ssize_t rad_recv_header(int sockfd, fr_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; @@ -347,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); @@ -431,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. @@ -512,8 +486,8 @@ 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) { FR_MD5_CTX context, old; @@ -526,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; @@ -537,9 +516,6 @@ static void make_passwd(uint8_t *output, int *outlen, } *outlen = len; - memcpy(passwd, input, len); - memset(passwd + len, 0, sizeof(passwd) - len); - fr_MD5Init(&context); fr_MD5Update(&context, (const uint8_t *) secret, strlen(secret)); old = context; @@ -566,8 +542,8 @@ 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) { FR_MD5_CTX context, old; @@ -650,6 +626,7 @@ static void make_tunnel_passwd(uint8_t *output, int *outlen, } fr_MD5Final(digest, &context); + for (i = 0; i < AUTH_PASS_LEN; i++) { passwd[i + 2 + n] ^= digest[i]; } @@ -657,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. @@ -794,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: @@ -832,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 @@ -852,22 +734,22 @@ 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: + 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: @@ -875,18 +757,20 @@ int rad_vp2attr(const RADIUS_PACKET *packet, const RADIUS_PACKET *original, case PW_ACCESS_CHALLENGE: default: if (!original) { - librad_log("ERROR: No request packet, cannot encrypt %s attribute in the vp.", vp->name); + 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, + + 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: - make_tunnel_passwd(ptr + offset, &len, - data, len, 255 - offset - total_length, + ptr[0] = vp->flags.tag; + make_tunnel_passwd(ptr + 1, &len, data, len - 1, room, secret, packet->vector); break; } @@ -897,40 +781,395 @@ 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; + } + + /* + * The attribute is a top-level integer, ipaddr, etc. + * Encode it. + */ + 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; + + } else if (vp->flags.extended_flags) { + if (room < 4) return 0; + + ptr[1] = 4; + ptr[2] = (attribute & 0xff00) >> 8; + ptr[3] = 0; + } + + /* + * 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 (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; + + /* + * RFC format attributes take the fast path. + */ + 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); + } + /* - * Account for the tag (if any). + * Not enough room for: + * attr, len, vendor-id, vsa, vsalen */ - len += offset; + if (room < 8) return 0; /* - * RFC 2865 section 5 says that zero-length attributes - * MUST NOT be sent. + * Build the Vendor-Specific header */ - if (len == 0) return 0; + ptr = start; + *ptr++ = PW_VENDOR_SPECIFIC; + *ptr++ = 6; + room -= 6; + lvalue = htonl(vp->vendor); + memcpy(ptr, &lvalue, 4); + ptr += 4; /* - * Update the various lengths. + * Unknown vendors, and type=1,length=1,no-continuation + * are RFC format attributes. */ - *length_ptr += len; - if (vsa_length_ptr) *vsa_length_ptr += len; + 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, *vsa; + 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; - return total_length; /* of attribute */ + attr = REORDER(vp->attribute); + if (attr >= maxattr) { + maxattr = attr; + goto redo_tlv; + } + } + + return ptr - start; } @@ -949,16 +1188,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"; } @@ -978,7 +1213,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; @@ -1032,10 +1267,12 @@ 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 @@ -1072,19 +1309,30 @@ 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; } @@ -1104,11 +1352,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); @@ -1130,13 +1378,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; } @@ -1163,7 +1411,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, @@ -1249,8 +1497,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"; } @@ -1278,7 +1526,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, @@ -1286,7 +1534,7 @@ int rad_send(RADIUS_PACKET *packet, const RADIUS_PACKET *original, packet->dst_port); for (reply = packet->vps; reply; reply = reply->next) { - if ((VENDOR(reply->attribute) == 0) && + if ((reply->vendor == 0) && ((reply->attribute & 0xFFFF) > 0xff)) continue; debug_pair(reply); } @@ -1300,6 +1548,26 @@ int rad_send(RADIUS_PACKET *packet, const RADIUS_PACKET *original, &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 */ +} + /* * Validates the requesting client NAS. Calculates the @@ -1329,7 +1597,7 @@ static int calc_acctdigest(RADIUS_PACKET *packet, const char *secret) /* * 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; } @@ -1372,7 +1640,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; } @@ -1402,11 +1670,11 @@ int rad_packet_ok(RADIUS_PACKET *packet, int flags) * "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; } @@ -1416,11 +1684,11 @@ int rad_packet_ok(RADIUS_PACKET *packet, int flags) * " ... 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; } @@ -1437,8 +1705,8 @@ int rad_packet_ok(RADIUS_PACKET *packet, int flags) * 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)), @@ -1469,7 +1737,7 @@ int rad_packet_ok(RADIUS_PACKET *packet, int flags) * "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)), @@ -1485,7 +1753,7 @@ int rad_packet_ok(RADIUS_PACKET *packet, int flags) * " ... 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)), @@ -1502,11 +1770,11 @@ int rad_packet_ok(RADIUS_PACKET *packet, int flags) * 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; } @@ -1543,10 +1811,22 @@ int rad_packet_ok(RADIUS_PACKET *packet, int flags) 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))); @@ -1558,7 +1838,7 @@ int rad_packet_ok(RADIUS_PACKET *packet, int flags) * 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)), @@ -1567,6 +1847,19 @@ int rad_packet_ok(RADIUS_PACKET *packet, int flags) } /* + * 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]) { @@ -1583,7 +1876,7 @@ int rad_packet_ok(RADIUS_PACKET *packet, int flags) 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)), @@ -1611,7 +1904,7 @@ int rad_packet_ok(RADIUS_PACKET *packet, int flags) * 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))); @@ -1623,13 +1916,13 @@ int rad_packet_ok(RADIUS_PACKET *packet, int flags) * 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; } @@ -1645,7 +1938,7 @@ int rad_packet_ok(RADIUS_PACKET *packet, int flags) * 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))); @@ -1669,18 +1962,24 @@ int rad_packet_ok(RADIUS_PACKET *packet, int flags) */ 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); @@ -1688,7 +1987,7 @@ RADIUS_PACKET *rad_recv(int fd, int flags) * 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; @@ -1700,7 +1999,7 @@ RADIUS_PACKET *rad_recv(int fd, int flags) * 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; @@ -1713,7 +2012,7 @@ RADIUS_PACKET *rad_recv(int fd, int flags) * packet->data == NULL */ if ((packet->data_len == 0) || !packet->data) { - librad_log("Empty packet: Socket is not ready."); + fr_strerror_printf("Empty packet: Socket is not ready."); free(packet); return NULL; } @@ -1742,12 +2041,12 @@ RADIUS_PACKET *rad_recv(int fd, int flags) */ packet->vps = NULL; - if (librad_debug) { + if (fr_debug_flag) { char host_ipaddr[128]; - if ((packet->code > 0) && (packet->code < MAX_PACKET_CODE)) { + if ((packet->code > 0) && (packet->code < FR_MAX_PACKET_CODE)) { DEBUG("rad_recv: %s packet from host %s port %d", - packet_codes[packet->code], + fr_packet_codes[packet->code], inet_ntop(packet->src_ipaddr.af, &packet->src_ipaddr.ipaddr, host_ipaddr, sizeof(host_ipaddr)), @@ -1760,7 +2059,8 @@ RADIUS_PACKET *rad_recv(int fd, int flags) packet->src_port, packet->code); } - DEBUG(", id=%d, length=%d\n", packet->id, packet->data_len); + DEBUG(", id=%d, length=%d\n", + packet->id, (int) packet->data_len); } return packet; @@ -1822,7 +2122,7 @@ 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); @@ -1832,10 +2132,10 @@ int rad_verify(RADIUS_PACKET *packet, RADIUS_PACKET *original, fr_hmac_md5(packet->data, packet->data_len, (const uint8_t *) secret, strlen(secret), calc_auth_vector); - if (memcmp(calc_auth_vector, msg_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))); @@ -1859,9 +2159,9 @@ 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 " + fr_strerror_printf("Received Unknown packet code %d " "from client %s port %d: Cannot validate signature.", packet->code, inet_ntop(packet->src_ipaddr.af, @@ -1890,9 +2190,9 @@ int rad_verify(RADIUS_PACKET *packet, RADIUS_PACKET *original, case PW_DISCONNECT_REQUEST: case PW_ACCOUNTING_REQUEST: if (calc_acctdigest(packet, secret) > 1) { - librad_log("Received %s packet " - "from %s with invalid signature! (Shared secret is incorrect.)", - packet_codes[packet->code], + 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))); @@ -1911,20 +2211,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, @@ -1938,19 +2237,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 @@ -1963,6 +2255,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) { @@ -1975,7 +2276,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; + } } } @@ -2077,7 +2381,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, @@ -2140,23 +2444,491 @@ 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) { /* flags.extended MUST be set */ + + /* + * MUST have at least an "extended type" octet. + */ + 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); + } + } + + /* + * We could avoid another dictionary lookup here + * by using pairalloc(da), but it's not serious... + */ + } + 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. @@ -2171,13 +2943,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; @@ -2199,6 +2971,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. @@ -2259,19 +3032,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; } /* @@ -2291,9 +3063,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 @@ -2322,25 +3095,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; @@ -2388,12 +3166,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: @@ -2403,10 +3181,50 @@ 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 @@ -2415,18 +3233,22 @@ int rad_decode(RADIUS_PACKET *packet, RADIUS_PACKET *original, * over-ride this one. * * If the attribute has no data, then discard it. + * + * Unless it's CUI. Damn you, CUI! */ create_pair: - if (!attrlen) goto next; + 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++; @@ -2441,20 +3263,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: + next: + if (vendorlen == 0) vendorcode = 0; ptr += attrlen; packet_length -= attrlen; } @@ -2714,7 +3537,7 @@ int rad_tunnel_pwdecode(uint8_t *passwd, size_t *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; } @@ -2769,7 +3592,7 @@ int rad_tunnel_pwdecode(uint8_t *passwd, size_t *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; } @@ -2841,7 +3664,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; @@ -2879,7 +3702,7 @@ void fr_rand_seed(const void *data, size_t size) size_t total; ssize_t this; - total = this = 0; + total = 0; while (total < sizeof(fr_rand_pool.randrsl)) { this = read(fd, fr_rand_pool.randrsl, sizeof(fr_rand_pool.randrsl) - total); @@ -2943,7 +3766,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)); @@ -2969,6 +3792,35 @@ RADIUS_PACKET *rad_alloc(int newvector) 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 */