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, Version 1.0 only 6 * (the "License"). You may not use this file except in compliance 7 * with the License. 8 * 9 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 10 * or http://www.opensolaris.org/os/licensing. 11 * See the License for the specific language governing permissions 12 * and limitations under the License. 13 * 14 * When distributing Covered Code, include this CDDL HEADER in each 15 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 16 * If applicable, add the following below this CDDL HEADER, with the 17 * fields enclosed by brackets "[]" replaced with your own identifying 18 * information: Portions Copyright [yyyy] [name of copyright owner] 19 * 20 * CDDL HEADER END 21 */ 22 /* 23 * Copyright (c) 1999-2001 by Sun Microsystems, Inc. 24 * All rights reserved. 25 */ 26 27 #pragma ident "%Z%%M% %I% %E% SMI" 28 29 #include <sys/types.h> 30 #include <user_attr.h> 31 #include <pwd.h> 32 #include <grp.h> 33 #include <userdefs.h> 34 #include <project.h> 35 #include <memory.h> 36 #include <nss_dbdefs.h> 37 #include <stdio.h> 38 #include <stdlib.h> 39 #include <string.h> 40 #include <sys/param.h> 41 #include <sys/mman.h> 42 43 #pragma weak setprojent = _setprojent 44 #pragma weak endprojent = _endprojent 45 #pragma weak getprojent = _getprojent 46 #pragma weak fgetprojent = _fgetprojent 47 #pragma weak getprojbyid = _getprojbyid 48 #pragma weak getprojbyname = _getprojbyname 49 #pragma weak getdefaultproj = _getdefaultproj 50 #pragma weak inproj = _inproj 51 #pragma weak getprojidbyname = _getprojidbyname 52 53 #define DEFAULT_PROJECT 1 54 #define NORMAL_PROJECT 0 55 56 static int ismember(struct project *, const char *, gid_t, int); 57 static int str2project(const char *, int, void *, char *, int); 58 59 static DEFINE_NSS_DB_ROOT(db_root); 60 static DEFINE_NSS_GETENT(context); 61 62 void 63 _nss_initf_project(nss_db_params_t *p) 64 { 65 p->name = NSS_DBNAM_PROJECT; 66 p->default_config = NSS_DEFCONF_PROJECT; 67 } 68 69 void 70 _setprojent(void) 71 { 72 nss_setent(&db_root, _nss_initf_project, &context); 73 } 74 75 void 76 _endprojent(void) 77 { 78 nss_endent(&db_root, _nss_initf_project, &context); 79 nss_delete(&db_root); 80 } 81 82 struct project * 83 _getprojent(struct project *result, void *buffer, size_t buflen) 84 { 85 nss_XbyY_args_t arg; 86 87 NSS_XbyY_INIT(&arg, result, buffer, buflen, str2project); 88 (void) nss_getent(&db_root, _nss_initf_project, &context, &arg); 89 return ((struct project *)NSS_XbyY_FINI(&arg)); 90 } 91 92 struct project * 93 _fgetprojent(FILE *f, struct project *result, void *buffer, size_t buflen) 94 { 95 extern void _nss_XbyY_fgets(FILE *, nss_XbyY_args_t *); 96 nss_XbyY_args_t arg; 97 98 NSS_XbyY_INIT(&arg, result, buffer, buflen, str2project); 99 _nss_XbyY_fgets(f, &arg); 100 return ((struct project *)NSS_XbyY_FINI(&arg)); 101 } 102 103 struct project * 104 _getprojbyid(projid_t projid, struct project *result, 105 void *buffer, size_t buflen) 106 { 107 nss_XbyY_args_t arg; 108 109 NSS_XbyY_INIT(&arg, result, buffer, buflen, str2project); 110 arg.key.projid = projid; 111 (void) nss_search(&db_root, _nss_initf_project, 112 NSS_DBOP_PROJECT_BYID, &arg); 113 return ((struct project *)NSS_XbyY_FINI(&arg)); 114 } 115 116 struct project * 117 _getprojbyname(const char *name, struct project *result, 118 void *buffer, size_t buflen) 119 { 120 nss_XbyY_args_t arg; 121 NSS_XbyY_INIT(&arg, result, buffer, buflen, str2project); 122 arg.key.name = name; 123 (void) nss_search(&db_root, _nss_initf_project, 124 NSS_DBOP_PROJECT_BYNAME, &arg); 125 return ((struct project *)NSS_XbyY_FINI(&arg)); 126 } 127 128 /* 129 * The following routine checks if user specified by the second argument 130 * is allowed to join the project specified as project structure in first 131 * argument. Information about user's default group and whether or not 132 * the project specified in the first argument is user's default project 133 * (i.e., user_attr, "default", "user.username", or "group.groupname" 134 * should also be provided. If is_default is set to DEFAULT_PROJECT, 135 * then this function returns 1 (true), unless specified user explicitly 136 * excluded with "!user", or "!group" wildcards. 137 */ 138 static int 139 ismember(struct project *proj, const char *user, gid_t gid, int is_default) 140 { 141 char grbuf[NSS_BUFLEN_GROUP]; 142 char groupname[MAXGLEN + 1]; 143 int res = is_default; 144 struct group grp; 145 int group_ok = 0; 146 char **u, **g; 147 char *member; 148 149 if (getgrgid_r(gid, &grp, grbuf, NSS_BUFLEN_GROUP) != NULL) { 150 group_ok = 1; 151 (void) snprintf(groupname, MAXGLEN, grp.gr_name); 152 } 153 154 /* 155 * Scan project's user list. 156 */ 157 for (u = proj->pj_users; *u; u++) { 158 member = *u; 159 if (member[0] == '!' && 160 (strcmp(member + 1, user) == 0 || 161 strcmp(member + 1, "*") == 0)) 162 return (0); 163 if (strcmp(member, "*") == 0 || strcmp(member, user) == 0) 164 res = 1; 165 } 166 167 /* 168 * Scan project's group list. 169 */ 170 for (g = proj->pj_groups; *g; g++) { 171 member = *g; 172 /* 173 * Check if user's default group is included here. 174 */ 175 if (group_ok) { 176 if (member[0] == '!' && 177 (strcmp(member + 1, groupname) == 0 || 178 strcmp(member + 1, "*") == 0)) 179 return (0); 180 if (strcmp(member, "*") == 0 || 181 strcmp(member, groupname) == 0) 182 res = 1; 183 } 184 /* 185 * Check if user is a member of one of project's groups. 186 */ 187 if (getgrnam_r(member, &grp, grbuf, NSS_BUFLEN_GROUP) != NULL) { 188 for (u = grp.gr_mem; *u; u++) 189 if (strcmp(*u, user) == 0) 190 res = 1; 191 } 192 } 193 return (res); 194 } 195 196 struct project * 197 _getdefaultproj(const char *user, struct project *result, 198 void *buffer, size_t buflen) 199 { 200 char projname[PROJNAME_MAX + 1]; 201 nss_XbyY_args_t arg; 202 userattr_t *uattr; 203 struct passwd p; 204 struct group g; 205 char *attrproj; 206 207 NSS_XbyY_INIT(&arg, result, buffer, buflen, str2project); 208 209 /* 210 * Need user's default group ID for ismember() calls later 211 */ 212 if (getpwnam_r(user, &p, buffer, buflen) == NULL) 213 return (NULL); 214 215 /* 216 * Check user_attr database first 217 */ 218 if ((uattr = getusernam(user)) != NULL) { 219 if ((attrproj = kva_match(uattr->attr, "project")) != NULL) { 220 arg.key.name = attrproj; 221 (void) nss_search(&db_root, _nss_initf_project, 222 NSS_DBOP_PROJECT_BYNAME, &arg); 223 if ((result = NSS_XbyY_FINI(&arg)) != NULL) { 224 free_userattr(uattr); 225 return (result); 226 } 227 } 228 free_userattr(uattr); 229 } 230 231 /* 232 * Check user.{username} and group.{groupname} projects 233 */ 234 (void) snprintf(projname, PROJNAME_MAX, "user.%s", user); 235 arg.key.name = projname; 236 (void) nss_search(&db_root, _nss_initf_project, 237 NSS_DBOP_PROJECT_BYNAME, &arg); 238 if ((result = NSS_XbyY_FINI(&arg)) != NULL && 239 ismember(result, user, p.pw_gid, DEFAULT_PROJECT)) 240 return (result); 241 if (getgrgid_r(p.pw_gid, &g, buffer, buflen) != NULL) { 242 (void) snprintf(projname, PROJNAME_MAX, "group.%s", g.gr_name); 243 arg.key.name = projname; 244 (void) nss_search(&db_root, _nss_initf_project, 245 NSS_DBOP_PROJECT_BYNAME, &arg); 246 if ((result = NSS_XbyY_FINI(&arg)) != NULL && 247 ismember(result, user, p.pw_gid, DEFAULT_PROJECT)) 248 return (result); 249 } 250 arg.key.name = "default"; 251 (void) nss_search(&db_root, _nss_initf_project, 252 NSS_DBOP_PROJECT_BYNAME, &arg); 253 if ((result = NSS_XbyY_FINI(&arg)) != NULL && 254 ismember(result, user, p.pw_gid, DEFAULT_PROJECT)) 255 return (result); 256 return (NULL); 257 } 258 259 int 260 _inproj(const char *user, const char *name, void *buffer, size_t buflen) 261 { 262 char projname[PROJNAME_MAX + 1]; 263 char grbuf[NSS_BUFLEN_GROUP]; 264 nss_XbyY_args_t arg; 265 struct project proj; 266 struct passwd pwd; 267 userattr_t *uattr; 268 struct group grp; 269 char *attrproj; 270 gid_t gid; 271 272 NSS_XbyY_INIT(&arg, &proj, buffer, buflen, str2project); 273 274 /* 275 * 0. Sanity checks. 276 */ 277 if (getpwnam_r(user, &pwd, buffer, buflen) == NULL) 278 return (0); /* user does not exist */ 279 gid = pwd.pw_gid; 280 if (getprojbyname(name, &proj, buffer, buflen) == NULL) 281 return (0); /* project does not exist */ 282 283 /* 284 * 1. Check for special "default" project. 285 */ 286 if (strcmp("default", name) == 0) 287 return (ismember(&proj, user, gid, DEFAULT_PROJECT)); 288 289 /* 290 * 2. Check user_attr database. 291 */ 292 if ((uattr = getusernam(user)) != NULL) { 293 if ((attrproj = kva_match(uattr->attr, "project")) != NULL) { 294 if (strcmp(attrproj, name) == 0) { 295 free_userattr(uattr); 296 return (ismember(&proj, user, gid, 297 DEFAULT_PROJECT)); 298 } 299 } 300 free_userattr(uattr); 301 } 302 303 /* 304 * 3. Check if this is a special "user.username" project. 305 * 306 * User "username" is considered to be a member of project 307 * "user.username" even if project's user lists do not 308 * include "username". 309 */ 310 (void) snprintf(projname, PROJNAME_MAX, "user.%s", user); 311 if (strcmp(projname, name) == 0) 312 return (ismember(&proj, user, gid, DEFAULT_PROJECT)); 313 314 /* 315 * 4. Check if this is a special "group.groupname" project. 316 * 317 * User "username" with default group "groupname" is considered 318 * to be a member of project "group.groupname" even if project's 319 * group list does not include "groupname". 320 */ 321 if (getgrgid_r(gid, &grp, grbuf, NSS_LINELEN_GROUP) != NULL) { 322 (void) snprintf(projname, PROJNAME_MAX, 323 "group.%s", grp.gr_name); 324 if (strcmp(projname, name) == 0) 325 return (ismember(&proj, user, gid, DEFAULT_PROJECT)); 326 } 327 328 /* 329 * 5. Handle all other (non-default) projects. 330 */ 331 return (ismember(&proj, user, gid, NORMAL_PROJECT)); 332 } 333 334 /* 335 * Just a quick wrapper around getprojbyname so that the caller does not 336 * need to allocate the buffer. 337 */ 338 projid_t 339 _getprojidbyname(const char *name) 340 { 341 struct project proj; 342 char buf[PROJECT_BUFSZ]; 343 344 if (getprojbyname(name, &proj, &buf, PROJECT_BUFSZ) != NULL) 345 return (proj.pj_projid); 346 else 347 return ((projid_t)-1); 348 } 349 350 static char * 351 gettok(char **nextpp, char sep) 352 { 353 char *p = *nextpp; 354 char *q = p; 355 char c; 356 357 if (p == NULL) 358 return (NULL); 359 while ((c = *q) != '\0' && c != sep) 360 q++; 361 if (c == '\0') 362 *nextpp = 0; 363 else { 364 *q++ = '\0'; 365 *nextpp = q; 366 } 367 return (p); 368 } 369 370 371 /* 372 * Return values: 0 = success, 1 = parse error, 2 = erange ... 373 * The structure pointer passed in is a structure in the caller's space 374 * wherein the field pointers would be set to areas in the buffer if 375 * need be. instring and buffer should be separate areas. 376 */ 377 static int 378 str2project(const char *instr, int lenstr, void *ent, char *buffer, int buflen) 379 { 380 struct project *project = ent; 381 char *p, *next; 382 char *users, *groups; 383 char **uglist; 384 char **limit; 385 386 if (lenstr + 1 > buflen) 387 return (NSS_STR_PARSE_ERANGE); 388 /* 389 * We copy the input string into the output buffer and 390 * operate on it in place. 391 */ 392 (void) memcpy(buffer, instr, lenstr); 393 buffer[lenstr] = '\0'; 394 next = buffer; 395 396 limit = (char **)ROUND_DOWN(buffer + buflen, sizeof (char *)); 397 398 /* 399 * Parsers for passwd and group have always been pretty rigid; 400 * we wouldn't want to buck a Unix tradition 401 */ 402 p = gettok(&next, ':'); 403 if (p == NULL || *p == '\0' || strlen(p) > PROJNAME_MAX) { 404 /* 405 * empty or very long project names are not allowed 406 */ 407 return (NSS_STR_PARSE_ERANGE); 408 } 409 project->pj_name = p; 410 411 p = gettok(&next, ':'); 412 if (p == NULL || *p == '\0') { 413 /* 414 * projid field shouldn't be empty 415 */ 416 return (NSS_STR_PARSE_PARSE); 417 } 418 project->pj_projid = (projid_t)strtol(p, NULL, 10); 419 if (project->pj_projid < 0) { 420 /* 421 * projids should be positive number 422 */ 423 project->pj_projid = 0; 424 return (NSS_STR_PARSE_PARSE); 425 } 426 427 p = gettok(&next, ':'); 428 if (p == NULL) { 429 /* 430 * comment field can be empty but should not be last field 431 */ 432 return (NSS_STR_PARSE_PARSE); 433 } 434 project->pj_comment = p; 435 436 if ((users = gettok(&next, ':')) == NULL) { 437 /* 438 * users field should not be last field 439 */ 440 return (NSS_STR_PARSE_PARSE); 441 } 442 443 if ((groups = gettok(&next, ':')) == NULL) { 444 /* 445 * groups field should not be last field 446 */ 447 return (NSS_STR_PARSE_PARSE); 448 } 449 450 if (next == NULL) { 451 /* 452 * attributes field should be last 453 */ 454 return (NSS_STR_PARSE_PARSE); 455 } 456 457 project->pj_attr = next; 458 459 uglist = (char **)ROUND_UP(buffer + lenstr + 1, sizeof (char *)); 460 *uglist = NULL; 461 project->pj_users = uglist; 462 while (uglist < limit) { 463 p = gettok(&users, ','); 464 if (p == NULL || *p == '\0') { 465 *uglist = 0; 466 break; 467 } 468 *uglist++ = p; 469 } 470 if (uglist >= limit) 471 return (NSS_STR_PARSE_ERANGE); 472 473 uglist++; 474 *uglist = NULL; 475 project->pj_groups = uglist; 476 while (uglist < limit) { 477 p = gettok(&groups, ','); 478 if (p == NULL || *p == '\0') { 479 *uglist = 0; 480 break; 481 } 482 *uglist++ = p; 483 } 484 if (uglist >= limit) 485 return (NSS_STR_PARSE_ERANGE); 486 487 return (NSS_STR_PARSE_SUCCESS); 488 } 489