summaryrefslogtreecommitdiff
path: root/lib/radius/attrs.c
diff options
context:
space:
mode:
Diffstat (limited to 'lib/radius/attrs.c')
-rw-r--r--lib/radius/attrs.c1411
1 files changed, 0 insertions, 1411 deletions
diff --git a/lib/radius/attrs.c b/lib/radius/attrs.c
deleted file mode 100644
index 21cd3f0..0000000
--- a/lib/radius/attrs.c
+++ /dev/null
@@ -1,1411 +0,0 @@
-/*
-Copyright (c) 2011, Network RADIUS SARL
-All rights reserved.
-
-Redistribution and use in source and binary forms, with or without
-modification, are permitted provided that the following conditions are met:
- * Redistributions of source code must retain the above copyright
- notice, this list of conditions and the following disclaimer.
- * Redistributions in binary form must reproduce the above copyright
- notice, this list of conditions and the following disclaimer in the
- documentation and/or other materials provided with the distribution.
- * Neither the name of the <organization> nor the
- names of its contributors may be used to endorse or promote products
- derived from this software without specific prior written permission.
-
-THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
-ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
-WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
-DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY
-DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
-(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
-LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
-ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
-SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-
-/** \file attrs.c
- * \brief Attribute encoding and decoding routines.
- */
-
-#include "client.h"
-
-/*
- * Encodes the data portion of an attribute.
- * Returns -1 on error, or the length of the data portion.
- */
-static ssize_t vp2data_any(const RADIUS_PACKET *packet,
- const RADIUS_PACKET *original,
- int nest,
- const VALUE_PAIR **pvp,
- uint8_t *start, size_t room)
-{
- uint32_t lvalue;
- ssize_t len;
- const uint8_t *data;
- uint8_t *ptr = start;
- uint8_t array[4];
- const VALUE_PAIR *vp = *pvp;
-
-#ifdef RS_TYPE_TLV
- /*
- * See if we need to encode a TLV. The low portion of
- * the attribute has already been placed into the packer.
- * If there are still attribute bytes left, then go
- * encode them as TLVs.
- *
- * If we cared about the stack, we could unroll the loop.
- */
- if ((nest > 0) && (nest <= nr_attr_max_tlv) &&
- ((vp->da->attr >> nr_attr_shift[nest]) != 0)) {
- return vp2data_tlvs(packet, original, nest, pvp,
- start, room);
- }
-#else
- nest = nest; /* -Wunused */
-#endif
-
- /*
- * Set up the default sources for the data.
- */
- data = vp->vp_octets;
- len = vp->length;
-
- switch(vp->da->type) {
- case RS_TYPE_IPV6PREFIX:
- len = sizeof(vp->vp_ipv6prefix);
- break;
-
- case RS_TYPE_STRING:
- case RS_TYPE_OCTETS:
- case RS_TYPE_IFID:
- case RS_TYPE_IPV6ADDR:
-#ifdef RS_TYPE_ABINARY
- case RS_TYPE_ABINARY:
-#endif
- /* nothing more to do */
- break;
-
- case RS_TYPE_BYTE:
- len = 1; /* just in case */
- array[0] = vp->vp_integer & 0xff;
- data = array;
- break;
-
- case RS_TYPE_SHORT:
- len = 2; /* just in case */
- array[0] = (vp->vp_integer >> 8) & 0xff;
- array[1] = vp->vp_integer & 0xff;
- data = array;
- break;
-
- case RS_TYPE_INTEGER:
- len = 4; /* just in case */
- lvalue = htonl(vp->vp_integer);
- memcpy(array, &lvalue, sizeof(lvalue));
- data = array;
- break;
-
- case RS_TYPE_IPADDR:
- data = (const uint8_t *) &vp->vp_ipaddr;
- len = 4; /* just in case */
- break;
-
- /*
- * There are no tagged date attributes.
- */
- case RS_TYPE_DATE:
- lvalue = htonl(vp->vp_date);
- data = (const uint8_t *) &lvalue;
- len = 4; /* just in case */
- break;
-
-#ifdef VENDORPEC_WIMAX
- case RS_TYPE_SIGNED:
- {
- int32_t slvalue;
-
- len = 4; /* just in case */
- slvalue = htonl(vp->vp_signed);
- memcpy(array, &slvalue, sizeof(slvalue));
- break;
- }
-#endif
-
-#ifdef RS_TYPE_TLV
- case RS_TYPE_TLV:
- data = vp->vp_tlv;
- if (!data) {
- nr_debug_error("ERROR: Cannot encode NULL TLV");
- return -RSE_INVAL;
- }
- len = vp->length;
- break;
-#endif
-
- default: /* unknown type: ignore it */
- nr_debug_error("ERROR: Unknown attribute type %d", vp->da->type);
- return -RSE_ATTR_TYPE_UNKNOWN;
- }
-
- /*
- * Bound the data to the calling size
- */
- if (len > (ssize_t) room) len = room;
-
-#ifndef FLAG_ENCRYPT_TUNNEL_PASSWORD
- original = original; /* -Wunused */
-#endif
-
- /*
- * Encrypt the various password styles
- *
- * Attributes with encrypted values MUST be less than
- * 128 bytes long.
- */
- switch (vp->da->flags.encrypt) {
- case FLAG_ENCRYPT_USER_PASSWORD:
- len = nr_password_encrypt(ptr, room, data, len,
- packet->secret, packet->vector);
- break;
-
-#ifdef FLAG_ENCRYPT_TUNNEL_PASSWORD
- case FLAG_ENCRYPT_TUNNEL_PASSWORD:
- lvalue = 0;
- if (vp->da->flags.has_tag) lvalue = 1;
-
- /*
- * 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 (room < (18 + lvalue)) {
- *pvp = vp->next;
- return 0;
- }
-
- switch (packet->code) {
- case PW_ACCESS_ACCEPT:
- case PW_ACCESS_REJECT:
- case PW_ACCESS_CHALLENGE:
- default:
- if (!original) {
- nr_debug_error("ERROR: No request packet, cannot encrypt %s attribute in the vp.", vp->da->name);
- return -RSE_REQUEST_REQUIRED;
- }
-
- if (lvalue) ptr[0] = vp->tag;
- len = nr_tunnelpw_encrypt(ptr + lvalue,
- room - lvalue, data, len,
- packet->secret,
- original->vector);
- if (len < 0) return len;
- break;
- case PW_ACCOUNTING_REQUEST:
- case PW_DISCONNECT_REQUEST:
- case PW_COA_REQUEST:
- ptr[0] = vp->tag;
- len = nr_tunnelpw_encrypt(ptr + 1, room, data, len - 1,
- packet->secret,
- packet->vector);
- if (len < 0) return len;
- break;
- }
- break;
-#endif
-
- /*
- * The code above ensures that this attribute
- * always fits.
- */
-#ifdef FLAG_ENCRYPT_ASCEND_SECRET
- case FLAG_ENCRYPT_ASCEND_SECRET:
- make_secret(ptr, packet->vector, packet->secret, data);
- len = AUTH_VECTOR_LEN;
- break;
-#endif
-
- default:
- if (vp->da->flags.has_tag && TAG_VALID(vp->tag)) {
- if (vp->da->type == RS_TYPE_STRING) {
- if (len > ((ssize_t) (room - 1))) len = room - 1;
- ptr[0] = vp->tag;
- ptr++;
- } else if (vp->da->type == RS_TYPE_INTEGER) {
- array[0] = vp->tag;
- } /* else it can't be any other type */
- }
- memcpy(ptr, data, len);
- break;
- } /* switch over encryption flags */
-
- *(pvp) = vp->next;
- return len + (ptr - start);;
-}
-
-
-/*
- * Encode an RFC format TLV. This could be a standard attribute,
- * or a TLV data type. If it's a standard attribute, then
- * vp->da->attr == attribute. Otherwise, attribute may be
- * something else.
- */
-static ssize_t vp2attr_rfc(const RADIUS_PACKET *packet,
- const RADIUS_PACKET *original,
- const VALUE_PAIR **pvp,
- unsigned int attribute, uint8_t *ptr, size_t room)
-{
- ssize_t len;
-
- if (room < 2) {
- *pvp = (*pvp)->next;
- return 0;
- }
-
- ptr[0] = attribute & 0xff;
- ptr[1] = 2;
-
- if (room > ((unsigned) 255 - ptr[1])) room = 255 - ptr[1];
-
- len = vp2data_any(packet, original, 0, pvp, ptr + ptr[1], room);
- if (len < 0) return len;
-
- ptr[1] += len;
-
- return ptr[1];
-}
-
-
-#ifndef WITHOUT_VSAS
-/*
- * Encode a VSA which is a TLV. If it's in the RFC format, call
- * vp2attr_rfc. Otherwise, encode it here.
- */
-static ssize_t vp2attr_vsa(const RADIUS_PACKET *packet,
- const RADIUS_PACKET *original,
- const VALUE_PAIR **pvp,
- unsigned int attribute, unsigned int vendor,
- uint8_t *ptr, size_t room)
-{
- ssize_t len;
- const DICT_VENDOR *dv;
-
- /*
- * Unknown vendor: RFC format.
- * Known vendor and RFC format: go do that.
- */
- dv = nr_dict_vendor_byvalue(vendor);
- if (!dv ||
- (
-#ifdef RS_TYPE_TLV
- !(*pvp)->flags.is_tlv &&
-#endif
- (dv->type == 1) && (dv->length == 1))) {
- return vp2attr_rfc(packet, original, pvp,
- attribute, ptr, room);
- }
-
-#ifdef RS_TYPE_TLV
- if ((*pvp)->flags.is_tlv) {
- return data2vp_tlvs(packet, original, 0, pvp,
- ptr, room);
- }
-#endif
-
- switch (dv->type) {
- default:
- nr_debug_error("vp2attr_vsa: Internal sanity check failed,"
- " type %u", (unsigned) dv->type);
- return -RSE_INTERNAL;
-
- case 4:
- ptr[0] = 0; /* attr must be 24-bit */
- ptr[1] = (attribute >> 16) & 0xff;
- ptr[2] = (attribute >> 8) & 0xff;
- ptr[3] = attribute & 0xff;
- break;
-
- case 2:
- ptr[0] = (attribute >> 8) & 0xff;
- ptr[1] = attribute & 0xff;
- break;
-
- case 1:
- ptr[0] = attribute & 0xff;
- break;
- }
-
- switch (dv->length) {
- default:
- nr_debug_error("vp2attr_vsa: Internal sanity check failed,"
- " length %u", (unsigned) dv->length);
- return -RSE_INTERNAL;
-
- case 0:
- break;
-
- case 2:
- ptr[dv->type] = 0;
- /* FALL-THROUGH */
-
- case 1:
- ptr[dv->type + dv->length - 1] = dv->type + dv->length;
- break;
-
- }
-
- if (room > ((unsigned) 255 - (dv->type + dv->length))) {
- room = 255 - (dv->type + dv->length);
- }
-
- len = vp2data_any(packet, original, 0, pvp,
- ptr + dv->type + dv->length, room);
- if (len < 0) return len;
-
- if (dv->length) ptr[dv->type + dv->length - 1] += len;
-
- return dv->type + dv->length + len;
-}
-
-
-/*
- * Encode a Vendor-Specific attribute.
- */
-ssize_t nr_vp2vsa(const RADIUS_PACKET *packet, const RADIUS_PACKET *original,
- const VALUE_PAIR **pvp, uint8_t *ptr,
- size_t room)
-{
- ssize_t len;
- uint32_t lvalue;
- const VALUE_PAIR *vp = *pvp;
-
-#ifdef VENDORPEC_WIMAX
- /*
- * Double-check for WiMAX
- */
- if (vp->da->vendor == VENDORPEC_WIMAX) {
- return nr_vp2wimax(packet, original, pvp,
- ptr, room);
- }
-#endif
-
- if (vp->da->vendor > RS_MAX_VENDOR) {
- nr_debug_error("nr_vp2vsa: Invalid arguments");
- return -RSE_INVAL;
- }
-
- /*
- * Not enough room for:
- * attr, len, vendor-id
- */
- if (room < 6) {
- *pvp = vp->next;
- return 0;
- }
-
- /*
- * Build the Vendor-Specific header
- */
- ptr[0] = PW_VENDOR_SPECIFIC;
- ptr[1] = 6;
- lvalue = htonl(vp->da->vendor);
- memcpy(ptr + 2, &lvalue, 4);
-
- if (room > ((unsigned) 255 - ptr[1])) room = 255 - ptr[1];
-
- len = vp2attr_vsa(packet, original, pvp,
- vp->da->attr, vp->da->vendor,
- ptr + ptr[1], room);
- if (len < 0) return len;
-
- ptr[1] += len;
-
- return ptr[1];
-}
-#endif
-
-
-/*
- * Encode an RFC standard attribute 1..255
- */
-ssize_t nr_vp2rfc(const RADIUS_PACKET *packet,
- const RADIUS_PACKET *original,
- const VALUE_PAIR **pvp,
- uint8_t *ptr, size_t room)
-{
- const VALUE_PAIR *vp = *pvp;
-
- if (vp->da->vendor != 0) {
- nr_debug_error("nr_vp2rfc called with VSA");
- return -RSE_INVAL;
- }
-
- if ((vp->da->attr == 0) || (vp->da->attr > 255)) {
- nr_debug_error("nr_vp2rfc called with non-standard attribute %u", vp->da->attr);
- return -RSE_INVAL;
- }
-
-#ifdef PW_CHARGEABLE_USER_IDENTITY
- if ((vp->length == 0) &&
- (vp->da != RS_DA_CHARGEABLE_USER_IDENTITY)) {
- *pvp = vp->next;
- return 0;
- }
-#endif
-
- return vp2attr_rfc(packet, original, pvp, vp->da->attr,
- ptr, room);
-}
-
-#ifdef PW_CHAP_PASSWORD
-/*
- * Encode an RFC standard attribute 1..255
- */
-static ssize_t nr_chap2rfc(const RADIUS_PACKET *packet,
- const RADIUS_PACKET *original,
- const VALUE_PAIR **pvp,
- uint8_t *ptr, size_t room)
-{
- ssize_t rcode;
- const VALUE_PAIR *vp = *pvp;
- RS_MD5_CTX ctx;
- uint8_t buffer[RS_MAX_STRING_LEN*2 + 1], *p;
- VALUE_PAIR chap = {
- RS_DA_CHAP_PASSWORD,
- 17,
- 0,
- NULL,
- {
- .octets = {
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
- },
- },
- };
-
- if ((vp->da->vendor != 0) || (vp->da != RS_DA_CHAP_PASSWORD)) {
- nr_debug_error("nr_chap2rfc called with non-CHAP");
- return -RSE_INVAL;
- }
-
- p = buffer;
- *(p++) = nr_rand() & 0xff; /* id */
-
- memcpy(p, vp->vp_strvalue, strlen(vp->vp_strvalue));
- p += strlen(vp->vp_strvalue);
-
- vp = nr_vps_find(packet->vps, PW_CHAP_CHALLENGE, 0);
- if (vp) {
- memcpy(p, vp->vp_octets, vp->length);
- p += vp->length;
- } else {
- memcpy(p, packet->vector, sizeof(packet->vector));
- p += sizeof(packet->vector);
- }
-
- RS_MD5Init(&ctx);
- RS_MD5Update(&ctx, buffer, p - buffer);
- RS_MD5Final(&chap.vp_octets[1], &ctx);
-
- chap.vp_octets[0] = buffer[0];
- vp = &chap;
-
- rcode = vp2attr_rfc(packet, original, &vp, chap.da->attr,
- ptr, room);
- if (rcode < 0) return rcode;
-
- *pvp = (*pvp)->next;
- return rcode;
-}
-#endif /* PW_CHAP_PASSWORD */
-
-#ifdef PW_MESSAGE_AUTHENTICATOR
-/** Fake Message-Authenticator.
- *
- * This structure is used to replace a Message-Authenticator in the
- * input list of VALUE_PAIRs when encoding a packet. If the caller
- * asks us to encode a Message-Authenticator, we ignore the one given
- * to us by the caller (which may have the wrong length, etc.), and
- * instead use this one, which has the correct length and data.
- */
-static const VALUE_PAIR fake_ma = {
- RS_DA_MESSAGE_AUTHENTICATOR,
- 16,
- 0,
- NULL,
- {
- .octets = {
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
- },
- }
-};
-#endif /* PW_MESSAGE_AUTHENTICATOR */
-
-/*
- * Parse a data structure into a RADIUS attribute.
- */
-ssize_t nr_vp2attr(const RADIUS_PACKET *packet, const RADIUS_PACKET *original,
- const VALUE_PAIR **pvp, uint8_t *start,
- size_t room)
-{
- const VALUE_PAIR *vp = *pvp;
-
- /*
- * RFC format attributes take the fast path.
- */
- if (vp->da->vendor != 0) {
-#ifdef VENDORPEC_EXTENDED
- if (vp->da->vendor > RS_MAX_VENDOR) {
- return nr_vp2attr_extended(packet, original,
- pvp, start, room);
-
- }
-#endif
-
-#ifdef VENDORPEC_WIMAX
- if (vp->da->vendor == VENDORPEC_WIMAX) {
- return nr_vp2attr_wimax(packet, original,
- pvp, start, room);
- }
-#endif
-
-#ifndef WITHOUT_VSAS
- return nr_vp2vsa(packet, original, pvp, start, room);
-#else
- nr_debug_error("VSAs are not supported");
- return -RSE_UNSUPPORTED;
-#endif
- }
-
- /*
- * Ignore non-protocol attributes.
- */
- if (vp->da->attr > 255) {
- *pvp = vp->next;
- return 0;
- }
-
-#ifdef PW_MESSAGE_AUTHENTICATOR
- /*
- * The caller wants a Message-Authenticator, but doesn't
- * know how to calculate it, or what the correct values
- * are. So... create one for him.
- */
- if (vp->da == RS_DA_MESSAGE_AUTHENTICATOR) {
- ssize_t rcode;
-
- vp = &fake_ma;
- rcode = nr_vp2rfc(packet, original, &vp, start, room);
- if (rcode <= 0) return rcode;
- *pvp = (*pvp)->next;
- return rcode;
- }
-#endif
-
-#ifdef PW_CHAP_PASSWORD
- /*
- * The caller wants a CHAP-Password, but doesn't know how
- * to calculate it, or what the correct values are. To
- * help, we calculate it for him.
- */
- if (vp->da == RS_DA_CHAP_PASSWORD) {
- int encoded = 0;
-
- /*
- * CHAP is ID + MD5(...). If it's length is NOT
- * 17, then the caller has passed us a password,
- * and wants us to encode it. If the length IS
- * 17, then we need to double-check if the caller
- * has already encoded it.
- */
- if (vp->length == 17) {
- int i;
-
- /*
- * ASCII and UTF-8 disallow values 0..31.
- * If they appear, then the CHAP-Password
- * has already been encoded by the
- * caller. The probability of a
- * CHAP-Password being all 32..256 is
- * (1-32/256)^17 =~ .10
- *
- * This check isn't perfect, but it
- * should be pretty rare for people to
- * have 17-character passwords *and* have
- * them all 32..256.
- */
- for (i = 0; i < 17; i++) {
- if (vp->vp_octets[i] < 32) {
- encoded = 1;
- break;
- }
- }
- }
-
- if (!encoded) {
- return nr_chap2rfc(packet, original, pvp, start, room);
- }
- }
-#endif
-
- return nr_vp2rfc(packet, original, pvp,
- start, room);
-}
-
-
-/*
- * Ignore unknown attributes, but "decoding" them into nothing.
- */
-static ssize_t data2vp_raw(UNUSED const RADIUS_PACKET *packet,
- UNUSED const RADIUS_PACKET *original,
- unsigned int attribute,
- unsigned int vendor,
- const uint8_t *data, size_t length,
- VALUE_PAIR **pvp)
-{
- VALUE_PAIR *vp;
-
- if (length > sizeof(vp->vp_octets)) return -RSE_ATTR_OVERFLOW;
-
- vp = nr_vp_alloc_raw(attribute, vendor);
- if (!vp) return -RSE_NOMEM;
-
- memcpy(vp->vp_octets, data, length);
- vp->length = length;
-
- *pvp = vp;
- return length;
-}
-
-ssize_t nr_attr2vp_raw(const RADIUS_PACKET *packet,
- const RADIUS_PACKET *original,
- const uint8_t *data, size_t length,
- VALUE_PAIR **pvp)
-{
-
- if (length < 2) return -RSE_PACKET_TOO_SMALL;
- if (data[1] < 2) return -RSE_ATTR_TOO_SMALL;
- if (data[1] > length) return -RSE_ATTR_OVERFLOW;
-
- return data2vp_raw(packet, original, data[0], 0,
- data + 2, data[1] - 2, pvp);
-}
-
-/*
- * Create any kind of VP from the attribute contents.
- *
- * Will return -1 on error, or "length".
- */
-static ssize_t data2vp_any(const RADIUS_PACKET *packet,
- const RADIUS_PACKET *original,
- int nest,
- unsigned int attribute, unsigned int vendor,
- const uint8_t *data, size_t length,
- VALUE_PAIR **pvp)
-{
-#ifdef FLAG_ENCRYPT_TUNNEL_PASSWORD
- ssize_t rcode;
-#endif
- int data_offset = 0;
- const DICT_ATTR *da;
- VALUE_PAIR *vp = NULL;
-
- if (length == 0) {
- /*
- * Hacks for CUI. The WiMAX spec says that it
- * can be zero length, even though this is
- * forbidden by the RADIUS specs. So... we make
- * a special case for it.
- */
- if ((vendor == 0) &&
- (attribute == PW_CHARGEABLE_USER_IDENTITY)) {
- data = (const uint8_t *) "";
- length = 1;
- } else {
- *pvp = NULL;
- return 0;
- }
- }
-
- da = nr_dict_attr_byvalue(attribute, vendor);
-
- /*
- * Unknown attribute. Create it as a "raw" attribute.
- */
- if (!da) {
- raw:
- if (vp) nr_vp_free(&vp);
- return data2vp_raw(packet, original,
- attribute, vendor, data, length, pvp);
- }
-
-#ifdef RS_TYPE_TLV
- /*
- * TLVs are handled first. They can't be tagged, and
- * they can't be encrypted.
- */
- if (da->da->type == RS_TYPE_TLV) {
- return data2vp_tlvs(packet, original,
- attribute, vendor, nest,
- data, length, pvp);
- }
-#else
- nest = nest; /* -Wunused */
-#endif
-
- /*
- * The attribute is known, and well formed. We can now
- * create it. The main failure from here on in is being
- * out of memory.
- */
- vp = nr_vp_alloc(da);
- if (!vp) return -RSE_NOMEM;
-
- /*
- * Handle tags.
- */
- if (vp->da->flags.has_tag) {
- if (TAG_VALID(data[0])
-#ifdef FLAG_ENCRYPT_TUNNEL_PASSWORD
- || (vp->da->flags.encrypt == FLAG_ENCRYPT_TUNNEL_PASSWORD)
-#endif
- ) {
- /*
- * Tunnel passwords REQUIRE a tag, even
- * if don't have a valid tag.
- */
- vp->tag = data[0];
-
- if ((vp->da->type == RS_TYPE_STRING) ||
- (vp->da->type == RS_TYPE_OCTETS)) {
- if (length == 0) goto raw;
- data_offset = 1;
- }
- }
- }
-
- /*
- * Copy the data to be decrypted
- */
- vp->length = length - data_offset;
- memcpy(&vp->vp_octets[0], data + data_offset, vp->length);
-
- /*
- * Decrypt the attribute.
- */
- switch (vp->da->flags.encrypt) {
- /*
- * User-Password
- */
- case FLAG_ENCRYPT_USER_PASSWORD:
- if (original) {
- rcode = nr_password_encrypt(vp->vp_octets,
- sizeof(vp->vp_strvalue),
- data + data_offset, vp->length,
- packet->secret,
- original->vector);
- } else {
- rcode = nr_password_encrypt(vp->vp_octets,
- sizeof(vp->vp_strvalue),
- data + data_offset, vp->length,
- packet->secret,
- packet->vector);
- }
- if (rcode < 0) goto raw;
- vp->vp_strvalue[128] = '\0';
- vp->length = strlen(vp->vp_strvalue);
- break;
-
- /*
- * Tunnel-Password's may go ONLY
- * in response packets.
- */
-#ifdef FLAG_ENCRYPT_TUNNEL_PASSWORD
- case FLAG_ENCRYPT_TUNNEL_PASSWORD:
- if (!original) goto raw;
-
- rcode = nr_tunnelpw_decrypt(vp->vp_octets,
- sizeof(vp->vp_octets),
- data + data_offset, vp->length,
- packet->secret, original->vector);
- if (rcode < 0) goto raw;
- vp->length = rcode;
- break;
-#endif
-
-
-#ifdef FLAG_ENCRYPT_ASCEND_SECRET
- /*
- * Ascend-Send-Secret
- * Ascend-Receive-Secret
- */
- case FLAG_ENCRYPT_ASCEND_SECRET:
- if (!original) {
- goto raw;
- } else {
- uint8_t my_digest[AUTH_VECTOR_LEN];
- make_secret(my_digest,
- original->vector,
- packet->secret, data);
- memcpy(vp->vp_strvalue, my_digest,
- AUTH_VECTOR_LEN );
- vp->vp_strvalue[AUTH_VECTOR_LEN] = '\0';
- vp->length = strlen(vp->vp_strvalue);
- }
- break;
-#endif
-
- default:
- break;
- } /* switch over encryption flags */
-
- /*
- * Expected a certain length, but got something else.
- */
- if ((vp->da->flags.length != 0) &&
- (vp->length != vp->da->flags.length)) {
- goto raw;
- }
-
- switch (vp->da->type) {
- case RS_TYPE_STRING:
- case RS_TYPE_OCTETS:
-#ifdef RS_TYPE_ABINARY
- case RS_TYPE_ABINARY:
-#endif
- /* nothing more to do */
- break;
-
- case RS_TYPE_BYTE:
- vp->vp_integer = vp->vp_octets[0];
- break;
-
-
- case RS_TYPE_SHORT:
- vp->vp_integer = (vp->vp_octets[0] << 8) | vp->vp_octets[1];
- break;
-
- case RS_TYPE_INTEGER:
- memcpy(&vp->vp_integer, vp->vp_octets, 4);
- vp->vp_integer = ntohl(vp->vp_integer);
-
- if (vp->da->flags.has_tag) vp->vp_integer &= 0x00ffffff;
- break;
-
- case RS_TYPE_DATE:
- memcpy(&vp->vp_date, vp->vp_octets, 4);
- vp->vp_date = ntohl(vp->vp_date);
- break;
-
-
- case RS_TYPE_IPADDR:
- memcpy(&vp->vp_ipaddr, vp->vp_octets, 4);
- break;
-
- /*
- * IPv6 interface ID is 8 octets long.
- */
- case RS_TYPE_IFID:
- /* vp->vp_ifid == vp->vp_octets */
- break;
-
- /*
- * IPv6 addresses are 16 octets long
- */
- case RS_TYPE_IPV6ADDR:
- /* vp->vp_ipv6addr == vp->vp_octets */
- break;
-
- /*
- * IPv6 prefixes are 2 to 18 octets long.
- *
- * RFC 3162: The first octet is unused.
- * The second is the length of the prefix
- * the rest are the prefix data.
- *
- * The prefix length can have value 0 to 128.
- */
- case RS_TYPE_IPV6PREFIX:
- if (vp->length < 2 || vp->length > 18) goto raw;
- if (vp->vp_octets[1] > 128) goto raw;
-
- /*
- * FIXME: double-check that
- * (vp->vp_octets[1] >> 3) matches vp->length + 2
- */
- if (vp->length < 18) {
- memset(vp->vp_octets + vp->length, 0,
- 18 - vp->length);
- }
- break;
-
-#ifdef VENDORPEC_WIMAX
- case RS_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;
-#endif
-
-#ifdef RS_TYPE_TLV
- case RS_TYPE_TLV:
- nr_vp_free(&vp);
- nr_debug_error("data2vp_any: Internal sanity check failed");
- return -RSE_ATTR_TYPE_UNKNOWN;
-#endif
-
-#ifdef VENDORPEC_WIMAX
- case RS_TYPE_COMBO_IP:
- if (vp->length == 4) {
- vp->da->type = RS_TYPE_IPADDR;
- memcpy(&vp->vp_ipaddr, vp->vp_octets, 4);
- break;
-
- } else if (vp->length == 16) {
- vp->da->type = RS_TYPE_IPV6ADDR;
- /* vp->vp_ipv6addr == vp->vp_octets */
- break;
-
- }
- /* FALL-THROUGH */
-#endif
-
- default:
- goto raw;
- }
-
- *pvp = vp;
-
- return length;
-}
-
-
-/*
- * Create a "standard" RFC VALUE_PAIR from the given data.
- */
-ssize_t nr_attr2vp_rfc(const RADIUS_PACKET *packet,
- const RADIUS_PACKET *original,
- const uint8_t *data, size_t length,
- VALUE_PAIR **pvp)
-{
- ssize_t rcode;
-
- if (length < 2) return -RSE_PACKET_TOO_SMALL;
- if (data[1] < 2) return -RSE_ATTR_TOO_SMALL;
- if (data[1] > length) return -RSE_ATTR_OVERFLOW;
-
- rcode = data2vp_any(packet, original, 0,
- data[0], 0, data + 2, data[1] - 2, pvp);
- if (rcode < 0) return rcode;
-
- return data[1];
-}
-
-#ifndef WITHOUT_VSAS
-/*
- * Check if a set of RADIUS formatted TLVs are OK.
- */
-int nr_tlv_ok(const uint8_t *data, size_t length,
- size_t dv_type, size_t dv_length)
-{
- const uint8_t *end = data + length;
-
- if ((dv_length > 2) || (dv_type == 0) || (dv_type > 4)) {
- nr_debug_error("nr_tlv_ok: Invalid arguments");
- return -RSE_INVAL;
- }
-
- while (data < end) {
- size_t attrlen;
-
- if ((data + dv_type + dv_length) > end) {
- nr_debug_error("Attribute header overflow");
- return -RSE_ATTR_TOO_SMALL;
- }
-
- switch (dv_type) {
- case 4:
- if ((data[0] == 0) && (data[1] == 0) &&
- (data[2] == 0) && (data[3] == 0)) {
- zero:
- nr_debug_error("Invalid attribute 0");
- return -RSE_ATTR_INVALID;
- }
-
- if (data[0] != 0) {
- nr_debug_error("Invalid attribute > 2^24");
- return -RSE_ATTR_INVALID;
- }
- break;
-
- case 2:
- if ((data[1] == 0) && (data[1] == 0)) goto zero;
- break;
-
- case 1:
- if (data[0] == 0) goto zero;
- break;
-
- default:
- nr_debug_error("Internal sanity check failed");
- return -RSE_INTERNAL;
- }
-
- switch (dv_length) {
- case 0:
- return 0;
-
- case 2:
- if (data[dv_type + 1] != 0) {
- nr_debug_error("Attribute is longer than 256 octets");
- return -RSE_ATTR_TOO_LARGE;
- }
- /* FALL-THROUGH */
- case 1:
- attrlen = data[dv_type + dv_length - 1];
- break;
-
-
- default:
- nr_debug_error("Internal sanity check failed");
- return -RSE_INTERNAL;
- }
-
- if (attrlen < (dv_type + dv_length)) {
- nr_debug_error("Attribute header has invalid length");
- return -RSE_PACKET_TOO_SMALL;
- }
-
- if (attrlen > length) {
- nr_debug_error("Attribute overflows container");
- return -RSE_ATTR_OVERFLOW;
- }
-
- data += attrlen;
- length -= attrlen;
- }
-
- return 0;
-}
-
-
-/*
- * Convert a top-level VSA to a VP.
- */
-static ssize_t attr2vp_vsa(const RADIUS_PACKET *packet,
- const RADIUS_PACKET *original,
- unsigned int vendor,
- size_t dv_type, size_t dv_length,
- const uint8_t *data, size_t length,
- VALUE_PAIR **pvp)
-{
- unsigned int attribute;
- ssize_t attrlen, my_len;
-
-#ifndef NDEBUG
- if (length <= (dv_type + dv_length)) {
- nr_debug_error("attr2vp_vsa: Failure to call nr_tlv_ok");
- return -RSE_PACKET_TOO_SMALL;
- }
-#endif
-
- switch (dv_type) {
- case 4:
- /* data[0] must be zero */
- attribute = data[1] << 16;
- attribute |= data[2] << 8;
- attribute |= data[3];
- break;
-
- case 2:
- attribute = data[0] << 8;
- attribute |= data[1];
- break;
-
- case 1:
- attribute = data[0];
- break;
-
- default:
- nr_debug_error("attr2vp_vsa: Internal sanity check failed");
- return -RSE_INTERNAL;
- }
-
- switch (dv_length) {
- case 2:
- /* data[dv_type] must be zero */
- attrlen = data[dv_type + 1];
- break;
-
- case 1:
- attrlen = data[dv_type];
- break;
-
- case 0:
- attrlen = length;
- break;
-
- default:
- nr_debug_error("attr2vp_vsa: Internal sanity check failed");
- return -RSE_INTERNAL;
- }
-
-#ifndef NDEBUG
- if (attrlen <= (ssize_t) (dv_type + dv_length)) {
- nr_debug_error("attr2vp_vsa: Failure to call nr_tlv_ok");
- return -RSE_PACKET_TOO_SMALL;
- }
-#endif
-
- attrlen -= (dv_type + dv_length);
-
- my_len = data2vp_any(packet, original, 0,
- attribute, vendor,
- data + dv_type + dv_length, attrlen, pvp);
- if (my_len < 0) return my_len;
-
-#ifndef NDEBUG
- if (my_len != attrlen) {
- nr_vp_free(pvp);
- nr_debug_error("attr2vp_vsa: Incomplete decode %d != %d",
- (int) my_len, (int) attrlen);
- return -RSE_INTERNAL;
- }
-#endif
-
- return dv_type + dv_length + attrlen;
-}
-
-
-/*
- * Create Vendor-Specifc VALUE_PAIRs from a RADIUS attribute.
- */
-ssize_t nr_attr2vp_vsa(const RADIUS_PACKET *packet,
- const RADIUS_PACKET *original,
- const uint8_t *data, size_t length,
- VALUE_PAIR **pvp)
-{
- size_t dv_type, dv_length;
- ssize_t my_len;
- uint32_t lvalue;
- const DICT_VENDOR *dv;
-
- if (length < 2) return -RSE_PACKET_TOO_SMALL;
- if (data[1] < 2) return -RSE_ATTR_TOO_SMALL;
- if (data[1] > length) return -RSE_ATTR_OVERFLOW;
-
- if (data[0] != PW_VENDOR_SPECIFIC) {
- nr_debug_error("nr_attr2vp_vsa: Invalid attribute");
- return -RSE_INVAL;
- }
-
- /*
- * Not enough room for a Vendor-Id.
- * Or the high octet of the Vendor-Id is set.
- */
- if ((data[1] < 6) || (data[2] != 0)) {
- return nr_attr2vp_raw(packet, original,
- data, length, pvp);
- }
-
- memcpy(&lvalue, data + 2, 4);
- lvalue = ntohl(lvalue);
-
-#ifdef VENDORPEC_WIMAX
- /*
- * WiMAX gets its own set of magic.
- */
- if (lvalue == VENDORPEC_WIMAX) {
- return nr_attr2vp_wimax(packet, original,
- data, length, pvp);
- }
-#endif
-
- dv_type = dv_length = 1;
- dv = nr_dict_vendor_byvalue(lvalue);
- if (!dv) {
- return nr_attr2vp_rfc(packet, original,
- data, length, pvp);
- }
-
- dv_type = dv->type;
- dv_length = dv->length;
-
- /*
- * Attribute is not in the correct form.
- */
- if (nr_tlv_ok(data + 6, data[1] - 6, dv_type, dv_length) < 0) {
- return nr_attr2vp_raw(packet, original,
- data, length, pvp);
- }
-
- my_len = attr2vp_vsa(packet, original,
- lvalue, dv_type, dv_length,
- data + 6, data[1] - 6, pvp);
- if (my_len < 0) return my_len;
-
-#ifndef NDEBUG
- if (my_len != (data[1] - 6)) {
- nr_vp_free(pvp);
- nr_debug_error("nr_attr2vp_vsa: Incomplete decode");
- return -RSE_INTERNAL;
- }
-#endif
-
- return data[1];
-}
-#endif /* WITHOUT_VSAS */
-
-
-/*
- * Create a "normal" VALUE_PAIR from the given data.
- */
-ssize_t nr_attr2vp(const RADIUS_PACKET *packet,
- const RADIUS_PACKET *original,
- const uint8_t *data, size_t length,
- VALUE_PAIR **pvp)
-{
- if (length < 2) return -RSE_PACKET_TOO_SMALL;
- if (data[1] < 2) return -RSE_ATTR_TOO_SMALL;
- if (data[1] > length) return -RSE_ATTR_OVERFLOW;
-
-#ifndef WITHOUT_VSAS
- /*
- * VSAs get their own handler.
- */
- if (data[0] == PW_VENDOR_SPECIFIC) {
- return nr_attr2vp_vsa(packet, original,
- data, length, pvp);
- }
-#endif
-
-#ifdef VENDORPEC_EXTENDED
- /*
- * Extended attribute format gets their own handler.
- */
- if (nr_dict_attr_byvalue(data[0], VENDORPEC_EXTENDED) != NULL) {
- return nr_attr2vp_extended(packet, original,
- data, length, pvp);
- }
-#endif
-
- return nr_attr2vp_rfc(packet, original, data, length, pvp);
-}
-
-ssize_t nr_attr2data(const RADIUS_PACKET *packet, ssize_t start,
- unsigned int attribute, unsigned int vendor,
- const uint8_t **pdata, size_t *plength)
-{
- uint8_t *data, *attr;
- const uint8_t *end;
-
- if (!packet || !pdata || !plength) return -RSE_INVAL;
-
- if (!packet->data) return -RSE_INVAL;
- if (packet->length < 20) return -RSE_INVAL;
-
- /*
- * Too long or short, not good.
- */
- if ((start < 0) ||
- ((start > 0) && (start < 20))) return -RSE_INVAL;
-
- if ((size_t) start >= (packet->length - 2)) return -RSE_INVAL;
-
- end = packet->data + packet->length;
-
- /*
- * Loop over the packet, converting attrs to VPs.
- */
- if (start == 0) {
- data = packet->data + 20;
- } else {
- data = packet->data + start;
- data += data[1];
- if (data >= end) return 0;
- }
-
- for (attr = data; attr < end; attr += attr[1]) {
- const DICT_VENDOR *dv = NULL;
-
-#ifndef NEBUG
- /*
- * This code is copied from packet_ok().
- * It could be put into a separate function.
- */
- if ((attr + 2) > end) {
- nr_debug_error("Attribute overflows packet");
- return -RSE_ATTR_OVERFLOW;
- }
-
- if (attr[1] < 2) {
- nr_debug_error("Attribute length is too small");
- return -RSE_ATTR_TOO_SMALL;
- }
-
- if ((attr + attr[1]) > end) {
- nr_debug_error("Attribute length is too large");
- return -RSE_ATTR_TOO_LARGE;
- }
-#endif
-
- if ((vendor == 0) && (attr[0] == attribute)) {
- *pdata = attr + 2;
- *plength = attr[1] - 2;
- return attr - packet->data;
- }
-
-#ifndef WITHOUT_VSAS
- if (vendor != 0) {
- uint32_t vendorpec;
-
- if (attr[0] != PW_VENDOR_SPECIFIC) continue;
-
- if (attr[1] < 6) continue;
-
- memcpy(&vendorpec, attr + 2, 4);
- vendorpec = ntohl(vendorpec);
- if (vendor != vendorpec) continue;
-
- if (!dv) {
- dv = nr_dict_vendor_byvalue(vendor);
- if (dv &&
- ((dv->type != 1) || (dv->length != 1))) {
- return -RSE_VENDOR_UNKNOWN;
- }
- }
-
- /*
- * No data.
- */
- if (attr[1] < 9) continue;
-
- /*
- * Malformed, or more than one VSA in
- * the Vendor-Specific
- */
- if (attr[7] + 6 != attr[1]) continue;
-
- /*
- * Not the right VSA.
- */
- if (attr[6] != attribute) continue;
-
- *pdata = attr + 8;
- *plength = attr[1] - 8;
- return attr - packet->data;
- }
-#endif
- }
-
- return 0; /* nothing more: stop */
-}
-