xselinux_label.c revision 35c4bbdf
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
47/* Array of event SIDs indexed by event type */
48SELinuxArrayRec arr_events;
49
50/* Array of property and selection SID structures */
51SELinuxArrayRec arr_atoms;
52
53/*
54 * Dynamic array helpers
55 */
56static void *
57SELinuxArrayGet(SELinuxArrayRec * rec, unsigned key)
58{
59    return (rec->size > key) ? rec->array[key] : 0;
60}
61
62static int
63SELinuxArraySet(SELinuxArrayRec * rec, unsigned key, void *val)
64{
65    if (key >= rec->size) {
66        /* Need to increase size of array */
67        rec->array = reallocarray(rec->array, key + 1, sizeof(val));
68        if (!rec->array)
69            return FALSE;
70        memset(rec->array + rec->size, 0, (key - rec->size + 1) * sizeof(val));
71        rec->size = key + 1;
72    }
73
74    rec->array[key] = val;
75    return TRUE;
76}
77
78static void
79SELinuxArrayFree(SELinuxArrayRec * rec, int free_elements)
80{
81    if (free_elements) {
82        unsigned i = rec->size;
83
84        while (i)
85            free(rec->array[--i]);
86    }
87
88    free(rec->array);
89    rec->size = 0;
90    rec->array = NULL;
91}
92
93/*
94 * Looks up a name in the selection or property mappings
95 */
96static int
97SELinuxAtomToSIDLookup(Atom atom, SELinuxObjectRec * obj, int map, int polymap)
98{
99    const char *name = NameForAtom(atom);
100    security_context_t ctx;
101    int rc = Success;
102
103    obj->poly = 1;
104
105    /* Look in the mappings of names to contexts */
106    if (selabel_lookup_raw(label_hnd, &ctx, name, map) == 0) {
107        obj->poly = 0;
108    }
109    else if (errno != ENOENT) {
110        ErrorF("SELinux: a property label lookup failed!\n");
111        return BadValue;
112    }
113    else if (selabel_lookup_raw(label_hnd, &ctx, name, polymap) < 0) {
114        ErrorF("SELinux: a property label lookup failed!\n");
115        return BadValue;
116    }
117
118    /* Get a SID for context */
119    if (avc_context_to_sid_raw(ctx, &obj->sid) < 0) {
120        ErrorF("SELinux: a context_to_SID_raw call failed!\n");
121        rc = BadAlloc;
122    }
123
124    freecon(ctx);
125    return rc;
126}
127
128/*
129 * Looks up the SID corresponding to the given property or selection atom
130 */
131int
132SELinuxAtomToSID(Atom atom, int prop, SELinuxObjectRec ** obj_rtn)
133{
134    SELinuxAtomRec *rec;
135    SELinuxObjectRec *obj;
136    int rc, map, polymap;
137
138    rec = SELinuxArrayGet(&arr_atoms, atom);
139    if (!rec) {
140        rec = calloc(1, sizeof(SELinuxAtomRec));
141        if (!rec || !SELinuxArraySet(&arr_atoms, atom, rec))
142            return BadAlloc;
143    }
144
145    if (prop) {
146        obj = &rec->prp;
147        map = SELABEL_X_PROP;
148        polymap = SELABEL_X_POLYPROP;
149    }
150    else {
151        obj = &rec->sel;
152        map = SELABEL_X_SELN;
153        polymap = SELABEL_X_POLYSELN;
154    }
155
156    if (!obj->sid) {
157        rc = SELinuxAtomToSIDLookup(atom, obj, map, polymap);
158        if (rc != Success)
159            goto out;
160    }
161
162    *obj_rtn = obj;
163    rc = Success;
164 out:
165    return rc;
166}
167
168/*
169 * Looks up a SID for a selection/subject pair
170 */
171int
172SELinuxSelectionToSID(Atom selection, SELinuxSubjectRec * subj,
173                      security_id_t * sid_rtn, int *poly_rtn)
174{
175    int rc;
176    SELinuxObjectRec *obj;
177    security_id_t tsid;
178
179    /* Get the default context and polyinstantiation bit */
180    rc = SELinuxAtomToSID(selection, 0, &obj);
181    if (rc != Success)
182        return rc;
183
184    /* Check for an override context next */
185    if (subj->sel_use_sid) {
186        tsid = subj->sel_use_sid;
187        goto out;
188    }
189
190    tsid = obj->sid;
191
192    /* Polyinstantiate if necessary to obtain the final SID */
193    if (obj->poly && avc_compute_member(subj->sid, obj->sid,
194                                        SECCLASS_X_SELECTION, &tsid) < 0) {
195        ErrorF("SELinux: a compute_member call failed!\n");
196        return BadValue;
197    }
198 out:
199    *sid_rtn = tsid;
200    if (poly_rtn)
201        *poly_rtn = obj->poly;
202    return Success;
203}
204
205/*
206 * Looks up a SID for a property/subject pair
207 */
208int
209SELinuxPropertyToSID(Atom property, SELinuxSubjectRec * subj,
210                     security_id_t * sid_rtn, int *poly_rtn)
211{
212    int rc;
213    SELinuxObjectRec *obj;
214    security_id_t tsid, tsid2;
215
216    /* Get the default context and polyinstantiation bit */
217    rc = SELinuxAtomToSID(property, 1, &obj);
218    if (rc != Success)
219        return rc;
220
221    /* Check for an override context next */
222    if (subj->prp_use_sid) {
223        tsid = subj->prp_use_sid;
224        goto out;
225    }
226
227    /* Perform a transition */
228    if (avc_compute_create(subj->sid, obj->sid, SECCLASS_X_PROPERTY, &tsid) < 0) {
229        ErrorF("SELinux: a compute_create call failed!\n");
230        return BadValue;
231    }
232
233    /* Polyinstantiate if necessary to obtain the final SID */
234    if (obj->poly) {
235        tsid2 = tsid;
236        if (avc_compute_member(subj->sid, tsid2,
237                               SECCLASS_X_PROPERTY, &tsid) < 0) {
238            ErrorF("SELinux: a compute_member call failed!\n");
239            return BadValue;
240        }
241    }
242 out:
243    *sid_rtn = tsid;
244    if (poly_rtn)
245        *poly_rtn = obj->poly;
246    return Success;
247}
248
249/*
250 * Looks up the SID corresponding to the given event type
251 */
252int
253SELinuxEventToSID(unsigned type, security_id_t sid_of_window,
254                  SELinuxObjectRec * sid_return)
255{
256    const char *name = LookupEventName(type);
257    security_id_t sid;
258    security_context_t ctx;
259
260    type &= 127;
261
262    sid = SELinuxArrayGet(&arr_events, type);
263    if (!sid) {
264        /* Look in the mappings of event names to contexts */
265        if (selabel_lookup_raw(label_hnd, &ctx, name, SELABEL_X_EVENT) < 0) {
266            ErrorF("SELinux: an event label lookup failed!\n");
267            return BadValue;
268        }
269        /* Get a SID for context */
270        if (avc_context_to_sid_raw(ctx, &sid) < 0) {
271            ErrorF("SELinux: a context_to_SID_raw call failed!\n");
272            freecon(ctx);
273            return BadAlloc;
274        }
275        freecon(ctx);
276        /* Cache the SID value */
277        if (!SELinuxArraySet(&arr_events, type, sid))
278            return BadAlloc;
279    }
280
281    /* Perform a transition to obtain the final SID */
282    if (avc_compute_create(sid_of_window, sid, SECCLASS_X_EVENT,
283                           &sid_return->sid) < 0) {
284        ErrorF("SELinux: a compute_create call failed!\n");
285        return BadValue;
286    }
287
288    return Success;
289}
290
291int
292SELinuxExtensionToSID(const char *name, security_id_t * sid_rtn)
293{
294    security_context_t ctx;
295
296    /* Look in the mappings of extension names to contexts */
297    if (selabel_lookup_raw(label_hnd, &ctx, name, SELABEL_X_EXT) < 0) {
298        ErrorF("SELinux: a property label lookup failed!\n");
299        return BadValue;
300    }
301    /* Get a SID for context */
302    if (avc_context_to_sid_raw(ctx, sid_rtn) < 0) {
303        ErrorF("SELinux: a context_to_SID_raw call failed!\n");
304        freecon(ctx);
305        return BadAlloc;
306    }
307    freecon(ctx);
308    return Success;
309}
310
311/*
312 * Returns the object class corresponding to the given resource type.
313 */
314security_class_t
315SELinuxTypeToClass(RESTYPE type)
316{
317    void *tmp;
318
319    tmp = SELinuxArrayGet(&arr_types, type & TypeMask);
320    if (!tmp) {
321        unsigned long class = SECCLASS_X_RESOURCE;
322
323        if (type & RC_DRAWABLE)
324            class = SECCLASS_X_DRAWABLE;
325        else if (type == RT_GC)
326            class = SECCLASS_X_GC;
327        else if (type == RT_FONT)
328            class = SECCLASS_X_FONT;
329        else if (type == RT_CURSOR)
330            class = SECCLASS_X_CURSOR;
331        else if (type == RT_COLORMAP)
332            class = SECCLASS_X_COLORMAP;
333        else {
334            /* Need to do a string lookup */
335            const char *str = LookupResourceName(type);
336
337            if (!strcmp(str, "PICTURE"))
338                class = SECCLASS_X_DRAWABLE;
339            else if (!strcmp(str, "GLYPHSET"))
340                class = SECCLASS_X_FONT;
341        }
342
343        tmp = (void *) class;
344        SELinuxArraySet(&arr_types, type & TypeMask, tmp);
345    }
346
347    return (security_class_t) (unsigned long) tmp;
348}
349
350security_context_t
351SELinuxDefaultClientLabel(void)
352{
353    security_context_t ctx;
354
355    if (selabel_lookup_raw(label_hnd, &ctx, "remote", SELABEL_X_CLIENT) < 0)
356        FatalError("SELinux: failed to look up remote-client context\n");
357
358    return ctx;
359}
360
361void
362SELinuxLabelInit(void)
363{
364    struct selinux_opt selabel_option = { SELABEL_OPT_VALIDATE, (char *) 1 };
365
366    label_hnd = selabel_open(SELABEL_CTX_X, &selabel_option, 1);
367    if (!label_hnd)
368        FatalError("SELinux: Failed to open x_contexts mapping in policy\n");
369}
370
371void
372SELinuxLabelReset(void)
373{
374    selabel_close(label_hnd);
375    label_hnd = NULL;
376
377    /* Free local state */
378    SELinuxArrayFree(&arr_types, 0);
379    SELinuxArrayFree(&arr_events, 0);
380    SELinuxArrayFree(&arr_atoms, 1);
381}
382