Enable building #WITHOUT_PROXY
[freeradius.git] / src / lib / radius.c
index acd1174..359756a 100644 (file)
@@ -233,7 +233,7 @@ static int rad_sendto(int sockfd, void *data, size_t data_len, int flags,
         *      And if they don't specify a source IP address, don't
         *      use udpfromto.
         */
-       if ((dst_ipaddr->af == AF_INET) ||
+       if ((dst_ipaddr->af == AF_INET) &&
            (src_ipaddr->af != AF_UNSPEC)) {
                return sendfromto(sockfd, data, data_len, flags,
                                  (struct sockaddr *)&src, sizeof_src,
@@ -816,7 +816,8 @@ static int rad_vp2rfc(const RADIUS_PACKET *packet,
        ptr[0] = attribute & 0xff; /* NOT vp->attribute */
        ptr[1] = 2;
 
-       len = vp2data(packet, original, secret, vp, ptr + 2, room - 2);
+       if (room > (255 - ptr[1])) room = 255 - ptr[1];
+       len = vp2data(packet, original, secret, vp, ptr + 2, room);
        if (len < 0) return len;
 
        ptr[1] += len;
@@ -921,6 +922,52 @@ static int wimax2data(const RADIUS_PACKET *packet,
 }
 
 
+static int rad_vp2extended(const RADIUS_PACKET *packet,
+                     const RADIUS_PACKET *original,
+                     const char *secret, const VALUE_PAIR *vp,
+                     unsigned int attribute, uint8_t *ptr, size_t room)
+{
+       int len = 2;
+
+       if (room < 3) return 0;
+
+       ptr[0] = attribute & 0xff; /* NOT vp->attribute */
+       ptr[1] = 3;
+
+       if (vp->flags.extended) {
+               ptr[2] = (attribute & 0xff00) >> 8;
+               len++;
+
+       } else if (vp->flags.extended_flags) {
+               if (room < 4) return 0;
+
+               ptr[1] = 4;
+               ptr[2] = (attribute & 0xff00) >> 8;
+               ptr[3] = 0;
+
+               len += 2;
+       }
+
+       /*
+        *      For now, no extended attribute can be longer than the
+        *      encapsulating attribute.  Once we add support for the
+        *      "M" bit, this restriction will be relaxed.
+        */
+       if (room > (255 - ptr[1])) room = 255 - ptr[1];
+
+       if (!vp->flags.is_tlv) {
+               len = vp2data(packet, original, secret, vp, ptr + ptr[1], room);
+       } else {
+               len = tlv2data(packet, original, secret, vp, ptr + ptr[1], room, 2);
+       }
+
+       if (len < 0) return len;
+
+       ptr[1] += len;
+
+       return ptr[1];
+}
+
 /*
  *     Parse a data structure into a RADIUS attribute.
  */
@@ -937,23 +984,13 @@ int rad_vp2attr(const RADIUS_PACKET *packet, const RADIUS_PACKET *original,
         *      RFC format attributes take the fast path.
         */
        if (vp->vendor == 0) {
-               len = rad_vp2rfc(packet, original, secret, vp,
-                                vp->attribute, start, room);
-               if (len < 0) return -1;
-
-               /*
-                *      RFC 2865 section 5 says that zero-length
-                *      attributes MUST NOT be sent.
-                *
-                *      ... and the WiMAX forum ignores
-                *      this... because of one vendor.  Don't they
-                *      have anything better to do with their time?
-                */
-               if ((len == 0) &&
-                   (vp->attribute != PW_CHARGEABLE_USER_IDENTITY)) return 0;
-
-               return len;
+               return rad_vp2rfc(packet, original, secret, vp,
+                                 vp->attribute, start, room);
+       }
 
+       if (vp->vendor == VENDORPEC_EXTENDED) {
+               return rad_vp2extended(packet, original, secret, vp,
+                                      vp->attribute, start, room);
        }
 
        /*
@@ -1233,10 +1270,12 @@ int rad_encode(RADIUS_PACKET *packet, const RADIUS_PACKET *original,
         */
        for (reply = packet->vps; reply; reply = reply->next) {
                /*
-                *      Ignore non-wire attributes
+                *      Ignore non-wire attributes, but allow extended
+                *      attributes.
                 */
                if ((reply->vendor == 0) &&
-                   ((reply->attribute & 0xFFFF) > 0xff)) {
+                   ((reply->attribute & 0xFFFF) >= 256) &&
+                   !reply->flags.extended && !reply->flags.extended_flags) {
 #ifndef NDEBUG
                        /*
                         *      Permit the admin to send BADLY formatted
@@ -1278,7 +1317,7 @@ int rad_encode(RADIUS_PACKET *packet, const RADIUS_PACKET *original,
                 */
                if (reply->flags.encoded) continue;
 
-               if (reply->flags.is_tlv) {
+               if ((reply->vendor == VENDORPEC_WIMAX) && reply->flags.is_tlv) {
                        len = rad_encode_wimax(packet, original, secret,
                                               reply, ptr,
                                               ((uint8_t *) data) + sizeof(data) - ptr);
@@ -1290,6 +1329,16 @@ int rad_encode(RADIUS_PACKET *packet, const RADIUS_PACKET *original,
 
                if (len < 0) return -1;
 
+               /*
+                *      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;
+               }
+
        next:
                ptr += len;
                total_length += len;
@@ -2209,6 +2258,15 @@ static VALUE_PAIR *data2vp(const RADIUS_PACKET *packet,
        vp->next = NULL;
 
        /*
+        *      It's supposed to be a fixed length, but we found
+        *      a different length instead.  Make it type "octets",
+        *      and do no more processing on it.
+        */
+       if ((vp->flags.length > 0) && (vp->flags.length != length)) {
+               goto raw;
+       }
+
+       /*
         *      Handle tags.
         */
        if (vp->flags.has_tag) {
@@ -2428,16 +2486,35 @@ static VALUE_PAIR *data2vp(const RADIUS_PACKET *packet,
 
        default:
        raw:
-               vp->type = PW_TYPE_OCTETS;
-               vp->length = length;
-               memcpy(vp->vp_octets, data, length);
-
-
                /*
-                *      Ensure there's no encryption or tag stuff,
-                *      we just pass the attribute as-is.
+                *      Change the name to show the user that the
+                *      attribute is not of the correct format.
                 */
-               memset(&vp->flags, 0, sizeof(vp->flags));
+               {
+                       int attr = vp->attribute;
+                       int vendor = vp->vendor;
+                       VALUE_PAIR *vp2;
+
+                       vp2 = pairalloc(NULL);
+                       if (!vp2) {
+                               pairfree(&vp);
+                               return NULL;
+                       }
+                       pairfree(&vp);
+                       vp = vp2;
+
+                       /*
+                        *      This sets "vp->flags" appropriately,
+                        *      and vp->type.
+                        */
+                       if (!paircreate_raw(attr, vendor, PW_TYPE_OCTETS, vp)) {
+                               return NULL;
+                       }
+
+                       vp->length = length;
+                       memcpy(vp->vp_octets, data, length);
+               }
+               break;
        }
 
        return vp;
@@ -2763,6 +2840,31 @@ static VALUE_PAIR *rad_continuation2vp(const RADIUS_PACKET *packet,
 
 
 /*
+ *     Extended attribute TLV to VP.
+ */
+static VALUE_PAIR *tlv2vp(const RADIUS_PACKET *packet,
+                           const RADIUS_PACKET *original,
+                           const char *secret, int attribute,
+                           int length, const uint8_t *data)
+{
+       VALUE_PAIR *vp;
+
+       if ((length < 2) || (data[1] < 2)) return NULL;
+
+       /*
+        *      For now, only one TLV is allowed.
+        */
+       if (data[1] != length) return NULL;
+
+       attribute |= (data[0] << fr_wimax_shift[2]);
+
+       vp = paircreate(attribute, VENDORPEC_EXTENDED, PW_TYPE_OCTETS);
+       if (!vp) return NULL;
+
+       return data2vp(packet, original, secret, length - 2, data + 2, vp);
+}
+
+/*
  *     Parse a RADIUS attribute into a data structure.
  */
 VALUE_PAIR *rad_attr2vp(const RADIUS_PACKET *packet,
@@ -2772,6 +2874,58 @@ VALUE_PAIR *rad_attr2vp(const RADIUS_PACKET *packet,
 {
        VALUE_PAIR *vp;
 
+       /*
+        *      Hard-coded values are bad...
+        */
+       if ((vendor == 0) && (attribute >= 241) && (attribute <= 246)) {
+               DICT_ATTR *da;
+
+               da = dict_attrbyvalue(attribute, VENDORPEC_EXTENDED);
+               if (da) {       /* flags.extended MUST be set */
+
+                       /*
+                        *      MUST have at least an "extended type" octet.
+                        */
+                       if (length == 0) return NULL;
+
+                       attribute |= (data[0] << fr_wimax_shift[1]);
+                       vendor = VENDORPEC_EXTENDED;
+
+                       data++;
+                       length--;
+
+                       /*
+                        *      There may be a flag octet.
+                        */
+                       if (da->flags.extended_flags) {
+                               if (length == 0) return NULL;
+
+                               /*
+                                *      If there's a flag, we can't
+                                *      handle it.
+                                */
+                               if (data[0] != 0) return NULL;
+                               data++;
+                               length--;
+                       }
+
+                       /*
+                        *      Now look up the extended attribute, to
+                        *      see if it's a TLV carrying more data.
+                        *      
+                        */
+                       da = dict_attrbyvalue(attribute, VENDORPEC_EXTENDED);
+                       if (da && da->flags.has_tlv) {
+                               return tlv2vp(packet, original, secret,
+                                             attribute, length, data);
+                       }
+               }
+
+               /*
+                *      We could avoid another dictionary lookup here
+                *      by using pairalloc(da), but it's not serious...
+                */
+       }
        vp = paircreate(attribute, vendor, PW_TYPE_OCTETS);
        if (!vp) return NULL;