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