1 /************************************************************
2
3 Author: Eamon Walsh <ewalsh@tycho.nsa.gov>
4
5 Permission to use, copy, modify, distribute, and sell this software and its
6 documentation for any purpose is hereby granted without fee, provided that
7 this permission notice appear in supporting documentation. This permission
8 notice shall be included in all copies or substantial portions of the
9 Software.
10
11 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
12 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
13 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
14 AUTHOR BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
15 AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
16 CONNECTION 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 */
30 typedef struct {
31 SELinuxObjectRec prp;
32 SELinuxObjectRec sel;
33 } SELinuxAtomRec;
34
35 /* dynamic array */
36 typedef struct {
37 unsigned size;
38 void **array;
39 } SELinuxArrayRec;
40
41 /* labeling handle */
42 static struct selabel_handle *label_hnd;
43
44 /* Array of object classes indexed by resource type */
45 SELinuxArrayRec arr_types;
46
47 /* Array of event SIDs indexed by event type */
48 SELinuxArrayRec arr_events;
49
50 /* Array of property and selection SID structures */
51 SELinuxArrayRec arr_atoms;
52
53 /*
54 * Dynamic array helpers
55 */
56 static void *
SELinuxArrayGet(SELinuxArrayRec * rec,unsigned key)57 SELinuxArrayGet(SELinuxArrayRec * rec, unsigned key)
58 {
59 return (rec->size > key) ? rec->array[key] : 0;
60 }
61
62 static int
SELinuxArraySet(SELinuxArrayRec * rec,unsigned key,void * val)63 SELinuxArraySet(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
78 static void
SELinuxArrayFree(SELinuxArrayRec * rec,int free_elements)79 SELinuxArrayFree(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 */
96 static int
SELinuxAtomToSIDLookup(Atom atom,SELinuxObjectRec * obj,int map,int polymap)97 SELinuxAtomToSIDLookup(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 */
131 int
SELinuxAtomToSID(Atom atom,int prop,SELinuxObjectRec ** obj_rtn)132 SELinuxAtomToSID(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 */
171 int
SELinuxSelectionToSID(Atom selection,SELinuxSubjectRec * subj,security_id_t * sid_rtn,int * poly_rtn)172 SELinuxSelectionToSID(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 */
208 int
SELinuxPropertyToSID(Atom property,SELinuxSubjectRec * subj,security_id_t * sid_rtn,int * poly_rtn)209 SELinuxPropertyToSID(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 */
252 int
SELinuxEventToSID(unsigned type,security_id_t sid_of_window,SELinuxObjectRec * sid_return)253 SELinuxEventToSID(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
291 int
SELinuxExtensionToSID(const char * name,security_id_t * sid_rtn)292 SELinuxExtensionToSID(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 */
314 security_class_t
SELinuxTypeToClass(RESTYPE type)315 SELinuxTypeToClass(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
350 security_context_t
SELinuxDefaultClientLabel(void)351 SELinuxDefaultClientLabel(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
361 void
SELinuxLabelInit(void)362 SELinuxLabelInit(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
371 void
SELinuxLabelReset(void)372 SELinuxLabelReset(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