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