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