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