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