blob: 6bd734d756ccf5a9568240d042a69f089b42fe75 [file] [log] [blame]
Andrew Geissler615f2f12022-07-15 14:00:58 -05001From 3cdecc159e0f417a2f8d43d99632af26beea630f Mon Sep 17 00:00:00 2001
2From: Simon Kelley <simon@thekelleys.org.uk>
3Date: Thu, 31 Mar 2022 21:35:20 +0100
4Subject: [PATCH] Fix write-after-free error in DHCPv6 code. CVE-2022-0934
5 refers.
6
7CVE: CVE-2022-0934
8
9Upstream-Status: Backport
10[https://thekelleys.org.uk/gitweb/?p=dnsmasq.git;a=commitdiff;h=03345ecefe]
11
12Signed-off-by: Yi Zhao <yi.zhao@windriver.com>
13---
14 CHANGELOG | 3 +++
15 src/rfc3315.c | 48 +++++++++++++++++++++++++++---------------------
16 2 files changed, 30 insertions(+), 21 deletions(-)
17
18diff --git a/CHANGELOG b/CHANGELOG
19index 5e54df9..a28da2a 100644
20--- a/CHANGELOG
21+++ b/CHANGELOG
22@@ -1,4 +1,7 @@
23 version 2.86
24+ Fix write-after-free error in DHCPv6 server code.
25+ CVE-2022-0934 refers.
26+
27 Handle DHCPREBIND requests in the DHCPv6 server code.
28 Thanks to Aichun Li for spotting this omission, and the initial
29 patch.
30diff --git a/src/rfc3315.c b/src/rfc3315.c
31index 5c2ff97..6ecfeeb 100644
32--- a/src/rfc3315.c
33+++ b/src/rfc3315.c
34@@ -33,9 +33,9 @@ struct state {
35 unsigned int mac_len, mac_type;
36 };
37
38-static int dhcp6_maybe_relay(struct state *state, void *inbuff, size_t sz,
39+static int dhcp6_maybe_relay(struct state *state, unsigned char *inbuff, size_t sz,
40 struct in6_addr *client_addr, int is_unicast, time_t now);
41-static int dhcp6_no_relay(struct state *state, int msg_type, void *inbuff, size_t sz, int is_unicast, time_t now);
42+static int dhcp6_no_relay(struct state *state, int msg_type, unsigned char *inbuff, size_t sz, int is_unicast, time_t now);
43 static void log6_opts(int nest, unsigned int xid, void *start_opts, void *end_opts);
44 static void log6_packet(struct state *state, char *type, struct in6_addr *addr, char *string);
45 static void log6_quiet(struct state *state, char *type, struct in6_addr *addr, char *string);
46@@ -104,12 +104,12 @@ unsigned short dhcp6_reply(struct dhcp_context *context, int interface, char *if
47 }
48
49 /* This cost me blood to write, it will probably cost you blood to understand - srk. */
50-static int dhcp6_maybe_relay(struct state *state, void *inbuff, size_t sz,
51+static int dhcp6_maybe_relay(struct state *state, unsigned char *inbuff, size_t sz,
52 struct in6_addr *client_addr, int is_unicast, time_t now)
53 {
54 void *end = inbuff + sz;
55 void *opts = inbuff + 34;
56- int msg_type = *((unsigned char *)inbuff);
57+ int msg_type = *inbuff;
58 unsigned char *outmsgtypep;
59 void *opt;
60 struct dhcp_vendor *vendor;
61@@ -259,15 +259,15 @@ static int dhcp6_maybe_relay(struct state *state, void *inbuff, size_t sz,
62 return 1;
63 }
64
65-static int dhcp6_no_relay(struct state *state, int msg_type, void *inbuff, size_t sz, int is_unicast, time_t now)
66+static int dhcp6_no_relay(struct state *state, int msg_type, unsigned char *inbuff, size_t sz, int is_unicast, time_t now)
67 {
68 void *opt;
69- int i, o, o1, start_opts;
70+ int i, o, o1, start_opts, start_msg;
71 struct dhcp_opt *opt_cfg;
72 struct dhcp_netid *tagif;
73 struct dhcp_config *config = NULL;
74 struct dhcp_netid known_id, iface_id, v6_id;
75- unsigned char *outmsgtypep;
76+ unsigned char outmsgtype;
77 struct dhcp_vendor *vendor;
78 struct dhcp_context *context_tmp;
79 struct dhcp_mac *mac_opt;
80@@ -296,12 +296,13 @@ static int dhcp6_no_relay(struct state *state, int msg_type, void *inbuff, size_
81 v6_id.next = state->tags;
82 state->tags = &v6_id;
83
84- /* copy over transaction-id, and save pointer to message type */
85- if (!(outmsgtypep = put_opt6(inbuff, 4)))
86+ start_msg = save_counter(-1);
87+ /* copy over transaction-id */
88+ if (!put_opt6(inbuff, 4))
89 return 0;
90 start_opts = save_counter(-1);
91- state->xid = outmsgtypep[3] | outmsgtypep[2] << 8 | outmsgtypep[1] << 16;
92-
93+ state->xid = inbuff[3] | inbuff[2] << 8 | inbuff[1] << 16;
94+
95 /* We're going to be linking tags from all context we use.
96 mark them as unused so we don't link one twice and break the list */
97 for (context_tmp = state->context; context_tmp; context_tmp = context_tmp->current)
98@@ -347,7 +348,7 @@ static int dhcp6_no_relay(struct state *state, int msg_type, void *inbuff, size_
99 (msg_type == DHCP6REQUEST || msg_type == DHCP6RENEW || msg_type == DHCP6RELEASE || msg_type == DHCP6DECLINE))
100
101 {
102- *outmsgtypep = DHCP6REPLY;
103+ outmsgtype = DHCP6REPLY;
104 o1 = new_opt6(OPTION6_STATUS_CODE);
105 put_opt6_short(DHCP6USEMULTI);
106 put_opt6_string("Use multicast");
107@@ -619,11 +620,11 @@ static int dhcp6_no_relay(struct state *state, int msg_type, void *inbuff, size_
108 struct dhcp_netid *solicit_tags;
109 struct dhcp_context *c;
110
111- *outmsgtypep = DHCP6ADVERTISE;
112+ outmsgtype = DHCP6ADVERTISE;
113
114 if (opt6_find(state->packet_options, state->end, OPTION6_RAPID_COMMIT, 0))
115 {
116- *outmsgtypep = DHCP6REPLY;
117+ outmsgtype = DHCP6REPLY;
118 state->lease_allocate = 1;
119 o = new_opt6(OPTION6_RAPID_COMMIT);
120 end_opt6(o);
121@@ -809,7 +810,7 @@ static int dhcp6_no_relay(struct state *state, int msg_type, void *inbuff, size_
122 int start = save_counter(-1);
123
124 /* set reply message type */
125- *outmsgtypep = DHCP6REPLY;
126+ outmsgtype = DHCP6REPLY;
127 state->lease_allocate = 1;
128
129 log6_quiet(state, "DHCPREQUEST", NULL, ignore ? _("ignored") : NULL);
130@@ -924,7 +925,7 @@ static int dhcp6_no_relay(struct state *state, int msg_type, void *inbuff, size_
131 int address_assigned = 0;
132
133 /* set reply message type */
134- *outmsgtypep = DHCP6REPLY;
135+ outmsgtype = DHCP6REPLY;
136
137 log6_quiet(state, msg_type == DHCP6RENEW ? "DHCPRENEW" : "DHCPREBIND", NULL, NULL);
138
139@@ -1057,7 +1058,7 @@ static int dhcp6_no_relay(struct state *state, int msg_type, void *inbuff, size_
140 int good_addr = 0;
141
142 /* set reply message type */
143- *outmsgtypep = DHCP6REPLY;
144+ outmsgtype = DHCP6REPLY;
145
146 log6_quiet(state, "DHCPCONFIRM", NULL, NULL);
147
148@@ -1121,7 +1122,7 @@ static int dhcp6_no_relay(struct state *state, int msg_type, void *inbuff, size_
149 log6_quiet(state, "DHCPINFORMATION-REQUEST", NULL, ignore ? _("ignored") : state->hostname);
150 if (ignore)
151 return 0;
152- *outmsgtypep = DHCP6REPLY;
153+ outmsgtype = DHCP6REPLY;
154 tagif = add_options(state, 1);
155 break;
156 }
157@@ -1130,7 +1131,7 @@ static int dhcp6_no_relay(struct state *state, int msg_type, void *inbuff, size_
158 case DHCP6RELEASE:
159 {
160 /* set reply message type */
161- *outmsgtypep = DHCP6REPLY;
162+ outmsgtype = DHCP6REPLY;
163
164 log6_quiet(state, "DHCPRELEASE", NULL, NULL);
165
166@@ -1195,7 +1196,7 @@ static int dhcp6_no_relay(struct state *state, int msg_type, void *inbuff, size_
167 case DHCP6DECLINE:
168 {
169 /* set reply message type */
170- *outmsgtypep = DHCP6REPLY;
171+ outmsgtype = DHCP6REPLY;
172
173 log6_quiet(state, "DHCPDECLINE", NULL, NULL);
174
175@@ -1275,7 +1276,12 @@ static int dhcp6_no_relay(struct state *state, int msg_type, void *inbuff, size_
176 }
177
178 }
179-
180+
181+ /* Fill in the message type. Note that we store the offset,
182+ not a direct pointer, since the packet memory may have been
183+ reallocated. */
184+ ((unsigned char *)(daemon->outpacket.iov_base))[start_msg] = outmsgtype;
185+
186 log_tags(tagif, state->xid);
187 log6_opts(0, state->xid, daemon->outpacket.iov_base + start_opts, daemon->outpacket.iov_base + save_counter(-1));
188
189--
1902.25.1
191