newvector should be a bool
[freeradius.git] / src / modules / proto_dhcp / dhcp.c
index 956480e..eec47f1 100644 (file)
@@ -225,7 +225,7 @@ RADIUS_PACKET *fr_dhcp_recv(int sockfd)
        uint8_t                 *code;
        ssize_t                 data_len;
 
-       packet = rad_alloc(NULL, 0);
+       packet = rad_alloc(NULL, false);
        if (!packet) {
                fr_strerror_printf("Failed allocating packet");
                return NULL;
@@ -461,9 +461,9 @@ int fr_dhcp_send(RADIUS_PACKET *packet)
 
 static int fr_dhcp_attr2vp(RADIUS_PACKET *packet, VALUE_PAIR *vp, uint8_t const *p, size_t alen);
 
-static int decode_tlv(RADIUS_PACKET *packet, VALUE_PAIR *tlv, uint8_t const *data, size_t data_len)
+static int fr_dhcp_decode_suboption(RADIUS_PACKET *packet, VALUE_PAIR *tlv, uint8_t const *data, size_t data_len)
 {
-       uint8_t const *p;
+       uint8_t const *p, *q;
        VALUE_PAIR *head, *vp;
        vp_cursor_t cursor;
 
@@ -471,11 +471,34 @@ static int decode_tlv(RADIUS_PACKET *packet, VALUE_PAIR *tlv, uint8_t const *dat
         *      Take a pass at parsing it.
         */
        p = data;
-       while (p < (data + data_len)) {
-               if ((p + 2) > (data + data_len)) goto make_tlv;
+       q = data + data_len;
+       while (p < q) {
+               /*
+                *      The RFC 3046 is very specific about not allowing termination
+                *      with a 255 sub-option. But vendors are stupid, so allow it
+                *      and the 0 padding sub-option.
+                *      This requirement really should be a SHOULD anyway...
+                */
+               if (*p == 0) {
+                       p++;
+                       continue;
+               }
+               if (*p == 255) {
+                       q--;
+                       break;
+               }
 
-               if ((p + p[1] + 2) > (data + data_len)) goto make_tlv;
-               p += 2 + p[1];
+               /*
+                *      Check if reading length would take us past the end of the buffer
+                */
+               if (++p >= q) goto malformed;
+               p += p[0];
+
+               /*
+                *      Check if length > the length of the buffer we have left
+                */
+               if (p >= q) goto malformed;
+               p++;
        }
 
        /*
@@ -485,16 +508,16 @@ static int decode_tlv(RADIUS_PACKET *packet, VALUE_PAIR *tlv, uint8_t const *dat
        fr_cursor_init(&cursor, &head);
 
        p = data;
-       while (p < (data + data_len)) {
+       while (p < q) {
                vp = paircreate(packet, tlv->da->attr | (p[0] << 8), DHCP_MAGIC_VENDOR);
                if (!vp) {
                        pairfree(&head);
-                       goto make_tlv;
+                       goto malformed;
                }
 
                if (fr_dhcp_attr2vp(packet, vp, p + 2, p[1]) < 0) {
                        pairfree(&head);
-                       goto make_tlv;
+                       goto malformed;
                }
 
                fr_cursor_insert(&cursor, vp);
@@ -522,6 +545,7 @@ static int decode_tlv(RADIUS_PACKET *packet, VALUE_PAIR *tlv, uint8_t const *dat
                        case PW_TYPE_OCTETS:
                        case PW_TYPE_TLV:
                                (void) talloc_steal(tlv, head->data.ptr);
+
                        default:
                                break;
                }
@@ -531,7 +555,7 @@ static int decode_tlv(RADIUS_PACKET *packet, VALUE_PAIR *tlv, uint8_t const *dat
 
        return 0;
 
-make_tlv:
+malformed:
        tlv->vp_tlv = talloc_array(tlv, uint8_t, data_len);
        if (!tlv->vp_tlv) {
                fr_strerror_printf("No memory");
@@ -569,7 +593,7 @@ static int fr_dhcp_attr2vp(RADIUS_PACKET *packet, VALUE_PAIR *vp, uint8_t const
                vp->vp_integer = ntohl(vp->vp_integer);
                break;
 
-       case PW_TYPE_IPADDR:
+       case PW_TYPE_IPV4_ADDR:
                if (alen != 4) goto raw;
                /*
                 *      Keep value in Network Order!
@@ -598,12 +622,15 @@ static int fr_dhcp_attr2vp(RADIUS_PACKET *packet, VALUE_PAIR *vp, uint8_t const
                if (pair2unknown(vp) < 0) return -1;
 
        case PW_TYPE_OCTETS:
-               if (alen > 253) return -1;
+               if (alen > 255) return -1;
                pairmemcpy(vp, p, alen);
                break;
 
+       /*
+        *      For option 82 et al...
+        */
        case PW_TYPE_TLV:
-               return decode_tlv(packet, vp, p, alen);
+               return fr_dhcp_decode_suboption(packet, vp, p, alen);
 
        default:
                fr_strerror_printf("Internal sanity check %d %d", vp->da->type, __LINE__);
@@ -636,22 +663,19 @@ ssize_t fr_dhcp_decode_options(RADIUS_PACKET *packet,
 
                p = next;
 
-               if (*p == 0) break;
-               if (*p == 255) break; /* end of options signifier */
+               if (*p == 0) {          /* 0x00 - Padding option */
+                       next++;
+                       continue;
+               }
+               if (*p == 255) break;   /* 0xff - End of options signifier */
+
                if ((p + 2) > (data + len)) break;
 
                next = p + 2 + p[1];
 
-               if (p[1] >= 253) {
-                       fr_strerror_printf("Attribute too long %u %u",
-                                          p[0], p[1]);
-                       continue;
-               }
-
                da = dict_attrbyvalue(p[0], DHCP_MAGIC_VENDOR);
                if (!da) {
-                       fr_strerror_printf("Attribute not in our dictionary: %u",
-                                          p[0]);
+                       fr_strerror_printf("Attribute not in our dictionary: %u", p[0]);
                        continue;
                }
 
@@ -675,7 +699,7 @@ ssize_t fr_dhcp_decode_options(RADIUS_PACKET *packet,
                                alen = 2;
                                break;
 
-                       case PW_TYPE_IPADDR:
+                       case PW_TYPE_IPV4_ADDR:
                        case PW_TYPE_INTEGER:
                        case PW_TYPE_DATE: /* ignore any trailing data */
                                num_entries = alen >> 2;
@@ -694,21 +718,12 @@ ssize_t fr_dhcp_decode_options(RADIUS_PACKET *packet,
                for (i = 0; i < num_entries; i++) {
                        vp = pairmake(packet, NULL, da->name, NULL, T_OP_ADD);
                        if (!vp) {
-                               fr_strerror_printf("Cannot build attribute %s",
-                                       fr_strerror());
+                               fr_strerror_printf("Cannot build attribute %s", fr_strerror());
                                pairfree(head);
                                return -1;
                        }
 
-                       /*
-                        *      Hack for ease of use.
-                        */
-                       if ((da->vendor == DHCP_MAGIC_VENDOR) &&
-                           (da->attr == 61) && !da->flags.array &&
-                           (alen == 7) && (*p == 1) && (num_entries == 1)) {
-                               pairmemcpy(vp, p + 1, 6);
-
-                       } else if (fr_dhcp_attr2vp(packet, vp, p, alen) < 0) {
+                       if (fr_dhcp_attr2vp(packet, vp, p, alen) < 0) {
                                pairfree(&vp);
                                pairfree(head);
                                return -1;
@@ -799,7 +814,7 @@ int fr_dhcp_decode(RADIUS_PACKET *packet)
                        vp->length = 4;
                        break;
 
-               case PW_TYPE_IPADDR:
+               case PW_TYPE_IPV4_ADDR:
                        memcpy(&vp->vp_ipaddr, p, 4);
                        vp->length = 4;
                        break;
@@ -981,7 +996,7 @@ static ssize_t fr_dhcp_vp2attr(uint8_t *out, size_t outlen, VALUE_PAIR *vp)
                memcpy(p, &lvalue, 4);
                break;
 
-       case PW_TYPE_IPADDR:
+       case PW_TYPE_IPV4_ADDR:
                memcpy(p, &vp->vp_ipaddr, 4);
                break;
 
@@ -1379,7 +1394,8 @@ int fr_dhcp_encode(RADIUS_PACKET *packet)
         */
 
        /* DHCP-Boot-Filename */
-       if ((vp = pairfind(packet->vps, 269, DHCP_MAGIC_VENDOR, TAG_ANY))) {
+       vp = pairfind(packet->vps, 269, DHCP_MAGIC_VENDOR, TAG_ANY);
+       if (vp) {
                if (vp->length > DHCP_FILE_LEN) {
                        memcpy(p, vp->vp_strvalue, DHCP_FILE_LEN);
                } else {
@@ -1427,7 +1443,7 @@ int fr_dhcp_encode(RADIUS_PACKET *packet)
                                vp->vp_integer = ntohl(vp->vp_integer);
                                break;
 
-                       case PW_TYPE_IPADDR:
+                       case PW_TYPE_IPV4_ADDR:
                                memcpy(&vp->vp_ipaddr, p, 4);
                                break;
 
@@ -1482,7 +1498,7 @@ int fr_dhcp_encode(RADIUS_PACKET *packet)
         *  Each call to fr_dhcp_encode_option will encode one complete DHCP option,
         *  and sub options.
         */
-       while (fr_cursor_current(&cursor)) {
+       while ((vp = fr_cursor_current(&cursor))) {
                len = fr_dhcp_encode_option(p, packet->data_len - (p - packet->data), packet, &cursor);
                if (len < 0) break;
                if (len > 0) debug_pair(vp);