📄 radius.c
字号:
/* * radius.c Functions to send/receive radius packets. * * Version: $Id: radius.c,v 1.125.2.5.2.10 2007/02/13 09:37:12 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 The FreeRADIUS server project */static const char rcsid[] = "$Id: radius.c,v 1.125.2.5.2.10 2007/02/13 09:37:12 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 volatile int lrad_rand_index = -1;static unsigned int salt_offset = 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", "CoA-Request", "CoA-ACK", "CoA-NAK", "46", "47", "48", "49", "IP-Address-Allocate", "IP-Address-Release"};#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){ MD5_CTX context; int i; MD5Init(&context); MD5Update(&context, vector, AUTH_VECTOR_LEN); MD5Update(&context, secret, strlen(secret)); 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){ 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); MD5Init(&context); MD5Update(&context, secret, strlen(secret)); old = context; /* * Do first pass. */ MD5Update(&context, vector, AUTH_PASS_LEN); for (n = 0; n < len; n += AUTH_PASS_LEN) { if (n > 0) { context = old; MD5Update(&context, passwd + n - AUTH_PASS_LEN, AUTH_PASS_LEN); } MD5Final(digest, &context); for (i = 0; i < AUTH_PASS_LEN; i++) { passwd[i + n] ^= digest[i]; } } memcpy(output, passwd, len);}static void make_tunnel_passwd(uint8_t *output, int *outlen, const uint8_t *input, int inlen, int room, const char *secret, const uint8_t *vector){ MD5_CTX context, old; uint8_t digest[AUTH_VECTOR_LEN]; uint8_t passwd[MAX_STRING_LEN + AUTH_VECTOR_LEN]; int i, n; int len; /* * Account for 2 bytes of the salt, and round the room * available down to the nearest multiple of 16. Then, * subtract one from that to account for the length byte, * and the resulting number is the upper bound on the data * to copy. * * We could short-cut this calculation just be forcing * inlen to be no more than 239. It would work for all * VSA's, as we don't pack multiple VSA's into one * attribute. * * However, this calculation is more general, if a little * complex. And it will work in the future for all possible * kinds of weird attribute packing. */ room -= 2; room -= (room & 0x0f); room--; if (inlen > room) inlen = room; /* * Length of the encrypted data is password length plus * one byte for the length of the password. */ len = inlen + 1; if ((len & 0x0f) != 0) { len += 0x0f; len &= ~0x0f; } *outlen = len + 2; /* account for the salt */ /* * Copy the password over. */ memcpy(passwd + 3, input, inlen); memset(passwd + 3 + inlen, 0, sizeof(passwd) - 3 - inlen); /* * Generate salt. The RFC's say: * * The high bit of salt[0] must be set, each salt in a * packet should be unique, and they should be random * * So, we set the high bit, add in a counter, and then * add in some CSPRNG data. should be OK.. */ passwd[0] = (0x80 | ( ((salt_offset++) & 0x0f) << 3) | (lrad_rand() & 0x07)); passwd[1] = lrad_rand(); passwd[2] = inlen; /* length of the password string */ MD5Init(&context); MD5Update(&context, secret, strlen(secret)); old = context; MD5Update(&context, vector, AUTH_VECTOR_LEN); MD5Update(&context, &passwd[0], 2); for (n = 0; n < len; n += AUTH_PASS_LEN) { if (n > 0) { context = old; MD5Update(&context, passwd + 2 + n - AUTH_PASS_LEN, AUTH_PASS_LEN); } MD5Final(digest, &context); for (i = 0; i < AUTH_PASS_LEN; i++) { passwd[i + 2 + n] ^= digest[i]; } } memcpy(output, passwd, len + 2);}/* * 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 *ptr){ 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) { /* * 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; if (vendorcode == VENDORPEC_USR) { lvalue = htonl(vp->attribute & 0xFFFF); memcpy(ptr, &lvalue, 4); length_ptr = vsa_length_ptr; total_length += 4; *length_ptr += 4; ptr += 4; /* * We don't have two different lengths. */ vsa_length_ptr = NULL; } else if (vendorcode == VENDORPEC_LUCENT) { /* * 16-bit attribute, 8-bit length */ *ptr++ = ((vp->attribute >> 8) & 0xFF); *ptr++ = (vp->attribute & 0xFF); length_ptr = ptr; *vsa_length_ptr += 3; *ptr++ = 3; total_length += 3; } else if (vendorcode == VENDORPEC_STARENT) { /* * 16-bit attribute, 16-bit length * with the upper 8 bits of the length * always zero! */ *ptr++ = ((vp->attribute >> 8) & 0xFF); *ptr++ = (vp->attribute & 0xFF); *ptr++ = 0; length_ptr = ptr; *vsa_length_ptr += 4; *ptr++ = 4; total_length += 4; } else { /* * All other VSA's are encoded the same * as RFC attributes. */ *vsa_length_ptr += 2; goto rfc; } } else { rfc: /* * All other attributes are encoded as * per the RFC. */ *ptr++ = (vp->attribute & 0xFF); length_ptr = ptr; *ptr++ = 2; total_length += 2; } 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 */ /* * Set up the default sources for the data. */ data = vp->strvalue; len = vp->length; /* * Encrypted passwords can't be very long. * This check also ensures that the hashed version * of the password + attribute header fits into one * attribute. * * FIXME: Print a warning message if it's too long? */ if (vp->flags.encrypt && (len > MAX_PASS_LEN)) { len = MAX_PASS_LEN; } switch(vp->type) { case PW_TYPE_STRING: case PW_TYPE_OCTETS: case PW_TYPE_IFID: case PW_TYPE_IPV6ADDR: case PW_TYPE_IPV6PREFIX: case PW_TYPE_ABINARY: /* nothing more to do */ break; case PW_TYPE_INTEGER: len = 4; /* just in case */ lvalue = htonl(vp->lvalue); memcpy(array, &lvalue, sizeof(lvalue)); /* * Perhaps discard the first octet. */ data = &array[offset]; len -= offset; break; case PW_TYPE_IPADDR: data = (const uint8_t *) &vp->lvalue; len = 4; /* just in case */ break; /* * There are no tagged date attributes. */ case PW_TYPE_DATE: lvalue = htonl(vp->lvalue); data = (const uint8_t *) &lvalue; len = 4; /* just in case */ break; default: /* unknown type: ignore it */ librad_log("ERROR: Unknown attribute type %d", vp->type); return -1; } /* * Bound the data to 255 bytes. */ if (len + offset + total_length > 255) { len = 255 - offset - total_length; } /* * Encrypt the various password styles * * Attributes with encrypted values MUST be less than * 128 bytes long. */ switch (vp->flags.encrypt) { case FLAG_ENCRYPT_USER_PASSWORD: make_passwd(ptr + offset, &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; } /* * 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. * * This is ONLY a problem if we have multiple VSA's * in one Vendor-Specific, though. */ if ((255 - offset - total_length) < 18) return 0; /* * Can't make the password, suppress it. */ make_tunnel_passwd(ptr + offset, &len, data, len, 255 - offset - total_length, secret, original->vector); break; /* * The code above ensures that this attribute * always fits. */ case FLAG_ENCRYPT_ASCEND_SECRET: make_secret(ptr + offset, packet->vector, secret, data); len = AUTH_VECTOR_LEN; break; default: /* * Just copy the data over */ memcpy(ptr + offset, data, len); break; } /* switch over encryption flags */ /* * Account for the tag (if any). */ len += offset; /* * RFC 2865 section 5 says that zero-length attributes * MUST NOT be sent. */ if (len == 0) return 0; /* * Update the various lengths. */ *length_ptr += len; if (vsa_length_ptr) *vsa_length_ptr += len; ptr += len; total_length += len; return total_length; /* of attribute */}/* * Encode a packet. */int rad_encode(RADIUS_PACKET *packet, const RADIUS_PACKET *original, const char *secret){ radius_packet_t *hdr; uint8_t *ptr; uint16_t total_length; int len; VALUE_PAIR *reply; /* * 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. */ uint32_t data[(MAX_PACKET_LEN + 256) / 4]; /* * 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: case PW_COA_REQUEST: memset(packet->vector, 0, sizeof(packet->vector)); break; default: break; } /* * 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;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -