📄 radius.c
字号:
/* * radius.c Functions to send/receive radius packets. * * Version: $Id: radius.c,v 1.219 2008/04/11 09:55:02 aland Exp $ * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA * * Copyright 2000-2003,2006 The FreeRADIUS server project */#include <freeradius-devel/ident.h>RCSID("$Id: radius.c,v 1.219 2008/04/11 09:55:02 aland Exp $")#include <freeradius-devel/libradius.h>#include <freeradius-devel/md5.h>#include <fcntl.h>#include <ctype.h>#ifdef WITH_UDPFROMTO#include <freeradius-devel/udpfromto.h>#endif#ifdef HAVE_MALLOC_H#include <malloc.h>#endif/* * The RFC says 4096 octets max, and most packets are less than 256. */#define MAX_PACKET_LEN 4096/* * The maximum number of attributes which we allow in an incoming * request. If there are more attributes than this, the request * is rejected. * * This helps to minimize the potential for a DoS, when an * attacker spoofs Access-Request packets, which don't have a * Message-Authenticator attribute. This means that the packet * is unsigned, and the attacker can use resources on the server, * even if the end request is rejected. */int librad_max_attributes = 0;FILE *fr_log_fp = NULL;typedef struct radius_packet_t { uint8_t code; uint8_t id; uint8_t length[2]; uint8_t vector[AUTH_VECTOR_LEN]; uint8_t data[1];} radius_packet_t;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[] = { "", "Access-Request", "Access-Accept", "Access-Reject", "Accounting-Request", "Accounting-Response", "Accounting-Status", "Password-Request", "Password-Accept", "Password-Reject", "Accounting-Message", "Access-Challenge", "Status-Server", "Status-Client", "14", "15", "16", "17", "18", "19", "20", "Resource-Free-Request", "Resource-Free-Response", "Resource-Query-Request", "Resource-Query-Response", "Alternate-Resource-Reclaim-Request", "NAS-Reboot-Request", "NAS-Reboot-Response", "28", "Next-Passcode", "New-Pin", "Terminate-Session", "Password-Expired", "Event-Request", "Event-Response", "35", "36", "37", "38", "39", "Disconnect-Request", "Disconnect-ACK", "Disconnect-NAK", "CoA-Request", "CoA-ACK", "CoA-NAK", "46", "47", "48", "49", "IP-Address-Allocate", "IP-Address-Release"};void fr_printf_log(const char *fmt, ...){ va_list ap; va_start(ap, fmt); if ((librad_debug == 0) || !fr_log_fp) { va_end(ap); return; } vfprintf(fr_log_fp, fmt, ap); va_end(ap); return;}/* * 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 = 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); s4->sin_family = AF_INET; s4->sin_addr = src_ipaddr->ipaddr.ip4addr; s4->sin_port = htons(src_port);#else 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! */#ifdef WITH_UDPFROMTO /* * Only IPv4 is supported for udpfromto. * * And if they don't specify a source IP address, don't * use udpfromto. */ if ((dst_ipaddr->af == AF_INET) || (src_ipaddr->af != AF_UNSPEC)) { return sendfromto(sockfd, data, data_len, flags, (struct sockaddr *)&src, sizeof_src, (struct sockaddr *)&dst, sizeof_dst); }#else src_ipaddr = src_ipaddr; /* -Wunused */#endif /* * No udpfromto, OR an IPv6 socket, fail gracefully. */ return sendto(sockfd, data, data_len, flags, (struct sockaddr *)&dst, sizeof_dst);}void rad_recv_discard(int sockfd){ uint8_t header[4]; struct sockaddr_storage src; socklen_t sizeof_src = sizeof(src); recvfrom(sockfd, header, sizeof(header), 0, (struct sockaddr *)&src, &sizeof_src);}ssize_t rad_recv_header(int sockfd, fr_ipaddr_t *src_ipaddr, int *src_port, int *code){ ssize_t data_len, packet_len; uint8_t header[4]; struct sockaddr_storage src; socklen_t sizeof_src = sizeof(src); data_len = recvfrom(sockfd, header, sizeof(header), MSG_PEEK, (struct sockaddr *)&src, &sizeof_src); if (data_len < 0) { if ((errno == EAGAIN) || (errno == EINTR)) return 0; return -1; } /* * Too little data is available, discard the packet. */ if (data_len < 4) { recvfrom(sockfd, header, sizeof(header), 0, (struct sockaddr *)&src, &sizeof_src); return 1; } else { /* we got 4 bytes of data. */ /* * See how long the packet says it is. */ packet_len = (header[2] * 256) + header[3]; /* * The length in the packet says it's less than * a RADIUS header length: discard it. */ if (packet_len < AUTH_HDR_LEN) { recvfrom(sockfd, header, sizeof(header), 0, (struct sockaddr *)&src, &sizeof_src); return 1; /* * Enforce RFC requirements, for sanity. * Anything after 4k will be discarded. */ } else if (packet_len > MAX_PACKET_LEN) { recvfrom(sockfd, header, sizeof(header), 0, (struct sockaddr *)&src, &sizeof_src); return 1; } } 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 { recvfrom(sockfd, header, sizeof(header), 0, (struct sockaddr *)&src, &sizeof_src); return 1; } *code = header[0]; /* * The packet says it's this long, but the actual UDP * size could still be smaller. */ return packet_len;}/* * wrapper for recvfrom, which handles recvfromto, IPv6, and all * possible combinations. */static ssize_t rad_recvfrom(int sockfd, uint8_t **pbuf, int flags, 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; socklen_t sizeof_src = sizeof(src); socklen_t sizeof_dst = sizeof(dst); ssize_t data_len; uint8_t header[4]; void *buf; size_t len; memset(&src, 0, sizeof_src); memset(&dst, 0, sizeof_dst); /* * Get address family, etc. first, so we know if we * need to do udpfromto. * * FIXME: udpfromto also does this, but it's not * a critical problem. */ if (getsockname(sockfd, (struct sockaddr *)&dst, &sizeof_dst) < 0) return -1; /* * Read the length of the packet, from the packet. * This lets us allocate the buffer to use for * reading the rest of the packet. */ data_len = recvfrom(sockfd, header, sizeof(header), MSG_PEEK, (struct sockaddr *)&src, &sizeof_src); if (data_len < 0) { if ((errno == EAGAIN) || (errno == EINTR)) return 0; return -1; } /* * Too little data is available, discard the packet. */ if (data_len < 4) { recvfrom(sockfd, header, sizeof(header), flags, (struct sockaddr *)&src, &sizeof_src); return 0; } else { /* we got 4 bytes of data. */ /* * See how long the packet says it is. */ len = (header[2] * 256) + header[3]; /* * The length in the packet says it's less than * a RADIUS header length: discard it. */ if (len < AUTH_HDR_LEN) { recvfrom(sockfd, header, sizeof(header), flags, (struct sockaddr *)&src, &sizeof_src); return 0; /* * Enforce RFC requirements, for sanity. * Anything after 4k will be discarded. */ } else if (len > MAX_PACKET_LEN) { recvfrom(sockfd, header, sizeof(header), flags, (struct sockaddr *)&src, &sizeof_src); return len; } } buf = malloc(len); if (!buf) return -1; /* * Receive the packet. The OS will discard any data in the * packet after "len" bytes. */#ifdef WITH_UDPFROMTO if (dst.ss_family == AF_INET) { data_len = recvfromto(sockfd, buf, len, flags, (struct sockaddr *)&src, &sizeof_src, (struct sockaddr *)&dst, &sizeof_dst); } else#endif /* * No udpfromto, OR an IPv6 socket. Fail gracefully. */ data_len = recvfrom(sockfd, buf, len, flags, (struct sockaddr *)&src, &sizeof_src); if (data_len < 0) { free(buf); 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 { free(buf); return -1; /* Unknown address family, Die Die Die! */ } /* * Different address families should never happen. */ if (src.ss_family != dst.ss_family) { free(buf); return -1; } /* * Tell the caller about the data */ *pbuf = buf; return data_len;}#define AUTH_PASS_LEN (AUTH_VECTOR_LEN)/************************************************************************* * * Function: make_secret * * Purpose: Build an encrypted secret value to return in a reply * packet. The secret is hidden by xoring with a MD5 digest * created from the shared secret and the authentication * vector. We put them into MD5 in the reverse order from * that used when encrypting passwords to RADIUS. * *************************************************************************/static void make_secret(uint8_t *digest, const uint8_t *vector, const char *secret, const uint8_t *value){ FR_MD5_CTX context; int i; 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]; }}#define MAX_PASS_LEN (128)static void make_passwd(uint8_t *output, int *outlen, const uint8_t *input, int inlen, const char *secret, const uint8_t *vector){ FR_MD5_CTX context, old; uint8_t digest[AUTH_VECTOR_LEN]; uint8_t passwd[MAX_PASS_LEN]; int i, n; int len; /* * If the length is zero, round it up. */ len = inlen; if (len == 0) { len = AUTH_PASS_LEN; } else if (len > MAX_PASS_LEN) len = MAX_PASS_LEN; else if ((len & 0x0f) != 0) { len += 0x0f; len &= ~0x0f; } *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; /* * Do first pass. */ fr_MD5Update(&context, vector, AUTH_PASS_LEN); for (n = 0; n < len; n += AUTH_PASS_LEN) { if (n > 0) { context = old; fr_MD5Update(&context, passwd + n - AUTH_PASS_LEN, AUTH_PASS_LEN); } fr_MD5Final(digest, &context); for (i = 0; i < AUTH_PASS_LEN; i++) { passwd[i + n] ^= digest[i];
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -