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