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