diff --git a/src/rfc3315.c b/src/rfc3315.c index 3c316a1..f0bff01 100644 --- a/src/rfc3315.c +++ b/src/rfc3315.c @@ -41,6 +41,7 @@ static void log6_packet(struct state *state, char *type, struct in6_addr *addr, static void log6_quiet(struct state *state, char *type, struct in6_addr *addr, char *string); static void *opt6_find (uint8_t *opts, uint8_t *end, unsigned int search, int minsize); static void *opt6_first(uint8_t *opt, uint8_t *end); +static void *opt6_user_vendor_first(uint8_t *opt, uint8_t *end); static unsigned int opt6_uint(unsigned char *opt, int offset, int size); static void get_context_tag(struct state *state, struct dhcp_context *context); static int check_ia(struct state *state, void *opt, void **endp, void **ia_option); @@ -65,9 +66,9 @@ static void calculate_times(struct dhcp_context *context, unsigned int *min_time #define opt6_next(opt, end) (opt6_first(opt6_ptr((opt), opt6_len((opt))), (end))) #define opt6_user_vendor_ptr(opt, i) ((void *)&(((uint8_t *)(opt))[2+(i)])) -#define opt6_user_vendor_len(opt) ((int)(opt6_uint(opt, -4, 2))) -#define opt6_user_vendor_next(opt, end) (opt6_next(((uint8_t *) opt) - 2, end)) - +#define opt6_user_vendor_len(opt) ((int)(opt6_uint((opt), -4, 2))) +#define opt6_user_vendor_next(opt, end) (opt6_user_vendor_first(opt6_user_vendor_ptr((opt), opt6_user_vendor_len((opt))), (end))) + unsigned short dhcp6_reply(struct dhcp_context *context, int multicast_dest, int interface, char *iface_name, struct in6_addr *fallback, struct in6_addr *ll_addr, struct in6_addr *ula_addr, @@ -414,7 +415,8 @@ static int dhcp6_no_relay(struct state *state, int msg_type, unsigned char *inbu } /* Note that format if user/vendor classes is different to DHCP options - no option types. */ - for (enc_opt = opt6_ptr(opt, offset); enc_opt; enc_opt = opt6_user_vendor_next(enc_opt, enc_end)) + for (enc_opt = opt6_user_vendor_first(opt6_ptr(opt, offset), enc_end); + enc_opt; enc_opt = opt6_user_vendor_next(enc_opt, enc_end)) for (i = 0; i <= (opt6_user_vendor_len(enc_opt) - vendor->len); i++) if (memcmp(vendor->data, opt6_user_vendor_ptr(enc_opt, i), vendor->len) == 0) { @@ -1927,7 +1929,8 @@ static void update_leases(struct state *state, struct dhcp_context *context, str lease_add_extradata(lease, (unsigned char *)daemon->dhcp_buff2, strlen(daemon->dhcp_buff2), 0); if (opt6_len(opt) >= 6) - for (enc_opt = opt6_ptr(opt, 4); enc_opt; enc_opt = opt6_user_vendor_next(enc_opt, enc_end)) + for (enc_opt = opt6_user_vendor_first(opt6_ptr(opt, 4), enc_end); + enc_opt; enc_opt = opt6_user_vendor_next(enc_opt, enc_end)) { lease->vendorclass_count++; lease_add_extradata(lease, opt6_user_vendor_ptr(enc_opt, 0), opt6_user_vendor_len(enc_opt), 0); @@ -1987,7 +1990,8 @@ static void update_leases(struct state *state, struct dhcp_context *context, str if ((opt = opt6_find(state->packet_options, state->end, OPTION6_USER_CLASS, 2))) { void *enc_opt, *enc_end = opt6_ptr(opt, opt6_len(opt)); - for (enc_opt = opt6_ptr(opt, 0); enc_opt; enc_opt = opt6_user_vendor_next(enc_opt, enc_end)) + for (enc_opt = opt6_user_vendor_first(opt6_ptr(opt, 0), enc_end); + enc_opt; enc_opt = opt6_user_vendor_next(enc_opt, enc_end)) lease_add_extradata(lease, opt6_user_vendor_ptr(enc_opt, 0), opt6_user_vendor_len(enc_opt), 0); } } @@ -2130,7 +2134,23 @@ static void *opt6_first(uint8_t *opt, uint8_t *end) return opt; } - + +static void *opt6_user_vendor_first(uint8_t *opt, uint8_t *end) +{ + if (!opt) + return NULL; + + /* make sure we have length. */ + if ((uint8_t *)opt6_user_vendor_ptr(opt, 0) > end) + return NULL; + + /* make sure we have bytes promised by length. */ + if ((uint8_t *)opt6_user_vendor_ptr(opt, opt6_user_vendor_len(opt)) > end) + return NULL; + + return opt; +} + static unsigned int opt6_uint(unsigned char *opt, int offset, int size) { /* this worries about unaligned data and byte order */