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