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