xref: /illumos-gate/usr/src/lib/libproject/common/getprojent.c (revision 9164a50bf932130cbb5097a16f6986873ce0e6e5)
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