1/************************************************************
2
3Author: Eamon Walsh <ewalsh@tycho.nsa.gov>
4
5Permission to use, copy, modify, distribute, and sell this software and its
6documentation for any purpose is hereby granted without fee, provided that
7this permission notice appear in supporting documentation.  This permission
8notice shall be included in all copies or substantial portions of the
9Software.
10
11THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
12IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
13FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE
14AUTHOR BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
15AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
16CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
17
18********************************************************/
19
20#ifdef HAVE_DIX_CONFIG_H
21#include <dix-config.h>
22#endif
23
24#include <selinux/label.h>
25
26#include "registry.h"
27#include "xselinuxint.h"
28
29/* selection and property atom cache */
30typedef struct {
31    SELinuxObjectRec prp;
32    SELinuxObjectRec sel;
33} SELinuxAtomRec;
34
35/* dynamic array */
36typedef struct {
37    unsigned size;
38    void **array;
39} SELinuxArrayRec;
40
41/* labeling handle */
42static struct selabel_handle *label_hnd;
43
44/* Array of object classes indexed by resource type */
45SELinuxArrayRec arr_types;
46/* Array of event SIDs indexed by event type */
47SELinuxArrayRec arr_events;
48/* Array of property and selection SID structures */
49SELinuxArrayRec arr_atoms;
50
51/*
52 * Dynamic array helpers
53 */
54static void *
55SELinuxArrayGet(SELinuxArrayRec *rec, unsigned key)
56{
57    return (rec->size > key) ? rec->array[key] : 0;
58}
59
60static int
61SELinuxArraySet(SELinuxArrayRec *rec, unsigned key, void *val)
62{
63    if (key >= rec->size) {
64	/* Need to increase size of array */
65	rec->array = realloc(rec->array, (key + 1) * sizeof(val));
66	if (!rec->array)
67	    return FALSE;
68	memset(rec->array + rec->size, 0, (key - rec->size + 1) * sizeof(val));
69	rec->size = key + 1;
70    }
71
72    rec->array[key] = val;
73    return TRUE;
74}
75
76static void
77SELinuxArrayFree(SELinuxArrayRec *rec, int free_elements)
78{
79    if (free_elements) {
80	unsigned i = rec->size;
81	while (i)
82	    free(rec->array[--i]);
83    }
84
85    free(rec->array);
86    rec->size = 0;
87    rec->array = NULL;
88}
89
90/*
91 * Looks up a name in the selection or property mappings
92 */
93static int
94SELinuxAtomToSIDLookup(Atom atom, SELinuxObjectRec *obj, int map, int polymap)
95{
96    const char *name = NameForAtom(atom);
97    security_context_t ctx;
98    int rc = Success;
99
100    obj->poly = 1;
101
102    /* Look in the mappings of names to contexts */
103    if (selabel_lookup_raw(label_hnd, &ctx, name, map) == 0) {
104	obj->poly = 0;
105    } else if (errno != ENOENT) {
106	ErrorF("SELinux: a property label lookup failed!\n");
107	return BadValue;
108    } else if (selabel_lookup_raw(label_hnd, &ctx, name, polymap) < 0) {
109	ErrorF("SELinux: a property label lookup failed!\n");
110	return BadValue;
111    }
112
113    /* Get a SID for context */
114    if (avc_context_to_sid_raw(ctx, &obj->sid) < 0) {
115	ErrorF("SELinux: a context_to_SID_raw call failed!\n");
116	rc = BadAlloc;
117    }
118
119    freecon(ctx);
120    return rc;
121}
122
123/*
124 * Looks up the SID corresponding to the given property or selection atom
125 */
126int
127SELinuxAtomToSID(Atom atom, int prop, SELinuxObjectRec **obj_rtn)
128{
129    SELinuxAtomRec *rec;
130    SELinuxObjectRec *obj;
131    int rc, map, polymap;
132
133    rec = SELinuxArrayGet(&arr_atoms, atom);
134    if (!rec) {
135	rec = calloc(1, sizeof(SELinuxAtomRec));
136	if (!rec || !SELinuxArraySet(&arr_atoms, atom, rec))
137	    return BadAlloc;
138    }
139
140    if (prop) {
141	obj = &rec->prp;
142	map = SELABEL_X_PROP;
143	polymap = SELABEL_X_POLYPROP;
144    } else {
145	obj = &rec->sel;
146	map = SELABEL_X_SELN;
147	polymap = SELABEL_X_POLYSELN;
148    }
149
150    if (!obj->sid) {
151	rc = SELinuxAtomToSIDLookup(atom, obj, map, polymap);
152	if (rc != Success)
153	    goto out;
154    }
155
156    *obj_rtn = obj;
157    rc = Success;
158out:
159    return rc;
160}
161
162/*
163 * Looks up a SID for a selection/subject pair
164 */
165int
166SELinuxSelectionToSID(Atom selection, SELinuxSubjectRec *subj,
167		      security_id_t *sid_rtn, int *poly_rtn)
168{
169    int rc;
170    SELinuxObjectRec *obj;
171    security_id_t tsid;
172
173    /* Get the default context and polyinstantiation bit */
174    rc = SELinuxAtomToSID(selection, 0, &obj);
175    if (rc != Success)
176	return rc;
177
178    /* Check for an override context next */
179    if (subj->sel_use_sid) {
180	tsid = subj->sel_use_sid;
181	goto out;
182    }
183
184    tsid = obj->sid;
185
186    /* Polyinstantiate if necessary to obtain the final SID */
187    if (obj->poly && avc_compute_member(subj->sid, obj->sid,
188					SECCLASS_X_SELECTION, &tsid) < 0) {
189	ErrorF("SELinux: a compute_member call failed!\n");
190	return BadValue;
191    }
192out:
193    *sid_rtn = tsid;
194    if (poly_rtn)
195	*poly_rtn = obj->poly;
196    return Success;
197}
198
199/*
200 * Looks up a SID for a property/subject pair
201 */
202int
203SELinuxPropertyToSID(Atom property, SELinuxSubjectRec *subj,
204		     security_id_t *sid_rtn, int *poly_rtn)
205{
206    int rc;
207    SELinuxObjectRec *obj;
208    security_id_t tsid, tsid2;
209
210    /* Get the default context and polyinstantiation bit */
211    rc = SELinuxAtomToSID(property, 1, &obj);
212    if (rc != Success)
213	return rc;
214
215    /* Check for an override context next */
216    if (subj->prp_use_sid) {
217	tsid = subj->prp_use_sid;
218	goto out;
219    }
220
221    /* Perform a transition */
222    if (avc_compute_create(subj->sid, obj->sid,
223			   SECCLASS_X_PROPERTY, &tsid) < 0) {
224	ErrorF("SELinux: a compute_create call failed!\n");
225	return BadValue;
226    }
227
228    /* Polyinstantiate if necessary to obtain the final SID */
229    if (obj->poly) {
230	tsid2 = tsid;
231	if (avc_compute_member(subj->sid, tsid2,
232			       SECCLASS_X_PROPERTY, &tsid) < 0) {
233	    ErrorF("SELinux: a compute_member call failed!\n");
234	    return BadValue;
235	}
236    }
237out:
238    *sid_rtn = tsid;
239    if (poly_rtn)
240	*poly_rtn = obj->poly;
241    return Success;
242}
243
244/*
245 * Looks up the SID corresponding to the given event type
246 */
247int
248SELinuxEventToSID(unsigned type, security_id_t sid_of_window,
249		  SELinuxObjectRec *sid_return)
250{
251    const char *name = LookupEventName(type);
252    security_id_t sid;
253    security_context_t ctx;
254    type &= 127;
255
256    sid = SELinuxArrayGet(&arr_events, type);
257    if (!sid) {
258	/* Look in the mappings of event names to contexts */
259	if (selabel_lookup_raw(label_hnd, &ctx, name, SELABEL_X_EVENT) < 0) {
260	    ErrorF("SELinux: an event label lookup failed!\n");
261	    return BadValue;
262	}
263	/* Get a SID for context */
264	if (avc_context_to_sid_raw(ctx, &sid) < 0) {
265	    ErrorF("SELinux: a context_to_SID_raw call failed!\n");
266	    freecon(ctx);
267	    return BadAlloc;
268	}
269	freecon(ctx);
270	/* Cache the SID value */
271	if (!SELinuxArraySet(&arr_events, type, sid))
272	    return BadAlloc;
273    }
274
275    /* Perform a transition to obtain the final SID */
276    if (avc_compute_create(sid_of_window, sid, SECCLASS_X_EVENT,
277			   &sid_return->sid) < 0) {
278	ErrorF("SELinux: a compute_create call failed!\n");
279	return BadValue;
280    }
281
282    return Success;
283}
284
285int
286SELinuxExtensionToSID(const char *name, security_id_t *sid_rtn)
287{
288    security_context_t ctx;
289
290    /* Look in the mappings of extension names to contexts */
291    if (selabel_lookup_raw(label_hnd, &ctx, name, SELABEL_X_EXT) < 0) {
292	ErrorF("SELinux: a property label lookup failed!\n");
293	return BadValue;
294    }
295    /* Get a SID for context */
296    if (avc_context_to_sid_raw(ctx, sid_rtn) < 0) {
297	ErrorF("SELinux: a context_to_SID_raw call failed!\n");
298	freecon(ctx);
299	return BadAlloc;
300    }
301    freecon(ctx);
302    return Success;
303}
304
305/*
306 * Returns the object class corresponding to the given resource type.
307 */
308security_class_t
309SELinuxTypeToClass(RESTYPE type)
310{
311    void *tmp;
312
313    tmp = SELinuxArrayGet(&arr_types, type & TypeMask);
314    if (!tmp) {
315	unsigned long class = SECCLASS_X_RESOURCE;
316
317	if (type & RC_DRAWABLE)
318	    class = SECCLASS_X_DRAWABLE;
319	else if (type == RT_GC)
320	    class = SECCLASS_X_GC;
321	else if (type == RT_FONT)
322	    class = SECCLASS_X_FONT;
323	else if (type == RT_CURSOR)
324	    class = SECCLASS_X_CURSOR;
325	else if (type == RT_COLORMAP)
326	    class = SECCLASS_X_COLORMAP;
327	else {
328	    /* Need to do a string lookup */
329	    const char *str = LookupResourceName(type);
330	    if (!strcmp(str, "PICTURE"))
331		class = SECCLASS_X_DRAWABLE;
332	    else if (!strcmp(str, "GLYPHSET"))
333		class = SECCLASS_X_FONT;
334	}
335
336	tmp = (void *)class;
337	SELinuxArraySet(&arr_types, type & TypeMask, tmp);
338    }
339
340    return (security_class_t)(unsigned long)tmp;
341}
342
343security_context_t
344SELinuxDefaultClientLabel(void)
345{
346    security_context_t ctx;
347
348    if (selabel_lookup_raw(label_hnd, &ctx, "remote", SELABEL_X_CLIENT) < 0)
349	FatalError("SELinux: failed to look up remote-client context\n");
350
351    return ctx;
352}
353
354void
355SELinuxLabelInit(void)
356{
357    struct selinux_opt selabel_option = { SELABEL_OPT_VALIDATE, (char *)1 };
358
359    label_hnd = selabel_open(SELABEL_CTX_X, &selabel_option, 1);
360    if (!label_hnd)
361	FatalError("SELinux: Failed to open x_contexts mapping in policy\n");
362}
363
364void
365SELinuxLabelReset(void)
366{
367    selabel_close(label_hnd);
368    label_hnd = NULL;
369
370    /* Free local state */
371    SELinuxArrayFree(&arr_types, 0);
372    SELinuxArrayFree(&arr_events, 0);
373    SELinuxArrayFree(&arr_atoms, 1);
374}
375