0001-gdhcp-Avoid-reading-invalid-data-in-dhcp_get_option.patch 7.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226
  1. From 58d397ba74873384aee449690a9070bacd5676fa Mon Sep 17 00:00:00 2001
  2. From: Colin Wee <cwee@tesla.com>
  3. Date: Thu, 28 Jan 2021 19:39:14 +0100
  4. Subject: [PATCH] gdhcp: Avoid reading invalid data in dhcp_get_option
  5. Signed-off-by: Peter Korsgaard <peter@korsgaard.com>
  6. ---
  7. gdhcp/client.c | 20 +++++++++++---------
  8. gdhcp/common.c | 24 +++++++++++++++++++-----
  9. gdhcp/common.h | 2 +-
  10. gdhcp/server.c | 12 +++++++-----
  11. 4 files changed, 38 insertions(+), 20 deletions(-)
  12. diff --git a/gdhcp/client.c b/gdhcp/client.c
  13. index 09dfe5ec..6a5613e7 100644
  14. --- a/gdhcp/client.c
  15. +++ b/gdhcp/client.c
  16. @@ -1629,12 +1629,12 @@ static void start_request(GDHCPClient *dhcp_client)
  17. NULL);
  18. }
  19. -static uint32_t get_lease(struct dhcp_packet *packet)
  20. +static uint32_t get_lease(struct dhcp_packet *packet, uint16_t packet_len)
  21. {
  22. uint8_t *option;
  23. uint32_t lease_seconds;
  24. - option = dhcp_get_option(packet, DHCP_LEASE_TIME);
  25. + option = dhcp_get_option(packet, packet_len, DHCP_LEASE_TIME);
  26. if (!option)
  27. return 3600;
  28. @@ -2226,7 +2226,8 @@ static void get_dhcpv6_request(GDHCPClient *dhcp_client,
  29. }
  30. }
  31. -static void get_request(GDHCPClient *dhcp_client, struct dhcp_packet *packet)
  32. +static void get_request(GDHCPClient *dhcp_client, struct dhcp_packet *packet,
  33. + uint16_t packet_len)
  34. {
  35. GDHCPOptionType type;
  36. GList *list, *value_list;
  37. @@ -2237,7 +2238,7 @@ static void get_request(GDHCPClient *dhcp_client, struct dhcp_packet *packet)
  38. for (list = dhcp_client->request_list; list; list = list->next) {
  39. code = (uint8_t) GPOINTER_TO_INT(list->data);
  40. - option = dhcp_get_option(packet, code);
  41. + option = dhcp_get_option(packet, packet_len, code);
  42. if (!option) {
  43. g_hash_table_remove(dhcp_client->code_value_hash,
  44. GINT_TO_POINTER((int) code));
  45. @@ -2297,6 +2298,7 @@ static gboolean listener_event(GIOChannel *channel, GIOCondition condition,
  46. re = dhcp_recv_l2_packet(&packet,
  47. dhcp_client->listener_sockfd,
  48. &dst_addr);
  49. + pkt_len = (uint16_t)(unsigned int)re;
  50. xid = packet.xid;
  51. } else if (dhcp_client->listen_mode == L3) {
  52. if (dhcp_client->type == G_DHCP_IPV6) {
  53. @@ -2361,7 +2363,7 @@ static gboolean listener_event(GIOChannel *channel, GIOCondition condition,
  54. dhcp_client->status_code = status;
  55. }
  56. } else {
  57. - message_type = dhcp_get_option(&packet, DHCP_MESSAGE_TYPE);
  58. + message_type = dhcp_get_option(&packet, pkt_len, DHCP_MESSAGE_TYPE);
  59. if (!message_type)
  60. return TRUE;
  61. }
  62. @@ -2378,7 +2380,7 @@ static gboolean listener_event(GIOChannel *channel, GIOCondition condition,
  63. dhcp_client->timeout = 0;
  64. dhcp_client->retry_times = 0;
  65. - option = dhcp_get_option(&packet, DHCP_SERVER_ID);
  66. + option = dhcp_get_option(&packet, pkt_len, DHCP_SERVER_ID);
  67. dhcp_client->server_ip = get_be32(option);
  68. dhcp_client->requested_ip = ntohl(packet.yiaddr);
  69. @@ -2428,9 +2430,9 @@ static gboolean listener_event(GIOChannel *channel, GIOCondition condition,
  70. remove_timeouts(dhcp_client);
  71. - dhcp_client->lease_seconds = get_lease(&packet);
  72. + dhcp_client->lease_seconds = get_lease(&packet, pkt_len);
  73. - get_request(dhcp_client, &packet);
  74. + get_request(dhcp_client, &packet, pkt_len);
  75. switch_listening_mode(dhcp_client, L_NONE);
  76. @@ -2438,7 +2440,7 @@ static gboolean listener_event(GIOChannel *channel, GIOCondition condition,
  77. dhcp_client->assigned_ip = get_ip(packet.yiaddr);
  78. if (dhcp_client->state == REBOOTING) {
  79. - option = dhcp_get_option(&packet,
  80. + option = dhcp_get_option(&packet, pkt_len,
  81. DHCP_SERVER_ID);
  82. dhcp_client->server_ip = get_be32(option);
  83. }
  84. diff --git a/gdhcp/common.c b/gdhcp/common.c
  85. index 1d667d17..c8916aa8 100644
  86. --- a/gdhcp/common.c
  87. +++ b/gdhcp/common.c
  88. @@ -73,18 +73,21 @@ GDHCPOptionType dhcp_get_code_type(uint8_t code)
  89. return OPTION_UNKNOWN;
  90. }
  91. -uint8_t *dhcp_get_option(struct dhcp_packet *packet, int code)
  92. +uint8_t *dhcp_get_option(struct dhcp_packet *packet, uint16_t packet_len, int code)
  93. {
  94. int len, rem;
  95. - uint8_t *optionptr;
  96. + uint8_t *optionptr, *options_end;
  97. + size_t options_len;
  98. uint8_t overload = 0;
  99. /* option bytes: [code][len][data1][data2]..[dataLEN] */
  100. optionptr = packet->options;
  101. rem = sizeof(packet->options);
  102. + options_len = packet_len - (sizeof(*packet) - sizeof(packet->options));
  103. + options_end = optionptr + options_len - 1;
  104. while (1) {
  105. - if (rem <= 0)
  106. + if ((rem <= 0) && (optionptr + OPT_CODE > options_end))
  107. /* Bad packet, malformed option field */
  108. return NULL;
  109. @@ -115,14 +118,25 @@ uint8_t *dhcp_get_option(struct dhcp_packet *packet, int code)
  110. break;
  111. }
  112. + if (optionptr + OPT_LEN > options_end) {
  113. + /* bad packet, would read length field from OOB */
  114. + return NULL;
  115. + }
  116. +
  117. len = 2 + optionptr[OPT_LEN];
  118. rem -= len;
  119. if (rem < 0)
  120. continue; /* complain and return NULL */
  121. - if (optionptr[OPT_CODE] == code)
  122. - return optionptr + OPT_DATA;
  123. + if (optionptr[OPT_CODE] == code) {
  124. + if (optionptr + len > options_end) {
  125. + /* bad packet, option length points OOB */
  126. + return NULL;
  127. + } else {
  128. + return optionptr + OPT_DATA;
  129. + }
  130. + }
  131. if (optionptr[OPT_CODE] == DHCP_OPTION_OVERLOAD)
  132. overload |= optionptr[OPT_DATA];
  133. diff --git a/gdhcp/common.h b/gdhcp/common.h
  134. index 9660231c..8f63fd75 100644
  135. --- a/gdhcp/common.h
  136. +++ b/gdhcp/common.h
  137. @@ -179,7 +179,7 @@ struct in6_pktinfo {
  138. };
  139. #endif
  140. -uint8_t *dhcp_get_option(struct dhcp_packet *packet, int code);
  141. +uint8_t *dhcp_get_option(struct dhcp_packet *packet, uint16_t packet_len, int code);
  142. uint8_t *dhcpv6_get_option(struct dhcpv6_packet *packet, uint16_t pkt_len,
  143. int code, uint16_t *option_len, int *option_count);
  144. uint8_t *dhcpv6_get_sub_option(unsigned char *option, uint16_t max_len,
  145. diff --git a/gdhcp/server.c b/gdhcp/server.c
  146. index 85405f19..52ea2a55 100644
  147. --- a/gdhcp/server.c
  148. +++ b/gdhcp/server.c
  149. @@ -413,7 +413,7 @@ error:
  150. }
  151. -static uint8_t check_packet_type(struct dhcp_packet *packet)
  152. +static uint8_t check_packet_type(struct dhcp_packet *packet, uint16_t packet_len)
  153. {
  154. uint8_t *type;
  155. @@ -423,7 +423,7 @@ static uint8_t check_packet_type(struct dhcp_packet *packet)
  156. if (packet->op != BOOTREQUEST)
  157. return 0;
  158. - type = dhcp_get_option(packet, DHCP_MESSAGE_TYPE);
  159. + type = dhcp_get_option(packet, packet_len, DHCP_MESSAGE_TYPE);
  160. if (!type)
  161. return 0;
  162. @@ -651,6 +651,7 @@ static gboolean listener_event(GIOChannel *channel, GIOCondition condition,
  163. struct dhcp_lease *lease;
  164. uint32_t requested_nip = 0;
  165. uint8_t type, *server_id_option, *request_ip_option;
  166. + uint16_t packet_len;
  167. int re;
  168. if (condition & (G_IO_NVAL | G_IO_ERR | G_IO_HUP)) {
  169. @@ -661,12 +662,13 @@ static gboolean listener_event(GIOChannel *channel, GIOCondition condition,
  170. re = dhcp_recv_l3_packet(&packet, dhcp_server->listener_sockfd);
  171. if (re < 0)
  172. return TRUE;
  173. + packet_len = (uint16_t)(unsigned int)re;
  174. - type = check_packet_type(&packet);
  175. + type = check_packet_type(&packet, packet_len);
  176. if (type == 0)
  177. return TRUE;
  178. - server_id_option = dhcp_get_option(&packet, DHCP_SERVER_ID);
  179. + server_id_option = dhcp_get_option(&packet, packet_len, DHCP_SERVER_ID);
  180. if (server_id_option) {
  181. uint32_t server_nid =
  182. get_unaligned((const uint32_t *) server_id_option);
  183. @@ -675,7 +677,7 @@ static gboolean listener_event(GIOChannel *channel, GIOCondition condition,
  184. return TRUE;
  185. }
  186. - request_ip_option = dhcp_get_option(&packet, DHCP_REQUESTED_IP);
  187. + request_ip_option = dhcp_get_option(&packet, packet_len, DHCP_REQUESTED_IP);
  188. if (request_ip_option)
  189. requested_nip = get_be32(request_ip_option);
  190. --
  191. 2.20.1