]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/nss-resolve/nss-resolve.c
Merge pull request #12414 from keszybz/detect-podman
[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 sd_bus_error_has_name(e, SD_BUS_ERROR_DISCONNECTED) ||
31 sd_bus_error_has_name(e, SD_BUS_ERROR_TIMEOUT);
32 }
33
34 static int count_addresses(sd_bus_message *m, int af, const char **canonical) {
35 int c = 0, r;
36
37 assert(m);
38 assert(canonical);
39
40 r = sd_bus_message_enter_container(m, 'a', "(iiay)");
41 if (r < 0)
42 return r;
43
44 while ((r = sd_bus_message_enter_container(m, 'r', "iiay")) > 0) {
45 int family, ifindex;
46
47 assert_cc(sizeof(int32_t) == sizeof(int));
48
49 r = sd_bus_message_read(m, "ii", &ifindex, &family);
50 if (r < 0)
51 return r;
52
53 r = sd_bus_message_skip(m, "ay");
54 if (r < 0)
55 return r;
56
57 r = sd_bus_message_exit_container(m);
58 if (r < 0)
59 return r;
60
61 if (af != AF_UNSPEC && family != af)
62 continue;
63
64 c++;
65 }
66 if (r < 0)
67 return r;
68
69 r = sd_bus_message_exit_container(m);
70 if (r < 0)
71 return r;
72
73 r = sd_bus_message_read(m, "s", canonical);
74 if (r < 0)
75 return r;
76
77 r = sd_bus_message_rewind(m, true);
78 if (r < 0)
79 return r;
80
81 return c;
82 }
83
84 static uint32_t ifindex_to_scopeid(int family, const void *a, int ifindex) {
85 struct in6_addr in6;
86
87 if (family != AF_INET6)
88 return 0;
89
90 /* Some apps can't deal with the scope ID attached to non-link-local addresses. Hence, let's suppress that. */
91
92 assert(sizeof(in6) == FAMILY_ADDRESS_SIZE(AF_INET6));
93 memcpy(&in6, a, sizeof(struct in6_addr));
94
95 return IN6_IS_ADDR_LINKLOCAL(&in6) ? ifindex : 0;
96 }
97
98 static bool avoid_deadlock(void) {
99
100 /* Check whether this lookup might have a chance of deadlocking because we are called from the service manager
101 * code activating systemd-resolved.service. After all, we shouldn't synchronously do lookups to
102 * systemd-resolved if we are required to finish before it can be started. This of course won't detect all
103 * possible dead locks of this kind, but it should work for the most obvious cases. */
104
105 if (geteuid() != 0) /* Ignore the env vars unless we are privileged. */
106 return false;
107
108 return streq_ptr(getenv("SYSTEMD_ACTIVATION_UNIT"), "systemd-resolved.service") &&
109 streq_ptr(getenv("SYSTEMD_ACTIVATION_SCOPE"), "system");
110 }
111
112 enum nss_status _nss_resolve_gethostbyname4_r(
113 const char *name,
114 struct gaih_addrtuple **pat,
115 char *buffer, size_t buflen,
116 int *errnop, int *h_errnop,
117 int32_t *ttlp) {
118
119 _cleanup_(sd_bus_message_unrefp) sd_bus_message *req = NULL, *reply = NULL;
120 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
121 struct gaih_addrtuple *r_tuple, *r_tuple_first = NULL;
122 _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
123 const char *canonical = NULL;
124 size_t l, ms, idx;
125 char *r_name;
126 int c, r, i = 0;
127
128 PROTECT_ERRNO;
129 BLOCK_SIGNALS(NSS_SIGNALS_BLOCK);
130
131 assert(name);
132 assert(pat);
133 assert(buffer);
134 assert(errnop);
135 assert(h_errnop);
136
137 if (avoid_deadlock()) {
138 r = -EDEADLK;
139 goto fail;
140 }
141
142 r = sd_bus_open_system(&bus);
143 if (r < 0)
144 goto fail;
145
146 r = sd_bus_message_new_method_call(
147 bus,
148 &req,
149 "org.freedesktop.resolve1",
150 "/org/freedesktop/resolve1",
151 "org.freedesktop.resolve1.Manager",
152 "ResolveHostname");
153 if (r < 0)
154 goto fail;
155
156 r = sd_bus_message_set_auto_start(req, false);
157 if (r < 0)
158 goto fail;
159
160 r = sd_bus_message_append(req, "isit", 0, name, AF_UNSPEC, (uint64_t) 0);
161 if (r < 0)
162 goto fail;
163
164 r = sd_bus_call(bus, req, SD_RESOLVED_QUERY_TIMEOUT_USEC, &error, &reply);
165 if (r < 0) {
166 if (!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 distinguish 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 NSS_STATUS_UNAVAIL;
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 size_t l, idx, ms, alen;
297 const char *canonical;
298 int c, r, i = 0;
299
300 PROTECT_ERRNO;
301 BLOCK_SIGNALS(NSS_SIGNALS_BLOCK);
302
303 assert(name);
304 assert(result);
305 assert(buffer);
306 assert(errnop);
307 assert(h_errnop);
308
309 if (af == AF_UNSPEC)
310 af = AF_INET;
311
312 if (!IN_SET(af, AF_INET, AF_INET6)) {
313 r = -EAFNOSUPPORT;
314 goto fail;
315 }
316
317 if (avoid_deadlock()) {
318 r = -EDEADLK;
319 goto fail;
320 }
321
322 r = sd_bus_open_system(&bus);
323 if (r < 0)
324 goto fail;
325
326 r = sd_bus_message_new_method_call(
327 bus,
328 &req,
329 "org.freedesktop.resolve1",
330 "/org/freedesktop/resolve1",
331 "org.freedesktop.resolve1.Manager",
332 "ResolveHostname");
333 if (r < 0)
334 goto fail;
335
336 r = sd_bus_message_set_auto_start(req, false);
337 if (r < 0)
338 goto fail;
339
340 r = sd_bus_message_append(req, "isit", 0, name, af, (uint64_t) 0);
341 if (r < 0)
342 goto fail;
343
344 r = sd_bus_call(bus, req, SD_RESOLVED_QUERY_TIMEOUT_USEC, &error, &reply);
345 if (r < 0) {
346 if (!bus_error_shall_fallback(&error))
347 goto not_found;
348
349 goto fail;
350 }
351
352 c = count_addresses(reply, af, &canonical);
353 if (c < 0) {
354 r = c;
355 goto fail;
356 }
357 if (c == 0)
358 goto not_found;
359
360 if (isempty(canonical))
361 canonical = name;
362
363 alen = FAMILY_ADDRESS_SIZE(af);
364 l = strlen(canonical);
365
366 ms = ALIGN(l+1) + c * ALIGN(alen) + (c+2) * sizeof(char*);
367
368 if (buflen < ms) {
369 UNPROTECT_ERRNO;
370 *errnop = ERANGE;
371 *h_errnop = NETDB_INTERNAL;
372 return NSS_STATUS_TRYAGAIN;
373 }
374
375 /* First, append name */
376 r_name = buffer;
377 memcpy(r_name, canonical, l+1);
378 idx = ALIGN(l+1);
379
380 /* Second, create empty aliases array */
381 r_aliases = buffer + idx;
382 ((char**) r_aliases)[0] = NULL;
383 idx += sizeof(char*);
384
385 /* Third, append addresses */
386 r_addr = buffer + idx;
387
388 r = sd_bus_message_enter_container(reply, 'a', "(iiay)");
389 if (r < 0)
390 goto fail;
391
392 while ((r = sd_bus_message_enter_container(reply, 'r', "iiay")) > 0) {
393 int ifindex, family;
394 const void *a;
395 size_t sz;
396
397 r = sd_bus_message_read(reply, "ii", &ifindex, &family);
398 if (r < 0)
399 goto fail;
400
401 if (ifindex < 0) {
402 r = -EINVAL;
403 goto fail;
404 }
405
406 r = sd_bus_message_read_array(reply, 'y', &a, &sz);
407 if (r < 0)
408 goto fail;
409
410 r = sd_bus_message_exit_container(reply);
411 if (r < 0)
412 goto fail;
413
414 if (family != af)
415 continue;
416
417 if (sz != alen) {
418 r = -EINVAL;
419 goto fail;
420 }
421
422 memcpy(r_addr + i*ALIGN(alen), a, alen);
423 i++;
424 }
425 if (r < 0)
426 goto fail;
427
428 assert(i == c);
429 idx += c * ALIGN(alen);
430
431 /* Fourth, append address pointer array */
432 r_addr_list = buffer + idx;
433 for (i = 0; i < c; i++)
434 ((char**) r_addr_list)[i] = r_addr + i*ALIGN(alen);
435
436 ((char**) r_addr_list)[i] = NULL;
437 idx += (c+1) * sizeof(char*);
438
439 assert(idx == ms);
440
441 result->h_name = r_name;
442 result->h_aliases = (char**) r_aliases;
443 result->h_addrtype = af;
444 result->h_length = alen;
445 result->h_addr_list = (char**) r_addr_list;
446
447 if (ttlp)
448 *ttlp = 0;
449
450 if (canonp)
451 *canonp = r_name;
452
453 /* Explicitly reset both *h_errnop and h_errno to work around
454 * https://bugzilla.redhat.com/show_bug.cgi?id=1125975 */
455 *h_errnop = NETDB_SUCCESS;
456 h_errno = 0;
457
458 return NSS_STATUS_SUCCESS;
459
460 fail:
461 UNPROTECT_ERRNO;
462 *errnop = -r;
463 *h_errnop = NO_RECOVERY;
464 return NSS_STATUS_UNAVAIL;
465
466 not_found:
467 *h_errnop = HOST_NOT_FOUND;
468 return NSS_STATUS_NOTFOUND;
469 }
470
471 enum nss_status _nss_resolve_gethostbyaddr2_r(
472 const void* addr, socklen_t len,
473 int af,
474 struct hostent *result,
475 char *buffer, size_t buflen,
476 int *errnop, int *h_errnop,
477 int32_t *ttlp) {
478
479 _cleanup_(sd_bus_message_unrefp) sd_bus_message *req = NULL, *reply = NULL;
480 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
481 char *r_name, *r_aliases, *r_addr, *r_addr_list;
482 _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
483 unsigned c = 0, i = 0;
484 size_t ms = 0, idx;
485 const char *n;
486 int r, ifindex;
487
488 PROTECT_ERRNO;
489 BLOCK_SIGNALS(NSS_SIGNALS_BLOCK);
490
491 assert(addr);
492 assert(result);
493 assert(buffer);
494 assert(errnop);
495 assert(h_errnop);
496
497 if (!IN_SET(af, AF_INET, AF_INET6)) {
498 UNPROTECT_ERRNO;
499 *errnop = EAFNOSUPPORT;
500 *h_errnop = NO_DATA;
501 return NSS_STATUS_UNAVAIL;
502 }
503
504 if (len != FAMILY_ADDRESS_SIZE(af)) {
505 r = -EINVAL;
506 goto fail;
507 }
508
509 if (avoid_deadlock()) {
510 r = -EDEADLK;
511 goto fail;
512 }
513
514 r = sd_bus_open_system(&bus);
515 if (r < 0)
516 goto fail;
517
518 r = sd_bus_message_new_method_call(
519 bus,
520 &req,
521 "org.freedesktop.resolve1",
522 "/org/freedesktop/resolve1",
523 "org.freedesktop.resolve1.Manager",
524 "ResolveAddress");
525 if (r < 0)
526 goto fail;
527
528 r = sd_bus_message_set_auto_start(req, false);
529 if (r < 0)
530 goto fail;
531
532 r = sd_bus_message_append(req, "ii", 0, af);
533 if (r < 0)
534 goto fail;
535
536 r = sd_bus_message_append_array(req, 'y', addr, len);
537 if (r < 0)
538 goto fail;
539
540 r = sd_bus_message_append(req, "t", (uint64_t) 0);
541 if (r < 0)
542 goto fail;
543
544 r = sd_bus_call(bus, req, SD_RESOLVED_QUERY_TIMEOUT_USEC, &error, &reply);
545 if (r < 0) {
546 if (!bus_error_shall_fallback(&error))
547 goto not_found;
548
549 goto fail;
550 }
551
552 r = sd_bus_message_enter_container(reply, 'a', "(is)");
553 if (r < 0)
554 goto fail;
555
556 while ((r = sd_bus_message_read(reply, "(is)", &ifindex, &n)) > 0) {
557
558 if (ifindex < 0) {
559 r = -EINVAL;
560 goto fail;
561 }
562
563 c++;
564 ms += ALIGN(strlen(n) + 1);
565 }
566 if (r < 0)
567 goto fail;
568
569 r = sd_bus_message_rewind(reply, false);
570 if (r < 0)
571 goto fail;
572
573 if (c <= 0)
574 goto not_found;
575
576 ms += ALIGN(len) + /* the address */
577 2 * sizeof(char*) + /* pointers to the address, plus trailing NULL */
578 c * sizeof(char*); /* pointers to aliases, plus trailing NULL */
579
580 if (buflen < ms) {
581 UNPROTECT_ERRNO;
582 *errnop = ERANGE;
583 *h_errnop = NETDB_INTERNAL;
584 return NSS_STATUS_TRYAGAIN;
585 }
586
587 /* First, place address */
588 r_addr = buffer;
589 memcpy(r_addr, addr, len);
590 idx = ALIGN(len);
591
592 /* Second, place address list */
593 r_addr_list = buffer + idx;
594 ((char**) r_addr_list)[0] = r_addr;
595 ((char**) r_addr_list)[1] = NULL;
596 idx += sizeof(char*) * 2;
597
598 /* Third, reserve space for the aliases array */
599 r_aliases = buffer + idx;
600 idx += sizeof(char*) * c;
601
602 /* Fourth, place aliases */
603 i = 0;
604 r_name = buffer + idx;
605 while ((r = sd_bus_message_read(reply, "(is)", &ifindex, &n)) > 0) {
606 char *p;
607 size_t l;
608
609 l = strlen(n);
610 p = buffer + idx;
611 memcpy(p, n, l+1);
612
613 if (i > 0)
614 ((char**) r_aliases)[i-1] = p;
615 i++;
616
617 idx += ALIGN(l+1);
618 }
619 if (r < 0)
620 goto fail;
621
622 ((char**) r_aliases)[c-1] = NULL;
623 assert(idx == ms);
624
625 result->h_name = r_name;
626 result->h_aliases = (char**) r_aliases;
627 result->h_addrtype = af;
628 result->h_length = len;
629 result->h_addr_list = (char**) r_addr_list;
630
631 if (ttlp)
632 *ttlp = 0;
633
634 /* Explicitly reset both *h_errnop and h_errno to work around
635 * https://bugzilla.redhat.com/show_bug.cgi?id=1125975 */
636 *h_errnop = NETDB_SUCCESS;
637 h_errno = 0;
638
639 return NSS_STATUS_SUCCESS;
640
641 fail:
642 UNPROTECT_ERRNO;
643 *errnop = -r;
644 *h_errnop = NO_RECOVERY;
645 return NSS_STATUS_UNAVAIL;
646
647 not_found:
648 *h_errnop = HOST_NOT_FOUND;
649 return NSS_STATUS_NOTFOUND;
650 }
651
652 NSS_GETHOSTBYNAME_FALLBACKS(resolve);
653 NSS_GETHOSTBYADDR_FALLBACKS(resolve);