File: | lib/libswan/ttoaddress.c |
Warning: | line 421, column 10 Access to field 'ai_addrlen' results in a dereference of a null pointer (loaded from variable 'winner') |
Press '?' to see keyboard shortcuts
Keyboard shortcuts:
1 | /* | |||
2 | * conversion from text forms of addresses to internal ones | |||
3 | * | |||
4 | * Copyright (C) 2000 Henry Spencer. | |||
5 | * Copyright (C) 2019-2021 Andrew Cagney <cagney@gnu.org> | |||
6 | * | |||
7 | * This library is free software; you can redistribute it and/or modify it | |||
8 | * under the terms of the GNU Library General Public License as published by | |||
9 | * the Free Software Foundation; either version 2 of the License, or (at your | |||
10 | * option) any later version. See <https://www.gnu.org/licenses/lgpl-2.1.txt>. | |||
11 | * | |||
12 | * This library is distributed in the hope that it will be useful, but | |||
13 | * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY | |||
14 | * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public | |||
15 | * License for more details. | |||
16 | * | |||
17 | */ | |||
18 | ||||
19 | /* | |||
20 | * Unit testing is available through | |||
21 | * OBJ.$WHATEVER/testing/programs/ipcheck/ipcheck | |||
22 | * This does not require KVM and is built by "make base". | |||
23 | */ | |||
24 | ||||
25 | #include <string.h> | |||
26 | #include <netdb.h> /* for gethostbyname2() */ | |||
27 | #include <sys/socket.h> /* for AF_INET/AF_INET6/AF_UNSPEC */ | |||
28 | ||||
29 | #include "ip_address.h" | |||
30 | #include "ip_sockaddr.h" | |||
31 | #include "ip_info.h" | |||
32 | #include "lswalloc.h" /* for alloc_things(), pfree() */ | |||
33 | #include "lswlog.h" /* for pexpect() */ | |||
34 | #include "hunk.h" /* for char_is_xdigit() */ | |||
35 | ||||
36 | static bool_Bool tryhex(shunk_t hex, ip_address *dst); | |||
37 | static err_t trydotted(shunk_t src, ip_address *); | |||
38 | static err_t colon(shunk_t src, ip_address *); | |||
39 | static err_t getpiece(const char **, const char *, unsigned *); | |||
40 | ||||
41 | /* | |||
42 | * ttoaddress_num - convert "numeric" IP address to binary address. | |||
43 | * | |||
44 | * NULL for success, else static string literal diagnostic. | |||
45 | */ | |||
46 | ||||
47 | err_t ttoaddress_num(shunk_t src, const struct ip_info *afi, ip_address *dst) | |||
48 | { | |||
49 | *dst = unset_address; | |||
50 | ||||
51 | if (src.len == 0) { | |||
52 | return "empty string"; | |||
53 | } | |||
54 | ||||
55 | /* | |||
56 | * Hack to recognize a HEX IPv4 address; presumably DNS names | |||
57 | * can't start with a number so this can't be misinterpreted. | |||
58 | * | |||
59 | * If this fails, stumble on like nothing happened, letting | |||
60 | * the more typical code report an error. | |||
61 | */ | |||
62 | ||||
63 | if (afi == &ipv4_info) { | |||
64 | if (tryhex(src, dst)) { | |||
65 | return NULL((void*)0); | |||
66 | } | |||
67 | return trydotted(src, dst); | |||
68 | } | |||
69 | ||||
70 | if (afi == &ipv6_info) { | |||
71 | return colon(src, dst); | |||
72 | } | |||
73 | ||||
74 | if (afi == NULL((void*)0)) { | |||
75 | if (tryhex(src, dst)) { | |||
76 | return NULL((void*)0); | |||
77 | } | |||
78 | if (memchr(src.ptr, ':', src.len) != NULL((void*)0)) { | |||
79 | return colon(src, dst); | |||
80 | } | |||
81 | return trydotted(src, dst); | |||
82 | } | |||
83 | ||||
84 | return "address family unknown"; | |||
85 | } | |||
86 | ||||
87 | /* | |||
88 | * tryhex - try conversion as an eight-digit hex number (AF_INET only) | |||
89 | */ | |||
90 | ||||
91 | static bool_Bool tryhex(shunk_t hex, | |||
92 | ip_address *dst) | |||
93 | { | |||
94 | if (hex.len != strlen("0x12345678")) { | |||
95 | return false0; | |||
96 | } | |||
97 | ||||
98 | if (!shunk_strcaseeat(&hex, "0x")) { | |||
99 | return false0; | |||
100 | } | |||
101 | ||||
102 | uintmax_t ul; | |||
103 | err_t err = shunk_to_uintmax(hex, NULL((void*)0), 16, &ul, UINT32_MAX(4294967295U)); | |||
104 | if (err != NULL((void*)0)) { | |||
105 | return false0; | |||
106 | } | |||
107 | ||||
108 | struct in_addr addr = { htonl(ul), }; | |||
109 | *dst = address_from_in_addr(&addr); | |||
110 | return true1; | |||
111 | } | |||
112 | ||||
113 | /* | |||
114 | * trydotted - try conversion as dotted numeric (AF_INET only). | |||
115 | * | |||
116 | * But could a DNS name also be a valid IPv4 address, vis: | |||
117 | * - can start with a decimal-digit | |||
118 | * - can end with a decimal-digit | |||
119 | * - must contain at least one letter | |||
120 | * https://tools.ietf.org/html/rfc1912 | |||
121 | * | |||
122 | * Yes, for instance: 0x01.0x02.0x03.0x04. | |||
123 | * | |||
124 | * This code tries to avoid this pitfall by only allowing non-decimal | |||
125 | * fields when they match a very limited format. | |||
126 | */ | |||
127 | ||||
128 | static err_t trydotted(shunk_t src, ip_address *dst) | |||
129 | { | |||
130 | struct ip_bytes bytes = unset_bytes; | |||
131 | ||||
132 | shunk_t cursor = src; | |||
133 | for (unsigned b = 0; b < 4 && cursor.ptr != NULL((void*)0) /* more-input */; b++) { | |||
134 | /* Danger: nested breaks and returns. */ | |||
135 | ||||
136 | /* | |||
137 | * Carve off the next number. | |||
138 | * | |||
139 | * This way, given "09.1.2.", the full sub-string "09" | |||
140 | * is extracted and converted. Being invalid, it will | |||
141 | * report an error about a bogus digit (and not the | |||
142 | * less meaningful trailing garbage error). | |||
143 | * | |||
144 | * After the last token has been extracted cursor is | |||
145 | * left pointing at NULL i.e., cursor.ptr==NULL. | |||
146 | */ | |||
147 | shunk_t token = shunk_token(&cursor, NULL((void*)0), "."); | |||
148 | if (token.len == 0) { | |||
149 | /* ex: 0xa. */ | |||
150 | return "empty dotted-numeric address field"; | |||
151 | } | |||
152 | #if 0 | |||
153 | fprintf(stderrstderr, "cursor="PRI_SHUNK"%.*s"" token="PRI_SHUNK"%.*s""\n", | |||
154 | pri_shunk(cursor)((int) (cursor).len), (const char *) ((cursor).ptr), pri_shunk(token)((int) (token).len), (const char *) ((token).ptr)); | |||
155 | #endif | |||
156 | ||||
157 | /* | |||
158 | * Try converting to a number. | |||
159 | * | |||
160 | * See examples in wikepedia. | |||
161 | * | |||
162 | * This lets uintmax decide how to convert the number | |||
163 | * (play with wget). | |||
164 | * | |||
165 | * The alternative is to do a very strict check. | |||
166 | */ | |||
167 | #if 0 | |||
168 | unsigned base; | |||
169 | if (token.len == strlen("0xXX") && shunk_strcaseeat(&token, "0x")) { | |||
170 | base = 16; | |||
171 | } else if (token.len == strlen("0ooo") && shunk_strcaseeat(&token, "0")) { | |||
172 | base = 8; | |||
173 | } else { | |||
174 | base = 10; | |||
175 | } | |||
176 | if (hunk_strcasestarteq(token, "0")({ const typeof(token) hunk_ = token; const char *string_ = "0" ; size_t slen_ = string_ != ((void*)0) ? strlen(string_) : 0; hunk_.len < slen_ ? 0 : case_eq(hunk_.ptr, slen_, string_ , slen_); }) && token.len == strlen("0xXX")) { | |||
177 | base = 16; | |||
178 | } | |||
179 | uintmax_t byte; | |||
180 | err_t err = shunk_to_uintmax(token, NULL((void*)0), base, &byte, 0/*no-celing*/); | |||
181 | #else | |||
182 | uintmax_t byte; | |||
183 | err_t err = shunk_to_uintmax(token, NULL((void*)0), 0, &byte, 0/*no-celing*/); | |||
184 | #endif | |||
185 | if (err != NULL((void*)0)) { | |||
186 | return err; | |||
187 | } | |||
188 | ||||
189 | /* | |||
190 | * The last byte overflows into earlier unfilled | |||
191 | * bytes. For instance: | |||
192 | * | |||
193 | * 127.1 -> 127.0.0.1 | |||
194 | * 127.65534 -> 127.0.255.254 | |||
195 | * | |||
196 | * A bizare hangover from address classes: | |||
197 | * https://en.wikipedia.org/wiki/IPv4#Address_representations | |||
198 | * | |||
199 | * Now while it is arguable that: | |||
200 | * | |||
201 | * 65534 -> 0.0.255.254 | |||
202 | * 127 -> 0.0.0.127 !?!?! | |||
203 | * | |||
204 | * is also valid, that is too weird and rejected (but | |||
205 | * 0x01020304 is accepted by an earlier call to | |||
206 | * tryhex()). | |||
207 | */ | |||
208 | if (cursor.len == 0 && b > 0) { | |||
209 | for (unsigned e = 3; e >= b && byte != 0; e--) { | |||
210 | bytes.byte[e] = byte; /* truncate */ | |||
211 | byte = byte >> 8; | |||
212 | } | |||
213 | if (byte != 0) { | |||
214 | return "overflow in dotted-numeric address"; | |||
215 | } | |||
216 | break; | |||
217 | } | |||
218 | ||||
219 | /* A non-last bytes need to fit into the byte */ | |||
220 | if (byte > UINT8_MAX(255)) { | |||
221 | return "byte overflow in dotted-numeric address"; | |||
222 | } | |||
223 | ||||
224 | bytes.byte[b] = byte; | |||
225 | } | |||
226 | if (cursor.ptr != NULL((void*)0)) { | |||
227 | return "garbage at end of dotted-address"; | |||
228 | } | |||
229 | ||||
230 | *dst = address_from_raw(HERE({ static const struct where here = { .func = __func__, .file = "lib/libswan/ttoaddress.c", .line = 230, }; &here; }), ipv4_info.ip_version, bytes); | |||
231 | return NULL((void*)0); | |||
232 | } | |||
233 | ||||
234 | /* | |||
235 | * colon - convert IPv6 "numeric" address | |||
236 | */ | |||
237 | ||||
238 | static err_t colon(shunk_t cursor, ip_address *dst) | |||
239 | { | |||
240 | const char *src = cursor.ptr; | |||
241 | unsigned srclen = cursor.len; | |||
242 | const char *stop = src + srclen; /* just past end */ | |||
243 | unsigned piece; | |||
244 | int gapat; /* where was empty piece seen */ | |||
245 | err_t oops; | |||
246 | # define NPIECES8 8 | |||
247 | struct ip_bytes u = unset_bytes; | |||
248 | int i; | |||
249 | int j; | |||
250 | # define IT"IPv6 numeric address" "IPv6 numeric address" | |||
251 | int naftergap; | |||
252 | ||||
253 | /* leading or trailing :: becomes single empty field */ | |||
254 | if (*src == ':') { /* legal only if leading :: */ | |||
255 | if (srclen == 1 || *(src + 1) != ':') | |||
256 | return "illegal leading `:' in " IT"IPv6 numeric address"; | |||
257 | ||||
258 | if (srclen == 2) { | |||
259 | *dst = ipv6_info.address.any; | |||
260 | return NULL((void*)0); | |||
261 | } | |||
262 | src++; /* past first but not second */ | |||
263 | srclen--; | |||
264 | } | |||
265 | if (*(stop - 1) == ':') { /* legal only if trailing :: */ | |||
266 | if (srclen == 1 || *(stop - 2) != ':') | |||
267 | return "illegal trailing `:' in " IT"IPv6 numeric address"; | |||
268 | ||||
269 | srclen--; /* leave one */ | |||
270 | } | |||
271 | ||||
272 | gapat = -1; | |||
273 | piece = 0; | |||
274 | for (i = 0; i < NPIECES8 && src < stop; i++) { | |||
275 | oops = getpiece(&src, stop, &piece); | |||
276 | if (oops != NULL((void*)0) && *oops == ':') { /* empty field */ | |||
277 | if (gapat >= 0) | |||
278 | return "more than one :: in " IT"IPv6 numeric address"; | |||
279 | ||||
280 | gapat = i; | |||
281 | } else if (oops != NULL((void*)0)) { | |||
282 | return oops; | |||
283 | } | |||
284 | u.byte[2 * i] = piece >> 8; | |||
285 | u.byte[2 * i + 1] = piece & 0xff; | |||
286 | if (i < NPIECES8 - 1) { /* there should be more input */ | |||
287 | if (src == stop && gapat < 0) | |||
288 | return IT"IPv6 numeric address" " ends prematurely"; | |||
289 | ||||
290 | if (src != stop && *src++ != ':') | |||
291 | return "syntax error in " IT"IPv6 numeric address"; | |||
292 | } | |||
293 | } | |||
294 | if (src != stop) | |||
295 | return "extra garbage on end of " IT"IPv6 numeric address"; | |||
296 | ||||
297 | if (gapat < 0 && i < NPIECES8) /* should have been caught earlier */ | |||
298 | return "incomplete " IT"IPv6 numeric address" " (internal error)"; | |||
299 | ||||
300 | if (gapat >= 0 && i == NPIECES8) | |||
301 | return "non-abbreviating empty field in " IT"IPv6 numeric address"; | |||
302 | ||||
303 | if (gapat >= 0) { | |||
304 | naftergap = i - (gapat + 1); | |||
305 | for (i--, j = NPIECES8 - 1; naftergap > 0; | |||
306 | i--, j--, naftergap--) { | |||
307 | u.byte[2 * j] = u.byte[2 * i]; | |||
308 | u.byte[2 * j + 1] = u.byte[2 * i + 1]; | |||
309 | } | |||
310 | for (; j >= gapat; j--) | |||
311 | u.byte[2 * j] = u.byte[2 * j + 1] = 0; | |||
312 | } | |||
313 | ||||
314 | *dst = address_from_raw(HERE({ static const struct where here = { .func = __func__, .file = "lib/libswan/ttoaddress.c", .line = 314, }; &here; }), ipv6_info.ip_version, u); | |||
315 | return NULL((void*)0); | |||
316 | } | |||
317 | ||||
318 | /* | |||
319 | * getpiece - try to scan one 16-bit piece of an IPv6 address | |||
320 | * | |||
321 | * ":" means "empty field seen" | |||
322 | */ | |||
323 | err_t getpiece(const char **srcp, /* *srcp is updated */ | |||
324 | const char *stop, /* first untouchable char */ | |||
325 | unsigned *retp) /* return-value pointer */ | |||
326 | { | |||
327 | const char *p; | |||
328 | # define NDIG4 4 | |||
329 | int d; | |||
330 | unsigned long ret; | |||
331 | err_t oops; | |||
332 | ||||
333 | if (*srcp >= stop || **srcp == ':') { /* empty field */ | |||
334 | *retp = 0; | |||
335 | return ":"; | |||
336 | } | |||
337 | ||||
338 | p = *srcp; | |||
339 | d = 0; | |||
340 | while (p < stop && d < NDIG4 && char_isxdigit(*p)) { | |||
341 | p++; | |||
342 | d++; | |||
343 | } | |||
344 | if (d == 0) | |||
345 | return "non-hex field in IPv6 numeric address"; | |||
346 | ||||
347 | if (p < stop && d == NDIG4 && char_isxdigit(*p)) | |||
348 | return "field in IPv6 numeric address longer than 4 hex digits"; | |||
349 | ||||
350 | oops = ttoul(*srcp, d, 16, &ret); | |||
351 | if (oops != NULL((void*)0)) /* shouldn't happen, really... */ | |||
352 | return oops; | |||
353 | ||||
354 | *srcp = p; | |||
355 | *retp = ret; | |||
356 | return NULL((void*)0); | |||
357 | } | |||
358 | ||||
359 | /* | |||
360 | * ttoaddress_dns | |||
361 | * | |||
362 | * ??? numeric addresses are handled by getaddrinfo; perhaps the hex form is lost. | |||
363 | * ??? change: we no longer filter out bad characters. Surely getaddrinfo(3) does. | |||
364 | */ | |||
365 | err_t ttoaddress_dns(shunk_t src, const struct ip_info *afi, ip_address *dst) | |||
366 | { | |||
367 | *dst = unset_address; | |||
368 | ||||
369 | char *name = clone_hunk_as_string(src, "ttoaddress_dns")({ typeof(src) hunk_ = src; clone_bytes_as_string(hunk_.ptr, hunk_ .len, "ttoaddress_dns"); }); /* must free */ | |||
370 | struct addrinfo *res = NULL((void*)0); /* must-free when EAI==0 */ | |||
371 | int family = afi == NULL((void*)0) ? AF_UNSPEC0 : afi->af; | |||
| ||||
372 | const struct addrinfo hints = (struct addrinfo) { | |||
373 | .ai_family = family, | |||
374 | }; | |||
375 | int eai = getaddrinfo(name, NULL((void*)0), &hints, &res); | |||
376 | ||||
377 | if (eai != 0) { | |||
378 | /* | |||
379 | * Return what the pluto testsuite expects for now. | |||
380 | * | |||
381 | * Error return is intricate because we cannot compose | |||
382 | * a static string. | |||
383 | * | |||
384 | * XXX: How portable are errors returned by | |||
385 | * gai_strerror(eai)? | |||
386 | * | |||
387 | * XXX: what is with "(no validation performed)"? | |||
388 | * Perhaps it is refering to DNSSEC. | |||
389 | */ | |||
390 | pfree(name); | |||
391 | /* RES is not defined */ | |||
392 | switch (family) { | |||
393 | case AF_INET610: | |||
394 | return "not a numeric IPv6 address and name lookup failed (no validation performed)"; | |||
395 | case AF_INET2: | |||
396 | return "not a numeric IPv4 address and name lookup failed (no validation performed)"; | |||
397 | default: | |||
398 | return "not a numeric IPv4 or IPv6 address and name lookup failed (no validation performed)"; | |||
399 | } | |||
400 | } | |||
401 | ||||
402 | /* | |||
403 | * When AFI is specified, use the first entry; and prefer IPv4 | |||
404 | * when it wasn't. | |||
405 | * | |||
406 | * Linux orders things IPv4->IPv6, but NetBSD at least is the | |||
407 | * reverse; hence the search. | |||
408 | */ | |||
409 | struct addrinfo *winner = res; | |||
410 | if (afi
| |||
411 | for (struct addrinfo *r = res; r!= NULL((void*)0); r = r->ai_next) { | |||
412 | if (r->ai_family == AF_INET2) { | |||
413 | winner = r; | |||
414 | break; | |||
415 | } | |||
416 | } | |||
417 | } | |||
418 | ||||
419 | /* ai_addrlen is probably shorter than (sa.sa) */ | |||
420 | ip_sockaddr sa = { | |||
421 | .len = winner->ai_addrlen, | |||
| ||||
422 | }; | |||
423 | passert(winner->ai_addrlen <= sizeof(sa.sa))({ _Bool assertion__ = winner->ai_addrlen <= sizeof(sa. sa); if (!assertion__) { where_t here = ({ static const struct where here = { .func = __func__, .file = "lib/libswan/ttoaddress.c" , .line = 423, }; &here; }); const struct logger *logger_ = &failsafe_logger; llog_passert(logger_, here, "%s", "winner->ai_addrlen <= sizeof(sa.sa)" ); } (void) 1; }); | |||
424 | memcpy(&sa.sa, winner->ai_addr, winner->ai_addrlen); | |||
425 | passert(sa.sa.sa.sa_family == winner->ai_family)({ _Bool assertion__ = sa.sa.sa.sa_family == winner->ai_family ; if (!assertion__) { where_t here = ({ static const struct where here = { .func = __func__, .file = "lib/libswan/ttoaddress.c" , .line = 425, }; &here; }); const struct logger *logger_ = &failsafe_logger; llog_passert(logger_, here, "%s", "sa.sa.sa.sa_family == winner->ai_family" ); } (void) 1; }); | |||
426 | ||||
427 | /* boneheaded getaddrinfo(3) leaves port field undefined */ | |||
428 | err_t err = sockaddr_to_address_port(sa, dst, NULL((void*)0)/*ignore port*/); | |||
429 | ||||
430 | freeaddrinfo(res); | |||
431 | pfree(name); | |||
432 | return err; | |||
433 | } |