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