1/* $NetBSD: secmodel.c,v 1.1 2011/12/04 19:24:59 jym Exp $ */
2/*-
3 * Copyright (c) 2011 Elad Efrat <elad@NetBSD.org>
4 * All rights reserved.
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
9 * 1. Redistributions of source code must retain the above copyright
10 *    notice, this list of conditions and the following disclaimer.
11 * 2. Redistributions in binary form must reproduce the above copyright
12 *    notice, this list of conditions and the following disclaimer in the
13 *    documentation and/or other materials provided with the distribution.
14 * 3. The name of the author may not be used to endorse or promote products
15 *    derived from this software without specific prior written permission.
16 *
17 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
18 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
19 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
20 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
21 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
22 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
26 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 */
28
29#include <sys/types.h>
30#include <sys/param.h>
31#include <sys/errno.h>
32
33#include <sys/atomic.h>
34#include <sys/kauth.h>
35#include <sys/kmem.h>
36#include <sys/queue.h>
37#include <sys/rwlock.h>
38#include <secmodel/secmodel.h>
39#include <prop/proplib.h>
40
41/* List of secmodels, parameters, and lock. */
42static LIST_HEAD(, secmodel_descr) secmodels =
43    LIST_HEAD_INITIALIZER(secmodels);
44static unsigned int secmodel_copy_cred_on_fork = false;
45static krwlock_t secmodels_lock;
46static int nsecmodels = 0; /* number of registered secmodels */
47
48static int secmodel_plug(secmodel_t);
49static int secmodel_unplug(secmodel_t);
50
51int
52secmodel_nsecmodels(void)
53{
54
55	return nsecmodels;
56}
57
58void
59secmodel_init(void)
60{
61
62	rw_init(&secmodels_lock);
63
64	secmodel_copy_cred_on_fork = false;
65}
66
67/*
68 * Register a new secmodel.
69 */
70int
71secmodel_register(secmodel_t *secmodel, const char *id, const char *name,
72		  prop_dictionary_t behavior,
73		  secmodel_eval_t eval, secmodel_setinfo_t setinfo)
74{
75	int err;
76	secmodel_t sm;
77
78	sm = kmem_alloc(sizeof(*sm), KM_SLEEP);
79
80	sm->sm_id = id;
81	sm->sm_name = name;
82	sm->sm_behavior = behavior;
83	sm->sm_eval = eval;
84	sm->sm_setinfo = setinfo;
85
86	err = secmodel_plug(sm);
87	if (err == 0) {
88		atomic_inc_uint(&nsecmodels);
89	} else {
90		kmem_free(sm, sizeof(*sm));
91		sm = NULL;
92	}
93
94	*secmodel = sm;
95	return err;
96}
97
98/*
99 * Deregister a secmodel.
100 */
101int
102secmodel_deregister(secmodel_t sm)
103{
104	int error;
105
106	error = secmodel_unplug(sm);
107	if (error == 0) {
108		atomic_dec_uint(&nsecmodels);
109		kmem_free(sm, sizeof(*sm));
110	}
111
112	return error;
113}
114
115/*
116 * Lookup a secmodel by its id.
117 *
118 * Requires "secmodels_lock" handling by the caller.
119 */
120static secmodel_t
121secmodel_lookup(const char *id)
122{
123	secmodel_t tsm;
124
125	KASSERT(rw_lock_held(&secmodels_lock));
126
127	LIST_FOREACH(tsm, &secmodels, sm_list) {
128		if (strcasecmp(tsm->sm_id, id) == 0) {
129			return tsm;
130		}
131	}
132
133	return NULL;
134}
135
136/*
137 * Adjust system-global secmodel behavior following the addition
138 * or removal of a secmodel.
139 *
140 * Requires "secmodels_lock" to be held by the caller.
141 */
142static void
143secmodel_adjust_behavior(secmodel_t sm, bool added)
144{
145	bool r, b;
146
147	KASSERT(rw_write_held(&secmodels_lock));
148
149#define	ADJUST_COUNTER(which, added)		\
150	do {					\
151		if (added) {			\
152			(which)++;		\
153		} else {			\
154			if ((which) > 0)	\
155				(which)--;	\
156		}				\
157	} while (/*CONSTCOND*/0)
158
159	/* Copy credentials on fork? */
160	r = prop_dictionary_get_bool(sm->sm_behavior, "copy-cred-on-fork", &b);
161	if (r) {
162		ADJUST_COUNTER(secmodel_copy_cred_on_fork, added);
163	}
164
165#undef ADJUST_COUNTER
166}
167
168static int
169secmodel_plug(secmodel_t sm)
170{
171	secmodel_t tsm;
172	int error = 0;
173
174	if (sm == NULL)
175		return EFAULT;
176
177	/* Check if the secmodel is already present. */
178	rw_enter(&secmodels_lock, RW_WRITER);
179	tsm = secmodel_lookup(sm->sm_id);
180	if (tsm != NULL) {
181		error = EEXIST;
182		goto out;
183	}
184
185	/* Add the secmodel. */
186	LIST_INSERT_HEAD(&secmodels, sm, sm_list);
187
188	/* Adjust behavior. */
189	secmodel_adjust_behavior(sm, true);
190
191 out:
192	/* Unlock the secmodels list. */
193	rw_exit(&secmodels_lock);
194
195	return error;
196}
197
198static int
199secmodel_unplug(secmodel_t sm)
200{
201	secmodel_t tsm;
202	int error = 0;
203
204	if (sm == NULL)
205		return EFAULT;
206
207	/* Make sure the secmodel is present. */
208	rw_enter(&secmodels_lock, RW_WRITER);
209	tsm = secmodel_lookup(sm->sm_id);
210	if (tsm == NULL) {
211		error = ENOENT;
212		goto out;
213	}
214
215	/* Remove the secmodel. */
216	LIST_REMOVE(tsm, sm_list);
217
218	/* Adjust behavior. */
219	secmodel_adjust_behavior(tsm, false);
220
221 out:
222	/* Unlock the secmodels list. */
223	rw_exit(&secmodels_lock);
224
225	return error;
226}
227
228/* XXX TODO */
229int
230secmodel_setinfo(const char *id, void *v, int *err)
231{
232
233	return EOPNOTSUPP;
234}
235
236int
237secmodel_eval(const char *id, const char *what, void *arg, void *ret)
238{
239	secmodel_t sm;
240	int error = 0;
241
242	rw_enter(&secmodels_lock, RW_READER);
243	sm = secmodel_lookup(id);
244	if (sm == NULL) {
245		error = EINVAL;
246		goto out;
247	}
248
249	if (sm->sm_eval == NULL) {
250		error = ENOENT;
251		goto out;
252	}
253
254	if (ret == NULL) {
255		error = EFAULT;
256		goto out;
257	}
258
259	error = sm->sm_eval(what, arg, ret);
260	/* pass error from a secmodel(9) callback as a negative value */
261	error = -error;
262
263 out:
264	rw_exit(&secmodels_lock);
265
266	return error;
267}
268