Merge branch 'sam'
[freeradius.git] / src / lib / radius.c
index ca778d4..8bcca4f 100644 (file)
@@ -1,5 +1,6 @@
-/*
- * radius.c    Functions to send/receive radius packets.
+/**
+ * @file radius.c
+ * @brief Functions to send/receive radius packets.
  *
  * Version:    $Id$
  *
@@ -219,7 +220,6 @@ void rad_print_hex(RADIUS_PACKET *packet)
                        } else {
                                ptr += 2;
                                total -= 2;
-                               vendor = 0;
                        }
 
                        print_hex_data(ptr, attrlen, 3);
@@ -231,8 +231,8 @@ void rad_print_hex(RADIUS_PACKET *packet)
        fflush(stdout);
 }
 
-/*
- *     Wrapper for sendto which handles sendfromto, IPv6, and all
+/**
+ * @brief 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,
@@ -278,6 +278,9 @@ static int rad_sendto(int sockfd, void *data, size_t data_len, int flags,
         */
        rcode = sendto(sockfd, data, data_len, flags,
                       (struct sockaddr *) &dst, sizeof_dst);
+#ifdef WITH_UDPFROMTO
+done:
+#endif
        if (rcode < 0) {
                DEBUG("rad_send() failed: %s\n", strerror(errno));
        }
@@ -365,8 +368,8 @@ ssize_t rad_recv_header(int sockfd, fr_ipaddr_t *src_ipaddr, int *src_port,
 }
 
 
