1/*- 2 * Copyright (c) 2003 Networks Associates Technology, Inc. 3 * All rights reserved. 4 * 5 * This software was developed for the FreeBSD Project by 6 * Jacques A. Vidrine, Safeport Network Services, and Network 7 * Associates Laboratories, the Security Research Division of Network 8 * Associates, Inc. under DARPA/SPAWAR contract N66001-01-C-8035 9 * ("CBOSS"), as part of the DARPA CHATS research program. 10 * 11 * Redistribution and use in source and binary forms, with or without 12 * modification, are permitted provided that the following conditions 13 * are met: 14 * 1. Redistributions of source code must retain the above copyright 15 * notice, this list of conditions and the following disclaimer. 16 * 2. Redistributions in binary form must reproduce the above copyright 17 * notice, this list of conditions and the following disclaimer in the 18 * documentation and/or other materials provided with the distribution. 19 * 20 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 21 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 22 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 23 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 24 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 25 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 26 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 27 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 28 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 29 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 30 * SUCH DAMAGE. 31 * 32 */ 33#include <sys/cdefs.h> 34__FBSDID("$FreeBSD: stable/10/lib/libc/gen/getgrent.c 328943 2018-02-06 19:09:49Z mckusick $"); 35 36#include "namespace.h" 37#include <sys/param.h> 38#ifdef YP 39#include <rpc/rpc.h> 40#include <rpcsvc/yp_prot.h> 41#include <rpcsvc/ypclnt.h> 42#endif 43#include <assert.h> 44#include <ctype.h> 45#include <errno.h> 46#ifdef HESIOD 47#include <hesiod.h> 48#endif 49#include <grp.h> 50#include <nsswitch.h> 51#include <pthread.h> 52#include <pthread_np.h> 53#include <stdio.h> 54#include <stdlib.h> 55#include <string.h> 56#include <syslog.h> 57#include <unistd.h> 58#include "un-namespace.h" 59#include "libc_private.h" 60#include "nss_tls.h" 61#ifdef NS_CACHING 62#include "nscache.h" 63#endif 64 65enum constants { 66 GRP_STORAGE_INITIAL = 1 << 10, /* 1 KByte */ 67 GRP_STORAGE_MAX = 1 << 20, /* 1 MByte */ 68 SETGRENT = 1, 69 ENDGRENT = 2, 70 HESIOD_NAME_MAX = 256, 71}; 72 73static const ns_src defaultsrc[] = { 74 { NSSRC_COMPAT, NS_SUCCESS }, 75 { NULL, 0 } 76}; 77 78int __gr_match_entry(const char *, size_t, enum nss_lookup_type, 79 const char *, gid_t); 80int __gr_parse_entry(char *, size_t, struct group *, char *, size_t, 81 int *); 82 83static int is_comment_line(const char *, size_t); 84 85union key { 86 const char *name; 87 gid_t gid; 88}; 89static struct group *getgr(int (*)(union key, struct group *, char *, size_t, 90 struct group **), union key); 91static int wrap_getgrnam_r(union key, struct group *, char *, size_t, 92 struct group **); 93static int wrap_getgrgid_r(union key, struct group *, char *, size_t, 94 struct group **); 95static int wrap_getgrent_r(union key, struct group *, char *, size_t, 96 struct group **); 97 98struct files_state { 99 FILE *fp; 100 int stayopen; 101}; 102static void files_endstate(void *); 103NSS_TLS_HANDLING(files); 104static int files_setgrent(void *, void *, va_list); 105static int files_group(void *, void *, va_list); 106 107 108#ifdef HESIOD 109struct dns_state { 110 long counter; 111}; 112static void dns_endstate(void *); 113NSS_TLS_HANDLING(dns); 114static int dns_setgrent(void *, void *, va_list); 115static int dns_group(void *, void *, va_list); 116#endif 117 118 119#ifdef YP 120struct nis_state { 121 char domain[MAXHOSTNAMELEN]; 122 int done; 123 char *key; 124 int keylen; 125}; 126static void nis_endstate(void *); 127NSS_TLS_HANDLING(nis); 128static int nis_setgrent(void *, void *, va_list); 129static int nis_group(void *, void *, va_list); 130#endif 131 132struct compat_state { 133 FILE *fp; 134 int stayopen; 135 char *name; 136 enum _compat { 137 COMPAT_MODE_OFF = 0, 138 COMPAT_MODE_ALL, 139 COMPAT_MODE_NAME 140 } compat; 141}; 142static void compat_endstate(void *); 143NSS_TLS_HANDLING(compat); 144static int compat_setgrent(void *, void *, va_list); 145static int compat_group(void *, void *, va_list); 146 147static int gr_addgid(gid_t, gid_t *, int, int *); 148static int getgroupmembership_fallback(void *, void *, va_list); 149 150#ifdef NS_CACHING 151static int grp_id_func(char *, size_t *, va_list, void *); 152static int grp_marshal_func(char *, size_t *, void *, va_list, void *); 153static int grp_unmarshal_func(char *, size_t, void *, va_list, void *); 154 155static int 156grp_id_func(char *buffer, size_t *buffer_size, va_list ap, void *cache_mdata) 157{ 158 char *name; 159 gid_t gid; 160 161 size_t desired_size, size; 162 int res = NS_UNAVAIL; 163 enum nss_lookup_type lookup_type; 164 165 166 lookup_type = (enum nss_lookup_type)cache_mdata; 167 switch (lookup_type) { 168 case nss_lt_name: 169 name = va_arg(ap, char *); 170 size = strlen(name); 171 desired_size = sizeof(enum nss_lookup_type) + size + 1; 172 if (desired_size > *buffer_size) { 173 res = NS_RETURN; 174 goto fin; 175 } 176 177 memcpy(buffer, &lookup_type, sizeof(enum nss_lookup_type)); 178 memcpy(buffer + sizeof(enum nss_lookup_type), name, size + 1); 179 180 res = NS_SUCCESS; 181 break; 182 case nss_lt_id: 183 gid = va_arg(ap, gid_t); 184 desired_size = sizeof(enum nss_lookup_type) + sizeof(gid_t); 185 if (desired_size > *buffer_size) { 186 res = NS_RETURN; 187 goto fin; 188 } 189 190 memcpy(buffer, &lookup_type, sizeof(enum nss_lookup_type)); 191 memcpy(buffer + sizeof(enum nss_lookup_type), &gid, 192 sizeof(gid_t)); 193 194 res = NS_SUCCESS; 195 break; 196 default: 197 /* should be unreachable */ 198 return (NS_UNAVAIL); 199 } 200 201fin: 202 *buffer_size = desired_size; 203 return (res); 204} 205 206static int 207grp_marshal_func(char *buffer, size_t *buffer_size, void *retval, va_list ap, 208 void *cache_mdata) 209{ 210 char *name; 211 gid_t gid; 212 struct group *grp; 213 char *orig_buf; 214 size_t orig_buf_size; 215 216 struct group new_grp; 217 size_t desired_size, size, mem_size; 218 char *p, **mem; 219 220 switch ((enum nss_lookup_type)cache_mdata) { 221 case nss_lt_name: 222 name = va_arg(ap, char *); 223 break; 224 case nss_lt_id: 225 gid = va_arg(ap, gid_t); 226 break; 227 case nss_lt_all: 228 break; 229 default: 230 /* should be unreachable */ 231 return (NS_UNAVAIL); 232 } 233 234 grp = va_arg(ap, struct group *); 235 orig_buf = va_arg(ap, char *); 236 orig_buf_size = va_arg(ap, size_t); 237 238 desired_size = _ALIGNBYTES + sizeof(struct group) + sizeof(char *); 239 240 if (grp->gr_name != NULL) 241 desired_size += strlen(grp->gr_name) + 1; 242 if (grp->gr_passwd != NULL) 243 desired_size += strlen(grp->gr_passwd) + 1; 244 245 if (grp->gr_mem != NULL) { 246 mem_size = 0; 247 for (mem = grp->gr_mem; *mem; ++mem) { 248 desired_size += strlen(*mem) + 1; 249 ++mem_size; 250 } 251 252 desired_size += _ALIGNBYTES + (mem_size + 1) * sizeof(char *); 253 } 254 255 if (desired_size > *buffer_size) { 256 /* this assignment is here for future use */ 257 *buffer_size = desired_size; 258 return (NS_RETURN); 259 } 260 261 memcpy(&new_grp, grp, sizeof(struct group)); 262 memset(buffer, 0, desired_size); 263 264 *buffer_size = desired_size; 265 p = buffer + sizeof(struct group) + sizeof(char *); 266 memcpy(buffer + sizeof(struct group), &p, sizeof(char *)); 267 p = (char *)_ALIGN(p); 268 269 if (new_grp.gr_name != NULL) { 270 size = strlen(new_grp.gr_name); 271 memcpy(p, new_grp.gr_name, size); 272 new_grp.gr_name = p; 273 p += size + 1; 274 } 275 276 if (new_grp.gr_passwd != NULL) { 277 size = strlen(new_grp.gr_passwd); 278 memcpy(p, new_grp.gr_passwd, size); 279 new_grp.gr_passwd = p; 280 p += size + 1; 281 } 282 283 if (new_grp.gr_mem != NULL) { 284 p = (char *)_ALIGN(p); 285 memcpy(p, new_grp.gr_mem, sizeof(char *) * mem_size); 286 new_grp.gr_mem = (char **)p; 287 p += sizeof(char *) * (mem_size + 1); 288 289 for (mem = new_grp.gr_mem; *mem; ++mem) { 290 size = strlen(*mem); 291 memcpy(p, *mem, size); 292 *mem = p; 293 p += size + 1; 294 } 295 } 296 297 memcpy(buffer, &new_grp, sizeof(struct group)); 298 return (NS_SUCCESS); 299} 300 301static int 302grp_unmarshal_func(char *buffer, size_t buffer_size, void *retval, va_list ap, 303 void *cache_mdata) 304{ 305 char *name; 306 gid_t gid; 307 struct group *grp; 308 char *orig_buf; 309 size_t orig_buf_size; 310 int *ret_errno; 311 312 char *p; 313 char **mem; 314 315 switch ((enum nss_lookup_type)cache_mdata) { 316 case nss_lt_name: 317 name = va_arg(ap, char *); 318 break; 319 case nss_lt_id: 320 gid = va_arg(ap, gid_t); 321 break; 322 case nss_lt_all: 323 break; 324 default: 325 /* should be unreachable */ 326 return (NS_UNAVAIL); 327 } 328 329 grp = va_arg(ap, struct group *); 330 orig_buf = va_arg(ap, char *); 331 orig_buf_size = va_arg(ap, size_t); 332 ret_errno = va_arg(ap, int *); 333 334 if (orig_buf_size < 335 buffer_size - sizeof(struct group) - sizeof(char *)) { 336 *ret_errno = ERANGE; 337 return (NS_RETURN); 338 } 339 340 memcpy(grp, buffer, sizeof(struct group)); 341 memcpy(&p, buffer + sizeof(struct group), sizeof(char *)); 342 343 orig_buf = (char *)_ALIGN(orig_buf); 344 memcpy(orig_buf, buffer + sizeof(struct group) + sizeof(char *) + 345 _ALIGN(p) - (size_t)p, 346 buffer_size - sizeof(struct group) - sizeof(char *) - 347 _ALIGN(p) + (size_t)p); 348 p = (char *)_ALIGN(p); 349 350 NS_APPLY_OFFSET(grp->gr_name, orig_buf, p, char *); 351 NS_APPLY_OFFSET(grp->gr_passwd, orig_buf, p, char *); 352 if (grp->gr_mem != NULL) { 353 NS_APPLY_OFFSET(grp->gr_mem, orig_buf, p, char **); 354 355 for (mem = grp->gr_mem; *mem; ++mem) 356 NS_APPLY_OFFSET(*mem, orig_buf, p, char *); 357 } 358 359 if (retval != NULL) 360 *((struct group **)retval) = grp; 361 362 return (NS_SUCCESS); 363} 364 365NSS_MP_CACHE_HANDLING(group); 366#endif /* NS_CACHING */ 367 368#ifdef NS_CACHING 369static const nss_cache_info setgrent_cache_info = NS_MP_CACHE_INFO_INITIALIZER( 370 group, (void *)nss_lt_all, 371 NULL, NULL); 372#endif 373 374static const ns_dtab setgrent_dtab[] = { 375 { NSSRC_FILES, files_setgrent, (void *)SETGRENT }, 376#ifdef HESIOD 377 { NSSRC_DNS, dns_setgrent, (void *)SETGRENT }, 378#endif 379#ifdef YP 380 { NSSRC_NIS, nis_setgrent, (void *)SETGRENT }, 381#endif 382 { NSSRC_COMPAT, compat_setgrent, (void *)SETGRENT }, 383#ifdef NS_CACHING 384 NS_CACHE_CB(&setgrent_cache_info) 385#endif 386 { NULL, NULL, NULL } 387}; 388 389#ifdef NS_CACHING 390static const nss_cache_info endgrent_cache_info = NS_MP_CACHE_INFO_INITIALIZER( 391 group, (void *)nss_lt_all, 392 NULL, NULL); 393#endif 394 395static const ns_dtab endgrent_dtab[] = { 396 { NSSRC_FILES, files_setgrent, (void *)ENDGRENT }, 397#ifdef HESIOD 398 { NSSRC_DNS, dns_setgrent, (void *)ENDGRENT }, 399#endif 400#ifdef YP 401 { NSSRC_NIS, nis_setgrent, (void *)ENDGRENT }, 402#endif 403 { NSSRC_COMPAT, compat_setgrent, (void *)ENDGRENT }, 404#ifdef NS_CACHING 405 NS_CACHE_CB(&endgrent_cache_info) 406#endif 407 { NULL, NULL, NULL } 408}; 409 410#ifdef NS_CACHING 411static const nss_cache_info getgrent_r_cache_info = NS_MP_CACHE_INFO_INITIALIZER( 412 group, (void *)nss_lt_all, 413 grp_marshal_func, grp_unmarshal_func); 414#endif 415 416static const ns_dtab getgrent_r_dtab[] = { 417 { NSSRC_FILES, files_group, (void *)nss_lt_all }, 418#ifdef HESIOD 419 { NSSRC_DNS, dns_group, (void *)nss_lt_all }, 420#endif 421#ifdef YP 422 { NSSRC_NIS, nis_group, (void *)nss_lt_all }, 423#endif 424 { NSSRC_COMPAT, compat_group, (void *)nss_lt_all }, 425#ifdef NS_CACHING 426 NS_CACHE_CB(&getgrent_r_cache_info) 427#endif 428 { NULL, NULL, NULL } 429}; 430 431static int 432gr_addgid(gid_t gid, gid_t *groups, int maxgrp, int *grpcnt) 433{ 434 int ret, dupc; 435 436 for (dupc = 1; dupc < MIN(maxgrp, *grpcnt); dupc++) { 437 if (groups[dupc] == gid) 438 return 1; 439 } 440 441 ret = 1; 442 if (*grpcnt < maxgrp) 443 groups[*grpcnt] = gid; 444 else 445 ret = 0; 446 447 (*grpcnt)++; 448 449 return ret; 450} 451 452static int 453getgroupmembership_fallback(void *retval, void *mdata, va_list ap) 454{ 455 const ns_src src[] = { 456 { mdata, NS_SUCCESS }, 457 { NULL, 0} 458 }; 459 struct group grp; 460 struct group *grp_p; 461 char *buf; 462 size_t bufsize; 463 const char *uname; 464 gid_t *groups; 465 gid_t agroup; 466 int maxgrp, *grpcnt; 467 int i, rv, ret_errno; 468 469 /* 470 * As this is a fallback method, only provided src 471 * list will be respected during methods search. 472 */ 473 assert(src[0].name != NULL); 474 475 uname = va_arg(ap, const char *); 476 agroup = va_arg(ap, gid_t); 477 groups = va_arg(ap, gid_t *); 478 maxgrp = va_arg(ap, int); 479 grpcnt = va_arg(ap, int *); 480 481 rv = NS_UNAVAIL; 482 483 buf = malloc(GRP_STORAGE_INITIAL); 484 if (buf == NULL) 485 goto out; 486 487 bufsize = GRP_STORAGE_INITIAL; 488 489 gr_addgid(agroup, groups, maxgrp, grpcnt); 490 491 _nsdispatch(NULL, setgrent_dtab, NSDB_GROUP, "setgrent", src, 0); 492 for (;;) { 493 do { 494 ret_errno = 0; 495 grp_p = NULL; 496 rv = _nsdispatch(&grp_p, getgrent_r_dtab, NSDB_GROUP, 497 "getgrent_r", src, &grp, buf, bufsize, &ret_errno); 498 499 if (grp_p == NULL && ret_errno == ERANGE) { 500 free(buf); 501 if ((bufsize << 1) > GRP_STORAGE_MAX) { 502 buf = NULL; 503 errno = ERANGE; 504 goto out; 505 } 506 507 bufsize <<= 1; 508 buf = malloc(bufsize); 509 if (buf == NULL) { 510 goto out; 511 } 512 } 513 } while (grp_p == NULL && ret_errno == ERANGE); 514 515 if (ret_errno != 0) { 516 errno = ret_errno; 517 goto out; 518 } 519 520 if (grp_p == NULL) 521 break; 522 523 for (i = 0; grp.gr_mem[i]; i++) { 524 if (strcmp(grp.gr_mem[i], uname) == 0) 525 gr_addgid(grp.gr_gid, groups, maxgrp, grpcnt); 526 } 527 } 528 529 _nsdispatch(NULL, endgrent_dtab, NSDB_GROUP, "endgrent", src); 530out: 531 free(buf); 532 return (rv); 533} 534 535/* XXX IEEE Std 1003.1, 2003 specifies `void setgrent(void)' */ 536int 537setgrent(void) 538{ 539 (void)_nsdispatch(NULL, setgrent_dtab, NSDB_GROUP, "setgrent", defaultsrc, 0); 540 return (1); 541} 542 543 544int 545setgroupent(int stayopen) 546{ 547 (void)_nsdispatch(NULL, setgrent_dtab, NSDB_GROUP, "setgrent", defaultsrc, 548 stayopen); 549 return (1); 550} 551 552 553void 554endgrent(void) 555{ 556 (void)_nsdispatch(NULL, endgrent_dtab, NSDB_GROUP, "endgrent", defaultsrc); 557} 558 559 560int 561getgrent_r(struct group *grp, char *buffer, size_t bufsize, 562 struct group **result) 563{ 564 int rv, ret_errno; 565 566 ret_errno = 0; 567 *result = NULL; 568 rv = _nsdispatch(result, getgrent_r_dtab, NSDB_GROUP, "getgrent_r", defaultsrc, 569 grp, buffer, bufsize, &ret_errno); 570 if (rv == NS_SUCCESS) 571 return (0); 572 else 573 return (ret_errno); 574} 575 576 577int 578getgrnam_r(const char *name, struct group *grp, char *buffer, size_t bufsize, 579 struct group **result) 580{ 581#ifdef NS_CACHING 582 static const nss_cache_info cache_info = 583 NS_COMMON_CACHE_INFO_INITIALIZER( 584 group, (void *)nss_lt_name, 585 grp_id_func, grp_marshal_func, grp_unmarshal_func); 586#endif 587 588 static const ns_dtab dtab[] = { 589 { NSSRC_FILES, files_group, (void *)nss_lt_name }, 590#ifdef HESIOD 591 { NSSRC_DNS, dns_group, (void *)nss_lt_name }, 592#endif 593#ifdef YP 594 { NSSRC_NIS, nis_group, (void *)nss_lt_name }, 595#endif 596 { NSSRC_COMPAT, compat_group, (void *)nss_lt_name }, 597#ifdef NS_CACHING 598 NS_CACHE_CB(&cache_info) 599#endif 600 { NULL, NULL, NULL } 601 }; 602 int rv, ret_errno; 603 604 ret_errno = 0; 605 *result = NULL; 606 rv = _nsdispatch(result, dtab, NSDB_GROUP, "getgrnam_r", defaultsrc, 607 name, grp, buffer, bufsize, &ret_errno); 608 if (rv == NS_SUCCESS) 609 return (0); 610 else 611 return (ret_errno); 612} 613 614 615int 616getgrgid_r(gid_t gid, struct group *grp, char *buffer, size_t bufsize, 617 struct group **result) 618{ 619#ifdef NS_CACHING 620 static const nss_cache_info cache_info = 621 NS_COMMON_CACHE_INFO_INITIALIZER( 622 group, (void *)nss_lt_id, 623 grp_id_func, grp_marshal_func, grp_unmarshal_func); 624#endif 625 626 static const ns_dtab dtab[] = { 627 { NSSRC_FILES, files_group, (void *)nss_lt_id }, 628#ifdef HESIOD 629 { NSSRC_DNS, dns_group, (void *)nss_lt_id }, 630#endif 631#ifdef YP 632 { NSSRC_NIS, nis_group, (void *)nss_lt_id }, 633#endif 634 { NSSRC_COMPAT, compat_group, (void *)nss_lt_id }, 635#ifdef NS_CACHING 636 NS_CACHE_CB(&cache_info) 637#endif 638 { NULL, NULL, NULL } 639 }; 640 int rv, ret_errno; 641 642 ret_errno = 0; 643 *result = NULL; 644 rv = _nsdispatch(result, dtab, NSDB_GROUP, "getgrgid_r", defaultsrc, 645 gid, grp, buffer, bufsize, &ret_errno); 646 if (rv == NS_SUCCESS) 647 return (0); 648 else 649 return (ret_errno); 650} 651 652 653 654int 655__getgroupmembership(const char *uname, gid_t agroup, gid_t *groups, 656 int maxgrp, int *grpcnt) 657{ 658 static const ns_dtab dtab[] = { 659 NS_FALLBACK_CB(getgroupmembership_fallback) 660 { NULL, NULL, NULL } 661 }; 662 663 assert(uname != NULL); 664 /* groups may be NULL if just sizing when invoked with maxgrp = 0 */ 665 assert(grpcnt != NULL); 666 667 *grpcnt = 0; 668 (void)_nsdispatch(NULL, dtab, NSDB_GROUP, "getgroupmembership", 669 defaultsrc, uname, agroup, groups, maxgrp, grpcnt); 670 671 /* too many groups found? */ 672 return (*grpcnt > maxgrp ? -1 : 0); 673} 674 675 676static struct group grp; 677static char *grp_storage; 678static size_t grp_storage_size; 679 680static struct group * 681getgr(int (*fn)(union key, struct group *, char *, size_t, struct group **), 682 union key key) 683{ 684 int rv; 685 struct group *res; 686 687 if (grp_storage == NULL) { 688 grp_storage = malloc(GRP_STORAGE_INITIAL); 689 if (grp_storage == NULL) 690 return (NULL); 691 grp_storage_size = GRP_STORAGE_INITIAL; 692 } 693 do { 694 rv = fn(key, &grp, grp_storage, grp_storage_size, &res); 695 if (res == NULL && rv == ERANGE) { 696 free(grp_storage); 697 if ((grp_storage_size << 1) > GRP_STORAGE_MAX) { 698 grp_storage = NULL; 699 errno = ERANGE; 700 return (NULL); 701 } 702 grp_storage_size <<= 1; 703 grp_storage = malloc(grp_storage_size); 704 if (grp_storage == NULL) 705 return (NULL); 706 } 707 } while (res == NULL && rv == ERANGE); 708 if (rv != 0) 709 errno = rv; 710 return (res); 711} 712 713 714static int 715wrap_getgrnam_r(union key key, struct group *grp, char *buffer, size_t bufsize, 716 struct group **res) 717{ 718 return (getgrnam_r(key.name, grp, buffer, bufsize, res)); 719} 720 721 722static int 723wrap_getgrgid_r(union key key, struct group *grp, char *buffer, size_t bufsize, 724 struct group **res) 725{ 726 return (getgrgid_r(key.gid, grp, buffer, bufsize, res)); 727} 728 729 730static int 731wrap_getgrent_r(union key key __unused, struct group *grp, char *buffer, 732 size_t bufsize, struct group **res) 733{ 734 return (getgrent_r(grp, buffer, bufsize, res)); 735} 736 737 738struct group * 739getgrnam(const char *name) 740{ 741 union key key; 742 743 key.name = name; 744 return (getgr(wrap_getgrnam_r, key)); 745} 746 747 748struct group * 749getgrgid(gid_t gid) 750{ 751 union key key; 752 753 key.gid = gid; 754 return (getgr(wrap_getgrgid_r, key)); 755} 756 757 758struct group * 759getgrent(void) 760{ 761 union key key; 762 763 key.gid = 0; /* not used */ 764 return (getgr(wrap_getgrent_r, key)); 765} 766 767 768static int 769is_comment_line(const char *s, size_t n) 770{ 771 const char *eom; 772 773 eom = &s[n]; 774 775 for (; s < eom; s++) 776 if (*s == '#' || !isspace((unsigned char)*s)) 777 break; 778 return (*s == '#' || s == eom); 779} 780 781 782/* 783 * files backend 784 */ 785static void 786files_endstate(void *p) 787{ 788 789 if (p == NULL) 790 return; 791 if (((struct files_state *)p)->fp != NULL) 792 fclose(((struct files_state *)p)->fp); 793 free(p); 794} 795 796 797static int 798files_setgrent(void *retval, void *mdata, va_list ap) 799{ 800 struct files_state *st; 801 int rv, stayopen; 802 803 rv = files_getstate(&st); 804 if (rv != 0) 805 return (NS_UNAVAIL); 806 switch ((enum constants)mdata) { 807 case SETGRENT: 808 stayopen = va_arg(ap, int); 809 if (st->fp != NULL) 810 rewind(st->fp); 811 else if (stayopen) 812 st->fp = fopen(_PATH_GROUP, "re"); 813 break; 814 case ENDGRENT: 815 if (st->fp != NULL) { 816 fclose(st->fp); 817 st->fp = NULL; 818 } 819 break; 820 default: 821 break; 822 } 823 return (NS_UNAVAIL); 824} 825 826 827static int 828files_group(void *retval, void *mdata, va_list ap) 829{ 830 struct files_state *st; 831 enum nss_lookup_type how; 832 const char *name, *line; 833 struct group *grp; 834 gid_t gid; 835 char *buffer; 836 size_t bufsize, linesize; 837 off_t pos; 838 int rv, stayopen, *errnop; 839 840 name = NULL; 841 gid = (gid_t)-1; 842 how = (enum nss_lookup_type)mdata; 843 switch (how) { 844 case nss_lt_name: 845 name = va_arg(ap, const char *); 846 break; 847 case nss_lt_id: 848 gid = va_arg(ap, gid_t); 849 break; 850 case nss_lt_all: 851 break; 852 default: 853 return (NS_NOTFOUND); 854 } 855 grp = va_arg(ap, struct group *); 856 buffer = va_arg(ap, char *); 857 bufsize = va_arg(ap, size_t); 858 errnop = va_arg(ap, int *); 859 *errnop = files_getstate(&st); 860 if (*errnop != 0) 861 return (NS_UNAVAIL); 862 if (st->fp == NULL && 863 ((st->fp = fopen(_PATH_GROUP, "re")) == NULL)) { 864 *errnop = errno; 865 return (NS_UNAVAIL); 866 } 867 if (how == nss_lt_all) 868 stayopen = 1; 869 else { 870 rewind(st->fp); 871 stayopen = st->stayopen; 872 } 873 rv = NS_NOTFOUND; 874 pos = ftello(st->fp); 875 while ((line = fgetln(st->fp, &linesize)) != NULL) { 876 if (line[linesize-1] == '\n') 877 linesize--; 878 rv = __gr_match_entry(line, linesize, how, name, gid); 879 if (rv != NS_SUCCESS) 880 continue; 881 /* We need room at least for the line, a string NUL 882 * terminator, alignment padding, and one (char *) 883 * pointer for the member list terminator. 884 */ 885 if (bufsize <= linesize + _ALIGNBYTES + sizeof(char *)) { 886 *errnop = ERANGE; 887 rv = NS_RETURN; 888 break; 889 } 890 memcpy(buffer, line, linesize); 891 buffer[linesize] = '\0'; 892 rv = __gr_parse_entry(buffer, linesize, grp, 893 &buffer[linesize + 1], bufsize - linesize - 1, errnop); 894 if (rv & NS_TERMINATE) 895 break; 896 pos = ftello(st->fp); 897 } 898 if (!stayopen && st->fp != NULL) { 899 fclose(st->fp); 900 st->fp = NULL; 901 } 902 if (rv == NS_SUCCESS && retval != NULL) 903 *(struct group **)retval = grp; 904 else if (rv == NS_RETURN && *errnop == ERANGE && st->fp != NULL) 905 fseeko(st->fp, pos, SEEK_SET); 906 return (rv); 907} 908 909 910#ifdef HESIOD 911/* 912 * dns backend 913 */ 914static void 915dns_endstate(void *p) 916{ 917 918 free(p); 919} 920 921 922static int 923dns_setgrent(void *retval, void *cb_data, va_list ap) 924{ 925 struct dns_state *st; 926 int rv; 927 928 rv = dns_getstate(&st); 929 if (rv != 0) 930 return (NS_UNAVAIL); 931 st->counter = 0; 932 return (NS_UNAVAIL); 933} 934 935 936static int 937dns_group(void *retval, void *mdata, va_list ap) 938{ 939 char buf[HESIOD_NAME_MAX]; 940 struct dns_state *st; 941 struct group *grp; 942 const char *name, *label; 943 void *ctx; 944 char *buffer, **hes; 945 size_t bufsize, adjsize, linesize; 946 gid_t gid; 947 enum nss_lookup_type how; 948 int rv, *errnop; 949 950 ctx = NULL; 951 hes = NULL; 952 name = NULL; 953 gid = (gid_t)-1; 954 how = (enum nss_lookup_type)mdata; 955 switch (how) { 956 case nss_lt_name: 957 name = va_arg(ap, const char *); 958 break; 959 case nss_lt_id: 960 gid = va_arg(ap, gid_t); 961 break; 962 case nss_lt_all: 963 break; 964 } 965 grp = va_arg(ap, struct group *); 966 buffer = va_arg(ap, char *); 967 bufsize = va_arg(ap, size_t); 968 errnop = va_arg(ap, int *); 969 *errnop = dns_getstate(&st); 970 if (*errnop != 0) 971 return (NS_UNAVAIL); 972 if (hesiod_init(&ctx) != 0) { 973 *errnop = errno; 974 rv = NS_UNAVAIL; 975 goto fin; 976 } 977 do { 978 rv = NS_NOTFOUND; 979 switch (how) { 980 case nss_lt_name: 981 label = name; 982 break; 983 case nss_lt_id: 984 if (snprintf(buf, sizeof(buf), "%lu", 985 (unsigned long)gid) >= sizeof(buf)) 986 goto fin; 987 label = buf; 988 break; 989 case nss_lt_all: 990 if (st->counter < 0) 991 goto fin; 992 if (snprintf(buf, sizeof(buf), "group-%ld", 993 st->counter++) >= sizeof(buf)) 994 goto fin; 995 label = buf; 996 break; 997 } 998 hes = hesiod_resolve(ctx, label, 999 how == nss_lt_id ? "gid" : "group"); 1000 if ((how == nss_lt_id && hes == NULL && 1001 (hes = hesiod_resolve(ctx, buf, "group")) == NULL) || 1002 hes == NULL) { 1003 if (how == nss_lt_all) 1004 st->counter = -1; 1005 if (errno != ENOENT) 1006 *errnop = errno; 1007 goto fin; 1008 } 1009 rv = __gr_match_entry(hes[0], strlen(hes[0]), how, name, gid); 1010 if (rv != NS_SUCCESS) { 1011 hesiod_free_list(ctx, hes); 1012 hes = NULL; 1013 continue; 1014 } 1015 /* We need room at least for the line, a string NUL 1016 * terminator, alignment padding, and one (char *) 1017 * pointer for the member list terminator. 1018 */ 1019 adjsize = bufsize - _ALIGNBYTES - sizeof(char *); 1020 linesize = strlcpy(buffer, hes[0], adjsize); 1021 if (linesize >= adjsize) { 1022 *errnop = ERANGE; 1023 rv = NS_RETURN; 1024 goto fin; 1025 } 1026 hesiod_free_list(ctx, hes); 1027 hes = NULL; 1028 rv = __gr_parse_entry(buffer, linesize, grp, 1029 &buffer[linesize + 1], bufsize - linesize - 1, errnop); 1030 } while (how == nss_lt_all && !(rv & NS_TERMINATE)); 1031fin: 1032 if (hes != NULL) 1033 hesiod_free_list(ctx, hes); 1034 if (ctx != NULL) 1035 hesiod_end(ctx); 1036 if (rv == NS_SUCCESS && retval != NULL) 1037 *(struct group **)retval = grp; 1038 return (rv); 1039} 1040#endif /* HESIOD */ 1041 1042 1043#ifdef YP 1044/* 1045 * nis backend 1046 */ 1047static void 1048nis_endstate(void *p) 1049{ 1050 1051 if (p == NULL) 1052 return; 1053 free(((struct nis_state *)p)->key); 1054 free(p); 1055} 1056 1057 1058static int 1059nis_setgrent(void *retval, void *cb_data, va_list ap) 1060{ 1061 struct nis_state *st; 1062 int rv; 1063 1064 rv = nis_getstate(&st); 1065 if (rv != 0) 1066 return (NS_UNAVAIL); 1067 st->done = 0; 1068 free(st->key); 1069 st->key = NULL; 1070 return (NS_UNAVAIL); 1071} 1072 1073 1074static int 1075nis_group(void *retval, void *mdata, va_list ap) 1076{ 1077 char *map; 1078 struct nis_state *st; 1079 struct group *grp; 1080 const char *name; 1081 char *buffer, *key, *result; 1082 size_t bufsize; 1083 gid_t gid; 1084 enum nss_lookup_type how; 1085 int *errnop, keylen, resultlen, rv; 1086 1087 name = NULL; 1088 gid = (gid_t)-1; 1089 how = (enum nss_lookup_type)mdata; 1090 switch (how) { 1091 case nss_lt_name: 1092 name = va_arg(ap, const char *); 1093 map = "group.byname"; 1094 break; 1095 case nss_lt_id: 1096 gid = va_arg(ap, gid_t); 1097 map = "group.bygid"; 1098 break; 1099 case nss_lt_all: 1100 map = "group.byname"; 1101 break; 1102 } 1103 grp = va_arg(ap, struct group *); 1104 buffer = va_arg(ap, char *); 1105 bufsize = va_arg(ap, size_t); 1106 errnop = va_arg(ap, int *); 1107 *errnop = nis_getstate(&st); 1108 if (*errnop != 0) 1109 return (NS_UNAVAIL); 1110 if (st->domain[0] == '\0') { 1111 if (getdomainname(st->domain, sizeof(st->domain)) != 0) { 1112 *errnop = errno; 1113 return (NS_UNAVAIL); 1114 } 1115 } 1116 result = NULL; 1117 do { 1118 rv = NS_NOTFOUND; 1119 switch (how) { 1120 case nss_lt_name: 1121 if (strlcpy(buffer, name, bufsize) >= bufsize) 1122 goto erange; 1123 break; 1124 case nss_lt_id: 1125 if (snprintf(buffer, bufsize, "%lu", 1126 (unsigned long)gid) >= bufsize) 1127 goto erange; 1128 break; 1129 case nss_lt_all: 1130 if (st->done) 1131 goto fin; 1132 break; 1133 } 1134 result = NULL; 1135 if (how == nss_lt_all) { 1136 if (st->key == NULL) 1137 rv = yp_first(st->domain, map, &st->key, 1138 &st->keylen, &result, &resultlen); 1139 else { 1140 key = st->key; 1141 keylen = st->keylen; 1142 st->key = NULL; 1143 rv = yp_next(st->domain, map, key, keylen, 1144 &st->key, &st->keylen, &result, 1145 &resultlen); 1146 free(key); 1147 } 1148 if (rv != 0) { 1149 free(result); 1150 free(st->key); 1151 st->key = NULL; 1152 if (rv == YPERR_NOMORE) { 1153 st->done = 1; 1154 rv = NS_NOTFOUND; 1155 } else 1156 rv = NS_UNAVAIL; 1157 goto fin; 1158 } 1159 } else { 1160 rv = yp_match(st->domain, map, buffer, strlen(buffer), 1161 &result, &resultlen); 1162 if (rv == YPERR_KEY) { 1163 rv = NS_NOTFOUND; 1164 continue; 1165 } else if (rv != 0) { 1166 free(result); 1167 rv = NS_UNAVAIL; 1168 continue; 1169 } 1170 } 1171 /* We need room at least for the line, a string NUL 1172 * terminator, alignment padding, and one (char *) 1173 * pointer for the member list terminator. 1174 */ 1175 if (resultlen >= bufsize - _ALIGNBYTES - sizeof(char *)) 1176 goto erange; 1177 memcpy(buffer, result, resultlen); 1178 buffer[resultlen] = '\0'; 1179 free(result); 1180 rv = __gr_match_entry(buffer, resultlen, how, name, gid); 1181 if (rv == NS_SUCCESS) 1182 rv = __gr_parse_entry(buffer, resultlen, grp, 1183 &buffer[resultlen+1], bufsize - resultlen - 1, 1184 errnop); 1185 } while (how == nss_lt_all && !(rv & NS_TERMINATE)); 1186fin: 1187 if (rv == NS_SUCCESS && retval != NULL) 1188 *(struct group **)retval = grp; 1189 return (rv); 1190erange: 1191 *errnop = ERANGE; 1192 return (NS_RETURN); 1193} 1194#endif /* YP */ 1195 1196 1197 1198/* 1199 * compat backend 1200 */ 1201static void 1202compat_endstate(void *p) 1203{ 1204 struct compat_state *st; 1205 1206 if (p == NULL) 1207 return; 1208 st = (struct compat_state *)p; 1209 free(st->name); 1210 if (st->fp != NULL) 1211 fclose(st->fp); 1212 free(p); 1213} 1214 1215 1216static int 1217compat_setgrent(void *retval, void *mdata, va_list ap) 1218{ 1219 static const ns_src compatsrc[] = { 1220#ifdef YP 1221 { NSSRC_NIS, NS_SUCCESS }, 1222#endif 1223 { NULL, 0 } 1224 }; 1225 ns_dtab dtab[] = { 1226#ifdef HESIOD 1227 { NSSRC_DNS, dns_setgrent, NULL }, 1228#endif 1229#ifdef YP 1230 { NSSRC_NIS, nis_setgrent, NULL }, 1231#endif 1232 { NULL, NULL, NULL } 1233 }; 1234 struct compat_state *st; 1235 int rv, stayopen; 1236 1237#define set_setent(x, y) do { \ 1238 int i; \ 1239 \ 1240 for (i = 0; i < (sizeof(x)/sizeof(x[0])) - 1; i++) \ 1241 x[i].mdata = (void *)y; \ 1242} while (0) 1243 1244 rv = compat_getstate(&st); 1245 if (rv != 0) 1246 return (NS_UNAVAIL); 1247 switch ((enum constants)mdata) { 1248 case SETGRENT: 1249 stayopen = va_arg(ap, int); 1250 if (st->fp != NULL) 1251 rewind(st->fp); 1252 else if (stayopen) 1253 st->fp = fopen(_PATH_GROUP, "re"); 1254 set_setent(dtab, mdata); 1255 (void)_nsdispatch(NULL, dtab, NSDB_GROUP_COMPAT, "setgrent", 1256 compatsrc, 0); 1257 break; 1258 case ENDGRENT: 1259 if (st->fp != NULL) { 1260 fclose(st->fp); 1261 st->fp = NULL; 1262 } 1263 set_setent(dtab, mdata); 1264 (void)_nsdispatch(NULL, dtab, NSDB_GROUP_COMPAT, "endgrent", 1265 compatsrc, 0); 1266 break; 1267 default: 1268 break; 1269 } 1270 st->compat = COMPAT_MODE_OFF; 1271 free(st->name); 1272 st->name = NULL; 1273 return (NS_UNAVAIL); 1274#undef set_setent 1275} 1276 1277 1278static int 1279compat_group(void *retval, void *mdata, va_list ap) 1280{ 1281 static const ns_src compatsrc[] = { 1282#ifdef YP 1283 { NSSRC_NIS, NS_SUCCESS }, 1284#endif 1285 { NULL, 0 } 1286 }; 1287 ns_dtab dtab[] = { 1288#ifdef YP 1289 { NSSRC_NIS, nis_group, NULL }, 1290#endif 1291#ifdef HESIOD 1292 { NSSRC_DNS, dns_group, NULL }, 1293#endif 1294 { NULL, NULL, NULL } 1295 }; 1296 struct compat_state *st; 1297 enum nss_lookup_type how; 1298 const char *name, *line; 1299 struct group *grp; 1300 gid_t gid; 1301 char *buffer, *p; 1302 void *discard; 1303 size_t bufsize, linesize; 1304 off_t pos; 1305 int rv, stayopen, *errnop; 1306 1307#define set_lookup_type(x, y) do { \ 1308 int i; \ 1309 \ 1310 for (i = 0; i < (sizeof(x)/sizeof(x[0])) - 1; i++) \ 1311 x[i].mdata = (void *)y; \ 1312} while (0) 1313 1314 name = NULL; 1315 gid = (gid_t)-1; 1316 how = (enum nss_lookup_type)mdata; 1317 switch (how) { 1318 case nss_lt_name: 1319 name = va_arg(ap, const char *); 1320 break; 1321 case nss_lt_id: 1322 gid = va_arg(ap, gid_t); 1323 break; 1324 case nss_lt_all: 1325 break; 1326 default: 1327 return (NS_NOTFOUND); 1328 } 1329 grp = va_arg(ap, struct group *); 1330 buffer = va_arg(ap, char *); 1331 bufsize = va_arg(ap, size_t); 1332 errnop = va_arg(ap, int *); 1333 *errnop = compat_getstate(&st); 1334 if (*errnop != 0) 1335 return (NS_UNAVAIL); 1336 if (st->fp == NULL && 1337 ((st->fp = fopen(_PATH_GROUP, "re")) == NULL)) { 1338 *errnop = errno; 1339 rv = NS_UNAVAIL; 1340 goto fin; 1341 } 1342 if (how == nss_lt_all) 1343 stayopen = 1; 1344 else { 1345 rewind(st->fp); 1346 stayopen = st->stayopen; 1347 } 1348docompat: 1349 switch (st->compat) { 1350 case COMPAT_MODE_ALL: 1351 set_lookup_type(dtab, how); 1352 switch (how) { 1353 case nss_lt_all: 1354 rv = _nsdispatch(&discard, dtab, NSDB_GROUP_COMPAT, 1355 "getgrent_r", compatsrc, grp, buffer, bufsize, 1356 errnop); 1357 break; 1358 case nss_lt_id: 1359 rv = _nsdispatch(&discard, dtab, NSDB_GROUP_COMPAT, 1360 "getgrgid_r", compatsrc, gid, grp, buffer, bufsize, 1361 errnop); 1362 break; 1363 case nss_lt_name: 1364 rv = _nsdispatch(&discard, dtab, NSDB_GROUP_COMPAT, 1365 "getgrnam_r", compatsrc, name, grp, buffer, 1366 bufsize, errnop); 1367 break; 1368 } 1369 if (rv & NS_TERMINATE) 1370 goto fin; 1371 st->compat = COMPAT_MODE_OFF; 1372 break; 1373 case COMPAT_MODE_NAME: 1374 set_lookup_type(dtab, nss_lt_name); 1375 rv = _nsdispatch(&discard, dtab, NSDB_GROUP_COMPAT, 1376 "getgrnam_r", compatsrc, st->name, grp, buffer, bufsize, 1377 errnop); 1378 switch (rv) { 1379 case NS_SUCCESS: 1380 switch (how) { 1381 case nss_lt_name: 1382 if (strcmp(name, grp->gr_name) != 0) 1383 rv = NS_NOTFOUND; 1384 break; 1385 case nss_lt_id: 1386 if (gid != grp->gr_gid) 1387 rv = NS_NOTFOUND; 1388 break; 1389 default: 1390 break; 1391 } 1392 break; 1393 case NS_RETURN: 1394 goto fin; 1395 default: 1396 break; 1397 } 1398 free(st->name); 1399 st->name = NULL; 1400 st->compat = COMPAT_MODE_OFF; 1401 if (rv == NS_SUCCESS) 1402 goto fin; 1403 break; 1404 default: 1405 break; 1406 } 1407 rv = NS_NOTFOUND; 1408 pos = ftello(st->fp); 1409 while ((line = fgetln(st->fp, &linesize)) != NULL) { 1410 if (line[linesize-1] == '\n') 1411 linesize--; 1412 if (linesize > 2 && line[0] == '+') { 1413 p = memchr(&line[1], ':', linesize); 1414 if (p == NULL || p == &line[1]) 1415 st->compat = COMPAT_MODE_ALL; 1416 else { 1417 st->name = malloc(p - line); 1418 if (st->name == NULL) { 1419 syslog(LOG_ERR, 1420 "getgrent memory allocation failure"); 1421 *errnop = ENOMEM; 1422 rv = NS_UNAVAIL; 1423 break; 1424 } 1425 memcpy(st->name, &line[1], p - line - 1); 1426 st->name[p - line - 1] = '\0'; 1427 st->compat = COMPAT_MODE_NAME; 1428 } 1429 goto docompat; 1430 } 1431 rv = __gr_match_entry(line, linesize, how, name, gid); 1432 if (rv != NS_SUCCESS) 1433 continue; 1434 /* We need room at least for the line, a string NUL 1435 * terminator, alignment padding, and one (char *) 1436 * pointer for the member list terminator. 1437 */ 1438 if (bufsize <= linesize + _ALIGNBYTES + sizeof(char *)) { 1439 *errnop = ERANGE; 1440 rv = NS_RETURN; 1441 break; 1442 } 1443 memcpy(buffer, line, linesize); 1444 buffer[linesize] = '\0'; 1445 rv = __gr_parse_entry(buffer, linesize, grp, 1446 &buffer[linesize + 1], bufsize - linesize - 1, errnop); 1447 if (rv & NS_TERMINATE) 1448 break; 1449 pos = ftello(st->fp); 1450 } 1451fin: 1452 if (!stayopen && st->fp != NULL) { 1453 fclose(st->fp); 1454 st->fp = NULL; 1455 } 1456 if (rv == NS_SUCCESS && retval != NULL) 1457 *(struct group **)retval = grp; 1458 else if (rv == NS_RETURN && *errnop == ERANGE && st->fp != NULL) 1459 fseeko(st->fp, pos, SEEK_SET); 1460 return (rv); 1461#undef set_lookup_type 1462} 1463 1464 1465/* 1466 * common group line matching and parsing 1467 */ 1468int 1469__gr_match_entry(const char *line, size_t linesize, enum nss_lookup_type how, 1470 const char *name, gid_t gid) 1471{ 1472 size_t namesize; 1473 const char *p, *eol; 1474 char *q; 1475 unsigned long n; 1476 int i, needed; 1477 1478 if (linesize == 0 || is_comment_line(line, linesize)) 1479 return (NS_NOTFOUND); 1480 switch (how) { 1481 case nss_lt_name: needed = 1; break; 1482 case nss_lt_id: needed = 2; break; 1483 default: needed = 2; break; 1484 } 1485 eol = &line[linesize]; 1486 for (p = line, i = 0; i < needed && p < eol; p++) 1487 if (*p == ':') 1488 i++; 1489 if (i < needed) 1490 return (NS_NOTFOUND); 1491 switch (how) { 1492 case nss_lt_name: 1493 namesize = strlen(name); 1494 if (namesize + 1 == (size_t)(p - line) && 1495 memcmp(line, name, namesize) == 0) 1496 return (NS_SUCCESS); 1497 break; 1498 case nss_lt_id: 1499 n = strtoul(p, &q, 10); 1500 if (q < eol && *q == ':' && gid == (gid_t)n) 1501 return (NS_SUCCESS); 1502 break; 1503 case nss_lt_all: 1504 return (NS_SUCCESS); 1505 default: 1506 break; 1507 } 1508 return (NS_NOTFOUND); 1509} 1510 1511 1512int 1513__gr_parse_entry(char *line, size_t linesize, struct group *grp, char *membuf, 1514 size_t membufsize, int *errnop) 1515{ 1516 char *s_gid, *s_mem, *p, **members; 1517 unsigned long n; 1518 int maxmembers; 1519 1520 memset(grp, 0, sizeof(*grp)); 1521 members = (char **)_ALIGN(membuf); 1522 membufsize -= (char *)members - membuf; 1523 maxmembers = membufsize / sizeof(*members); 1524 if (maxmembers <= 0 || 1525 (grp->gr_name = strsep(&line, ":")) == NULL || 1526 grp->gr_name[0] == '\0' || 1527 (grp->gr_passwd = strsep(&line, ":")) == NULL || 1528 (s_gid = strsep(&line, ":")) == NULL || 1529 s_gid[0] == '\0') 1530 return (NS_NOTFOUND); 1531 s_mem = line; 1532 n = strtoul(s_gid, &s_gid, 10); 1533 if (s_gid[0] != '\0') 1534 return (NS_NOTFOUND); 1535 grp->gr_gid = (gid_t)n; 1536 grp->gr_mem = members; 1537 while (maxmembers > 1 && s_mem != NULL) { 1538 p = strsep(&s_mem, ","); 1539 if (p != NULL && *p != '\0') { 1540 *members++ = p; 1541 maxmembers--; 1542 } 1543 } 1544 *members = NULL; 1545 if (s_mem == NULL) 1546 return (NS_SUCCESS); 1547 else { 1548 *errnop = ERANGE; 1549 return (NS_RETURN); 1550 } 1551} 1552 1553 1554