]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/nss-mymachines/nss-mymachines.c
Merge pull request #8104 from keszybz/tmpfiles-allow-overrides
[thirdparty/systemd.git] / src / nss-mymachines / nss-mymachines.c
1 /* SPDX-License-Identifier: LGPL-2.1+ */
2 /***
3 This file is part of systemd.
4
5 Copyright 2014 Lennart Poettering
6
7 systemd is free software; you can redistribute it and/or modify it
8 under the terms of the GNU Lesser General Public License as published by
9 the Free Software Foundation; either version 2.1 of the License, or
10 (at your option) any later version.
11
12 systemd is distributed in the hope that it will be useful, but
13 WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 Lesser General Public License for more details.
16
17 You should have received a copy of the GNU Lesser General Public License
18 along with systemd; If not, see <http://www.gnu.org/licenses/>.
19 ***/
20
21 #include <netdb.h>
22 #include <nss.h>
23
24 #include "sd-bus.h"
25 #include "sd-login.h"
26
27 #include "alloc-util.h"
28 #include "bus-common-errors.h"
29 #include "env-util.h"
30 #include "hostname-util.h"
31 #include "in-addr-util.h"
32 #include "macro.h"
33 #include "nss-util.h"
34 #include "signal-util.h"
35 #include "string-util.h"
36 #include "user-util.h"
37 #include "util.h"
38
39 NSS_GETHOSTBYNAME_PROTOTYPES(mymachines);
40 NSS_GETPW_PROTOTYPES(mymachines);
41 NSS_GETGR_PROTOTYPES(mymachines);
42
43 #define HOST_UID_LIMIT ((uid_t) UINT32_C(0x10000))
44 #define HOST_GID_LIMIT ((gid_t) UINT32_C(0x10000))
45
46 static int count_addresses(sd_bus_message *m, int af, unsigned *ret) {
47 unsigned c = 0;
48 int r;
49
50 assert(m);
51 assert(ret);
52
53 while ((r = sd_bus_message_enter_container(m, 'r', "iay")) > 0) {
54 int family;
55
56 r = sd_bus_message_read(m, "i", &family);
57 if (r < 0)
58 return r;
59
60 r = sd_bus_message_skip(m, "ay");
61 if (r < 0)
62 return r;
63
64 r = sd_bus_message_exit_container(m);
65 if (r < 0)
66 return r;
67
68 if (af != AF_UNSPEC && family != af)
69 continue;
70
71 c++;
72 }
73 if (r < 0)
74 return r;
75
76 r = sd_bus_message_rewind(m, false);
77 if (r < 0)
78 return r;
79
80 *ret = c;
81 return 0;
82 }
83
84 enum nss_status _nss_mymachines_gethostbyname4_r(
85 const char *name,
86 struct gaih_addrtuple **pat,
87 char *buffer, size_t buflen,
88 int *errnop, int *h_errnop,
89 int32_t *ttlp) {
90
91 struct gaih_addrtuple *r_tuple, *r_tuple_first = NULL;
92 _cleanup_(sd_bus_message_unrefp) sd_bus_message* reply = NULL;
93 _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
94 _cleanup_free_ int *ifindices = NULL;
95 _cleanup_free_ char *class = NULL;
96 size_t l, ms, idx;
97 unsigned i = 0, c = 0;
98 char *r_name;
99 int n_ifindices, r;
100
101 BLOCK_SIGNALS(NSS_SIGNALS_BLOCK);
102
103 assert(name);
104 assert(pat);
105 assert(buffer);
106 assert(errnop);
107 assert(h_errnop);
108
109 r = sd_machine_get_class(name, &class);
110 if (r < 0)
111 goto fail;
112 if (!streq(class, "container")) {
113 r = -ENOTTY;
114 goto fail;
115 }
116
117 n_ifindices = sd_machine_get_ifindices(name, &ifindices);
118 if (n_ifindices < 0) {
119 r = n_ifindices;
120 goto fail;
121 }
122
123 r = sd_bus_open_system(&bus);
124 if (r < 0)
125 goto fail;
126
127 r = sd_bus_call_method(bus,
128 "org.freedesktop.machine1",
129 "/org/freedesktop/machine1",
130 "org.freedesktop.machine1.Manager",
131 "GetMachineAddresses",
132 NULL,
133 &reply,
134 "s", name);
135 if (r < 0)
136 goto fail;
137
138 r = sd_bus_message_enter_container(reply, 'a', "(iay)");
139 if (r < 0)
140 goto fail;
141
142 r = count_addresses(reply, AF_UNSPEC, &c);
143 if (r < 0)
144 goto fail;
145
146 if (c <= 0) {
147 *errnop = ESRCH;
148 *h_errnop = HOST_NOT_FOUND;
149 return NSS_STATUS_NOTFOUND;
150 }
151
152 l = strlen(name);
153 ms = ALIGN(l+1) + ALIGN(sizeof(struct gaih_addrtuple)) * c;
154 if (buflen < ms) {
155 *errnop = ERANGE;
156 *h_errnop = NETDB_INTERNAL;
157 return NSS_STATUS_TRYAGAIN;
158 }
159
160 /* First, append name */
161 r_name = buffer;
162 memcpy(r_name, name, l+1);
163 idx = ALIGN(l+1);
164
165 /* Second, append addresses */
166 r_tuple_first = (struct gaih_addrtuple*) (buffer + idx);
167 while ((r = sd_bus_message_enter_container(reply, 'r', "iay")) > 0) {
168 int family;
169 const void *a;
170 size_t sz;
171
172 r = sd_bus_message_read(reply, "i", &family);
173 if (r < 0)
174 goto fail;
175
176 r = sd_bus_message_read_array(reply, 'y', &a, &sz);
177 if (r < 0)
178 goto fail;
179
180 r = sd_bus_message_exit_container(reply);
181 if (r < 0)
182 goto fail;
183
184 if (!IN_SET(family, AF_INET, AF_INET6)) {
185 r = -EAFNOSUPPORT;
186 goto fail;
187 }
188
189 if (sz != FAMILY_ADDRESS_SIZE(family)) {
190 r = -EINVAL;
191 goto fail;
192 }
193
194 r_tuple = (struct gaih_addrtuple*) (buffer + idx);
195 r_tuple->next = i == c-1 ? NULL : (struct gaih_addrtuple*) ((char*) r_tuple + ALIGN(sizeof(struct gaih_addrtuple)));
196 r_tuple->name = r_name;
197 r_tuple->family = family;
198 r_tuple->scopeid = n_ifindices == 1 ? ifindices[0] : 0;
199 memcpy(r_tuple->addr, a, sz);
200
201 idx += ALIGN(sizeof(struct gaih_addrtuple));
202 i++;
203 }
204
205 assert(i == c);
206
207 r = sd_bus_message_exit_container(reply);
208 if (r < 0)
209 goto fail;
210
211 assert(idx == ms);
212
213 if (*pat)
214 **pat = *r_tuple_first;
215 else
216 *pat = r_tuple_first;
217
218 if (ttlp)
219 *ttlp = 0;
220
221 /* Explicitly reset all error variables */
222 *errnop = 0;
223 *h_errnop = NETDB_SUCCESS;
224 h_errno = 0;
225
226 return NSS_STATUS_SUCCESS;
227
228 fail:
229 *errnop = -r;
230 *h_errnop = NO_DATA;
231 return NSS_STATUS_UNAVAIL;
232 }
233
234 enum nss_status _nss_mymachines_gethostbyname3_r(
235 const char *name,
236 int af,
237 struct hostent *result,
238 char *buffer, size_t buflen,
239 int *errnop, int *h_errnop,
240 int32_t *ttlp,
241 char **canonp) {
242
243 _cleanup_(sd_bus_message_unrefp) sd_bus_message* reply = NULL;
244 _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
245 _cleanup_free_ char *class = NULL;
246 unsigned c = 0, i = 0;
247 char *r_name, *r_aliases, *r_addr, *r_addr_list;
248 size_t l, idx, ms, alen;
249 int r;
250
251 BLOCK_SIGNALS(NSS_SIGNALS_BLOCK);
252
253 assert(name);
254 assert(result);
255 assert(buffer);
256 assert(errnop);
257 assert(h_errnop);
258
259 if (af == AF_UNSPEC)
260 af = AF_INET;
261
262 if (af != AF_INET && af != AF_INET6) {
263 r = -EAFNOSUPPORT;
264 goto fail;
265 }
266
267 r = sd_machine_get_class(name, &class);
268 if (r < 0)
269 goto fail;
270 if (!streq(class, "container")) {
271 r = -ENOTTY;
272 goto fail;
273 }
274
275 r = sd_bus_open_system(&bus);
276 if (r < 0)
277 goto fail;
278
279 r = sd_bus_call_method(bus,
280 "org.freedesktop.machine1",
281 "/org/freedesktop/machine1",
282 "org.freedesktop.machine1.Manager",
283 "GetMachineAddresses",
284 NULL,
285 &reply,
286 "s", name);
287 if (r < 0)
288 goto fail;
289
290 r = sd_bus_message_enter_container(reply, 'a', "(iay)");
291 if (r < 0)
292 goto fail;
293
294 r = count_addresses(reply, af, &c);
295 if (r < 0)
296 goto fail;
297
298 if (c <= 0) {
299 *errnop = ENOENT;
300 *h_errnop = HOST_NOT_FOUND;
301 return NSS_STATUS_NOTFOUND;
302 }
303
304 alen = FAMILY_ADDRESS_SIZE(af);
305 l = strlen(name);
306
307 ms = ALIGN(l+1) + c * ALIGN(alen) + (c+2) * sizeof(char*);
308
309 if (buflen < ms) {
310 *errnop = ERANGE;
311 *h_errnop = NETDB_INTERNAL;
312 return NSS_STATUS_TRYAGAIN;
313 }
314
315 /* First, append name */
316 r_name = buffer;
317 memcpy(r_name, name, l+1);
318 idx = ALIGN(l+1);
319
320 /* Second, create aliases array */
321 r_aliases = buffer + idx;
322 ((char**) r_aliases)[0] = NULL;
323 idx += sizeof(char*);
324
325 /* Third, append addresses */
326 r_addr = buffer + idx;
327 while ((r = sd_bus_message_enter_container(reply, 'r', "iay")) > 0) {
328 int family;
329 const void *a;
330 size_t sz;
331
332 r = sd_bus_message_read(reply, "i", &family);
333 if (r < 0)
334 goto fail;
335
336 r = sd_bus_message_read_array(reply, 'y', &a, &sz);
337 if (r < 0)
338 goto fail;
339
340 r = sd_bus_message_exit_container(reply);
341 if (r < 0)
342 goto fail;
343
344 if (family != af)
345 continue;
346
347 if (sz != alen) {
348 r = -EINVAL;
349 goto fail;
350 }
351
352 memcpy(r_addr + i*ALIGN(alen), a, alen);
353 i++;
354 }
355
356 assert(i == c);
357 idx += c * ALIGN(alen);
358
359 r = sd_bus_message_exit_container(reply);
360 if (r < 0)
361 goto fail;
362
363 /* Third, append address pointer array */
364 r_addr_list = buffer + idx;
365 for (i = 0; i < c; i++)
366 ((char**) r_addr_list)[i] = r_addr + i*ALIGN(alen);
367
368 ((char**) r_addr_list)[i] = NULL;
369 idx += (c+1) * sizeof(char*);
370
371 assert(idx == ms);
372
373 result->h_name = r_name;
374 result->h_aliases = (char**) r_aliases;
375 result->h_addrtype = af;
376 result->h_length = alen;
377 result->h_addr_list = (char**) r_addr_list;
378
379 if (ttlp)
380 *ttlp = 0;
381
382 if (canonp)
383 *canonp = r_name;
384
385 /* Explicitly reset all error variables */
386 *errnop = 0;
387 *h_errnop = NETDB_SUCCESS;
388 h_errno = 0;
389
390 return NSS_STATUS_SUCCESS;
391
392 fail:
393 *errnop = -r;
394 *h_errnop = NO_DATA;
395 return NSS_STATUS_UNAVAIL;
396 }
397
398 NSS_GETHOSTBYNAME_FALLBACKS(mymachines);
399
400 enum nss_status _nss_mymachines_getpwnam_r(
401 const char *name,
402 struct passwd *pwd,
403 char *buffer, size_t buflen,
404 int *errnop) {
405
406 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
407 _cleanup_(sd_bus_message_unrefp) sd_bus_message* reply = NULL;
408 _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
409 const char *p, *e, *machine;
410 uint32_t mapped;
411 uid_t uid;
412 size_t l;
413 int r;
414
415 BLOCK_SIGNALS(NSS_SIGNALS_BLOCK);
416
417 assert(name);
418 assert(pwd);
419
420 p = startswith(name, "vu-");
421 if (!p)
422 goto not_found;
423
424 e = strrchr(p, '-');
425 if (!e || e == p)
426 goto not_found;
427
428 if (e - p > HOST_NAME_MAX - 1) /* -1 for the last dash */
429 goto not_found;
430
431 r = parse_uid(e + 1, &uid);
432 if (r < 0)
433 goto not_found;
434
435 machine = strndupa(p, e - p);
436 if (!machine_name_is_valid(machine))
437 goto not_found;
438
439 if (getenv_bool_secure("SYSTEMD_NSS_BYPASS_BUS") > 0)
440 /* Make sure we can't deadlock if we are invoked by dbus-daemon. This way, it won't be able to resolve
441 * these UIDs, but that should be unproblematic as containers should never be able to connect to a bus
442 * running on the host. */
443 goto not_found;
444
445 r = sd_bus_open_system(&bus);
446 if (r < 0)
447 goto fail;
448
449 r = sd_bus_call_method(bus,
450 "org.freedesktop.machine1",
451 "/org/freedesktop/machine1",
452 "org.freedesktop.machine1.Manager",
453 "MapFromMachineUser",
454 &error,
455 &reply,
456 "su",
457 machine, (uint32_t) uid);
458 if (r < 0) {
459 if (sd_bus_error_has_name(&error, BUS_ERROR_NO_SUCH_USER_MAPPING))
460 goto not_found;
461
462 goto fail;
463 }
464
465 r = sd_bus_message_read(reply, "u", &mapped);
466 if (r < 0)
467 goto fail;
468
469 /* Refuse to work if the mapped address is in the host UID range, or if there was no mapping at all. */
470 if (mapped < HOST_UID_LIMIT || mapped == uid)
471 goto not_found;
472
473 l = strlen(name);
474 if (buflen < l+1) {
475 *errnop = ERANGE;
476 return NSS_STATUS_TRYAGAIN;
477 }
478
479 memcpy(buffer, name, l+1);
480
481 pwd->pw_name = buffer;
482 pwd->pw_uid = mapped;
483 pwd->pw_gid = GID_NOBODY;
484 pwd->pw_gecos = buffer;
485 pwd->pw_passwd = (char*) "*"; /* locked */
486 pwd->pw_dir = (char*) "/";
487 pwd->pw_shell = (char*) "/sbin/nologin";
488
489 *errnop = 0;
490 return NSS_STATUS_SUCCESS;
491
492 not_found:
493 *errnop = 0;
494 return NSS_STATUS_NOTFOUND;
495
496 fail:
497 *errnop = -r;
498 return NSS_STATUS_UNAVAIL;
499 }
500
501 enum nss_status _nss_mymachines_getpwuid_r(
502 uid_t uid,
503 struct passwd *pwd,
504 char *buffer, size_t buflen,
505 int *errnop) {
506
507 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
508 _cleanup_(sd_bus_message_unrefp) sd_bus_message* reply = NULL;
509 _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
510 const char *machine, *object;
511 uint32_t mapped;
512 int r;
513
514 BLOCK_SIGNALS(NSS_SIGNALS_BLOCK);
515
516 if (!uid_is_valid(uid))
517 goto not_found;
518
519 /* We consider all uids < 65536 host uids */
520 if (uid < HOST_UID_LIMIT)
521 goto not_found;
522
523 if (getenv_bool_secure("SYSTEMD_NSS_BYPASS_BUS") > 0)
524 goto not_found;
525
526 r = sd_bus_open_system(&bus);
527 if (r < 0)
528 goto fail;
529
530 r = sd_bus_call_method(bus,
531 "org.freedesktop.machine1",
532 "/org/freedesktop/machine1",
533 "org.freedesktop.machine1.Manager",
534 "MapToMachineUser",
535 &error,
536 &reply,
537 "u",
538 (uint32_t) uid);
539 if (r < 0) {
540 if (sd_bus_error_has_name(&error, BUS_ERROR_NO_SUCH_USER_MAPPING))
541 goto not_found;
542
543 goto fail;
544 }
545
546 r = sd_bus_message_read(reply, "sou", &machine, &object, &mapped);
547 if (r < 0)
548 goto fail;
549
550 if (mapped == uid)
551 goto not_found;
552
553 if (snprintf(buffer, buflen, "vu-%s-" UID_FMT, machine, (uid_t) mapped) >= (int) buflen) {
554 *errnop = ERANGE;
555 return NSS_STATUS_TRYAGAIN;
556 }
557
558 pwd->pw_name = buffer;
559 pwd->pw_uid = uid;
560 pwd->pw_gid = GID_NOBODY;
561 pwd->pw_gecos = buffer;
562 pwd->pw_passwd = (char*) "*"; /* locked */
563 pwd->pw_dir = (char*) "/";
564 pwd->pw_shell = (char*) "/sbin/nologin";
565
566 *errnop = 0;
567 return NSS_STATUS_SUCCESS;
568
569 not_found:
570 *errnop = 0;
571 return NSS_STATUS_NOTFOUND;
572
573 fail:
574 *errnop = -r;
575 return NSS_STATUS_UNAVAIL;
576 }
577
578 #pragma GCC diagnostic ignored "-Wsizeof-pointer-memaccess"
579
580 enum nss_status _nss_mymachines_getgrnam_r(
581 const char *name,
582 struct group *gr,
583 char *buffer, size_t buflen,
584 int *errnop) {
585
586 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
587 _cleanup_(sd_bus_message_unrefp) sd_bus_message* reply = NULL;
588 _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
589 const char *p, *e, *machine;
590 uint32_t mapped;
591 uid_t gid;
592 size_t l;
593 int r;
594
595 BLOCK_SIGNALS(NSS_SIGNALS_BLOCK);
596
597 assert(name);
598 assert(gr);
599
600 p = startswith(name, "vg-");
601 if (!p)
602 goto not_found;
603
604 e = strrchr(p, '-');
605 if (!e || e == p)
606 goto not_found;
607
608 if (e - p > HOST_NAME_MAX - 1) /* -1 for the last dash */
609 goto not_found;
610
611 r = parse_gid(e + 1, &gid);
612 if (r < 0)
613 goto not_found;
614
615 machine = strndupa(p, e - p);
616 if (!machine_name_is_valid(machine))
617 goto not_found;
618
619 if (getenv_bool_secure("SYSTEMD_NSS_BYPASS_BUS") > 0)
620 goto not_found;
621
622 r = sd_bus_open_system(&bus);
623 if (r < 0)
624 goto fail;
625
626 r = sd_bus_call_method(bus,
627 "org.freedesktop.machine1",
628 "/org/freedesktop/machine1",
629 "org.freedesktop.machine1.Manager",
630 "MapFromMachineGroup",
631 &error,
632 &reply,
633 "su",
634 machine, (uint32_t) gid);
635 if (r < 0) {
636 if (sd_bus_error_has_name(&error, BUS_ERROR_NO_SUCH_GROUP_MAPPING))
637 goto not_found;
638
639 goto fail;
640 }
641
642 r = sd_bus_message_read(reply, "u", &mapped);
643 if (r < 0)
644 goto fail;
645
646 if (mapped < HOST_GID_LIMIT || mapped == gid)
647 goto not_found;
648
649 l = sizeof(char*) + strlen(name) + 1;
650 if (buflen < l) {
651 *errnop = ERANGE;
652 return NSS_STATUS_TRYAGAIN;
653 }
654
655 memzero(buffer, sizeof(char*));
656 strcpy(buffer + sizeof(char*), name);
657
658 gr->gr_name = buffer + sizeof(char*);
659 gr->gr_gid = gid;
660 gr->gr_passwd = (char*) "*"; /* locked */
661 gr->gr_mem = (char**) buffer;
662
663 *errnop = 0;
664 return NSS_STATUS_SUCCESS;
665
666 not_found:
667 *errnop = 0;
668 return NSS_STATUS_NOTFOUND;
669
670 fail:
671 *errnop = -r;
672 return NSS_STATUS_UNAVAIL;
673 }
674
675 enum nss_status _nss_mymachines_getgrgid_r(
676 gid_t gid,
677 struct group *gr,
678 char *buffer, size_t buflen,
679 int *errnop) {
680
681 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
682 _cleanup_(sd_bus_message_unrefp) sd_bus_message* reply = NULL;
683 _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
684 const char *machine, *object;
685 uint32_t mapped;
686 int r;
687
688 BLOCK_SIGNALS(NSS_SIGNALS_BLOCK);
689
690 if (!gid_is_valid(gid))
691 goto not_found;
692
693 /* We consider all gids < 65536 host gids */
694 if (gid < HOST_GID_LIMIT)
695 goto not_found;
696
697 if (getenv_bool_secure("SYSTEMD_NSS_BYPASS_BUS") > 0)
698 goto not_found;
699
700 r = sd_bus_open_system(&bus);
701 if (r < 0)
702 goto fail;
703
704 r = sd_bus_call_method(bus,
705 "org.freedesktop.machine1",
706 "/org/freedesktop/machine1",
707 "org.freedesktop.machine1.Manager",
708 "MapToMachineGroup",
709 &error,
710 &reply,
711 "u",
712 (uint32_t) gid);
713 if (r < 0) {
714 if (sd_bus_error_has_name(&error, BUS_ERROR_NO_SUCH_GROUP_MAPPING))
715 goto not_found;
716
717 goto fail;
718 }
719
720 r = sd_bus_message_read(reply, "sou", &machine, &object, &mapped);
721 if (r < 0)
722 goto fail;
723
724 if (mapped == gid)
725 goto not_found;
726
727 if (buflen < sizeof(char*) + 1) {
728 *errnop = ERANGE;
729 return NSS_STATUS_TRYAGAIN;
730 }
731
732 memzero(buffer, sizeof(char*));
733 if (snprintf(buffer + sizeof(char*), buflen - sizeof(char*), "vg-%s-" GID_FMT, machine, (gid_t) mapped) >= (int) buflen) {
734 *errnop = ERANGE;
735 return NSS_STATUS_TRYAGAIN;
736 }
737
738 gr->gr_name = buffer + sizeof(char*);
739 gr->gr_gid = gid;
740 gr->gr_passwd = (char*) "*"; /* locked */
741 gr->gr_mem = (char**) buffer;
742
743 *errnop = 0;
744 return NSS_STATUS_SUCCESS;
745
746 not_found:
747 *errnop = 0;
748 return NSS_STATUS_NOTFOUND;
749
750 fail:
751 *errnop = -r;
752 return NSS_STATUS_UNAVAIL;
753 }