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