1 /* 2 * CDDL HEADER START 3 * 4 * The contents of this file are subject to the terms of the 5 * Common Development and Distribution License, Version 1.0 only 6 * (the "License"). You may not use this file except in compliance 7 * with the License. 8 * 9 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 10 * or http://www.opensolaris.org/os/licensing. 11 * See the License for the specific language governing permissions 12 * and limitations under the License. 13 * 14 * When distributing Covered Code, include this CDDL HEADER in each 15 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 16 * If applicable, add the following below this CDDL HEADER, with the 17 * fields enclosed by brackets "[]" replaced with your own identifying 18 * information: Portions Copyright [yyyy] [name of copyright owner] 19 * 20 * CDDL HEADER END 21 */ 22 /* 23 * Copyright 2004 Sun Microsystems, Inc. All rights reserved. 24 * Use is subject to license terms. 25 */ 26 27 #pragma ident "%Z%%M% %I% %E% SMI" 28 29 #pragma weak endgrent = _endgrent 30 #pragma weak setgrent = _setgrent 31 32 #pragma weak getgrnam_r = _getgrnam_r 33 #pragma weak getgrgid_r = _getgrgid_r 34 #pragma weak getgrent_r = _getgrent_r 35 #pragma weak fgetgrent_r = _fgetgrent_r 36 37 #include "synonyms.h" 38 #include <mtlib.h> 39 #include <sys/types.h> 40 #include <grp.h> 41 #include <memory.h> 42 #include <nsswitch.h> 43 #include <nss_dbdefs.h> 44 #include <stdio.h> 45 #include <stdlib.h> 46 #include <string.h> 47 #include <synch.h> 48 #include <sys/param.h> 49 #include <sys/mman.h> 50 51 #define __NSS_PRIVATE_INTERFACE 52 #include "nsswitch_priv.h" 53 #undef __NSS_PRIVATE_INTERFACE 54 55 extern int _getgroupsbymember(const char *, gid_t[], int, int); 56 int str2group(const char *, int, void *, 57 char *, int); 58 59 static DEFINE_NSS_DB_ROOT(db_root); 60 static DEFINE_NSS_GETENT(context); 61 62 #define USE_NETID_STR "NETID_AUTHORITATIVE=TRUE" 63 64 void 65 _nss_initf_group(nss_db_params_t *p) 66 { 67 p->name = NSS_DBNAM_GROUP; 68 p->default_config = NSS_DEFCONF_GROUP; 69 } 70 71 #include <getxby_door.h> 72 #include <sys/door.h> 73 74 static struct group * 75 process_getgr(struct group *result, char *buffer, int buflen, 76 nsc_data_t *sptr, int ndata); 77 78 struct group * 79 _uncached_getgrnam_r(const char *name, struct group *result, char *buffer, 80 int buflen); 81 82 struct group * 83 _uncached_getgrgid_r(gid_t gid, struct group *result, char *buffer, int buflen); 84 85 /* 86 * POSIX.1c Draft-6 version of the function getgrnam_r. 87 * It was implemented by Solaris 2.3. 88 */ 89 struct group * 90 _getgrnam_r(const char *name, struct group *result, char *buffer, int buflen) 91 { 92 /* 93 * allocate room on the stack for the nscd to return 94 * group and group member information 95 */ 96 union { 97 nsc_data_t s_d; 98 char s_b[8192]; 99 } space; 100 nsc_data_t *sptr; 101 int ndata; 102 int adata; 103 struct group *resptr = NULL; 104 105 if ((name == (const char *)NULL) || 106 (strlen(name) >= (sizeof (space) - sizeof (nsc_data_t)))) { 107 errno = ERANGE; 108 return (NULL); 109 } 110 111 ndata = sizeof (space); 112 adata = strlen(name) + sizeof (nsc_call_t) + 1; 113 space.s_d.nsc_call.nsc_callnumber = GETGRNAM; 114 (void) strcpy(space.s_d.nsc_call.nsc_u.name, name); 115 sptr = &space.s_d; 116 117 switch (_nsc_trydoorcall(&sptr, &ndata, &adata)) { 118 case SUCCESS: /* positive cache hit */ 119 break; 120 case NOTFOUND: /* negative cache hit */ 121 return (NULL); 122 default: 123 return ((struct group *)_uncached_getgrnam_r(name, result, 124 buffer, buflen)); 125 } 126 127 resptr = process_getgr(result, buffer, buflen, sptr, ndata); 128 129 /* 130 * check to see if doors reallocated memory underneath us 131 * if they did munmap the memory or suffer a memory leak 132 */ 133 134 if (sptr != &space.s_d) 135 munmap((void *)sptr, ndata); 136 137 return (resptr); 138 } 139 140 /* 141 * POSIX.1c Draft-6 version of the function getgrgid_r. 142 * It was implemented by Solaris 2.3. 143 */ 144 struct group * 145 _getgrgid_r(gid_t gid, struct group *result, char *buffer, int buflen) 146 { 147 /* 148 * allocate room on the stack for the nscd to return 149 * group and group member information 150 */ 151 union { 152 nsc_data_t s_d; 153 char s_b[8192]; 154 } space; 155 nsc_data_t *sptr; 156 int ndata; 157 int adata; 158 struct group *resptr = NULL; 159 160 ndata = sizeof (space); 161 adata = sizeof (nsc_call_t) + 1; 162 space.s_d.nsc_call.nsc_callnumber = GETGRGID; 163 space.s_d.nsc_call.nsc_u.gid = gid; 164 sptr = &space.s_d; 165 166 switch (_nsc_trydoorcall(&sptr, &ndata, &adata)) { 167 case SUCCESS: /* positive cache hit */ 168 break; 169 case NOTFOUND: /* negative cache hit */ 170 return (NULL); 171 default: 172 return ((struct group *)_uncached_getgrgid_r(gid, result, 173 buffer, buflen)); 174 } 175 176 resptr = process_getgr(result, buffer, buflen, sptr, ndata); 177 178 /* 179 * check to see if doors reallocated memory underneath us 180 * if they did munmap the memory or suffer a memory leak 181 */ 182 183 if (sptr != &space.s_d) 184 munmap((void *)sptr, ndata); 185 186 return (resptr); 187 } 188 /* 189 * This routine should be rewritten - there's no reason it 190 * cannot be the same code for 32 and 64 bit w/ a bit of care. 191 */ 192 /* ARGSUSED4 */ 193 static struct group * 194 process_getgr(struct group *result, char *buffer, int buflen, 195 nsc_data_t *sptr, int ndata) 196 { 197 int i; 198 char *fixed; 199 #ifdef _LP64 200 char *buffer32; 201 char **gr_mem32; 202 uptr32_t index; 203 struct group group64; 204 #endif /* _LP64 */ 205 206 /* align buffer on a pointer boundry 4bytes in 32bits and 8 bytes in 64bits */ 207 #ifdef _LP64 208 fixed = (char *)(((uintptr_t)buffer + 7) & ~7); 209 #else 210 fixed = (char *)(((uintptr_t)buffer + 3) & ~3); 211 #endif /* _LP64 */ 212 213 if (buflen <= fixed - buffer) { /* watch out for wrap-around */ 214 errno = ERANGE; 215 return (NULL); 216 } 217 218 buflen -= fixed - buffer; 219 220 buffer = fixed; 221 222 #ifdef _LP64 223 /* 224 * this is just a rationality check; we need to make 225 * sure that there's enough space for the gr_mem array 226 * as well... easiest place to do that is when we copy 227 * them in place. 228 */ 229 230 if (sptr->nsc_ret.nsc_bufferbytesused + 231 /* 232 * ^^^ number of bytes from nscd 233 */ 234 (sizeof (char **)) + 235 /* 236 * ^^^ we need 8 bytes for gr_mem 237 */ 238 (sizeof (char *) - 1) - 239 /* 240 * ^^^ plus space for pssibly fixing aligment of gr_mem 241 */ 242 sizeof (group32_t) 243 /* 244 * ^^^ because we don't put this in the usr buffer 245 */ 246 > buflen) { 247 #else 248 if (sptr->nsc_ret.nsc_bufferbytesused - sizeof (struct group) 249 > buflen) { 250 #endif /* _LP64 */ 251 errno = ERANGE; 252 return (NULL); 253 } 254 255 if (sptr->nsc_ret.nsc_return_code != SUCCESS) 256 return (NULL); 257 258 /* 259 * ncsd is a 32bit application, so use 32bit data items if we are in 64bit mode 260 */ 261 #ifdef _LP64 262 263 (void) memcpy(buffer, 264 (sptr->nsc_ret.nsc_u.buff + sizeof (group32_t)), 265 (sptr->nsc_ret.nsc_bufferbytesused - sizeof (group32_t))); 266 267 group64.gr_name = (char *)(sptr->nsc_ret.nsc_u.grp.gr_name + 268 (uintptr_t)buffer); 269 group64.gr_passwd = (char *)(sptr->nsc_ret.nsc_u.grp.gr_passwd + 270 (uintptr_t)buffer); 271 group64.gr_gid = sptr->nsc_ret.nsc_u.grp.gr_gid; 272 273 group64.gr_mem = (char **)((uintptr_t)buffer + 274 sptr->nsc_ret.nsc_bufferbytesused - sizeof (group32_t)); 275 group64.gr_mem = (char **)(((uintptr_t)group64.gr_mem + 7) & ~7); 276 277 gr_mem32 = (char **)(uintptr_t)sptr->nsc_ret.nsc_u.grp.gr_mem; 278 buffer32 = buffer; 279 280 for (i = 0; ; i++) { 281 index = *((uptr32_t *) 282 ((uintptr_t)gr_mem32 + (uintptr_t)buffer32)); 283 284 /* 285 * make sure there's enough space to copy the pointer... 286 */ 287 if (&group64.gr_mem[i + 1] > 288 (char **)((uintptr_t)buffer + buflen)) { 289 errno = ERANGE; 290 return (NULL); 291 } 292 293 if (index == 0) 294 break; 295 296 group64.gr_mem[i] = (char *)(index + buffer); 297 buffer32 += sizeof (uptr32_t); 298 299 } 300 group64.gr_mem[i] = NULL; 301 302 *result = group64; 303 #else 304 305 (void) memcpy(buffer, 306 (sptr->nsc_ret.nsc_u.buff + sizeof (struct group)), 307 (sptr->nsc_ret.nsc_bufferbytesused - sizeof (struct group))); 308 309 sptr->nsc_ret.nsc_u.grp.gr_name += (uintptr_t)buffer; 310 sptr->nsc_ret.nsc_u.grp.gr_passwd += (uintptr_t)buffer; 311 312 sptr->nsc_ret.nsc_u.grp.gr_mem = 313 (char **)((uintptr_t)sptr->nsc_ret.nsc_u.grp.gr_mem + 314 (uintptr_t)buffer); 315 316 i = 0; 317 while (sptr->nsc_ret.nsc_u.grp.gr_mem[i]) { 318 sptr->nsc_ret.nsc_u.grp.gr_mem[i] += (uintptr_t)buffer; 319 i++; 320 } 321 322 *result = sptr->nsc_ret.nsc_u.grp; 323 324 #endif /* _LP64 */ 325 326 return (result); 327 } 328 329 struct group * 330 _uncached_getgrgid_r(gid_t gid, struct group *result, char *buffer, 331 int buflen) 332 { 333 nss_XbyY_args_t arg; 334 335 NSS_XbyY_INIT(&arg, result, buffer, buflen, str2group); 336 arg.key.gid = gid; 337 (void) nss_search(&db_root, _nss_initf_group, 338 NSS_DBOP_GROUP_BYGID, &arg); 339 return ((struct group *)NSS_XbyY_FINI(&arg)); 340 } 341 342 /* 343 * POSIX.1c standard version of the function getgrgid_r. 344 * User gets it via static getgrgid_r from the header file. 345 */ 346 int 347 __posix_getgrgid_r(gid_t gid, struct group *grp, char *buffer, 348 size_t bufsize, struct group **result) 349 { 350 int nerrno = 0; 351 int oerrno = errno; 352 353 errno = 0; 354 if ((*result = _getgrgid_r(gid, grp, buffer, (uintptr_t)bufsize)) 355 == NULL) { 356 nerrno = errno; 357 } 358 errno = oerrno; 359 return (nerrno); 360 } 361 362 extern struct group * 363 _getgrnam_r(const char *name, struct group *result, char *buffer, 364 int buflen); 365 366 struct group * 367 _uncached_getgrnam_r(const char *name, struct group *result, char *buffer, 368 int buflen) 369 { 370 nss_XbyY_args_t arg; 371 372 NSS_XbyY_INIT(&arg, result, buffer, buflen, str2group); 373 arg.key.name = name; 374 (void) nss_search(&db_root, _nss_initf_group, 375 NSS_DBOP_GROUP_BYNAME, &arg); 376 return ((struct group *)NSS_XbyY_FINI(&arg)); 377 } 378 379 /* 380 * POSIX.1c standard version of the function getgrnam_r. 381 * User gets it via static getgrnam_r from the header file. 382 */ 383 int 384 __posix_getgrnam_r(const char *name, struct group *grp, char *buffer, 385 size_t bufsize, struct group **result) 386 { 387 int nerrno = 0; 388 int oerrno = errno; 389 390 if ((*result = _getgrnam_r(name, grp, buffer, (uintptr_t)bufsize)) 391 == NULL) { 392 nerrno = errno; 393 } 394 errno = oerrno; 395 return (nerrno); 396 } 397 398 void 399 setgrent(void) 400 { 401 nss_setent(&db_root, _nss_initf_group, &context); 402 } 403 404 void 405 endgrent(void) 406 { 407 nss_endent(&db_root, _nss_initf_group, &context); 408 nss_delete(&db_root); 409 } 410 411 struct group * 412 getgrent_r(struct group *result, char *buffer, int buflen) 413 { 414 nss_XbyY_args_t arg; 415 char *nam; 416 417 /* In getXXent_r(), protect the unsuspecting caller from +/- entries */ 418 419 do { 420 NSS_XbyY_INIT(&arg, result, buffer, buflen, str2group); 421 /* No key to fill in */ 422 (void) nss_getent(&db_root, _nss_initf_group, &context, &arg); 423 } while (arg.returnval != 0 && 424 (nam = ((struct group *)arg.returnval)->gr_name) != 0 && 425 (*nam == '+' || *nam == '-')); 426 427 return ((struct group *)NSS_XbyY_FINI(&arg)); 428 } 429 430 struct group * 431 fgetgrent_r(FILE *f, struct group *result, char *buffer, int buflen) 432 { 433 extern void _nss_XbyY_fgets(FILE *, nss_XbyY_args_t *); 434 nss_XbyY_args_t arg; 435 436 /* ... but in fgetXXent_r, the caller deserves any +/- entry he gets */ 437 438 /* No key to fill in */ 439 NSS_XbyY_INIT(&arg, result, buffer, buflen, str2group); 440 _nss_XbyY_fgets(f, &arg); 441 return ((struct group *)NSS_XbyY_FINI(&arg)); 442 } 443 444 /* 445 * _getgroupsbymember(uname, gid_array, maxgids, numgids): 446 * Private interface for initgroups(). It returns the group ids of 447 * groups of which the specified user is a member. 448 * 449 * Arguments: 450 * username Username of the putative member 451 * gid_array Space in which to return the gids. The first [numgids] 452 * elements are assumed to already contain valid gids. 453 * maxgids Maximum number of elements in gid_array. 454 * numgids Number of elements (normally 0 or 1) that already contain 455 * valid gids. 456 * Return value: 457 * number of valid gids in gid_array (may be zero) 458 * or 459 * -1 (and errno set appropriately) on errors (none currently defined) 460 */ 461 462 static nss_status_t process_cstr(const char *, int, struct nss_groupsbymem *); 463 464 int 465 _getgroupsbymember(const char *username, gid_t gid_array[], 466 int maxgids, int numgids) 467 { 468 struct nss_groupsbymem arg; 469 char defval[BUFSIZ]; 470 __NSL_FILE *defl; 471 472 arg.username = username; 473 arg.gid_array = gid_array; 474 arg.maxgids = maxgids; 475 arg.numgids = numgids; 476 arg.str2ent = str2group; 477 arg.process_cstr = process_cstr; 478 479 /* 480 * The old value being provided here was 0, ie do the quick 481 * way. Given that this was never actually used under NIS 482 * and had the wrong (now corrected) meaning for NIS+ we need 483 * to change the default to be 1 (TRUE) as we now need the 484 * admin to decided to use netid, setting NETID_AUTHORITATIVE 485 * in /etc/default/nss to TRUE gets us a value of 0 for 486 * force_slow_way - don't you just love double negatives ;-) 487 * 488 * We need to do this to preserve the behaviour seen when the 489 * admin makes no changes. 490 */ 491 arg.force_slow_way = 1; 492 493 /* 494 * The "easy" way to do /etc/default/nss is to use the defread() 495 * stuff from libcmd, but since we are in libc we don't want to 496 * link ourselfs against libcmd, so instead we just do it by hand 497 */ 498 499 if ((defl = __nsl_c_fopen(__NSW_DEFAULT_FILE, "r")) != NULL) { 500 while (__nsl_c_fgets(defval, sizeof (defval), defl) != NULL) { 501 if (strncmp(USE_NETID_STR, defval, 502 sizeof (USE_NETID_STR) - 1) == 0) { 503 arg.force_slow_way = 0; 504 break; 505 } 506 } 507 (void) __nsl_c_fclose(defl); 508 } 509 510 (void) nss_search(&db_root, _nss_initf_group, 511 NSS_DBOP_GROUP_BYMEMBER, &arg); 512 513 #ifdef undef 514 /* 515 * Only do this if there's existing code somewhere that relies on 516 * initgroups() doing an endgrent() -- most unlikely. 517 */ 518 endgrent(); 519 #endif /* undef */ 520 521 return (arg.numgids); 522 } 523 524 525 static char * 526 gettok(char **nextpp, char sep) 527 { 528 char *p = *nextpp; 529 char *q = p; 530 char c; 531 532 if (p == 0) 533 return (0); 534 535 while ((c = *q) != '\0' && c != sep) 536 q++; 537 538 if (c == '\0') 539 *nextpp = 0; 540 else { 541 *q++ = '\0'; 542 *nextpp = q; 543 } 544 return (p); 545 } 546 547 /* 548 * Return values: 0 = success, 1 = parse error, 2 = erange ... 549 * The structure pointer passed in is a structure in the caller's space 550 * wherein the field pointers would be set to areas in the buffer if 551 * need be. instring and buffer should be separate areas. 552 */ 553 int 554 str2group(const char *instr, int lenstr, void *ent, char *buffer, int buflen) 555 { 556 struct group *group = (struct group *)ent; 557 char *p, *next; 558 int black_magic; /* "+" or "-" entry */ 559 char **memlist, **limit; 560 561 if (lenstr + 1 > buflen) 562 return (NSS_STR_PARSE_ERANGE); 563 564 /* 565 * We copy the input string into the output buffer and 566 * operate on it in place. 567 */ 568 (void) memcpy(buffer, instr, lenstr); 569 buffer[lenstr] = '\0'; 570 571 next = buffer; 572 573 /* 574 * Parsers for passwd and group have always been pretty rigid; 575 * we wouldn't want to buck a Unix tradition 576 */ 577 578 group->gr_name = p = gettok(&next, ':'); 579 if (*p == '\0') { 580 /* Empty group-name; not allowed */ 581 return (NSS_STR_PARSE_PARSE); 582 } 583 584 /* Always return at least an empty gr_mem list */ 585 memlist = (char **)ROUND_UP(buffer + lenstr + 1, sizeof (char *)); 586 limit = (char **)ROUND_DOWN(buffer + buflen, sizeof (char *)); 587 *memlist = 0; 588 group->gr_mem = memlist; 589 590 black_magic = (*p == '+' || *p == '-'); 591 if (black_magic) { 592 /* Then the rest of the group entry is optional */ 593 group->gr_passwd = 0; 594 group->gr_gid = 0; 595 } 596 597 group->gr_passwd = p = gettok(&next, ':'); 598 if (p == 0) { 599 if (black_magic) 600 return (NSS_STR_PARSE_SUCCESS); 601 else 602 return (NSS_STR_PARSE_PARSE); 603 } 604 605 p = next; /* gid */ 606 if (p == 0 || *p == '\0') { 607 if (black_magic) 608 return (NSS_STR_PARSE_SUCCESS); 609 else 610 return (NSS_STR_PARSE_PARSE); 611 } 612 if (!black_magic) { 613 group->gr_gid = (gid_t)strtol(p, &next, 10); 614 if (next == p) { 615 /* gid field should be nonempty */ 616 return (NSS_STR_PARSE_PARSE); 617 } 618 /* 619 * gids should be non-negative; anything else 620 * is administrative policy. 621 */ 622 if (group->gr_gid < 0) 623 group->gr_gid = GID_NOBODY; 624 } 625 if (*next++ != ':') { 626 /* Parse error, even for a '+' entry (which should have */ 627 /* an empty gid field, since it's always overridden) */ 628 return (NSS_STR_PARSE_PARSE); 629 } 630 631 /* === Could check and complain if there are any extra colons */ 632 while (memlist < limit) { 633 p = gettok(&next, ','); 634 if (p == 0 || *p == '\0') { 635 *memlist = 0; 636 /* Successfully parsed and stored */ 637 return (NSS_STR_PARSE_SUCCESS); 638 } 639 *memlist++ = p; 640 } 641 /* Out of space; error even for black_magic */ 642 return (NSS_STR_PARSE_ERANGE); 643 } 644 645 static nss_status_t 646 process_cstr(const char *instr, int instr_len, struct nss_groupsbymem *gbm) 647 { 648 /* 649 * It's possible to do a much less inefficient version of this by 650 * selectively duplicating code from str2group(). For now, 651 * however, we'll take the easy way out and implement this on 652 * top of str2group(). 653 */ 654 655 const char *username = gbm->username; 656 nss_XbyY_buf_t *buf; 657 struct group *grp; 658 char **memp; 659 char *mem; 660 int parsestat; 661 662 buf = _nss_XbyY_buf_alloc(sizeof (struct group), NSS_BUFLEN_GROUP); 663 if (buf == 0) 664 return (NSS_UNAVAIL); 665 666 grp = (struct group *)buf->result; 667 668 parsestat = (*gbm->str2ent)(instr, instr_len, 669 grp, buf->buffer, buf->buflen); 670 671 if (parsestat != NSS_STR_PARSE_SUCCESS) { 672 _nss_XbyY_buf_free(buf); 673 return (NSS_NOTFOUND); /* === ? */ 674 } 675 676 if (grp->gr_mem) { 677 for (memp = grp->gr_mem; (memp) && ((mem = *memp) != 0); 678 memp++) { 679 if (strcmp(mem, username) == 0) { 680 gid_t gid = grp->gr_gid; 681 gid_t *gidp = gbm->gid_array; 682 int numgids = gbm->numgids; 683 int i; 684 685 _nss_XbyY_buf_free(buf); 686 687 for (i = 0; i < numgids && *gidp != gid; i++, 688 gidp++) { 689 ; 690 } 691 if (i >= numgids) { 692 if (i >= gbm->maxgids) { 693 /* Filled the array; stop searching */ 694 return (NSS_SUCCESS); 695 } 696 *gidp = gid; 697 gbm->numgids = numgids + 1; 698 } 699 return (NSS_NOTFOUND); /* Explained in */ 700 /* <nss_dbdefs.h> */ 701 } 702 } 703 } 704 _nss_XbyY_buf_free(buf); 705 return (NSS_NOTFOUND); 706 } 707