]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/nss-resolve/nss-resolve.c
headers: remove unneeded includes from util.h
[thirdparty/systemd.git] / src / nss-resolve / nss-resolve.c
1 /* SPDX-License-Identifier: LGPL-2.1+ */
2
3 #include <errno.h>
4 #include <netdb.h>
5 #include <nss.h>
6 #include <stdlib.h>
7 #include <string.h>
8 #include <sys/types.h>
9 #include <unistd.h>
10
11 #include "sd-bus.h"
12
13 #include "bus-common-errors.h"
14 #include "errno-util.h"
15 #include "in-addr-util.h"
16 #include "macro.h"
17 #include "nss-util.h"
18 #include "resolved-def.h"
19 #include "signal-util.h"
20 #include "string-util.h"
21
22 NSS_GETHOSTBYNAME_PROTOTYPES(resolve);
23 NSS_GETHOSTBYADDR_PROTOTYPES(resolve);
24
25 static bool bus_error_shall_fallback(sd_bus_error *e) {
26 return sd_bus_error_has_name(e, SD_BUS_ERROR_SERVICE_UNKNOWN) ||
27 sd_bus_error_has_name(e, SD_BUS_ERROR_NAME_HAS_NO_OWNER) ||
28 sd_bus_error_has_name(e, SD_BUS_ERROR_NO_REPLY) ||
29 sd_bus_error_has_name(e, SD_BUS_ERROR_ACCESS_DENIED);
30 }
31
32 static int count_addresses(sd_bus_message *m, int af, const char **canonical) {
33 int c = 0, r;
34
35 assert(m);
36 assert(canonical);
37
38 r = sd_bus_message_enter_container(m, 'a', "(iiay)");
39 if (r < 0)
40 return r;
41
42 while ((r = sd_bus_message_enter_container(m, 'r', "iiay")) > 0) {
43 int family, ifindex;
44
45 assert_cc(sizeof(int32_t) == sizeof(int));
46
47 r = sd_bus_message_read(m, "ii", &ifindex, &family);
48 if (r < 0)
49 return r;
50
51 r = sd_bus_message_skip(m, "ay");
52 if (r < 0)
53 return r;
54
55 r = sd_bus_message_exit_container(m);
56 if (r < 0)
57 return r;
58
59 if (af != AF_UNSPEC && family != af)
60 continue;
61
62 c++;
63 }
64 if (r < 0)
65 return r;
66
67 r = sd_bus_message_exit_container(m);
68 if (r < 0)
69 return r;
70
71 r = sd_bus_message_read(m, "s", canonical);
72 if (r < 0)
73 return r;
74
75 r = sd_bus_message_rewind(m, true);
76 if (r < 0)
77 return r;
78
79 return c;
80 }
81
82 static uint32_t ifindex_to_scopeid(int family, const void *a, int ifindex) {
83 struct in6_addr in6;
84
85 if (family != AF_INET6)
86 return 0;
87
88 /* Some apps can't deal with the scope ID attached to non-link-local addresses. Hence, let's suppress that. */
89
90 assert(sizeof(in6) == FAMILY_ADDRESS_SIZE(AF_INET6));
91 memcpy(&in6, a, sizeof(struct in6_addr));
92
93 return IN6_IS_ADDR_LINKLOCAL(&in6) ? ifindex : 0;
94 }
95
96 static bool avoid_deadlock(void) {
97
98 /* Check whether this lookup might have a chance of deadlocking because we are called from the service manager
99 * code activating systemd-resolved.service. After all, we shouldn't synchronously do lookups to
100 * systemd-resolved if we are required to finish before it can be started. This of course won't detect all
101 * possible dead locks of this kind, but it should work for the most obvious cases. */
102
103 if (geteuid() != 0) /* Ignore the env vars unless we are privileged. */
104 return false;
105
106 return streq_ptr(getenv("SYSTEMD_ACTIVATION_UNIT"), "systemd-resolved.service") &&
107 streq_ptr(getenv("SYSTEMD_ACTIVATION_SCOPE"), "system");
108 }
109
110 enum nss_status _nss_resolve_gethostbyname4_r(
111 const char *name,
112 struct gaih_addrtuple **pat,
113 char *buffer, size_t buflen,
114 int *errnop, int *h_errnop,
115 int32_t *ttlp) {
116
117 _cleanup_(sd_bus_message_unrefp) sd_bus_message *req = NULL, *reply = NULL;
118 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
119 struct gaih_addrtuple *r_tuple, *r_tuple_first = NULL;
120 _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
121 enum nss_status ret = NSS_STATUS_UNAVAIL;
122 const char *canonical = NULL;
123 size_t l, ms, idx;
124 char *r_name;
125 int c, r, i = 0;
126
127 PROTECT_ERRNO;
128 BLOCK_SIGNALS(NSS_SIGNALS_BLOCK);
129
130 assert(name);
131 assert(pat);
132 assert(buffer);
133 assert(errnop);
134 assert(h_errnop);
135
136 if (avoid_deadlock()) {
137 r = -EDEADLK;
138 goto fail;
139 }
140
141 r = sd_bus_open_system(&bus);
142 if (r < 0)
143 goto fail;
144
145 r = sd_bus_message_new_method_call(
146 bus,
147 &req,
148 "org.freedesktop.resolve1",
149 "/org/freedesktop/resolve1",
150 "org.freedesktop.resolve1.Manager",
151 "ResolveHostname");
152 if (r < 0)
153 goto fail;
154
155 r = sd_bus_message_set_auto_start(req, false);
156 if (r < 0)
157 goto fail;
158
159 r = sd_bus_message_append(req, "isit", 0, name, AF_UNSPEC, (uint64_t) 0);
160 if (r < 0)
161 goto fail;
162
163 r = sd_bus_call(bus, req, SD_RESOLVED_QUERY_TIMEOUT_USEC, &error, &reply);
164 if (r < 0) {
165 if (sd_bus_error_has_name(&error, _BUS_ERROR_DNS "NXDOMAIN") ||
166 !bus_error_shall_fallback(&error))
167 goto not_found;
168
169 /* Return NSS_STATUS_UNAVAIL when communication with systemd-resolved fails,
170 allowing falling back to other nss modules. Treat all other error conditions as
171 NOTFOUND. This includes DNSSEC errors and suchlike. (We don't use UNAVAIL in this
172 case so that the nsswitch.conf configuration can distuingish such executed but
173 negative replies from complete failure to talk to resolved). */
174 goto fail;
175 }
176
177 c = count_addresses(reply, AF_UNSPEC, &canonical);
178 if (c < 0) {
179 r = c;
180 goto fail;
181 }
182 if (c == 0)
183 goto not_found;
184
185 if (isempty(canonical))
186 canonical = name;
187
188 l = strlen(canonical);
189 ms = ALIGN(l+1) + ALIGN(sizeof(struct gaih_addrtuple)) * c;
190 if (buflen < ms) {
191 UNPROTECT_ERRNO;
192 *errnop = ERANGE;
193 *h_errnop = NETDB_INTERNAL;
194 return NSS_STATUS_TRYAGAIN;
195 }
196
197 /* First, append name */
198 r_name = buffer;
199 memcpy(r_name, canonical, l+1);
200 idx = ALIGN(l+1);
201
202 /* Second, append addresses */
203 r_tuple_first = (struct gaih_addrtuple*) (buffer + idx);
204
205 r = sd_bus_message_enter_container(reply, 'a', "(iiay)");
206 if (r < 0)
207 goto fail;
208
209 while ((r = sd_bus_message_enter_container(reply, 'r', "iiay")) > 0) {
210 int family, ifindex;
211 const void *a;
212 size_t sz;
213
214 assert_cc(sizeof(int32_t) == sizeof(int));
215
216 r = sd_bus_message_read(reply, "ii", &ifindex, &family);
217 if (r < 0)
218 goto fail;
219
220 if (ifindex < 0) {
221 r = -EINVAL;
222 goto fail;
223 }
224
225 r = sd_bus_message_read_array(reply, 'y', &a, &sz);
226 if (r < 0)
227 goto fail;
228
229 r = sd_bus_message_exit_container(reply);
230 if (r < 0)
231 goto fail;
232
233 if (!IN_SET(family, AF_INET, AF_INET6))
234 continue;
235
236 if (sz != FAMILY_ADDRESS_SIZE(family)) {
237 r = -EINVAL;
238 goto fail;
239 }
240
241 r_tuple = (struct gaih_addrtuple*) (buffer + idx);
242 r_tuple->next = i == c-1 ? NULL : (struct gaih_addrtuple*) ((char*) r_tuple + ALIGN(sizeof(struct gaih_addrtuple)));
243 r_tuple->name = r_name;
244 r_tuple->family = family;
245 r_tuple->scopeid = ifindex_to_scopeid(family, a, ifindex);
246 memcpy(r_tuple->addr, a, sz);
247
248 idx += ALIGN(sizeof(struct gaih_addrtuple));
249 i++;
250 }
251 if (r < 0)
252 goto fail;
253
254 assert(i == c);
255 assert(idx == ms);
256
257 if (*pat)
258 **pat = *r_tuple_first;
259 else
260 *pat = r_tuple_first;
261
262 if (ttlp)
263 *ttlp = 0;
264
265 /* Explicitly reset both *h_errnop and h_errno to work around
266 * https://bugzilla.redhat.com/show_bug.cgi?id=1125975 */
267 *h_errnop = NETDB_SUCCESS;
268 h_errno = 0;
269
270 return NSS_STATUS_SUCCESS;
271
272 fail:
273 UNPROTECT_ERRNO;
274 *errnop = -r;
275 *h_errnop = NO_RECOVERY;
276 return ret;
277
278 not_found:
279 *h_errnop = HOST_NOT_FOUND;
280 return NSS_STATUS_NOTFOUND;
281 }
282
283 enum nss_status _nss_resolve_gethostbyname3_r(
284 const char *name,
285 int af,
286 struct hostent *result,
287 char *buffer, size_t buflen,
288 int *errnop, int *h_errnop,
289 int32_t *ttlp,
290 char **canonp) {
291
292 _cleanup_(sd_bus_message_unrefp) sd_bus_message *req = NULL, *reply = NULL;
293 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
294 char *r_name, *r_aliases, *r_addr, *r_addr_list;
295 _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
296 enum nss_status ret = NSS_STATUS_UNAVAIL;
297 size_t l, idx, ms, alen;
298 const char *canonical;
299 int c, r, i = 0;
300
301 PROTECT_ERRNO;
302 BLOCK_SIGNALS(NSS_SIGNALS_BLOCK);
303
304 assert(name);
305 assert(result);
306 assert(buffer);
307 assert(errnop);
308 assert(h_errnop);
309
310 if (af == AF_UNSPEC)
311 af = AF_INET;
312
313 if (!IN_SET(af, AF_INET, AF_INET6)) {
314 r = -EAFNOSUPPORT;
315 goto fail;
316 }
317
318 if (avoid_deadlock()) {
319 r = -EDEADLK;
320 goto fail;
321 }
322
323 r = sd_bus_open_system(&bus);
324 if (r < 0)
325 goto fail;
326
327 r = sd_bus_message_new_method_call(
328 bus,
329 &req,
330 "org.freedesktop.resolve1",
331 "/org/freedesktop/resolve1",
332 "org.freedesktop.resolve1.Manager",
333 "ResolveHostname");
334 if (r < 0)
335 goto fail;
336
337 r = sd_bus_message_set_auto_start(req, false);
338 if (r < 0)
339 goto fail;
340
341 r = sd_bus_message_append(req, "isit", 0, name, af, (uint64_t) 0);
342 if (r < 0)
343 goto fail;
344
345 r = sd_bus_call(bus, req, SD_RESOLVED_QUERY_TIMEOUT_USEC, &error, &reply);
346 if (r < 0) {
347 if (sd_bus_error_has_name(&error, _BUS_ERROR_DNS "NXDOMAIN") ||
348 !bus_error_shall_fallback(&error))
349 goto not_found;
350
351 goto fail;
352 }
353
354 c = count_addresses(reply, af, &canonical);
355 if (c < 0) {
356 r = c;
357 goto fail;
358 }
359 if (c == 0)
360 goto not_found;
361
362 if (isempty(canonical))
363 canonical = name;
364
365 alen = FAMILY_ADDRESS_SIZE(af);
366 l = strlen(canonical);
367
368 ms = ALIGN(l+1) + c * ALIGN(alen) + (c+2) * sizeof(char*);
369
370 if (buflen < ms) {
371 UNPROTECT_ERRNO;
372 *errnop = ERANGE;
373 *h_errnop = NETDB_INTERNAL;
374 return NSS_STATUS_TRYAGAIN;
375 }
376
377 /* First, append name */
378 r_name = buffer;
379 memcpy(r_name, canonical, l+1);
380 idx = ALIGN(l+1);
381
382 /* Second, create empty aliases array */
383 r_aliases = buffer + idx;
384 ((char**) r_aliases)[0] = NULL;
385 idx += sizeof(char*);
386
387 /* Third, append addresses */
388 r_addr = buffer + idx;
389
390 r = sd_bus_message_enter_container(reply, 'a', "(iiay)");
391 if (r < 0)
392 goto fail;
393
394 while ((r = sd_bus_message_enter_container(reply, 'r', "iiay")) > 0) {
395 int ifindex, family;
396 const void *a;
397 size_t sz;
398
399 r = sd_bus_message_read(reply, "ii", &ifindex, &family);
400 if (r < 0)
401 goto fail;
402
403 if (ifindex < 0) {
404 r = -EINVAL;
405 goto fail;
406 }
407
408 r = sd_bus_message_read_array(reply, 'y', &a, &sz);
409 if (r < 0)
410 goto fail;
411
412 r = sd_bus_message_exit_container(reply);
413 if (r < 0)
414 goto fail;
415
416 if (family != af)
417 continue;
418
419 if (sz != alen) {
420 r = -EINVAL;
421 goto fail;
422 }
423
424 memcpy(r_addr + i*ALIGN(alen), a, alen);
425 i++;
426 }
427 if (r < 0)
428 goto fail;
429
430 assert(i == c);
431 idx += c * ALIGN(alen);
432
433 /* Fourth, append address pointer array */
434 r_addr_list = buffer + idx;
435 for (i = 0; i < c; i++)
436 ((char**) r_addr_list)[i] = r_addr + i*ALIGN(alen);
437
438 ((char**) r_addr_list)[i] = NULL;
439 idx += (c+1) * sizeof(char*);
440
441 assert(idx == ms);
442
443 result->h_name = r_name;
444 result->h_aliases = (char**) r_aliases;
445 result->h_addrtype = af;
446 result->h_length = alen;
447 result->h_addr_list = (char**) r_addr_list;
448
449 if (ttlp)
450 *ttlp = 0;
451
452 if (canonp)
453 *canonp = r_name;
454
455 /* Explicitly reset both *h_errnop and h_errno to work around
456 * https://bugzilla.redhat.com/show_bug.cgi?id=1125975 */
457 *h_errnop = NETDB_SUCCESS;
458 h_errno = 0;
459
460 return NSS_STATUS_SUCCESS;
461
462 fail:
463 UNPROTECT_ERRNO;
464 *errnop = -r;
465 *h_errnop = NO_RECOVERY;
466 return ret;
467
468 not_found:
469 *h_errnop = HOST_NOT_FOUND;
470 return NSS_STATUS_NOTFOUND;
471 }
472
473 enum nss_status _nss_resolve_gethostbyaddr2_r(
474 const void* addr, socklen_t len,
475 int af,
476 struct hostent *result,
477 char *buffer, size_t buflen,
478 int *errnop, int *h_errnop,
479 int32_t *ttlp) {
480
481 _cleanup_(sd_bus_message_unrefp) sd_bus_message *req = NULL, *reply = NULL;
482 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
483 char *r_name, *r_aliases, *r_addr, *r_addr_list;
484 _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
485 enum nss_status ret = NSS_STATUS_UNAVAIL;
486 unsigned c = 0, i = 0;
487 size_t ms = 0, idx;
488 const char *n;
489 int r, ifindex;
490
491 PROTECT_ERRNO;
492 BLOCK_SIGNALS(NSS_SIGNALS_BLOCK);
493
494 assert(addr);
495 assert(result);
496 assert(buffer);
497 assert(errnop);
498 assert(h_errnop);
499
500 if (!IN_SET(af, AF_INET, AF_INET6)) {
501 UNPROTECT_ERRNO;
502 *errnop = EAFNOSUPPORT;
503 *h_errnop = NO_DATA;
504 return NSS_STATUS_UNAVAIL;
505 }
506
507 if (len != FAMILY_ADDRESS_SIZE(af)) {
508 UNPROTECT_ERRNO;
509 *errnop = EINVAL;
510 *h_errnop = NO_RECOVERY;
511 return NSS_STATUS_UNAVAIL;
512 }
513
514 if (avoid_deadlock()) {
515 r = -EDEADLK;
516 goto fail;
517 }
518
519 r = sd_bus_open_system(&bus);
520 if (r < 0)
521 goto fail;
522
523 r = sd_bus_message_new_method_call(
524 bus,
525 &req,
526 "org.freedesktop.resolve1",
527 "/org/freedesktop/resolve1",
528 "org.freedesktop.resolve1.Manager",
529 "ResolveAddress");
530 if (r < 0)
531 goto fail;
532
533 r = sd_bus_message_set_auto_start(req, false);
534 if (r < 0)
535 goto fail;
536
537 r = sd_bus_message_append(req, "ii", 0, af);
538 if (r < 0)
539 goto fail;
540
541 r = sd_bus_message_append_array(req, 'y', addr, len);
542 if (r < 0)
543 goto fail;
544
545 r = sd_bus_message_append(req, "t", (uint64_t) 0);
546 if (r < 0)
547 goto fail;
548
549 r = sd_bus_call(bus, req, SD_RESOLVED_QUERY_TIMEOUT_USEC, &error, &reply);
550 if (r < 0) {
551 if (sd_bus_error_has_name(&error, _BUS_ERROR_DNS "NXDOMAIN") ||
552 !bus_error_shall_fallback(&error))
553 goto not_found;
554
555 goto fail;
556 }
557
558 r = sd_bus_message_enter_container(reply, 'a', "(is)");
559 if (r < 0)
560 goto fail;
561
562 while ((r = sd_bus_message_read(reply, "(is)", &ifindex, &n)) > 0) {
563
564 if (ifindex < 0) {
565 r = -EINVAL;
566 goto fail;
567 }
568
569 c++;
570 ms += ALIGN(strlen(n) + 1);
571 }
572 if (r < 0)
573 goto fail;
574
575 r = sd_bus_message_rewind(reply, false);
576 if (r < 0)
577 return r;
578
579 if (c <= 0)
580 goto not_found;
581
582 ms += ALIGN(len) + /* the address */
583 2 * sizeof(char*) + /* pointers to the address, plus trailing NULL */
584 c * sizeof(char*); /* pointers to aliases, plus trailing NULL */
585
586 if (buflen < ms) {
587 UNPROTECT_ERRNO;
588 *errnop = ERANGE;
589 *h_errnop = NETDB_INTERNAL;
590 return NSS_STATUS_TRYAGAIN;
591 }
592
593 /* First, place address */
594 r_addr = buffer;
595 memcpy(r_addr, addr, len);
596 idx = ALIGN(len);
597
598 /* Second, place address list */
599 r_addr_list = buffer + idx;
600 ((char**) r_addr_list)[0] = r_addr;
601 ((char**) r_addr_list)[1] = NULL;
602 idx += sizeof(char*) * 2;
603
604 /* Third, reserve space for the aliases array */
605 r_aliases = buffer + idx;
606 idx += sizeof(char*) * c;
607
608 /* Fourth, place aliases */
609 i = 0;
610 r_name = buffer + idx;
611 while ((r = sd_bus_message_read(reply, "(is)", &ifindex, &n)) > 0) {
612 char *p;
613 size_t l;
614
615 l = strlen(n);
616 p = buffer + idx;
617 memcpy(p, n, l+1);
618
619 if (i > 0)
620 ((char**) r_aliases)[i-1] = p;
621 i++;
622
623 idx += ALIGN(l+1);
624 }
625 if (r < 0)
626 goto fail;
627
628 ((char**) r_aliases)[c-1] = NULL;
629 assert(idx == ms);
630
631 result->h_name = r_name;
632 result->h_aliases = (char**) r_aliases;
633 result->h_addrtype = af;
634 result->h_length = len;
635 result->h_addr_list = (char**) r_addr_list;
636
637 if (ttlp)
638 *ttlp = 0;
639
640 /* Explicitly reset both *h_errnop and h_errno to work around
641 * https://bugzilla.redhat.com/show_bug.cgi?id=1125975 */
642 *h_errnop = NETDB_SUCCESS;
643 h_errno = 0;
644
645 return NSS_STATUS_SUCCESS;
646
647 fail:
648 UNPROTECT_ERRNO;
649 *errnop = -r;
650 *h_errnop = NO_RECOVERY;
651 return ret;
652
653 not_found:
654 *h_errnop = HOST_NOT_FOUND;
655 return NSS_STATUS_NOTFOUND;
656 }
657
658 NSS_GETHOSTBYNAME_FALLBACKS(resolve);
659 NSS_GETHOSTBYADDR_FALLBACKS(resolve);