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
_nss_initf_project(nss_db_params_t * p)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
_setprojent(void)68 _setprojent(void)
69 {
70 nss_setent(&db_root, _nss_initf_project, &context);
71 }
72
73 void
_endprojent(void)74 _endprojent(void)
75 {
76 nss_endent(&db_root, _nss_initf_project, &context);
77 nss_delete(&db_root);
78 }
79
80 struct project *
_getprojent(struct project * result,void * buffer,size_t buflen)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 *
_fgetprojent(FILE * f,struct project * result,void * buffer,size_t buflen)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 *
_getprojbyid(projid_t projid,struct project * result,void * buffer,size_t buflen)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 *
_getprojbyname(const char * name,struct project * result,void * buffer,size_t buflen)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
ismember(struct project * proj,const char * user,gid_t gid,int is_default)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 *
_getdefaultproj(const char * user,struct project * result,void * buffer,size_t buflen)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
_inproj(const char * user,const char * name,void * buffer,size_t buflen)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
_getprojidbyname(const char * name)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 *
gettok(char ** nextpp,char sep)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
str2project(const char * instr,int lenstr,void * ent,char * buffer,int buflen)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