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