-/*
- *     wrapper for recvfrom, which handles recvfromto, IPv6, and all
+/**
+ * @brief wrapper for recvfrom, which handles recvfromto, IPv6, and all
  *     possible combinations.
  */
 static ssize_t rad_recvfrom(int sockfd, uint8_t **pbuf, int flags,
@@ -493,17 +496,15 @@ static ssize_t rad_recvfrom(int sockfd, uint8_t **pbuf, int flags,
 
 
 #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
+/**
+ * @brief 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)
 {
@@ -673,7 +674,7 @@ extern int fr_attr_max_tlv;
 extern int fr_attr_shift[];
 extern int fr_attr_mask[];
 
-static int do_next_tlv(const VALUE_PAIR *vp, int nest)
+static int do_next_tlv(const VALUE_PAIR *vp, const VALUE_PAIR *next, int nest)
 {
        unsigned int tlv1, tlv2;
 
@@ -692,18 +693,18 @@ static int do_next_tlv(const VALUE_PAIR *vp, int nest)
        /*
         *      Nothing to follow, we're done.
         */
-       if (!vp->next) return 0;
+       if (!next) return 0;
 
        /*
         *      Not from the same vendor, skip it.
         */
-       if (vp->vendor != vp->next->vendor) return 0;
+       if (vp->vendor != next->vendor) return 0;
 
        /*
         *      In a different TLV space, skip it.
         */
        tlv1 = vp->attribute;
-       tlv2 = vp->next->attribute;
+       tlv2 = next->attribute;
        
        tlv1 &= ((1 << fr_attr_shift[nest]) - 1);
        tlv2 &= ((1 << fr_attr_shift[nest]) - 1);
@@ -725,8 +726,8 @@ static ssize_t vp2attr_rfc(const RADIUS_PACKET *packet,
                           const char *secret, const VALUE_PAIR **pvp,
                           unsigned int attribute, uint8_t *ptr, size_t room);
 
-/*
- *     This is really a sub-function of vp2data_any.  It encodes
+/**
+ * @brief This is really a sub-function of vp2data_any().  It encodes
  *     the *data* portion of the TLV, and assumes that the encapsulating
  *     attribute has already been encoded.
  */
@@ -739,12 +740,12 @@ static ssize_t vp2data_tlvs(const RADIUS_PACKET *packet,
        ssize_t len;
        size_t my_room;
        uint8_t *ptr = start;
-       const VALUE_PAIR *old_vp;
        const VALUE_PAIR *vp = *pvp;
-
-#ifndef NDEBUG
        const VALUE_PAIR *svp = vp;
 
+       if (!svp) return 0;
+
+#ifndef NDEBUG
        if (nest > fr_attr_max_tlv) {
                fr_strerror_printf("vp2data_tlvs: attribute nesting overflow");
                return -1;
@@ -754,12 +755,9 @@ static ssize_t vp2data_tlvs(const RADIUS_PACKET *packet,
        while (vp) {
                if (room < 2) return ptr - start;
                
-               old_vp = vp;
                ptr[0] = (vp->attribute >> fr_attr_shift[nest]) & fr_attr_mask[nest];
                ptr[1] = 2;
                
-               VP_TRACE("TLV encoded %s %u\n", vp->name, start[0]);
-               
                my_room = room;
                if (room > 255) my_room = 255;
 
@@ -768,7 +766,7 @@ static ssize_t vp2data_tlvs(const RADIUS_PACKET *packet,
                if (len < 0) return len;
                if (len == 0) return ptr - start;
                /* len can NEVER be more than 253 */
-               
+
                ptr[1] += len;
 
 #ifndef NDEBUG
@@ -782,8 +780,7 @@ static ssize_t vp2data_tlvs(const RADIUS_PACKET *packet,
                ptr += ptr[1];
                *pvp = vp;
                
-               if (!do_next_tlv(old_vp, nest)) break;
-               debug_pair(vp);
+               if (!do_next_tlv(svp, vp, nest)) break;
        }
 
 #ifndef NDEBUG
@@ -799,9 +796,9 @@ static ssize_t vp2data_tlvs(const RADIUS_PACKET *packet,
 }
 
 
-/*
- *     Encodes the data portion of an attribute.
- *     Returns -1 on error, or the length of the data portion.
+/**
+ * @brief Encodes the data portion of an attribute.
+ * @return -1 on error, or the length of the data portion.
  */
 static ssize_t vp2data_any(const RADIUS_PACKET *packet,
                           const RADIUS_PACKET *original,
@@ -814,6 +811,7 @@ static ssize_t vp2data_any(const RADIUS_PACKET *packet,
        const uint8_t *data;
        uint8_t *ptr = start;
        uint8_t array[4];
+       uint64_t lvalue64;
        const VALUE_PAIR *vp = *pvp;
 
        /*
@@ -824,14 +822,13 @@ static ssize_t vp2data_any(const RADIUS_PACKET *packet,
         *
         *      If we cared about the stack, we could unroll the loop.
         */
-       VP_TRACE("vp2data_any: %u attr %u -> %u\n",
-                nest, vp->attribute, vp->attribute >> fr_attr_shift[nest + 1]);
        if (vp->flags.is_tlv && (nest < fr_attr_max_tlv) &&
            ((vp->attribute >> fr_attr_shift[nest + 1]) != 0)) {
                return vp2data_tlvs(packet, original, secret, nest + 1, pvp,
                                    start, room);
        }
-       VP_TRACE("vp2data_any: Encoding %s\n", vp->name);
+
+       debug_pair(vp);
 
        /*
         *      Set up the default sources for the data.
@@ -839,6 +836,12 @@ static ssize_t vp2data_any(const RADIUS_PACKET *packet,
        data = vp->vp_octets;
        len = vp->length;
 
+       /*
+        *      Short-circuit it for long attributes.  They can't be
+        *      encrypted, tagged, etc.
+        */
+       if ((vp->type & PW_FLAG_LONG) != 0) goto do_tlv;
+
        switch(vp->type) {
        case PW_TYPE_STRING:
        case PW_TYPE_OCTETS:
@@ -869,6 +872,12 @@ static ssize_t vp2data_any(const RADIUS_PACKET *packet,
                data = array;
                break;
 
+       case PW_TYPE_INTEGER64:
+               len = 8;        /* just in case */
+               lvalue64 = htonll(vp->vp_integer64);
+               data = (uint8_t *) &lvalue64;
+               break;
+
        case PW_TYPE_IPADDR:
                data = (const uint8_t *) &vp->vp_ipaddr;
                len = 4;        /* just in case */
@@ -894,12 +903,12 @@ static ssize_t vp2data_any(const RADIUS_PACKET *packet,
        }
 
        case PW_TYPE_TLV:
+       do_tlv:
                data = vp->vp_tlv;
                if (!data) {
                        fr_strerror_printf("ERROR: Cannot encode NULL TLV");
                        return -1;
                }
-               len = vp->length;
                break;
 
        default:                /* unknown type: ignore it */
@@ -908,6 +917,14 @@ static ssize_t vp2data_any(const RADIUS_PACKET *packet,
        }
 
        /*
+        *      No data: skip it.
+        */
+       if (len == 0) {
+               *pvp = vp->next;
+               return 0;
+       }
+
+       /*
         *      Bound the data to the calling size
         */
        if (len > (ssize_t) room) len = room;
@@ -987,7 +1004,7 @@ static ssize_t vp2data_any(const RADIUS_PACKET *packet,
        } /* switch over encryption flags */
 
        *pvp = vp->next;
-       return len + (ptr - start);;
+       return len + (ptr - start);
 }
 
 static ssize_t attr_shift(const uint8_t *start, const uint8_t *end,
@@ -1050,8 +1067,8 @@ static ssize_t attr_shift(const uint8_t *start, const uint8_t *end,
 }
 
 
-/*
- *     Encode an "extended" attribute.
+/**
+ * @brief Encode an "extended" attribute.
  */
 int rad_vp2extended(const RADIUS_PACKET *packet,
                    const RADIUS_PACKET *original,
@@ -1064,7 +1081,6 @@ int rad_vp2extended(const RADIUS_PACKET *packet,
        uint8_t *start = ptr;
        const VALUE_PAIR *vp = *pvp;
 
-       VP_TRACE("rad_vp2extended %s\n", vp->name);
        if (vp->vendor < VENDORPEC_EXTENDED) {
                fr_strerror_printf("rad_vp2extended called for non-extended attribute");
                return -1;
@@ -1124,7 +1140,7 @@ int rad_vp2extended(const RADIUS_PACKET *packet,
 
        len = vp2data_any(packet, original, secret, nest,
                          pvp, ptr + ptr[1], room - hdr_len);
-       if (len < 0) return len;
+       if (len <= 0) return len;
 
        /*
         *      There may be more than 252 octets of data encoded in
@@ -1170,8 +1186,8 @@ int rad_vp2extended(const RADIUS_PACKET *packet,
 }
 
 
-/*
- *     Encode a WiMAX attribute.
+/**
+ * @brief Encode a WiMAX attribute.
  */
 int rad_vp2wimax(const RADIUS_PACKET *packet,
                 const RADIUS_PACKET *original,
@@ -1243,8 +1259,10 @@ int rad_vp2wimax(const RADIUS_PACKET *packet,
        return (ptr + ptr[1]) - start;
 }
 
-/*
- *     Encode an RFC format TLV.  This could be a standard attribute,
+/**
+ * @brief Encode an RFC format TLV.
+ *
+ *     This could be a standard attribute,
  *     or a TLV data type.  If it's a standard attribute, then
  *     vp->attribute == attribute.  Otherwise, attribute may be
  *     something else.
@@ -1264,7 +1282,7 @@ static ssize_t vp2attr_rfc(const RADIUS_PACKET *packet,
        if (room > ((unsigned) 255 - ptr[1])) room = 255 - ptr[1];
 
        len = vp2data_any(packet, original, secret, 0, pvp, ptr + ptr[1], room);
-       if (len < 0) return len;
+       if (len <= 0) return len;
 
        ptr[1] += len;
 
@@ -1279,8 +1297,8 @@ static ssize_t vp2attr_rfc(const RADIUS_PACKET *packet,
 }
 
 
-/*
- *     Encode a VSA which is a TLV.  If it's in the RFC format, call
+/**
+ * @brief 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,
@@ -1297,12 +1315,9 @@ static ssize_t vp2attr_vsa(const RADIUS_PACKET *packet,
         *      Unknown vendor: RFC format.
         *      Known vendor and RFC format: go do that.
         */
-       VP_TRACE("Encoding VSA %u.%u\n", vendor, attribute);
        dv = dict_vendorbyvalue(vendor);
-       VP_TRACE("Flags %d %d\n", vp->flags.is_tlv, vp->flags.has_tlv);
        if (!dv ||
            (!vp->flags.is_tlv && (dv->type == 1) && (dv->length == 1))) {
-               VP_TRACE("Encoding RFC %u.%u\n", vendor, attribute);
                return vp2attr_rfc(packet, original, secret, pvp,
                                   attribute, ptr, room);
        }
@@ -1356,7 +1371,7 @@ static ssize_t vp2attr_vsa(const RADIUS_PACKET *packet,
 
        len = vp2data_any(packet, original, secret, 0, pvp,
                          ptr + dv->type + dv->length, room);
-       if (len < 0) return len;
+       if (len <= 0) return len;
 
        if (dv->length) ptr[dv->type + dv->length - 1] += len;
 
@@ -1411,8 +1426,8 @@ static ssize_t vp2attr_vsa(const RADIUS_PACKET *packet,
 }
 
 
-/*
- *     Encode a Vendor-Specific attribute.
+/**
+ * @brief Encode a Vendor-Specific attribute.
  */
 int rad_vp2vsa(const RADIUS_PACKET *packet, const RADIUS_PACKET *original,
                const char *secret, const VALUE_PAIR **pvp, uint8_t *ptr,
@@ -1472,8 +1487,8 @@ int rad_vp2vsa(const RADIUS_PACKET *packet, const RADIUS_PACKET *original,
 }
 
 
-/*
- *     Encode an RFC standard attribute 1..255
+/**
+ * @brief Encode an RFC standard attribute 1..255
  */
 int rad_vp2rfc(const RADIUS_PACKET *packet,
               const RADIUS_PACKET *original,
@@ -1492,9 +1507,17 @@ int rad_vp2rfc(const RADIUS_PACKET *packet,
                return -1;
        }
 
+       /*
+        *      Only CUI is allowed to have zero length.
+        *      Thank you, WiMAX!
+        */
        if ((vp->length == 0) &&
-           (vp->attribute != PW_CHARGEABLE_USER_IDENTITY)) {
-               return 0;
+           (vp->attribute == PW_CHARGEABLE_USER_IDENTITY)) {
+               ptr[0] = PW_CHARGEABLE_USER_IDENTITY;
+               ptr[1] = 2;
+
+               *pvp = vp->next;
+               return 2;
        }
 
        return vp2attr_rfc(packet, original, secret, pvp, vp->attribute,
@@ -1502,8 +1525,8 @@ int rad_vp2rfc(const RADIUS_PACKET *packet,
 }
 
 
-/*
- *     Parse a data structure into a RADIUS attribute.
+/**
+ * @brief 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 **pvp, uint8_t *start,
@@ -1527,6 +1550,7 @@ int rad_vp2attr(const RADIUS_PACKET *packet, const RADIUS_PACKET *original,
                if (vp->attribute == PW_MESSAGE_AUTHENTICATOR) {
                        if (room < 18) return -1;
                        
+                       debug_pair(vp);
                        start[0] = PW_MESSAGE_AUTHENTICATOR;
                        start[1] = 18;
                        memset(start + 2, 0, 16);
@@ -1559,8 +1583,8 @@ int rad_vp2attr(const RADIUS_PACKET *packet, const RADIUS_PACKET *original,
 }
 
 
-/*
- *     Encode a packet.
+/**
+ * @brief Encode a packet.
  */
 int rad_encode(RADIUS_PACKET *packet, const RADIUS_PACKET *original,
               const char *secret)
@@ -1653,6 +1677,9 @@ int rad_encode(RADIUS_PACKET *packet, const RADIUS_PACKET *original,
         */
        reply = packet->vps;
        while (reply) {
+               size_t last_len;
+               const char *last_name = NULL;
+
                /*
                 *      Ignore non-wire attributes, but allow extended
                 *      attributes.
@@ -1686,14 +1713,11 @@ int rad_encode(RADIUS_PACKET *packet, const RADIUS_PACKET *original,
                         *      Message-Authenticator
                         */
                        packet->offset = total_length;
+                       last_len = 16;
+               } else {
+                       last_len = reply->length;
                }
-
-               /*
-                *      Print out ONLY the attributes which
-                *      we're sending over the wire, and print
-                *      them out BEFORE they're encrypted.
-                */
-               debug_pair(reply);
+               last_name = reply->name;
 
                len = rad_vp2attr(packet, original, secret, &reply, ptr,
                                  ((uint8_t *) data) + sizeof(data) - ptr);
@@ -1703,10 +1727,12 @@ int rad_encode(RADIUS_PACKET *packet, const RADIUS_PACKET *original,
                 *      Failed to encode the attribute, likely because
                 *      the packet is full.
                 */
-               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;
+               if (len == 0) {
+                       if (last_len != 0) {
+                               DEBUG("WARNING: Failed encoding attribute %s\n", last_name);
+                       } else {
+                               DEBUG("WARNING: Skipping zero-length attribute %s\n", last_name);
+                       }
                }
 
 #ifndef NDEBUG
@@ -1741,8 +1767,8 @@ int rad_encode(RADIUS_PACKET *packet, const RADIUS_PACKET *original,
 }
 
 
-/*
- *     Sign a previously encoded packet.
+/**
+ * @brief Sign a previously encoded packet.
  */
 int rad_sign(RADIUS_PACKET *packet, const RADIUS_PACKET *original,
             const char *secret)
@@ -1771,8 +1797,12 @@ int rad_sign(RADIUS_PACKET *packet, const RADIUS_PACKET *original,
                uint8_t calc_auth_vector[AUTH_VECTOR_LEN];
 
                switch (packet->code) {
-               case PW_ACCOUNTING_REQUEST:
                case PW_ACCOUNTING_RESPONSE:
+                       if (original && original->code == PW_STATUS_SERVER) {
+                               goto do_ack;
+                       }
+
+               case PW_ACCOUNTING_REQUEST:
                case PW_DISCONNECT_REQUEST:
                case PW_DISCONNECT_ACK:
                case PW_DISCONNECT_NAK:
@@ -1782,6 +1812,7 @@ int rad_sign(RADIUS_PACKET *packet, const RADIUS_PACKET *original,
                        memset(hdr->vector, 0, AUTH_VECTOR_LEN);
                        break;
 
+               do_ack:
                case PW_AUTHENTICATION_ACK:
                case PW_AUTHENTICATION_REJECT:
                case PW_ACCESS_CHALLENGE:
@@ -1854,8 +1885,8 @@ int rad_sign(RADIUS_PACKET *packet, const RADIUS_PACKET *original,
        return 0;
 }
 
-/*
- *     Reply to the request.  Also attach
+/**
+ * @brief 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,
@@ -1927,9 +1958,11 @@ 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
+/**
+ * @brief Do a comparison of two authentication digests by comparing
+ *     the FULL digest.
+ *
+ *     Otherwise, the server can be subject to
  *     timing attacks that allow attackers find a valid message
  *     authenticator.
  *
@@ -1948,8 +1981,8 @@ int rad_digest_cmp(const uint8_t *a, const uint8_t *b, size_t length)
 }
 
 
-/*
- *     Validates the requesting client NAS.  Calculates the
+/**
+ * @brief Validates the requesting client NAS.  Calculates the
  *     signature based on the clients private key.
  */
 static int calc_acctdigest(RADIUS_PACKET *packet, const char *secret)
@@ -1981,8 +2014,8 @@ static int calc_acctdigest(RADIUS_PACKET *packet, const char *secret)
 }
 
 
-/*
- *     Validates the requesting client NAS.  Calculates the
+/**
+ * @brief Validates the requesting client NAS.  Calculates the
  *     signature based on the clients private key.
  */
 static int calc_replydigest(RADIUS_PACKET *packet, RADIUS_PACKET *original,
@@ -2024,8 +2057,8 @@ static int calc_replydigest(RADIUS_PACKET *packet, RADIUS_PACKET *original,
 }
 
 
-/*
- *     Check if a set of RADIUS formatted TLVs are OK.
+/**
+ * @brief Check if a set of RADIUS formatted TLVs are OK.
  */
 int rad_tlv_ok(const uint8_t *data, size_t length,
               size_t dv_type, size_t dv_length)
@@ -2111,8 +2144,8 @@ int rad_tlv_ok(const uint8_t *data, size_t length,
 }
 
 
-/*
- *     See if the data pointed to by PTR is a valid RADIUS packet.
+/**
+ * @brief See if the data pointed to by PTR is a valid RADIUS packet.
  *
  *     packet is not 'const * const' because we may update data_len,
  *     if there's more data in the UDP packet than in the RADIUS packet.
@@ -2422,8 +2455,8 @@ int rad_packet_ok(RADIUS_PACKET *packet, int flags)
 }
 
 
-/*
- *     Receive UDP client requests, and fill in
+/**
+ * @brief Receive UDP client requests, and fill in
  *     the basics of a RADIUS_PACKET structure.
  */
 RADIUS_PACKET *rad_recv(int fd, int flags)
@@ -2537,8 +2570,8 @@ RADIUS_PACKET *rad_recv(int fd, int flags)
 }
 
 
-/*
- *     Verify the signature of a packet.
+/**
+ * @brief Verify the signature of a packet.
  */
 int rad_verify(RADIUS_PACKET *packet, RADIUS_PACKET *original,
               const char *secret)
@@ -2577,8 +2610,13 @@ int rad_verify(RADIUS_PACKET *packet, RADIUS_PACKET *original,
                        default:
                                break;
 
-                       case PW_ACCOUNTING_REQUEST:
                        case PW_ACCOUNTING_RESPONSE:
+                               if (original &&
+                                   (original->code == PW_STATUS_SERVER)) {
+                                       goto do_ack;
+                               }
+
+                       case PW_ACCOUNTING_REQUEST:
                        case PW_DISCONNECT_REQUEST:
                        case PW_DISCONNECT_ACK:
                        case PW_DISCONNECT_NAK:
@@ -2588,6 +2626,7 @@ int rad_verify(RADIUS_PACKET *packet, RADIUS_PACKET *original,
                                memset(packet->data + 4, 0, AUTH_VECTOR_LEN);
                                break;
 
+                       do_ack:
                        case PW_AUTHENTICATION_ACK:
                        case PW_AUTHENTICATION_REJECT:
                        case PW_ACCESS_CHALLENGE:
@@ -2707,8 +2746,8 @@ int rad_verify(RADIUS_PACKET *packet, RADIUS_PACKET *original,
 }
 
 
-/*
- *     Create a "raw" attribute from the attribute contents.
+/**
+ * @brief Create a "raw" attribute from the attribute contents.
  */
 static ssize_t data2vp_raw(UNUSED const RADIUS_PACKET *packet,
                           UNUSED const RADIUS_PACKET *original,
@@ -2732,12 +2771,13 @@ static ssize_t data2vp_raw(UNUSED const RADIUS_PACKET *packet,
        vp->length = length;
 
        /*
-        *      If the data is too large, mark it as a "TLV".
+        *      If it's short, put it into the array.  If it's too
+        *      long, flag it as such, and put it somewhere else;
         */
        if (length <= sizeof(vp->vp_octets)) {
                memcpy(vp->vp_octets, data, length);
        } else {
-               vp->type = PW_TYPE_TLV;
+               vp->type |= PW_FLAG_LONG;
                vp->vp_tlv = malloc(length);
                if (!vp->vp_tlv) {
                        pairfree(&vp);
@@ -2760,10 +2800,9 @@ static ssize_t data2vp_tlvs(const RADIUS_PACKET *packet,
                            const uint8_t *start, size_t length,
                            VALUE_PAIR **pvp);
 
-/*
- *     Create any kind of VP from the attribute contents.
- *
- *     Will return -1 on error, or "length".
+/**
+ * @brief Create any kind of VP from the attribute contents.
+ * @return -1 on error, or "length".
  */
 static ssize_t data2vp_any(const RADIUS_PACKET *packet,
                           const RADIUS_PACKET *original,
@@ -2775,6 +2814,7 @@ static ssize_t data2vp_any(const RADIUS_PACKET *packet,
        int data_offset = 0;
        DICT_ATTR *da;
        VALUE_PAIR *vp = NULL;
+       uint8_t buffer[256];
 
        if (length == 0) {
                /*
@@ -2818,6 +2858,31 @@ static ssize_t data2vp_any(const RADIUS_PACKET *packet,
        }
 
        /*
+        *      The data is very long.
+        */
+       if (length > sizeof(vp->vp_octets)) {
+               /*
+                *      Long encrypted attributes are forbidden.
+                */
+               if (da->flags.encrypt != FLAG_ENCRYPT_NONE) goto raw;
+
+#ifndef NDEBUG
+               /*
+                *      Catch programming errors.
+                */
+               if ((da->type != PW_TYPE_STRING) &&
+                   (da->type != PW_TYPE_OCTETS)) goto raw;
+
+#endif
+
+               /*
+                *      FIXME: Figure out how to deal with long
+                *      strings and binary data!
+                */
+               goto raw;
+       }
+
+       /*
         *      The attribute is known, and well formed.  We can now
         *      create it.  The main failure from here on in is being
         *      out of memory.
@@ -2848,28 +2913,29 @@ static ssize_t data2vp_any(const RADIUS_PACKET *packet,
        /*
         *      Copy the data to be decrypted
         */
-       vp->length = length - data_offset;
-       memcpy(&vp->vp_octets[0], data + data_offset, vp->length);
+       vp->length = length - data_offset;      
+       memcpy(buffer, data + data_offset, vp->length);
 
        /*
         *      Decrypt the attribute.
         */
-       switch (vp->flags.encrypt) {
+       if (secret && packet) switch (vp->flags.encrypt) {
                /*
                 *  User-Password
                 */
        case FLAG_ENCRYPT_USER_PASSWORD:
                if (original) {
-                       rad_pwdecode(vp->vp_strvalue,
+                       rad_pwdecode((char *) buffer,
                                     vp->length, secret,
                                     original->vector);
                } else {
-                       rad_pwdecode(vp->vp_strvalue,
+                       rad_pwdecode((char *) buffer,
                                     vp->length, secret,
                                     packet->vector);
                }
+               buffer[253] = '\0';
                if (vp->attribute == PW_USER_PASSWORD) {
-                       vp->length = strlen(vp->vp_strvalue);
+                       vp->length = strlen((char *) buffer);
                }
                break;
 
@@ -2880,7 +2946,7 @@ static ssize_t data2vp_any(const RADIUS_PACKET *packet,
        case FLAG_ENCRYPT_TUNNEL_PASSWORD:
                if (!original) goto raw;
 
-               if (rad_tunnel_pwdecode(vp->vp_octets, &vp->length,
+               if (rad_tunnel_pwdecode(buffer, &vp->length,
                                        secret, original->vector) < 0) {
                        goto raw;
                }
@@ -2898,10 +2964,10 @@ static ssize_t data2vp_any(const RADIUS_PACKET *packet,
                        make_secret(my_digest,
                                    original->vector,
                                    secret, data);
-                       memcpy(vp->vp_strvalue, my_digest,
+                       memcpy(buffer, my_digest,
                               AUTH_VECTOR_LEN );
-                       vp->vp_strvalue[AUTH_VECTOR_LEN] = '\0';
-                       vp->length = strlen(vp->vp_strvalue);
+                       buffer[AUTH_VECTOR_LEN] = '\0';
+                       vp->length = strlen((char *) buffer);
                }
                break;
 
@@ -2912,51 +2978,49 @@ static ssize_t data2vp_any(const RADIUS_PACKET *packet,
 
        switch (vp->type) {
        case PW_TYPE_STRING:
+               memcpy(vp->vp_strvalue, buffer, vp->length);
+               vp->vp_strvalue[vp->length] = '\0';
+               break;
+
        case PW_TYPE_OCTETS:
        case PW_TYPE_ABINARY:
-               /* nothing more to do */
+               memcpy(vp->vp_octets, buffer, vp->length);
                break;
 
        case PW_TYPE_BYTE:
                if (vp->length != 1) goto raw;
 
-               vp->vp_integer = vp->vp_octets[0];
+               vp->vp_integer = buffer[0];
                break;
 
 
        case PW_TYPE_SHORT:
                if (vp->length != 2) goto raw;
 
-               vp->vp_integer = (vp->vp_octets[0] << 8) | vp->vp_octets[1];
+               vp->vp_integer = (buffer[0] << 8) | buffer[1];
                break;
 
        case PW_TYPE_INTEGER:
                if (vp->length != 4) goto raw;
 
-               memcpy(&vp->vp_integer, vp->vp_octets, 4);
+               memcpy(&vp->vp_integer, buffer, 4);
                vp->vp_integer = ntohl(vp->vp_integer);
 
                if (vp->flags.has_tag) vp->vp_integer &= 0x00ffffff;
+               break;
 
-               /*
-                *      Try to get named VALUEs
-                */
-               {
-                       DICT_VALUE *dval;
-                       dval = dict_valbyattr(vp->attribute, vp->vendor,
-                                             vp->vp_integer);
-                       if (dval) {
-                               strlcpy(vp->vp_strvalue,
-                                       dval->name,
-                                       sizeof(vp->vp_strvalue));
-                       }
-               }
+       case PW_TYPE_INTEGER64:
+               if (vp->length != 8) goto raw;
+
+               /* vp_integer64 is a union with vp_octets */
+               memcpy(&vp->vp_integer64, buffer, 8);
+               vp->vp_integer64 = ntohll(vp->vp_integer64);
                break;
 
        case PW_TYPE_DATE:
                if (vp->length != 4) goto raw;
 
-               memcpy(&vp->vp_date, vp->vp_octets, 4);
+               memcpy(&vp->vp_date, buffer, 4);
                vp->vp_date = ntohl(vp->vp_date);
                break;
 
@@ -2964,7 +3028,7 @@ static ssize_t data2vp_any(const RADIUS_PACKET *packet,
        case PW_TYPE_IPADDR:
                if (vp->length != 4) goto raw;
 
-               memcpy(&vp->vp_ipaddr, vp->vp_octets, 4);
+               memcpy(&vp->vp_ipaddr, buffer, 4);
                break;
 
                /*
@@ -2972,7 +3036,7 @@ static ssize_t data2vp_any(const RADIUS_PACKET *packet,
                 */
        case PW_TYPE_IFID:
                if (vp->length != 8) goto raw;
-               /* vp->vp_ifid == vp->vp_octets */
+               memcpy(&vp->vp_ifid, buffer, 8);
                break;
 
                /*
@@ -2980,7 +3044,7 @@ static ssize_t data2vp_any(const RADIUS_PACKET *packet,
                 */
        case PW_TYPE_IPV6ADDR:
                if (vp->length != 16) goto raw;
-               /* vp->vp_ipv6addr == vp->vp_octets */
+               memcpy(&vp->vp_ipv6addr, buffer, 16);
                break;
 
                /*
@@ -2994,14 +3058,15 @@ static ssize_t data2vp_any(const RADIUS_PACKET *packet,
                 */
        case PW_TYPE_IPV6PREFIX:
                if (vp->length < 2 || vp->length > 18) goto raw;
-               if (vp->vp_octets[1] > 128) goto raw;
+               if (buffer[1] > 128) goto raw;
 
                /*
                 *      FIXME: double-check that
                 *      (vp->vp_octets[1] >> 3) matches vp->length + 2
                 */
+               memcpy(&vp->vp_ipv6prefix, buffer, vp->length);
                if (vp->length < 18) {
-                       memset(vp->vp_octets + vp->length, 0,
+                       memset(((uint8_t *)vp->vp_ipv6prefix) + vp->length, 0,
                               18 - vp->length);
                }
                break;
@@ -3013,9 +3078,8 @@ static ssize_t data2vp_any(const RADIUS_PACKET *packet,
                 *      Overload vp_integer for ntohl, which takes
                 *      uint32_t, not int32_t
                 */
-               memcpy(&vp->vp_integer, vp->vp_octets, 4);
+               memcpy(&vp->vp_integer, buffer, 4);
                vp->vp_integer = ntohl(vp->vp_integer);
-               memcpy(&vp->vp_signed, &vp->vp_integer, 4);
                break;
 
        case PW_TYPE_TLV:
@@ -3026,12 +3090,12 @@ static ssize_t data2vp_any(const RADIUS_PACKET *packet,
        case PW_TYPE_COMBO_IP:
                if (vp->length == 4) {
                        vp->type = PW_TYPE_IPADDR;
-                       memcpy(&vp->vp_ipaddr, vp->vp_octets, 4);
+                       memcpy(&vp->vp_ipaddr, buffer, 4);
                        break;
 
                } else if (vp->length == 16) {
                        vp->type = PW_TYPE_IPV6ADDR;
-                       /* vp->vp_ipv6addr == vp->vp_octets */
+                       memcpy(&vp->vp_ipv6addr, buffer, 16);
                        break;
 
                }
@@ -3047,8 +3111,8 @@ static ssize_t data2vp_any(const RADIUS_PACKET *packet,
 }
 
 
-/*
- *     Convert a top-level VSA to a VP.
+/**
+ * @brief Convert a top-level VSA to a VP.
  */
 static ssize_t attr2vp_vsa(const RADIUS_PACKET *packet,
                           const RADIUS_PACKET *original,
@@ -3134,8 +3198,8 @@ static ssize_t attr2vp_vsa(const RADIUS_PACKET *packet,
        return dv_type + dv_length + attrlen;
 }
 
-/*
- *     Convert one or more TLVs to VALUE_PAIRs.  This function can
+/**
+ * @brief Convert one or more TLVs to VALUE_PAIRs.  This function can
  *     be called recursively...
  */
 static ssize_t data2vp_tlvs(const RADIUS_PACKET *packet,
@@ -3278,8 +3342,9 @@ static ssize_t data2vp_tlvs(const RADIUS_PACKET *packet,
 }
 
 
-/*
- *     Group "continued" attributes together, and create VPs from them.
+/**
+ * @brief Group "continued" attributes together, and create VPs from them.
+ *
  *     The caller ensures that the RADIUS packet is OK, and that the
  *     continuations have all been checked.
  */
@@ -3317,6 +3382,7 @@ static ssize_t data2vp_continued(const RADIUS_PACKET *packet,
        while (left > 0) {
 #ifndef NDEBUG
                if (data >= (start + length)) {
+                       free(attr);
                        fr_strerror_printf("data2vp_continued: Internal sanity check failed");
                        return -1;
                }
@@ -3337,8 +3403,8 @@ static ssize_t data2vp_continued(const RADIUS_PACKET *packet,
 }
 
 
-/*
- *     Create a "raw" VALUE_PAIR from a RADIUS attribute.
+/**
+ * @brief Create a "raw" VALUE_PAIR from a RADIUS attribute.
  */
 ssize_t rad_attr2vp_raw(const RADIUS_PACKET *packet,
                        const RADIUS_PACKET *original,
@@ -3361,10 +3427,10 @@ ssize_t rad_attr2vp_raw(const RADIUS_PACKET *packet,
 }
 
 
-/*
- *     Get the length of the data portion of all of the contiguous
+/**
+ * @brief Get the length of the data portion of all of the contiguous
  *     continued attributes.
- *
+ * @return
  *     0 for "no continuation"
  *     -1 on malformed packets (continuation followed by non-wimax, etc.)
  */
@@ -3397,11 +3463,12 @@ static ssize_t wimax_attrlen(uint32_t vendor,
 }
 
 
-/*
- *     Get the length of the data portion of all of the contiguous
+/**
+ * @brief Get the length of the data portion of all of the contiguous
  *     continued attributes.
  *
- *     0 for "no continuation"
+ * @return
+ *     0 for "no continuation"
  *     -1 on malformed packets (continuation followed by non-wimax, etc.)
  */
 static ssize_t extended_attrlen(const uint8_t *start, const uint8_t *end)
@@ -3429,8 +3496,8 @@ static ssize_t extended_attrlen(const uint8_t *start, const uint8_t *end)
 }
 
 
-/*
- *     Create WiMAX VALUE_PAIRs from a RADIUS attribute.
+/**
+ * @brief Create WiMAX VALUE_PAIRs from a RADIUS attribute.
  */
 ssize_t rad_attr2vp_wimax(const RADIUS_PACKET *packet,
                          const RADIUS_PACKET *original,
@@ -3510,8 +3577,8 @@ ssize_t rad_attr2vp_wimax(const RADIUS_PACKET *packet,
        return data[1];
 }
 
-/*
- *     Create Vendor-Specifc VALUE_PAIRs from a RADIUS attribute.
+/**
+ * @brief Create Vendor-Specifc VALUE_PAIRs from a RADIUS attribute.
  */
 ssize_t rad_attr2vp_vsa(const RADIUS_PACKET *packet,
                        const RADIUS_PACKET *original,
@@ -3590,8 +3657,8 @@ ssize_t rad_attr2vp_vsa(const RADIUS_PACKET *packet,
        return data[1];
 }
 
-/*
- *     Create an "extended" VALUE_PAIR from a RADIUS attribute.
+/**
+ * @brief Create an "extended" VALUE_PAIR from a RADIUS attribute.
  */
 ssize_t rad_attr2vp_extended(const RADIUS_PACKET *packet,
                             const RADIUS_PACKET *original,
@@ -3724,8 +3791,8 @@ ssize_t rad_attr2vp_extended(const RADIUS_PACKET *packet,
 }
 
 
-/*
- *     Create a "standard" RFC VALUE_PAIR from the given data.
+/**
+ * @brief Create a "standard" RFC VALUE_PAIR from the given data.
  */
 ssize_t rad_attr2vp_rfc(const RADIUS_PACKET *packet,
                        const RADIUS_PACKET *original,
@@ -3746,8 +3813,8 @@ ssize_t rad_attr2vp_rfc(const RADIUS_PACKET *packet,
        return data[1];
 }      
 
-/*
- *     Create a "normal" VALUE_PAIR from the given data.
+/**
+ * @brief Create a "normal" VALUE_PAIR from the given data.
  */
 ssize_t rad_attr2vp(const RADIUS_PACKET *packet,
                    const RADIUS_PACKET *original,
@@ -3780,11 +3847,9 @@ ssize_t rad_attr2vp(const RADIUS_PACKET *packet,
 }
 
 
-/*
- *     Calculate/check digest, and decode radius attributes.
- *     Returns:
- *     -1 on decoding error
- *     0 on success
+/**
+ * @brief Calculate/check digest, and decode radius attributes.
+ * @return -1 on decoding error, 0 on success
  */
 int rad_decode(RADIUS_PACKET *packet, RADIUS_PACKET *original,
               const char *secret)
@@ -3873,8 +3938,8 @@ int rad_decode(RADIUS_PACKET *packet, RADIUS_PACKET *original,
 }
 
 
-/*
- *     Encode password.
+/**
+ * @brief Encode password.
  *
  *     We assume that the passwd buffer passed is big enough.
  *     RFC2138 says the password is max 128 chars, so the size
@@ -3946,8 +4011,8 @@ int rad_pwencode(char *passwd, size_t *pwlen, const char *secret,
        return 0;
 }
 
-/*
- *     Decode password.
+/**
+ * @brief Decode password.
  */
 int rad_pwdecode(char *passwd, size_t pwlen, const char *secret,
                 const uint8_t *vector)
@@ -4012,8 +4077,8 @@ int rad_pwdecode(char *passwd, size_t pwlen, const char *secret,
 }
 
 
-/*
- *     Encode Tunnel-Password attributes when sending them out on the wire.
+/**
+ * @brief Encode Tunnel-Password attributes when sending them out on the wire.
  *
  *     int *pwlen is updated to the new length of the encrypted
  *     password - a multiple of 16 bytes.
@@ -4097,8 +4162,8 @@ int rad_tunnel_pwencode(char *passwd, size_t *pwlen, const char *secret,
        return 0;
 }
 
-/*
- *     Decode Tunnel-Password encrypted attributes.
+/**
+ * @brief Decode Tunnel-Password encrypted attributes.
  *
  *      Defined in RFC-2868, this uses a two char SALT along with the
  *      initial intermediate value, to differentiate it from the
@@ -4203,10 +4268,10 @@ int rad_tunnel_pwdecode(uint8_t *passwd, size_t *pwlen, const char *secret,
        return reallen;
 }
 
-/*
- *     Encode a CHAP password
+/**
+ * @brief Encode a CHAP password
  *
- *     FIXME: might not work with Ascend because
+ *     @bug FIXME: might not work with Ascend because
  *     we use vp->length, and Ascend gear likes
  *     to send an extra '\0' in the string!
  */
@@ -4261,8 +4326,8 @@ int rad_chap_encode(RADIUS_PACKET *packet, uint8_t *output, int id,
 }
 
 
-/*
- *     Seed the random number generator.
+/**
+ * @brief Seed the random number generator.
  *
  *     May be called any number of times.
  */
@@ -4315,8 +4380,8 @@ void fr_rand_seed(const void *data, size_t size)
 }
 
 
-/*
- *     Return a 32-bit random number.
+/**
+ * @brief Return a 32-bit random number.
  */
 uint32_t fr_rand(void)
 {
@@ -4339,8 +4404,8 @@ uint32_t fr_rand(void)
 }
 
 
-/*
- *     Allocate a new RADIUS_PACKET
+/**
+ * @brief Allocate a new RADIUS_PACKET
  */
 RADIUS_PACKET *rad_alloc(int newvector)
 {
@@ -4402,8 +4467,8 @@ RADIUS_PACKET *rad_alloc_reply(RADIUS_PACKET *packet)
 }
 
 
-/*
- *     Free a RADIUS_PACKET
+/**
+ * @brief Free a RADIUS_PACKET
  */
 void rad_free(RADIUS_PACKET **radius_packet_ptr)
 {