📄 radius.c
字号:
/* * radius.c Functions to send/receive radius packets. * * Version: $Id: radius.c,v 1.125.2.1 2004/08/30 17:48:31 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA * * Copyright 2000-2003 The FreeRADIUS server project */static const char rcsid[] = "$Id: radius.c,v 1.125.2.1 2004/08/30 17:48:31 aland Exp $";#include "autoconf.h"#include "md5.h"#include <stdlib.h>#ifdef HAVE_UNISTD_H#include <unistd.h>#endif#include <fcntl.h>#include <string.h>#include <ctype.h>#include "libradius.h"#ifdef WITH_UDPFROMTO#include "udpfromto.h"#endif#ifdef HAVE_NETINET_IN_H#include <netinet/in.h>#endif#include <sys/socket.h>#ifdef HAVE_ARPA_INET_H#include <arpa/inet.h>#endif#ifdef HAVE_MALLOC_H#include <malloc.h>#endif#ifdef WIN32#include <process.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;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 lrad_randctx lrad_rand_pool; /* across multiple calls */static int lrad_pool_initialized = 0;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", "CoF-Request", "CoF-ACK", "CoF-NAK", "46", "47", "48", "49", "IP-Address-Allocate", "IP-Address-Release"};/* * Internal prototypes */static void make_secret(unsigned char *digest, uint8_t *vector, const char *secret, char *value);/* * Reply to the request. Also attach * reply attribute value pairs and any user message provided. */int rad_send(RADIUS_PACKET *packet, const RADIUS_PACKET *original, const char *secret){ VALUE_PAIR *reply; struct sockaddr_in saremote; struct sockaddr_in *sa; const char *what; uint8_t ip_buffer[16]; /* * Maybe it's a fake packet. Don't send it. */ if (!packet || (packet->sockfd < 0)) { return 0; } if ((packet->code > 0) && (packet->code < 52)) { what = packet_codes[packet->code]; } else { what = "Reply"; } /* * First time through, allocate room for the packet */ if (!packet->data) { radius_packet_t *hdr; uint32_t lvalue; uint8_t *ptr, *length_ptr, *vsa_length_ptr; uint8_t digest[16]; int secretlen; int vendorcode, vendorpec; u_short total_length; int len, allowed; int msg_auth_offset = 0; /* * For simplicity in the following logic, we allow * the attributes to "overflow" the 4k maximum * RADIUS packet size, by one attribute. */ uint8_t data[MAX_PACKET_LEN + 256]; /* * Use memory on the stack, until we know how * large the packet will be. */ hdr = (radius_packet_t *) data; /* * Build standard header */ hdr->code = packet->code; hdr->id = packet->id; /* * Double-check some things based on packet code. */ switch (packet->code) { case PW_AUTHENTICATION_ACK: case PW_AUTHENTICATION_REJECT: case PW_ACCESS_CHALLENGE: if (!original) { librad_log("ERROR: Cannot sign response packet without a request packet."); return -1; } break; /* * These packet vectors start off as all zero. */ case PW_ACCOUNTING_REQUEST: case PW_DISCONNECT_REQUEST: memset(packet->vector, 0, sizeof(packet->vector)); break; default: break; } memcpy(hdr->vector, packet->vector, sizeof(hdr->vector)); DEBUG("Sending %s of id %d to %s:%d\n", what, packet->id, ip_ntoa((char *)ip_buffer, packet->dst_ipaddr), packet->dst_port); total_length = AUTH_HDR_LEN; /* * Load up the configuration values for the user */ ptr = hdr->data; vendorcode = 0; vendorpec = 0; vsa_length_ptr = NULL; /* * Loop over the reply attributes for the packet. */ for (reply = packet->vps; reply; reply = reply->next) { /* * Ignore non-wire attributes */ if ((VENDOR(reply->attribute) == 0) && ((reply->attribute & 0xFFFF) > 0xff)) { continue; } /* * Check that the packet is no more than * 4k in size, AFTER over-flowing the 4k * boundary. Note that the 'data' * buffer, above, is one attribute longer * than necessary, in order to permit * this overflow. */ if (total_length > MAX_PACKET_LEN) { librad_log("ERROR: Too many attributes for packet, result is larger than RFC maximum of 4k"); return -1; } /* * Set the Message-Authenticator to the * correct length and initial value. */ if (reply->attribute == PW_MESSAGE_AUTHENTICATOR) { reply->length = AUTH_VECTOR_LEN; memset(reply->strvalue, 0, AUTH_VECTOR_LEN); msg_auth_offset = total_length; } /* * Print out ONLY the attributes which * we're sending over the wire, and print * them out BEFORE they're encrypted. */ debug_pair(reply); /* * We have a different vendor. Re-set * the vendor codes. */ if (vendorcode != VENDOR(reply->attribute)) { vendorcode = 0; vendorpec = 0; vsa_length_ptr = NULL; } /* * If the Vendor-Specific attribute is getting * full, then create a new VSA attribute * * FIXME: Multiple VSA's per Vendor-Specific * SHOULD be configurable. When that's done, * the (1), below, can be changed to point to * a configuration variable which is set TRUE * if the NAS cannot understand multiple VSA's * per Vendor-Specific */ if ((1) || /* ALWAYS create a new Vendor-Specific */ (vsa_length_ptr && (reply->length + *vsa_length_ptr) >= MAX_STRING_LEN)) { vendorcode = 0; vendorpec = 0; vsa_length_ptr = NULL; } /* * Maybe we have the start of a set of * (possibly many) VSA attributes from * one vendor. Set a global VSA wrapper */ if ((vendorcode == 0) && ((vendorcode = VENDOR(reply->attribute)) != 0)) { vendorpec = dict_vendorpec(vendorcode); /* * This is a potentially bad error... * we can't find the vendor ID! */ if (vendorpec == 0) { /* FIXME: log an error */ continue; } /* * Build a VSA header. */ *ptr++ = PW_VENDOR_SPECIFIC; vsa_length_ptr = ptr; *ptr++ = 6; lvalue = htonl(vendorpec); memcpy(ptr, &lvalue, 4); ptr += 4; total_length += 6; } if (vendorpec == VENDORPEC_USR) { lvalue = htonl(reply->attribute & 0xFFFF); memcpy(ptr, &lvalue, 4); length_ptr = vsa_length_ptr; total_length += 4; *length_ptr += 4; ptr += 4; /* * Each USR-style attribute gets * it's own VSA wrapper, so we * re-set the vendor specific * information. */ vendorcode = 0; vendorpec = 0; vsa_length_ptr = NULL; } else { /* * All other attributes are as * per the RFC spec. */ *ptr++ = (reply->attribute & 0xFF); length_ptr = ptr; if (vsa_length_ptr) *vsa_length_ptr += 2; *ptr++ = 2; total_length += 2; } switch(reply->type) { /* * Ascend binary attributes are * stored internally in binary form. */ case PW_TYPE_IFID: case PW_TYPE_IPV6ADDR: case PW_TYPE_IPV6PREFIX: case PW_TYPE_ABINARY: case PW_TYPE_STRING: case PW_TYPE_OCTETS: /* * Encrypt the various password styles */ switch (reply->flags.encrypt) { default: break; case FLAG_ENCRYPT_USER_PASSWORD: rad_pwencode((char *)reply->strvalue, &(reply->length), (const char *)secret, (const char *)packet->vector); break; case FLAG_ENCRYPT_TUNNEL_PASSWORD: if (!original) { librad_log("ERROR: No request packet, cannot encrypt Tunnel-Password attribute in the reply."); return -1; } rad_tunnel_pwencode(reply->strvalue, &(reply->length), secret, original->vector); break; case FLAG_ENCRYPT_ASCEND_SECRET: make_secret(digest, packet->vector, secret, reply->strvalue); memcpy(reply->strvalue, digest, AUTH_VECTOR_LEN ); reply->length = AUTH_VECTOR_LEN; break; } /* switch over encryption flags */ len = reply->length; /* * Set the TAG at the beginning * of the string if tagged. If * tag value is not valid for * tagged attribute, make it 0x00 * per RFC 2868. -cparker */ if (reply->flags.has_tag) { if (TAG_VALID(reply->flags.tag)) { len++; *ptr++ = reply->flags.tag; } else if (reply->flags.encrypt == FLAG_ENCRYPT_TUNNEL_PASSWORD) { /* * Tunnel passwords * REQUIRE a tag, * even if we don't * have a valid * tag. */ len++; *ptr++ = 0x00; } /* else don't write a tag */ } /* else the attribute doesn't have a tag */ /* * Ensure we don't go too far. * The 'length' of the attribute * may be 0..255, minus whatever * octets are used in the attribute * header. */ allowed = 255; if (vsa_length_ptr) { allowed -= *vsa_length_ptr; } else { allowed -= *length_ptr; } if (len > allowed) { len = allowed; } *length_ptr += len; if (vsa_length_ptr) *vsa_length_ptr += len; /* * If we have tagged attributes we can't assume that * len == reply->length. Use reply->length for copying * the string data into the packet. Use len for the * true length of the string+tags. */ memcpy(ptr, reply->strvalue, reply->length); ptr += reply->length; total_length += len; break; case PW_TYPE_INTEGER: case PW_TYPE_IPADDR: *length_ptr += 4; if (vsa_length_ptr) *vsa_length_ptr += 4; if (reply->type == PW_TYPE_INTEGER ) { /* If tagged, the tag becomes the MSB of the value */ if(reply->flags.has_tag) { /* Tag must be ( 0x01 -> 0x1F ) OR 0x00 */ if(!TAG_VALID(reply->flags.tag)) { reply->flags.tag = 0x00; } lvalue = htonl((reply->lvalue & 0xffffff) | ((reply->flags.tag & 0xff) << 24)); } else { lvalue = htonl(reply->lvalue); } } else { /* * IP address is already in * network byte order. */ lvalue = reply->lvalue; } memcpy(ptr, &lvalue, 4); ptr += 4; total_length += 4; break; /* * There are no tagged date attributes. */ case PW_TYPE_DATE: *length_ptr += 4; if (vsa_length_ptr) *vsa_length_ptr += 4; lvalue = htonl(reply->lvalue); memcpy(ptr, &lvalue, 4); ptr += 4; total_length += 4; break; default: break; } } /* done looping over all attributes */ /* * Fill in the rest of the fields, and copy * the data over from the local stack to * the newly allocated memory. * * Yes, all this 'memcpy' is slow, but it means * that we only allocate the minimum amount of * memory for a request. */ packet->data_len = total_length; packet->data = (uint8_t *) malloc(packet->data_len);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -