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