chiark / gitweb /
machine: make sure unpriviliged "machinectl status" can show the machine's OS version
[elogind.git] / src / nss-resolve / nss-resolve.c
1 /*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
2
3 /***
4   This file is part of systemd.
5
6   Copyright 2014 Lennart Poettering
7
8   systemd is free software; you can redistribute it and/or modify it
9   under the terms of the GNU Lesser General Public License as published by
10   the Free Software Foundation; either version 2.1 of the License, or
11   (at your option) any later version.
12
13   systemd is distributed in the hope that it will be useful, but
14   WITHOUT ANY WARRANTY; without even the implied warranty of
15   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16   Lesser General Public License for more details.
17
18   You should have received a copy of the GNU Lesser General Public License
19   along with systemd; If not, see <http://www.gnu.org/licenses/>.
20 ***/
21
22 #include <limits.h>
23 #include <nss.h>
24 #include <sys/types.h>
25 #include <netdb.h>
26 #include <errno.h>
27 #include <string.h>
28 #include <unistd.h>
29 #include <net/if.h>
30 #include <stdlib.h>
31 #include <arpa/inet.h>
32 #include <dlfcn.h>
33
34 #include "sd-bus.h"
35 #include "bus-util.h"
36 #include "bus-errors.h"
37 #include "macro.h"
38 #include "nss-util.h"
39 #include "util.h"
40 #include "in-addr-util.h"
41
42 NSS_GETHOSTBYNAME_PROTOTYPES(resolve);
43 NSS_GETHOSTBYADDR_PROTOTYPES(resolve);
44
45 #define DNS_CALL_TIMEOUT_USEC (45*USEC_PER_SEC)
46
47 typedef void (*voidfunc_t)(void);
48
49 static voidfunc_t find_fallback(const char *module, const char *symbol) {
50         void *dl;
51
52         /* Try to find a fallback NSS module symbol */
53
54         dl = dlopen(module, RTLD_LAZY|RTLD_NODELETE);
55         if (!dl)
56                 return NULL;
57
58         return dlsym(dl, symbol);
59 }
60
61 static bool bus_error_shall_fallback(sd_bus_error *e) {
62         return sd_bus_error_has_name(e, SD_BUS_ERROR_SERVICE_UNKNOWN) ||
63                sd_bus_error_has_name(e, SD_BUS_ERROR_NAME_HAS_NO_OWNER) ||
64                sd_bus_error_has_name(e, SD_BUS_ERROR_NO_REPLY) ||
65                sd_bus_error_has_name(e, SD_BUS_ERROR_ACCESS_DENIED);
66 }
67
68 static int count_addresses(sd_bus_message *m, int af, const char **canonical) {
69         int c = 0, r;
70
71         assert(m);
72         assert(canonical);
73
74         r = sd_bus_message_enter_container(m, 'a', "(iayi)");
75         if (r < 0)
76                 return r;
77
78         while ((r = sd_bus_message_enter_container(m, 'r', "iayi")) > 0) {
79                 int family;
80
81                 r = sd_bus_message_read(m, "i", &family);
82                 if (r < 0)
83                         return r;
84
85                 r = sd_bus_message_skip(m, "ayi");
86                 if (r < 0)
87                         return r;
88
89                 r = sd_bus_message_exit_container(m);
90                 if (r < 0)
91                         return r;
92
93                 if (af != AF_UNSPEC && family != af)
94                         continue;
95
96                 c ++;
97         }
98         if (r < 0)
99                 return r;
100
101         r = sd_bus_message_exit_container(m);
102         if (r < 0)
103                 return r;
104
105         r = sd_bus_message_read(m, "s", canonical);
106         if (r < 0)
107                 return r;
108
109         r = sd_bus_message_rewind(m, true);
110         if (r < 0)
111                 return r;
112
113         return c;
114 }
115
116 enum nss_status _nss_resolve_gethostbyname4_r(
117                 const char *name,
118                 struct gaih_addrtuple **pat,
119                 char *buffer, size_t buflen,
120                 int *errnop, int *h_errnop,
121                 int32_t *ttlp) {
122
123         _cleanup_bus_message_unref_ sd_bus_message *req = NULL, *reply = NULL;
124         _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
125         struct gaih_addrtuple *r_tuple, *r_tuple_first = NULL;
126         _cleanup_bus_close_unref_ sd_bus *bus = NULL;
127         const char *canonical = NULL;
128         size_t l, ms, idx;
129         char *r_name;
130         int c, r, i = 0;
131
132         assert(name);
133         assert(pat);
134         assert(buffer);
135         assert(errnop);
136         assert(h_errnop);
137
138         r = sd_bus_open_system(&bus);
139         if (r < 0)
140                 goto fail;
141
142         r = sd_bus_message_new_method_call(
143                         bus,
144                         &req,
145                         "org.freedesktop.resolve1",
146                         "/org/freedesktop/resolve1",
147                         "org.freedesktop.resolve1.Manager",
148                         "ResolveHostname");
149         if (r < 0)
150                 goto fail;
151
152         r = sd_bus_message_set_auto_start(req, false);
153         if (r < 0)
154                 goto fail;
155
156         r = sd_bus_message_append(req, "si", name, AF_UNSPEC);
157         if (r < 0)
158                 goto fail;
159
160         r = sd_bus_call(bus, req, DNS_CALL_TIMEOUT_USEC, &error, &reply);
161         if (r < 0) {
162                 if (sd_bus_error_has_name(&error, _BUS_ERROR_DNS "NXDOMAIN")) {
163                         *errnop = ESRCH;
164                         *h_errnop = HOST_NOT_FOUND;
165                         return NSS_STATUS_NOTFOUND;
166                 }
167
168                 if (bus_error_shall_fallback(&error)) {
169
170                         enum nss_status (*fallback)(
171                                         const char *name,
172                                         struct gaih_addrtuple **pat,
173                                         char *buffer, size_t buflen,
174                                         int *errnop, int *h_errnop,
175                                         int32_t *ttlp);
176
177                         fallback = (enum nss_status (*)(const char *name,
178                                                         struct gaih_addrtuple **pat,
179                                                         char *buffer, size_t buflen,
180                                                         int *errnop, int *h_errnop,
181                                                         int32_t *ttlp))
182                                 find_fallback("libnss_dns.so.2", "_nss_dns_gethostbyname4_r");
183                         if (fallback)
184                                 return fallback(name, pat, buffer, buflen, errnop, h_errnop, ttlp);
185                 }
186
187                 *errnop = -r;
188                 *h_errnop = NO_RECOVERY;
189                 return NSS_STATUS_UNAVAIL;
190         }
191
192         c = count_addresses(reply, AF_UNSPEC, &canonical);
193         if (c < 0) {
194                 r = c;
195                 goto fail;
196         }
197         if (c == 0) {
198                 *errnop = ESRCH;
199                 *h_errnop = HOST_NOT_FOUND;
200                 return NSS_STATUS_NOTFOUND;
201         }
202
203         if (isempty(canonical))
204                 canonical = name;
205
206         l = strlen(canonical);
207         ms = ALIGN(l+1) + ALIGN(sizeof(struct gaih_addrtuple)) * c;
208         if (buflen < ms) {
209                 *errnop = ENOMEM;
210                 *h_errnop = TRY_AGAIN;
211                 return NSS_STATUS_TRYAGAIN;
212         }
213
214         /* First, append name */
215         r_name = buffer;
216         memcpy(r_name, canonical, l+1);
217         idx = ALIGN(l+1);
218
219         /* Second, append addresses */
220         r_tuple_first = (struct gaih_addrtuple*) (buffer + idx);
221
222         r = sd_bus_message_enter_container(reply, 'a', "(iayi)");
223         if (r < 0)
224                 goto fail;
225
226         while ((r = sd_bus_message_enter_container(reply, 'r', "iayi")) > 0) {
227                 int family, ifindex;
228                 const void *a;
229                 size_t sz;
230
231                 r = sd_bus_message_read(reply, "i", &family);
232                 if (r < 0)
233                         goto fail;
234
235                 r = sd_bus_message_read_array(reply, 'y', &a, &sz);
236                 if (r < 0)
237                         goto fail;
238
239                 r = sd_bus_message_read(reply, "i", &ifindex);
240                 if (r < 0)
241                         goto fail;
242
243                 r = sd_bus_message_exit_container(reply);
244                 if (r < 0)
245                         goto fail;
246
247                 if (!IN_SET(family, AF_INET, AF_INET6))
248                         continue;
249
250                 if (sz != FAMILY_ADDRESS_SIZE(family)) {
251                         r = -EINVAL;
252                         goto fail;
253                 }
254
255                 if (ifindex < 0) {
256                         r = -EINVAL;
257                         goto fail;
258                 }
259
260                 r_tuple = (struct gaih_addrtuple*) (buffer + idx);
261                 r_tuple->next = i == c-1 ? NULL : (struct gaih_addrtuple*) ((char*) r_tuple + ALIGN(sizeof(struct gaih_addrtuple)));
262                 r_tuple->name = r_name;
263                 r_tuple->family = family;
264                 r_tuple->scopeid = ifindex;
265                 memcpy(r_tuple->addr, a, sz);
266
267                 idx += ALIGN(sizeof(struct gaih_addrtuple));
268                 i++;
269         }
270         if (r < 0)
271                 goto fail;
272
273         assert(i == c);
274         assert(idx == ms);
275
276         if (*pat)
277                 **pat = *r_tuple_first;
278         else
279                 *pat = r_tuple_first;
280
281         if (ttlp)
282                 *ttlp = 0;
283
284         /* Explicitly reset all error variables */
285         *errnop = 0;
286         *h_errnop = NETDB_SUCCESS;
287         h_errno = 0;
288
289         return NSS_STATUS_SUCCESS;
290
291 fail:
292         *errnop = -r;
293         *h_errnop = NO_DATA;
294         return NSS_STATUS_UNAVAIL;
295 }
296
297 enum nss_status _nss_resolve_gethostbyname3_r(
298                 const char *name,
299                 int af,
300                 struct hostent *result,
301                 char *buffer, size_t buflen,
302                 int *errnop, int *h_errnop,
303                 int32_t *ttlp,
304                 char **canonp) {
305
306         _cleanup_bus_message_unref_ sd_bus_message *req = NULL, *reply = NULL;
307         _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
308         char *r_name, *r_aliases, *r_addr, *r_addr_list;
309         _cleanup_bus_close_unref_ sd_bus *bus = NULL;
310         size_t l, idx, ms, alen;
311         const char *canonical;
312         int c, r, i = 0;
313
314         assert(name);
315         assert(result);
316         assert(buffer);
317         assert(errnop);
318         assert(h_errnop);
319
320         if (af == AF_UNSPEC)
321                 af = AF_INET;
322
323         if (af != AF_INET && af != AF_INET6) {
324                 r = -EAFNOSUPPORT;
325                 goto fail;
326         }
327
328         r = sd_bus_open_system(&bus);
329         if (r < 0)
330                 goto fail;
331
332         r = sd_bus_message_new_method_call(
333                         bus,
334                         &req,
335                         "org.freedesktop.resolve1",
336                         "/org/freedesktop/resolve1",
337                         "org.freedesktop.resolve1.Manager",
338                         "ResolveHostname");
339         if (r < 0)
340                 goto fail;
341
342         r = sd_bus_message_set_auto_start(req, false);
343         if (r < 0)
344                 goto fail;
345
346         r = sd_bus_message_append(req, "si", name, af);
347         if (r < 0)
348                 goto fail;
349
350         r = sd_bus_call(bus, req, DNS_CALL_TIMEOUT_USEC, &error, &reply);
351         if (r < 0) {
352                 if (sd_bus_error_has_name(&error, _BUS_ERROR_DNS "NXDOMAIN")) {
353                         *errnop = ESRCH;
354                         *h_errnop = HOST_NOT_FOUND;
355                         return NSS_STATUS_NOTFOUND;
356                 }
357
358                 if (bus_error_shall_fallback(&error)) {
359
360                         enum nss_status (*fallback)(
361                                         const char *name,
362                                         int af,
363                                         struct hostent *result,
364                                         char *buffer, size_t buflen,
365                                         int *errnop, int *h_errnop,
366                                         int32_t *ttlp,
367                                         char **canonp);
368
369                         fallback =  (enum nss_status (*)(const char *name,
370                                                          int af,
371                                                          struct hostent *result,
372                                                          char *buffer, size_t buflen,
373                                                          int *errnop, int *h_errnop,
374                                                          int32_t *ttlp,
375                                                          char **canonp))
376                                 find_fallback("libnss_dns.so.2", "_nss_dns_gethostbyname3_r");
377                         if (fallback)
378                                 return fallback(name, af, result, buffer, buflen, errnop, h_errnop, ttlp, canonp);
379                 }
380
381                 *errnop = -r;
382                 *h_errnop = NO_RECOVERY;
383                 return NSS_STATUS_UNAVAIL;
384         }
385
386         c = count_addresses(reply, af, &canonical);
387         if (c < 0) {
388                 r = c;
389                 goto fail;
390         }
391         if (c == 0) {
392                 *errnop = ESRCH;
393                 *h_errnop = HOST_NOT_FOUND;
394                 return NSS_STATUS_NOTFOUND;
395         }
396
397         if (isempty(canonical))
398                 canonical = name;
399
400         alen = FAMILY_ADDRESS_SIZE(af);
401         l = strlen(canonical);
402
403         ms = ALIGN(l+1) +
404                 sizeof(char*) +
405                 (c > 0 ? c : 1) * ALIGN(alen) +
406                 (c > 0 ? c+1 : 2) * sizeof(char*);
407
408         if (buflen < ms) {
409                 *errnop = ENOMEM;
410                 *h_errnop = TRY_AGAIN;
411                 return NSS_STATUS_TRYAGAIN;
412         }
413
414         /* First, append name */
415         r_name = buffer;
416         memcpy(r_name, canonical, l+1);
417         idx = ALIGN(l+1);
418
419         /* Second, create empty aliases array */
420         r_aliases = buffer + idx;
421         ((char**) r_aliases)[0] = NULL;
422         idx += sizeof(char*);
423
424         /* Third, append addresses */
425         r_addr = buffer + idx;
426
427         r = sd_bus_message_enter_container(reply, 'a', "(iayi)");
428         if (r < 0)
429                 goto fail;
430
431         while ((r = sd_bus_message_enter_container(reply, 'r', "iayi")) > 0) {
432                 int family, ifindex;
433                 const void *a;
434                 size_t sz;
435
436                 r = sd_bus_message_read(reply, "i", &family);
437                 if (r < 0)
438                         goto fail;
439
440                 r = sd_bus_message_read_array(reply, 'y', &a, &sz);
441                 if (r < 0)
442                         goto fail;
443
444                 r = sd_bus_message_read(reply, "i", &ifindex);
445                 if (r < 0)
446                         goto fail;
447
448                 r = sd_bus_message_exit_container(reply);
449                 if (r < 0)
450                         goto fail;
451
452                 if (family != af)
453                         continue;
454
455                 if (sz != alen) {
456                         r = -EINVAL;
457                         goto fail;
458                 }
459
460                 if (ifindex < 0) {
461                         r = -EINVAL;
462                         goto fail;
463                 }
464
465                 memcpy(r_addr + i*ALIGN(alen), a, alen);
466                 i++;
467         }
468         if (r < 0)
469                 goto fail;
470
471         assert(i == c);
472         idx += c * ALIGN(alen);
473
474         /* Fourth, append address pointer array */
475         r_addr_list = buffer + idx;
476         for (i = 0; i < c; i++)
477                 ((char**) r_addr_list)[i] = r_addr + i*ALIGN(alen);
478
479         ((char**) r_addr_list)[i] = NULL;
480         idx += (c+1) * sizeof(char*);
481
482         assert(idx == ms);
483
484         result->h_name = r_name;
485         result->h_aliases = (char**) r_aliases;
486         result->h_addrtype = af;
487         result->h_length = alen;
488         result->h_addr_list = (char**) r_addr_list;
489
490         /* Explicitly reset all error variables */
491         *errnop = 0;
492         *h_errnop = NETDB_SUCCESS;
493         h_errno = 0;
494
495         if (ttlp)
496                 *ttlp = 0;
497
498         if (canonp)
499                 *canonp = r_name;
500
501         return NSS_STATUS_SUCCESS;
502
503 fail:
504         *errnop = -r;
505         *h_errnop = NO_DATA;
506         return NSS_STATUS_UNAVAIL;
507 }
508
509 enum nss_status _nss_resolve_gethostbyaddr2_r(
510                 const void* addr, socklen_t len,
511                 int af,
512                 struct hostent *result,
513                 char *buffer, size_t buflen,
514                 int *errnop, int *h_errnop,
515                 int32_t *ttlp) {
516
517         _cleanup_bus_message_unref_ sd_bus_message *req = NULL, *reply = NULL;
518         _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
519         char *r_name, *r_aliases, *r_addr, *r_addr_list;
520         _cleanup_bus_close_unref_ sd_bus *bus = NULL;
521         unsigned c = 0, i = 0;
522         size_t ms = 0, idx;
523         const char *n;
524         int r;
525
526         assert(addr);
527         assert(result);
528         assert(buffer);
529         assert(errnop);
530         assert(h_errnop);
531
532         if (!IN_SET(af, AF_INET, AF_INET6)) {
533                 *errnop = EAFNOSUPPORT;
534                 *h_errnop = NO_DATA;
535                 return NSS_STATUS_UNAVAIL;
536         }
537
538         if (len != FAMILY_ADDRESS_SIZE(af)) {
539                 *errnop = EINVAL;
540                 *h_errnop = NO_RECOVERY;
541                 return NSS_STATUS_UNAVAIL;
542         }
543
544         r = sd_bus_open_system(&bus);
545         if (r < 0)
546                 goto fail;
547
548         r = sd_bus_message_new_method_call(
549                         bus,
550                         &req,
551                         "org.freedesktop.resolve1",
552                         "/org/freedesktop/resolve1",
553                         "org.freedesktop.resolve1.Manager",
554                         "ResolveAddress");
555         if (r < 0)
556                 goto fail;
557
558         r = sd_bus_message_set_auto_start(req, false);
559         if (r < 0)
560                 goto fail;
561
562         r = sd_bus_message_append(req, "i", af);
563         if (r < 0)
564                 goto fail;
565
566         r = sd_bus_message_append_array(req, 'y', addr, len);
567         if (r < 0)
568                 goto fail;
569
570         r = sd_bus_message_append(req, "i", 0);
571         if (r < 0)
572                 goto fail;
573
574         r = sd_bus_call(bus, req, DNS_CALL_TIMEOUT_USEC, &error, &reply);
575         if (r < 0) {
576                 if (sd_bus_error_has_name(&error, _BUS_ERROR_DNS "NXDOMAIN")) {
577                         *errnop = ESRCH;
578                         *h_errnop = HOST_NOT_FOUND;
579                         return NSS_STATUS_NOTFOUND;
580                 }
581
582                 if (bus_error_shall_fallback(&error)) {
583
584                         enum nss_status (*fallback)(
585                                         const void* addr, socklen_t len,
586                                         int af,
587                                         struct hostent *result,
588                                         char *buffer, size_t buflen,
589                                         int *errnop, int *h_errnop,
590                                         int32_t *ttlp);
591
592                         fallback = (enum nss_status (*)(
593                                         const void* addr, socklen_t len,
594                                         int af,
595                                         struct hostent *result,
596                                         char *buffer, size_t buflen,
597                                         int *errnop, int *h_errnop,
598                                         int32_t *ttlp))
599                                 find_fallback("libnss_dns.so.2", "_nss_dns_gethostbyaddr2_r");
600
601                         if (fallback)
602                                 return fallback(addr, len, af, result, buffer, buflen, errnop, h_errnop, ttlp);
603                 }
604
605                 *errnop = -r;
606                 *h_errnop = NO_RECOVERY;
607                 return NSS_STATUS_UNAVAIL;
608         }
609
610         r = sd_bus_message_enter_container(reply, 'a', "s");
611         if (r < 0)
612                 goto fail;
613
614         while ((r = sd_bus_message_read(reply, "s", &n)) > 0) {
615                 c++;
616                 ms += ALIGN(strlen(n) + 1);
617         }
618         if (r < 0)
619                 goto fail;
620
621         r = sd_bus_message_rewind(reply, false);
622         if (r < 0)
623                 return r;
624
625         if (c <= 0) {
626                 *errnop = ESRCH;
627                 *h_errnop = HOST_NOT_FOUND;
628                 return NSS_STATUS_NOTFOUND;
629         }
630
631         ms += ALIGN(len) +              /* the address */
632               2 * sizeof(char*) +       /* pointers to the address, plus trailing NULL */
633               c * sizeof(char*);        /* pointers to aliases, plus trailing NULL */
634
635         if (buflen < ms) {
636                 *errnop = ENOMEM;
637                 *h_errnop = TRY_AGAIN;
638                 return NSS_STATUS_TRYAGAIN;
639         }
640
641         /* First, place address */
642         r_addr = buffer;
643         memcpy(r_addr, addr, len);
644         idx = ALIGN(len);
645
646         /* Second, place address list */
647         r_addr_list = buffer + idx;
648         ((char**) r_addr_list)[0] = r_addr;
649         ((char**) r_addr_list)[1] = NULL;
650         idx += sizeof(char*) * 2;
651
652         /* Third, reserve space for the aliases array */
653         r_aliases = buffer + idx;
654         idx += sizeof(char*) * c;
655
656         /* Fourth, place aliases */
657         i = 0;
658         r_name = buffer + idx;
659         while ((r = sd_bus_message_read(reply, "s", &n)) > 0) {
660                 char *p;
661                 size_t l;
662
663                 l = strlen(n);
664                 p = buffer + idx;
665                 memcpy(p, n, l+1);
666
667                 if (i > 1)
668                         ((char**) r_aliases)[i-1] = p;
669                 i++;
670
671                 idx += ALIGN(l+1);
672         }
673         if (r < 0)
674                 goto fail;
675
676         ((char**) r_aliases)[c-1] = NULL;
677         assert(idx == ms);
678
679         result->h_name = r_name;
680         result->h_aliases = (char**) r_aliases;
681         result->h_addrtype = af;
682         result->h_length = len;
683         result->h_addr_list = (char**) r_addr_list;
684
685         if (ttlp)
686                 *ttlp = 0;
687
688         /* Explicitly reset all error variables */
689         *errnop = 0;
690         *h_errnop = NETDB_SUCCESS;
691         h_errno = 0;
692
693         return NSS_STATUS_SUCCESS;
694
695 fail:
696         *errnop = -r;
697         *h_errnop = NO_DATA;
698         return NSS_STATUS_UNAVAIL;
699 }
700
701 NSS_GETHOSTBYNAME_FALLBACKS(resolve);
702 NSS_GETHOSTBYADDR_FALLBACKS(resolve);