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