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 *, char *, int); 52 53 static DEFINE_NSS_DB_ROOT(db_root); 54 static DEFINE_NSS_GETENT(context); 55 56 #define USE_NETID_STR "NETID_AUTHORITATIVE=TRUE" 57 58 void 59 _nss_initf_group(nss_db_params_t *p) 60 { 61 p->name = NSS_DBNAM_GROUP; 62 p->default_config = NSS_DEFCONF_GROUP; 63 } 64 65 #include <getxby_door.h> 66 #include <sys/door.h> 67 68 struct group * 69 _uncached_getgrnam_r(const char *name, struct group *result, char *buffer, 70 int buflen); 71 72 struct group * 73 _uncached_getgrgid_r(gid_t gid, struct group *result, char *buffer, int buflen); 74 75 /* 76 * POSIX.1c Draft-6 version of the function getgrnam_r. 77 * It was implemented by Solaris 2.3. 78 */ 79 struct group * 80 _getgrnam_r(const char *name, struct group *result, char *buffer, int buflen) 81 { 82 nss_XbyY_args_t arg; 83 84 if (name == (const char *)NULL) { 85 errno = ERANGE; 86 return (NULL); 87 } 88 NSS_XbyY_INIT(&arg, result, buffer, buflen, str2group); 89 arg.key.name = name; 90 (void) nss_search(&db_root, _nss_initf_group, 91 NSS_DBOP_GROUP_BYNAME, &arg); 92 return ((struct group *)NSS_XbyY_FINI(&arg)); 93 } 94 95 /* 96 * POSIX.1c Draft-6 version of the function getgrgid_r. 97 * It was implemented by Solaris 2.3. 98 */ 99 struct group * 100 _getgrgid_r(gid_t gid, struct group *result, char *buffer, int buflen) 101 { 102 nss_XbyY_args_t arg; 103 104 NSS_XbyY_INIT(&arg, result, buffer, buflen, str2group); 105 arg.key.gid = gid; 106 (void) nss_search(&db_root, _nss_initf_group, 107 NSS_DBOP_GROUP_BYGID, &arg); 108 return ((struct group *)NSS_XbyY_FINI(&arg)); 109 } 110 111 struct group * 112 _uncached_getgrgid_r(gid_t gid, struct group *result, char *buffer, 113 int buflen) 114 { 115 nss_XbyY_args_t arg; 116 117 NSS_XbyY_INIT(&arg, result, buffer, buflen, str2group); 118 arg.key.gid = gid; 119 (void) nss_search(&db_root, _nss_initf_group, 120 NSS_DBOP_GROUP_BYGID, &arg); 121 return ((struct group *)NSS_XbyY_FINI(&arg)); 122 } 123 124 /* 125 * POSIX.1c standard version of the function getgrgid_r. 126 * User gets it via static getgrgid_r from the header file. 127 */ 128 int 129 __posix_getgrgid_r(gid_t gid, struct group *grp, char *buffer, 130 size_t bufsize, struct group **result) 131 { 132 int nerrno = 0; 133 int oerrno = errno; 134 135 errno = 0; 136 if ((*result = _getgrgid_r(gid, grp, buffer, (uintptr_t)bufsize)) 137 == NULL) { 138 nerrno = errno; 139 } 140 errno = oerrno; 141 return (nerrno); 142 } 143 144 extern struct group * 145 _getgrnam_r(const char *name, struct group *result, char *buffer, 146 int buflen); 147 148 struct group * 149 _uncached_getgrnam_r(const char *name, struct group *result, char *buffer, 150 int buflen) 151 { 152 nss_XbyY_args_t arg; 153 154 NSS_XbyY_INIT(&arg, result, buffer, buflen, str2group); 155 arg.key.name = name; 156 (void) nss_search(&db_root, _nss_initf_group, 157 NSS_DBOP_GROUP_BYNAME, &arg); 158 return ((struct group *)NSS_XbyY_FINI(&arg)); 159 } 160 161 /* 162 * POSIX.1c standard version of the function getgrnam_r. 163 * User gets it via static getgrnam_r from the header file. 164 */ 165 int 166 __posix_getgrnam_r(const char *name, struct group *grp, char *buffer, 167 size_t bufsize, struct group **result) 168 { 169 int nerrno = 0; 170 int oerrno = errno; 171 172 if ((*result = _getgrnam_r(name, grp, buffer, (uintptr_t)bufsize)) 173 == NULL) { 174 nerrno = errno; 175 } 176 errno = oerrno; 177 return (nerrno); 178 } 179 180 void 181 setgrent(void) 182 { 183 nss_setent(&db_root, _nss_initf_group, &context); 184 } 185 186 void 187 endgrent(void) 188 { 189 nss_endent(&db_root, _nss_initf_group, &context); 190 nss_delete(&db_root); 191 } 192 193 struct group * 194 getgrent_r(struct group *result, char *buffer, int buflen) 195 { 196 nss_XbyY_args_t arg; 197 char *nam; 198 199 /* In getXXent_r(), protect the unsuspecting caller from +/- entries */ 200 201 do { 202 NSS_XbyY_INIT(&arg, result, buffer, buflen, str2group); 203 /* No key to fill in */ 204 (void) nss_getent(&db_root, _nss_initf_group, &context, &arg); 205 } while (arg.returnval != 0 && 206 (nam = ((struct group *)arg.returnval)->gr_name) != 0 && 207 (*nam == '+' || *nam == '-')); 208 209 return ((struct group *)NSS_XbyY_FINI(&arg)); 210 } 211 212 struct group * 213 fgetgrent_r(FILE *f, struct group *result, char *buffer, int buflen) 214 { 215 extern void _nss_XbyY_fgets(FILE *, nss_XbyY_args_t *); 216 nss_XbyY_args_t arg; 217 218 /* ... but in fgetXXent_r, the caller deserves any +/- entry he gets */ 219 220 /* No key to fill in */ 221 NSS_XbyY_INIT(&arg, result, buffer, buflen, str2group); 222 _nss_XbyY_fgets(f, &arg); 223 return ((struct group *)NSS_XbyY_FINI(&arg)); 224 } 225 226 /* 227 * _getgroupsbymember(uname, gid_array, maxgids, numgids): 228 * Private interface for initgroups(). It returns the group ids of 229 * groups of which the specified user is a member. 230 * 231 * Arguments: 232 * username Username of the putative member 233 * gid_array Space in which to return the gids. The first [numgids] 234 * elements are assumed to already contain valid gids. 235 * maxgids Maximum number of elements in gid_array. 236 * numgids Number of elements (normally 0 or 1) that already contain 237 * valid gids. 238 * Return value: 239 * number of valid gids in gid_array (may be zero) 240 * or 241 * -1 (and errno set appropriately) on errors (none currently defined) 242 * 243 * NSS2 Consistency enhancements: 244 * The "files normal" format between an application and nscd for the 245 * NSS_DBOP_GROUP_BYMEMBER nss_search operation is defined to be a 246 * processed array of numgids [up to maxgids] gid_t values. gid_t 247 * values in the array are unique. 248 */ 249 250 static nss_status_t process_cstr(const char *, int, struct nss_groupsbymem *); 251 252 int 253 _getgroupsbymember(const char *username, gid_t gid_array[], 254 int maxgids, int numgids) 255 { 256 struct nss_groupsbymem arg; 257 char defval[BUFSIZ]; 258 FILE *defl; 259 260 arg.username = username; 261 arg.gid_array = gid_array; 262 arg.maxgids = maxgids; 263 arg.numgids = numgids; 264 /* 265 * In backwards compatibility mode, use the old str2group & 266 * process_cstr interfaces. Ditto within nscd processing. 267 */ 268 arg.str2ent = str2group; 269 arg.process_cstr = process_cstr; 270 271 /* 272 * The old value being provided here was 0, ie do the quick 273 * way. Given that this was never actually used under NIS 274 * and had the wrong (now corrected) meaning for NIS+ we need 275 * to change the default to be 1 (TRUE) as we now need the 276 * admin to decided to use netid, setting NETID_AUTHORITATIVE 277 * in /etc/default/nss to TRUE gets us a value of 0 for 278 * force_slow_way - don't you just love double negatives ;-) 279 * 280 * We need to do this to preserve the behaviour seen when the 281 * admin makes no changes. 282 */ 283 arg.force_slow_way = 1; 284 285 /* 286 * The "easy" way to do /etc/default/nss is to use the defread() 287 * stuff from libcmd, but since we are in libc we don't want to 288 * link ourselfs against libcmd, so instead we just do it by hand 289 */ 290 291 if ((defl = fopen(__NSW_DEFAULT_FILE, "rF")) != NULL) { 292 while (fgets(defval, sizeof (defval), defl) != NULL) { 293 if (strncmp(USE_NETID_STR, defval, 294 sizeof (USE_NETID_STR) - 1) == 0) { 295 arg.force_slow_way = 0; 296 break; 297 } 298 } 299 (void) fclose(defl); 300 } 301 302 (void) nss_search(&db_root, _nss_initf_group, 303 NSS_DBOP_GROUP_BYMEMBER, &arg); 304 305 return (arg.numgids); 306 } 307 308 309 static char * 310 gettok(char **nextpp, char sep) 311 { 312 char *p = *nextpp; 313 char *q = p; 314 char c; 315 316 if (p == 0) 317 return (0); 318 319 while ((c = *q) != '\0' && c != sep) 320 q++; 321 322 if (c == '\0') 323 *nextpp = 0; 324 else { 325 *q++ = '\0'; 326 *nextpp = q; 327 } 328 return (p); 329 } 330 331 /* 332 * Return values: 0 = success, 1 = parse error, 2 = erange ... 333 * The structure pointer passed in is a structure in the caller's space 334 * wherein the field pointers would be set to areas in the buffer if 335 * need be. instring and buffer should be separate areas. 336 */ 337 int 338 str2group(const char *instr, int lenstr, void *ent, char *buffer, int buflen) 339 { 340 struct group *group = (struct group *)ent; 341 char *p, *next; 342 int black_magic; /* "+" or "-" entry */ 343 char **memlist, **limit; 344 345 if (lenstr + 1 > buflen) 346 return (NSS_STR_PARSE_ERANGE); 347 348 /* 349 * We copy the input string into the output buffer and 350 * operate on it in place. 351 */ 352 if (instr != buffer) { 353 /* Overlapping buffer copies are OK */ 354 (void) memmove(buffer, instr, lenstr); 355 buffer[lenstr] = '\0'; 356 } 357 358 /* quick exit do not entry fill if not needed */ 359 if (ent == (void *)NULL) 360 return (NSS_STR_PARSE_SUCCESS); 361 362 next = buffer; 363 364 /* 365 * Parsers for passwd and group have always been pretty rigid; 366 * we wouldn't want to buck a Unix tradition 367 */ 368 369 group->gr_name = p = gettok(&next, ':'); 370 if (*p == '\0') { 371 /* Empty group-name; not allowed */ 372 return (NSS_STR_PARSE_PARSE); 373 } 374 375 /* Always return at least an empty gr_mem list */ 376 memlist = (char **)ROUND_UP(buffer + lenstr + 1, sizeof (char *)); 377 limit = (char **)ROUND_DOWN(buffer + buflen, sizeof (char *)); 378 *memlist = 0; 379 group->gr_mem = memlist; 380 381 black_magic = (*p == '+' || *p == '-'); 382 if (black_magic) { 383 /* Then the rest of the group entry is optional */ 384 group->gr_passwd = 0; 385 group->gr_gid = 0; 386 } 387 388 group->gr_passwd = p = gettok(&next, ':'); 389 if (p == 0) { 390 if (black_magic) 391 return (NSS_STR_PARSE_SUCCESS); 392 else 393 return (NSS_STR_PARSE_PARSE); 394 } 395 396 p = next; /* gid */ 397 if (p == 0 || *p == '\0') { 398 if (black_magic) 399 return (NSS_STR_PARSE_SUCCESS); 400 else 401 return (NSS_STR_PARSE_PARSE); 402 } 403 if (!black_magic) { 404 group->gr_gid = (gid_t)strtol(p, &next, 10); 405 if (next == p) { 406 /* gid field should be nonempty */ 407 return (NSS_STR_PARSE_PARSE); 408 } 409 /* 410 * gids should be non-negative; anything else 411 * is administrative policy. 412 */ 413 if (group->gr_gid < 0) 414 group->gr_gid = GID_NOBODY; 415 } 416 if (*next++ != ':') { 417 /* Parse error, even for a '+' entry (which should have */ 418 /* an empty gid field, since it's always overridden) */ 419 return (NSS_STR_PARSE_PARSE); 420 } 421 422 /* === Could check and complain if there are any extra colons */ 423 while (memlist < limit) { 424 p = gettok(&next, ','); 425 if (p == 0 || *p == '\0') { 426 *memlist = 0; 427 /* Successfully parsed and stored */ 428 return (NSS_STR_PARSE_SUCCESS); 429 } 430 *memlist++ = p; 431 } 432 /* Out of space; error even for black_magic */ 433 return (NSS_STR_PARSE_ERANGE); 434 } 435 436 static nss_status_t 437 process_cstr(const char *instr, int instr_len, struct nss_groupsbymem *gbm) 438 { 439 /* 440 * It's possible to do a much less inefficient version of this by 441 * selectively duplicating code from str2group(). For now, 442 * however, we'll take the easy way out and implement this on 443 * top of str2group(). 444 */ 445 446 const char *username = gbm->username; 447 nss_XbyY_buf_t *buf; 448 struct group *grp; 449 char **memp; 450 char *mem; 451 int parsestat; 452 453 buf = _nss_XbyY_buf_alloc(sizeof (struct group), NSS_BUFLEN_GROUP); 454 if (buf == 0) 455 return (NSS_UNAVAIL); 456 457 grp = (struct group *)buf->result; 458 459 parsestat = (*gbm->str2ent)(instr, instr_len, 460 grp, buf->buffer, buf->buflen); 461 462 if (parsestat != NSS_STR_PARSE_SUCCESS) { 463 _nss_XbyY_buf_free(buf); 464 return (NSS_NOTFOUND); /* === ? */ 465 } 466 467 if (grp->gr_mem) { 468 for (memp = grp->gr_mem; (memp) && ((mem = *memp) != 0); 469 memp++) { 470 if (strcmp(mem, username) == 0) { 471 gid_t gid = grp->gr_gid; 472 gid_t *gidp = gbm->gid_array; 473 int numgids = gbm->numgids; 474 int i; 475 476 _nss_XbyY_buf_free(buf); 477 478 for (i = 0; i < numgids && *gidp != gid; i++, 479 gidp++) { 480 ; 481 } 482 if (i >= numgids) { 483 if (i >= gbm->maxgids) { 484 /* Filled the array; stop searching */ 485 return (NSS_SUCCESS); 486 } 487 *gidp = gid; 488 gbm->numgids = numgids + 1; 489 } 490 return (NSS_NOTFOUND); /* Explained in */ 491 /* <nss_dbdefs.h> */ 492 } 493 } 494 } 495 _nss_XbyY_buf_free(buf); 496 return (NSS_NOTFOUND); 497 } 498