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