xref: /illumos-gate/usr/src/lib/libsecdb/common/chkauthattr.c (revision 8d0c3d29bb99f6521f2dc5058a7e4debebad7899)
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 (the "License").
6  * You may not use this file except in compliance with the License.
7  *
8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9  * or http://www.opensolaris.org/os/licensing.
10  * See the License for the specific language governing permissions
11  * and limitations under the License.
12  *
13  * When distributing Covered Code, include this CDDL HEADER in each
14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15  * If applicable, add the following below this CDDL HEADER, with the
16  * fields enclosed by brackets "[]" replaced with your own identifying
17  * information: Portions Copyright [yyyy] [name of copyright owner]
18  *
19  * CDDL HEADER END
20  */
21 /*
22  * Copyright (c) 1999, 2010, Oracle and/or its affiliates. All rights reserved.
23  */
24 
25 #include <alloca.h>
26 #include <stdio.h>
27 #include <stdlib.h>
28 #include <string.h>
29 #include <sys/stat.h>
30 #include <pwd.h>
31 #include <nss_dbdefs.h>
32 #include <deflt.h>
33 #include <auth_attr.h>
34 #include <prof_attr.h>
35 #include <user_attr.h>
36 
37 #define	COPYTOSTACK(dst, csrc)		{	\
38 		size_t len = strlen(csrc) + 1;	\
39 		dst = alloca(len);		\
40 		(void) memcpy(dst, csrc, len);	\
41 	}
42 
43 static kva_t *get_default_attrs(const char *);
44 static void free_default_attrs(kva_t *);
45 
46 /*
47  * Enumeration functions for auths and profiles; the enumeration functions
48  * take a callback with four arguments:
49  *	const char *		profile name (or NULL unless wantattr is false)
50  *	kva_t *			attributes (or NULL unless wantattr is true)
51  *	void *			context
52  *	void *			pointer to the result
53  * When the call back returns non-zero, the enumeration ends.
54  * The function might be NULL but only for profiles as we are always collecting
55  * all the profiles.
56  * Both the auths and the profiles arguments may be NULL.
57  *
58  * These should be the only implementation of the algorithm of "finding me
59  * all the profiles/athorizations/keywords/etc.
60  */
61 
62 #define	CONSUSER_PROFILE_KW		"consprofile"
63 #define	DEF_LOCK_AFTER_RETRIES		"LOCK_AFTER_RETRIES="
64 
65 static struct dfltplcy {
66 	char *attr;
67 	const char *defkw;
68 } dfltply[] = {
69 	/* CONSUSER MUST BE FIRST! */
70 	{ CONSUSER_PROFILE_KW,			DEF_CONSUSER},
71 	{ PROFATTR_AUTHS_KW,			DEF_AUTH},
72 	{ PROFATTR_PROFS_KW,			DEF_PROF},
73 	{ USERATTR_LIMPRIV_KW,			DEF_LIMITPRIV},
74 	{ USERATTR_DFLTPRIV_KW,			DEF_DFLTPRIV},
75 	{ USERATTR_LOCK_AFTER_RETRIES_KW,	DEF_LOCK_AFTER_RETRIES}
76 };
77 
78 #define	NDFLTPLY	(sizeof (dfltply)/sizeof (struct dfltplcy))
79 #define	GETCONSPROF(a)	(kva_match((a), CONSUSER_PROFILE_KW))
80 #define	GETPROF(a)	(kva_match((a), PROFATTR_PROFS_KW))
81 
82 /*
83  * Enumerate profiles from listed profiles.
84  */
85 static int _auth_match_noun(const char *, const char *, size_t, const char *);
86 
87 int
88 _enum_common_p(const char *cprofiles,
89     int (*cb)(const char *, kva_t *, void *, void *),
90     void *ctxt, void *pres, boolean_t wantattr,
91     int *pcnt, char *profs[MAXPROFS])
92 {
93 	char *prof, *last;
94 	char *profiles;
95 	profattr_t *pa;
96 	int i;
97 	int res = 0;
98 
99 	if (cprofiles == NULL)
100 		return (0);
101 
102 	if (*pcnt > 0 && strcmp(profs[*pcnt - 1], PROFILE_STOP) == NULL)
103 		return (0);
104 
105 	COPYTOSTACK(profiles, cprofiles)
106 
107 	while (prof = strtok_r(profiles, KV_SEPSTR, &last)) {
108 
109 		profiles = NULL;	/* For next iterations of strtok_r */
110 
111 		for (i = 0; i < *pcnt; i++)
112 			if (strcmp(profs[i], prof) == 0)
113 				goto cont;
114 
115 		if (*pcnt >= MAXPROFS)		/* oops: too many profs */
116 			return (-1);
117 
118 		/* Add it */
119 		profs[(*pcnt)++] = strdup(prof);
120 
121 		if (strcmp(profs[*pcnt - 1], PROFILE_STOP) == 0)
122 			break;
123 
124 		/* find the profiles for this profile */
125 		pa = getprofnam(prof);
126 
127 		if (cb != NULL && (!wantattr || pa != NULL && pa->attr != NULL))
128 			res = cb(prof, pa ? pa->attr : NULL, ctxt, pres);
129 
130 		if (pa != NULL) {
131 			if (res == 0 && pa->attr != NULL) {
132 				res = _enum_common_p(GETPROF(pa->attr), cb,
133 				    ctxt, pres, wantattr, pcnt, profs);
134 			}
135 			free_profattr(pa);
136 		}
137 		if (res != 0)
138 			return (res);
139 cont:
140 		continue;
141 	}
142 	return (res);
143 }
144 
145 /*
146  * Enumerate all attributes associated with a username and the profiles
147  * associated with the user.
148  */
149 static int
150 _enum_common(const char *username,
151     int (*cb)(const char *, kva_t *, void *, void *),
152     void *ctxt, void *pres, boolean_t wantattr)
153 {
154 	userattr_t *ua;
155 	int res = 0;
156 	int cnt = 0;
157 	char *profs[MAXPROFS];
158 	kva_t *kattrs;
159 
160 	if (cb == NULL)
161 		return (-1);
162 
163 	ua = getusernam(username);
164 
165 	if (ua != NULL) {
166 		if (ua->attr != NULL) {
167 			if (wantattr)
168 				res = cb(NULL, ua->attr, ctxt, pres);
169 			if (res == 0) {
170 				res = _enum_common_p(GETPROF(ua->attr),
171 				    cb, ctxt, pres, wantattr, &cnt, profs);
172 			}
173 		}
174 		free_userattr(ua);
175 		if (res != 0) {
176 			free_proflist(profs, cnt);
177 			return (res);
178 		}
179 	}
180 
181 	if ((cnt == 0 || strcmp(profs[cnt-1], PROFILE_STOP) != 0) &&
182 	    (kattrs = get_default_attrs(username)) != NULL) {
183 
184 		res = _enum_common_p(GETCONSPROF(kattrs), cb, ctxt, pres,
185 		    wantattr, &cnt, profs);
186 
187 		if (res == 0) {
188 			res = _enum_common_p(GETPROF(kattrs), cb, ctxt, pres,
189 			    wantattr, &cnt, profs);
190 		}
191 
192 		if (res == 0 && wantattr)
193 			res = cb(NULL, kattrs, ctxt, pres);
194 
195 		free_default_attrs(kattrs);
196 	}
197 
198 	free_proflist(profs, cnt);
199 
200 	return (res);
201 }
202 
203 /*
204  * Enumerate profiles with a username argument.
205  */
206 int
207 _enum_profs(const char *username,
208     int (*cb)(const char *, kva_t *, void *, void *),
209     void *ctxt, void *pres)
210 {
211 	return (_enum_common(username, cb, ctxt, pres, B_FALSE));
212 }
213 
214 /*
215  * Enumerate attributes with a username argument.
216  */
217 int
218 _enum_attrs(const char *username,
219     int (*cb)(const char *, kva_t *, void *, void *),
220     void *ctxt, void *pres)
221 {
222 	return (_enum_common(username, cb, ctxt, pres, B_TRUE));
223 }
224 
225 
226 /*
227  * Enumerate authorizations in the "auths" argument.
228  */
229 static int
230 _enum_auths_a(const char *cauths, int (*cb)(const char *, void *, void *),
231     void *ctxt, void *pres)
232 {
233 	char *auth, *last, *auths;
234 	int res = 0;
235 
236 	if (cauths == NULL || cb == NULL)
237 		return (0);
238 
239 	COPYTOSTACK(auths, cauths)
240 
241 	while (auth = strtok_r(auths, KV_SEPSTR, &last)) {
242 		auths = NULL;		/* For next iterations of strtok_r */
243 
244 		res = cb(auth, ctxt, pres);
245 
246 		if (res != 0)
247 			return (res);
248 	}
249 	return (res);
250 }
251 
252 /*
253  * Magic struct and function to allow using the _enum_attrs functions to
254  * enumerate the authorizations.
255  */
256 typedef struct ccomm2auth {
257 	int (*cb)(const char *, void *, void *);
258 	void *ctxt;
259 } ccomm2auth;
260 
261 /*ARGSUSED*/
262 static int
263 comm2auth(const char *name, kva_t *attr, void *ctxt, void *pres)
264 {
265 	ccomm2auth *ca = ctxt;
266 	char *auths;
267 
268 	/* Note: PROFATTR_AUTHS_KW is equal to USERATTR_AUTHS_KW */
269 	auths = kva_match(attr, PROFATTR_AUTHS_KW);
270 	return (_enum_auths_a(auths, ca->cb, ca->ctxt, pres));
271 }
272 
273 /*
274  * Enumerate authorizations for username.
275  */
276 int
277 _enum_auths(const char *username,
278     int (*cb)(const char *, void *, void *),
279     void *ctxt, void *pres)
280 {
281 	ccomm2auth c2a;
282 
283 	if (cb == NULL)
284 		return (-1);
285 
286 	c2a.cb = cb;
287 	c2a.ctxt = ctxt;
288 
289 	return (_enum_common(username, comm2auth, &c2a, pres, B_TRUE));
290 }
291 
292 int
293 _auth_match_noun(const char *pattern, const char *auth,
294     size_t auth_len, const char *auth_noun)
295 {
296 	size_t pattern_len;
297 	char *grant;
298 	char *pattern_noun;
299 	char *slash;
300 
301 	pattern_len = strlen(pattern);
302 	/*
303 	 * If the specified authorization has a trailing object
304 	 * and the current authorization we're checking also has
305 	 * a trailing object, the object names must match.
306 	 *
307 	 * If there is no object name failure, then we must
308 	 * check for an exact match of the two authorizations
309 	 */
310 	if (auth_noun != NULL) {
311 		if ((slash = strchr(pattern, KV_OBJECTCHAR)) != NULL) {
312 			pattern_noun = slash + 1;
313 			pattern_len -= strlen(slash);
314 			if (strcmp(pattern_noun, auth_noun) != 0)
315 				return (0);
316 		} else if ((auth_len == pattern_len) &&
317 		    (strncmp(pattern, auth, pattern_len) == 0)) {
318 			return (1);
319 		}
320 	}
321 
322 	/*
323 	 * If the wildcard is not in the last position in the string, don't
324 	 * match against it.
325 	 */
326 	if (pattern[pattern_len-1] != KV_WILDCHAR)
327 		return (0);
328 
329 	/*
330 	 * If the strings are identical up to the wildcard and auth does not
331 	 * end in "grant", then we have a match.
332 	 */
333 	if (strncmp(pattern, auth, pattern_len - 1) == 0) {
334 		grant = strrchr(auth, '.');
335 		if (grant != NULL) {
336 			if (strncmp(grant + 1, "grant", 5) != NULL)
337 				return (1);
338 		}
339 	}
340 	return (0);
341 }
342 
343 int
344 _auth_match(const char *pattern, const char *auth)
345 {
346 	return (_auth_match_noun(pattern, auth, strlen(auth), NULL));
347 }
348 
349 static int
350 _is_authorized(const char *auth, void *authname, void *res)
351 {
352 	int *resp = res;
353 	char	*authname_noun;
354 	char	*slash;
355 	size_t	auth_len;
356 	size_t	noun_len;
357 
358 	auth_len = strlen(authname);
359 	if ((slash = strchr(authname, KV_OBJECTCHAR)) != NULL) {
360 		authname_noun = slash + 1;
361 		noun_len = strlen(slash);
362 		auth_len -= noun_len;
363 	} else {
364 		authname_noun = NULL;
365 	}
366 
367 	if (strcmp(authname, auth) == 0) {
368 		/* exact match, we're done */
369 		*resp = 1;
370 		return (1);
371 	} else if (noun_len || strchr(auth, KV_WILDCHAR) != NULL) {
372 		if (_auth_match_noun(auth, authname,
373 		    auth_len, authname_noun)) {
374 			*resp = 1;
375 			return (1);
376 		}
377 	}
378 
379 	return (0);
380 }
381 
382 int
383 chkauthattr(const char *authname, const char *username)
384 {
385 	int		auth_granted = 0;
386 
387 	if (authname == NULL || username == NULL)
388 		return (0);
389 
390 	(void) _enum_auths(username, _is_authorized, (char *)authname,
391 	    &auth_granted);
392 
393 	return (auth_granted);
394 }
395 
396 #define	CONSOLE_USER_LINK "/dev/vt/console_user"
397 
398 static int
399 is_cons_user(const char *user)
400 {
401 	struct stat	cons;
402 	struct passwd	pw;
403 	char		pwbuf[NSS_BUFLEN_PASSWD];
404 
405 	if (user == NULL) {
406 		return (0);
407 	}
408 	if (stat(CONSOLE_USER_LINK, &cons) == -1) {
409 		return (0);
410 	}
411 	if (getpwnam_r(user, &pw, pwbuf, sizeof (pwbuf)) == NULL) {
412 		return (0);
413 	}
414 
415 	return (pw.pw_uid == cons.st_uid);
416 }
417 
418 static void
419 free_default_attrs(kva_t *kva)
420 {
421 	int i;
422 
423 	for (i = 0; i < kva->length; i++)
424 		free(kva->data[i].value);
425 
426 	free(kva);
427 }
428 
429 /*
430  * Return the default attributes; this are ignored when a STOP profile
431  * was found.
432  */
433 static kva_t *
434 get_default_attrs(const char *user)
435 {
436 	void *defp;
437 	kva_t *kva;
438 	int i;
439 
440 	kva = malloc(sizeof (kva_t) + sizeof (kv_t) * NDFLTPLY);
441 
442 	if (kva == NULL)
443 		return (NULL);
444 
445 	kva->data = (kv_t *)(void *)&kva[1];
446 	kva->length = 0;
447 
448 	if ((defp = defopen_r(AUTH_POLICY)) == NULL)
449 		goto return_null;
450 
451 	for (i = is_cons_user(user) ? 0 : 1; i < NDFLTPLY; i++) {
452 		char *cp = defread_r(dfltply[i].defkw, defp);
453 
454 		if (cp == NULL)
455 			continue;
456 		if ((cp = strdup(cp)) == NULL)
457 			goto return_null;
458 
459 		kva->data[kva->length].key = dfltply[i].attr;
460 		kva->data[kva->length++].value = cp;
461 	}
462 
463 	(void) defclose_r(defp);
464 	return (kva);
465 
466 return_null:
467 	if (defp != NULL)
468 		(void) defclose_r(defp);
469 
470 	free_default_attrs(kva);
471 	return (NULL);
472 }
473