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
56static int ismember(struct project *, const char *, gid_t, int);
57static int str2project(const char *, int, void *, char *, int);
58
59static DEFINE_NSS_DB_ROOT(db_root);
60static DEFINE_NSS_GETENT(context);
61
62void
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
69void
70_setprojent(void)
71{
72	nss_setent(&db_root, _nss_initf_project, &context);
73}
74
75void
76_endprojent(void)
77{
78	nss_endent(&db_root, _nss_initf_project, &context);
79	nss_delete(&db_root);
80}
81
82struct 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
92struct 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
103struct 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
116struct 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 */
138static int
139ismember(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
196struct 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
259int
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 */
338projid_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
350static char *
351gettok(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 */
377static int
378str2project(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