]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/nss-mymachines/nss-mymachines.c
nss-mymachines: switch to BusLocator-oriented helpers
[thirdparty/systemd.git] / src / nss-mymachines / nss-mymachines.c
1 /* SPDX-License-Identifier: LGPL-2.1+ */
2
3 #include <netdb.h>
4 #include <nss.h>
5
6 #include "sd-bus.h"
7 #include "sd-login.h"
8
9 #include "alloc-util.h"
10 #include "bus-common-errors.h"
11 #include "bus-util.h"
12 #include "env-util.h"
13 #include "errno-util.h"
14 #include "format-util.h"
15 #include "hostname-util.h"
16 #include "in-addr-util.h"
17 #include "macro.h"
18 #include "memory-util.h"
19 #include "nss-util.h"
20 #include "signal-util.h"
21 #include "string-util.h"
22 #include "user-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 static bool avoid_deadlock(void) {
70
71 /* Check whether this lookup might have a chance of deadlocking because we are called from the service manager
72 * code activating systemd-machined.service. After all, we shouldn't synchronously do lookups to
73 * systemd-machined if we are required to finish before it can be started. This of course won't detect all
74 * possible dead locks of this kind, but it should work for the most obvious cases. */
75
76 if (geteuid() != 0) /* Ignore the env vars unless we are privileged. */
77 return false;
78
79 return streq_ptr(getenv("SYSTEMD_ACTIVATION_UNIT"), "systemd-machined.service") &&
80 streq_ptr(getenv("SYSTEMD_ACTIVATION_SCOPE"), "system");
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 PROTECT_ERRNO;
101 BLOCK_SIGNALS(NSS_SIGNALS_BLOCK);
102
103 assert(name);
104 assert(pat);
105 assert(buffer);
106 assert(errnop);
107 assert(h_errnop);
108
109 r = sd_machine_get_class(name, &class);
110 if (r < 0)
111 goto fail;
112 if (!streq(class, "container")) {
113 r = -ENOTTY;
114 goto fail;
115 }
116
117 n_ifindices = sd_machine_get_ifindices(name, &ifindices);
118 if (n_ifindices < 0) {
119 r = n_ifindices;
120 goto fail;
121 }
122
123 if (avoid_deadlock()) {
124 r = -EDEADLK;
125 goto fail;
126 }
127
128 r = sd_bus_open_system(&bus);
129 if (r < 0)
130 goto fail;
131
132 r = bus_call_method(bus, bus_machine_mgr, "GetMachineAddresses", NULL, &reply, "s", name);
133 if (r < 0)
134 goto fail;
135
136 r = sd_bus_message_enter_container(reply, 'a', "(iay)");
137 if (r < 0)
138 goto fail;
139
140 r = count_addresses(reply, AF_UNSPEC, &c);
141 if (r < 0)
142 goto fail;
143
144 if (c <= 0) {
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 UNPROTECT_ERRNO;
153 *errnop = ERANGE;
154 *h_errnop = NETDB_INTERNAL;
155 return NSS_STATUS_TRYAGAIN;
156 }
157
158 /* First, append name */
159 r_name = buffer;
160 memcpy(r_name, name, l+1);
161 idx = ALIGN(l+1);
162
163 /* Second, append addresses */
164 r_tuple_first = (struct gaih_addrtuple*) (buffer + idx);
165 while ((r = sd_bus_message_enter_container(reply, 'r', "iay")) > 0) {
166 int family;
167 const void *a;
168 size_t sz;
169
170 r = sd_bus_message_read(reply, "i", &family);
171 if (r < 0)
172 goto fail;
173
174 r = sd_bus_message_read_array(reply, 'y', &a, &sz);
175 if (r < 0)
176 goto fail;
177
178 r = sd_bus_message_exit_container(reply);
179 if (r < 0)
180 goto fail;
181
182 if (!IN_SET(family, AF_INET, AF_INET6)) {
183 r = -EAFNOSUPPORT;
184 goto fail;
185 }
186
187 if (sz != FAMILY_ADDRESS_SIZE(family)) {
188 r = -EINVAL;
189 goto fail;
190 }
191
192 r_tuple = (struct gaih_addrtuple*) (buffer + idx);
193 r_tuple->next = i == c-1 ? NULL : (struct gaih_addrtuple*) ((char*) r_tuple + ALIGN(sizeof(struct gaih_addrtuple)));
194 r_tuple->name = r_name;
195 r_tuple->family = family;
196 r_tuple->scopeid = n_ifindices == 1 ? ifindices[0] : 0;
197 memcpy(r_tuple->addr, a, sz);
198
199 idx += ALIGN(sizeof(struct gaih_addrtuple));
200 i++;
201 }
202
203 assert(i == c);
204
205 r = sd_bus_message_exit_container(reply);
206 if (r < 0)
207 goto fail;
208
209 assert(idx == ms);
210
211 if (*pat)
212 **pat = *r_tuple_first;
213 else
214 *pat = r_tuple_first;
215
216 if (ttlp)
217 *ttlp = 0;
218
219 /* Explicitly reset both *h_errnop and h_errno to work around
220 * https://bugzilla.redhat.com/show_bug.cgi?id=1125975 */
221 *h_errnop = NETDB_SUCCESS;
222 h_errno = 0;
223
224 return NSS_STATUS_SUCCESS;
225
226 fail:
227 UNPROTECT_ERRNO;
228 *errnop = -r;
229 *h_errnop = NO_RECOVERY;
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 PROTECT_ERRNO;
251 BLOCK_SIGNALS(NSS_SIGNALS_BLOCK);
252
253 assert(name);
254 assert(result);
255 assert(buffer);
256 assert(errnop);
257 assert(h_errnop);
258
259 if (af == AF_UNSPEC)
260 af = AF_INET;
261
262 if (af != AF_INET && af != AF_INET6) {
263 r = -EAFNOSUPPORT;
264 goto fail;
265 }
266
267 r = sd_machine_get_class(name, &class);
268 if (r < 0)
269 goto fail;
270 if (!streq(class, "container")) {
271 r = -ENOTTY;
272 goto fail;
273 }
274
275 if (avoid_deadlock()) {
276 r = -EDEADLK;
277 goto fail;
278 }
279
280 r = sd_bus_open_system(&bus);
281 if (r < 0)
282 goto fail;
283
284 r = bus_call_method(bus, bus_machine_mgr, "GetMachineAddresses", NULL, &reply, "s", name);
285 if (r < 0)
286 goto fail;
287
288 r = sd_bus_message_enter_container(reply, 'a', "(iay)");
289 if (r < 0)
290 goto fail;
291
292 r = count_addresses(reply, af, &c);
293 if (r < 0)
294 goto fail;
295
296 if (c <= 0) {
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 UNPROTECT_ERRNO;
308 *errnop = ERANGE;
309 *h_errnop = NETDB_INTERNAL;
310 return NSS_STATUS_TRYAGAIN;
311 }
312
313 /* First, append name */
314 r_name = buffer;
315 memcpy(r_name, name, l+1);
316 idx = ALIGN(l+1);
317
318 /* Second, create aliases array */
319 r_aliases = buffer + idx;
320 ((char**) r_aliases)[0] = NULL;
321 idx += sizeof(char*);
322
323 /* Third, append addresses */
324 r_addr = buffer + idx;
325 while ((r = sd_bus_message_enter_container(reply, 'r', "iay")) > 0) {
326 int family;
327 const void *a;
328 size_t sz;
329
330 r = sd_bus_message_read(reply, "i", &family);
331 if (r < 0)
332 goto fail;
333
334 r = sd_bus_message_read_array(reply, 'y', &a, &sz);
335 if (r < 0)
336 goto fail;
337
338 r = sd_bus_message_exit_container(reply);
339 if (r < 0)
340 goto fail;
341
342 if (family != af)
343 continue;
344
345 if (sz != alen) {
346 r = -EINVAL;
347 goto fail;
348 }
349
350 memcpy(r_addr + i*ALIGN(alen), a, alen);
351 i++;
352 }
353
354 assert(i == c);
355 idx += c * ALIGN(alen);
356
357 r = sd_bus_message_exit_container(reply);
358 if (r < 0)
359 goto fail;
360
361 /* Third, append address pointer array */
362 r_addr_list = buffer + idx;
363 for (i = 0; i < c; i++)
364 ((char**) r_addr_list)[i] = r_addr + i*ALIGN(alen);
365
366 ((char**) r_addr_list)[i] = NULL;
367 idx += (c+1) * sizeof(char*);
368
369 assert(idx == ms);
370
371 result->h_name = r_name;
372 result->h_aliases = (char**) r_aliases;
373 result->h_addrtype = af;
374 result->h_length = alen;
375 result->h_addr_list = (char**) r_addr_list;
376
377 if (ttlp)
378 *ttlp = 0;
379
380 if (canonp)
381 *canonp = r_name;
382
383 /* Explicitly reset both *h_errnop and h_errno to work around
384 * https://bugzilla.redhat.com/show_bug.cgi?id=1125975 */
385 *h_errnop = NETDB_SUCCESS;
386 h_errno = 0;
387
388 return NSS_STATUS_SUCCESS;
389
390 fail:
391 UNPROTECT_ERRNO;
392 *errnop = -r;
393 *h_errnop = NO_RECOVERY;
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 PROTECT_ERRNO;
415 BLOCK_SIGNALS(NSS_SIGNALS_BLOCK);
416
417 assert(name);
418 assert(pwd);
419
420 p = startswith(name, "vu-");
421 if (!p)
422 return NSS_STATUS_NOTFOUND;
423
424 e = strrchr(p, '-');
425 if (!e || e == p)
426 return NSS_STATUS_NOTFOUND;
427
428 if (e - p > HOST_NAME_MAX - 1) /* -1 for the last dash */
429 return NSS_STATUS_NOTFOUND;
430
431 r = parse_uid(e + 1, &uid);
432 if (r < 0)
433 return NSS_STATUS_NOTFOUND;
434
435 machine = strndupa(p, e - p);
436 if (!machine_name_is_valid(machine))
437 return NSS_STATUS_NOTFOUND;
438
439 if (getenv_bool_secure("SYSTEMD_NSS_BYPASS_BUS") > 0)
440 /* Make sure we can't deadlock if we are invoked by dbus-daemon. This way, it won't be able to resolve
441 * these UIDs, but that should be unproblematic as containers should never be able to connect to a bus
442 * running on the host. */
443 return NSS_STATUS_NOTFOUND;
444
445 if (avoid_deadlock()) {
446 r = -EDEADLK;
447 goto fail;
448 }
449
450 r = sd_bus_open_system(&bus);
451 if (r < 0)
452 goto fail;
453
454 r = bus_call_method(bus, bus_machine_mgr, "MapFromMachineUser", &error, &reply, "su", machine, (uint32_t) uid);
455 if (r < 0) {
456 if (sd_bus_error_has_name(&error, BUS_ERROR_NO_SUCH_USER_MAPPING))
457 return NSS_STATUS_NOTFOUND;
458
459 goto fail;
460 }
461
462 r = sd_bus_message_read(reply, "u", &mapped);
463 if (r < 0)
464 goto fail;
465
466 /* Refuse to work if the mapped address is in the host UID range, or if there was no mapping at all. */
467 if (mapped < HOST_UID_LIMIT || mapped == uid)
468 return NSS_STATUS_NOTFOUND;
469
470 l = strlen(name);
471 if (buflen < l+1) {
472 UNPROTECT_ERRNO;
473 *errnop = ERANGE;
474 return NSS_STATUS_TRYAGAIN;
475 }
476
477 memcpy(buffer, name, l+1);
478
479 pwd->pw_name = buffer;
480 pwd->pw_uid = mapped;
481 pwd->pw_gid = GID_NOBODY;
482 pwd->pw_gecos = buffer;
483 pwd->pw_passwd = (char*) "*"; /* locked */
484 pwd->pw_dir = (char*) "/";
485 pwd->pw_shell = (char*) NOLOGIN;
486
487 return NSS_STATUS_SUCCESS;
488
489 fail:
490 UNPROTECT_ERRNO;
491 *errnop = -r;
492 return NSS_STATUS_UNAVAIL;
493 }
494
495 enum nss_status _nss_mymachines_getpwuid_r(
496 uid_t uid,
497 struct passwd *pwd,
498 char *buffer, size_t buflen,
499 int *errnop) {
500
501 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
502 _cleanup_(sd_bus_message_unrefp) sd_bus_message* reply = NULL;
503 _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
504 const char *machine;
505 uint32_t mapped;
506 int r;
507
508 PROTECT_ERRNO;
509 BLOCK_SIGNALS(NSS_SIGNALS_BLOCK);
510
511 if (!uid_is_valid(uid))
512 return NSS_STATUS_NOTFOUND;
513
514 /* We consider all uids < 65536 host uids */
515 if (uid < HOST_UID_LIMIT)
516 return NSS_STATUS_NOTFOUND;
517
518 if (getenv_bool_secure("SYSTEMD_NSS_BYPASS_BUS") > 0)
519 return NSS_STATUS_NOTFOUND;
520
521 if (avoid_deadlock()) {
522 r = -EDEADLK;
523 goto fail;
524 }
525
526 r = sd_bus_open_system(&bus);
527 if (r < 0)
528 goto fail;
529
530 r = bus_call_method(bus, bus_machine_mgr, "MapToMachineUser", &error, &reply, "u", (uint32_t) uid);
531 if (r < 0) {
532 if (sd_bus_error_has_name(&error, BUS_ERROR_NO_SUCH_USER_MAPPING))
533 return NSS_STATUS_NOTFOUND;
534
535 goto fail;
536 }
537
538 r = sd_bus_message_read(reply, "sou", &machine, NULL, &mapped);
539 if (r < 0)
540 goto fail;
541
542 if (mapped == uid)
543 return NSS_STATUS_NOTFOUND;
544
545 if (snprintf(buffer, buflen, "vu-%s-" UID_FMT, machine, (uid_t) mapped) >= (int) buflen) {
546 UNPROTECT_ERRNO;
547 *errnop = ERANGE;
548 return NSS_STATUS_TRYAGAIN;
549 }
550
551 pwd->pw_name = buffer;
552 pwd->pw_uid = uid;
553 pwd->pw_gid = GID_NOBODY;
554 pwd->pw_gecos = buffer;
555 pwd->pw_passwd = (char*) "*"; /* locked */
556 pwd->pw_dir = (char*) "/";
557 pwd->pw_shell = (char*) NOLOGIN;
558
559 return NSS_STATUS_SUCCESS;
560
561 fail:
562 UNPROTECT_ERRNO;
563 *errnop = -r;
564 return NSS_STATUS_UNAVAIL;
565 }
566
567 #pragma GCC diagnostic ignored "-Wsizeof-pointer-memaccess"
568
569 enum nss_status _nss_mymachines_getgrnam_r(
570 const char *name,
571 struct group *gr,
572 char *buffer, size_t buflen,
573 int *errnop) {
574
575 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
576 _cleanup_(sd_bus_message_unrefp) sd_bus_message* reply = NULL;
577 _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
578 const char *p, *e, *machine;
579 uint32_t mapped;
580 uid_t gid;
581 size_t l;
582 int r;
583
584 PROTECT_ERRNO;
585 BLOCK_SIGNALS(NSS_SIGNALS_BLOCK);
586
587 assert(name);
588 assert(gr);
589
590 p = startswith(name, "vg-");
591 if (!p)
592 return NSS_STATUS_NOTFOUND;
593
594 e = strrchr(p, '-');
595 if (!e || e == p)
596 return NSS_STATUS_NOTFOUND;
597
598 if (e - p > HOST_NAME_MAX - 1) /* -1 for the last dash */
599 return NSS_STATUS_NOTFOUND;
600
601 r = parse_gid(e + 1, &gid);
602 if (r < 0)
603 return NSS_STATUS_NOTFOUND;
604
605 machine = strndupa(p, e - p);
606 if (!machine_name_is_valid(machine))
607 return NSS_STATUS_NOTFOUND;
608
609 if (getenv_bool_secure("SYSTEMD_NSS_BYPASS_BUS") > 0)
610 return NSS_STATUS_NOTFOUND;
611
612 if (avoid_deadlock()) {
613 r = -EDEADLK;
614 goto fail;
615 }
616
617 r = sd_bus_open_system(&bus);
618 if (r < 0)
619 goto fail;
620
621 r = bus_call_method(bus, bus_machine_mgr, "MapFromMachineGroup", &error, &reply, "su", machine, (uint32_t) gid);
622 if (r < 0) {
623 if (sd_bus_error_has_name(&error, BUS_ERROR_NO_SUCH_GROUP_MAPPING))
624 return NSS_STATUS_NOTFOUND;
625
626 goto fail;
627 }
628
629 r = sd_bus_message_read(reply, "u", &mapped);
630 if (r < 0)
631 goto fail;
632
633 if (mapped < HOST_GID_LIMIT || mapped == gid)
634 return NSS_STATUS_NOTFOUND;
635
636 l = sizeof(char*) + strlen(name) + 1;
637 if (buflen < l) {
638 UNPROTECT_ERRNO;
639 *errnop = ERANGE;
640 return NSS_STATUS_TRYAGAIN;
641 }
642
643 memzero(buffer, sizeof(char*));
644 strcpy(buffer + sizeof(char*), name);
645
646 gr->gr_name = buffer + sizeof(char*);
647 gr->gr_gid = mapped;
648 gr->gr_passwd = (char*) "*"; /* locked */
649 gr->gr_mem = (char**) buffer;
650
651 return NSS_STATUS_SUCCESS;
652
653 fail:
654 UNPROTECT_ERRNO;
655 *errnop = -r;
656 return NSS_STATUS_UNAVAIL;
657 }
658
659 enum nss_status _nss_mymachines_getgrgid_r(
660 gid_t gid,
661 struct group *gr,
662 char *buffer, size_t buflen,
663 int *errnop) {
664
665 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
666 _cleanup_(sd_bus_message_unrefp) sd_bus_message* reply = NULL;
667 _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
668 const char *machine;
669 uint32_t mapped;
670 int r;
671
672 PROTECT_ERRNO;
673 BLOCK_SIGNALS(NSS_SIGNALS_BLOCK);
674
675 if (!gid_is_valid(gid))
676 return NSS_STATUS_NOTFOUND;
677
678 /* We consider all gids < 65536 host gids */
679 if (gid < HOST_GID_LIMIT)
680 return NSS_STATUS_NOTFOUND;
681
682 if (getenv_bool_secure("SYSTEMD_NSS_BYPASS_BUS") > 0)
683 return NSS_STATUS_NOTFOUND;
684
685 if (avoid_deadlock()) {
686 r = -EDEADLK;
687 goto fail;
688 }
689
690 r = sd_bus_open_system(&bus);
691 if (r < 0)
692 goto fail;
693
694 r = bus_call_method(bus, bus_machine_mgr, "MapToMachineGroup", &error, &reply, "u", (uint32_t) gid);
695 if (r < 0) {
696 if (sd_bus_error_has_name(&error, BUS_ERROR_NO_SUCH_GROUP_MAPPING))
697 return NSS_STATUS_NOTFOUND;
698
699 goto fail;
700 }
701
702 r = sd_bus_message_read(reply, "sou", &machine, NULL, &mapped);
703 if (r < 0)
704 goto fail;
705
706 if (mapped == gid)
707 return NSS_STATUS_NOTFOUND;
708
709 if (buflen < sizeof(char*) + 1) {
710 UNPROTECT_ERRNO;
711 *errnop = ERANGE;
712 return NSS_STATUS_TRYAGAIN;
713 }
714
715 memzero(buffer, sizeof(char*));
716 if (snprintf(buffer + sizeof(char*), buflen - sizeof(char*), "vg-%s-" GID_FMT, machine, (gid_t) mapped) >= (int) buflen) {
717 UNPROTECT_ERRNO;
718 *errnop = ERANGE;
719 return NSS_STATUS_TRYAGAIN;
720 }
721
722 gr->gr_name = buffer + sizeof(char*);
723 gr->gr_gid = gid;
724 gr->gr_passwd = (char*) "*"; /* locked */
725 gr->gr_mem = (char**) buffer;
726
727 return NSS_STATUS_SUCCESS;
728
729 fail:
730 UNPROTECT_ERRNO;
731 *errnop = -r;
732 return NSS_STATUS_UNAVAIL;
733 }