| 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 * |
| 57 | SELinuxArrayGet(SELinuxArrayRec * rec, unsigned key) |
| 58 | { |
| 59 | return (rec->size > key) ? rec->array[key] : 0; |
| 60 | } |
| 61 | |
| 62 | static int |
| 63 | SELinuxArraySet(SELinuxArrayRec * rec, unsigned key, void *val) |
| 64 | { |
| 65 | if (key >= rec->size) { |
| 66 | /* Need to increase size of array */ |
| 67 | rec->array = realloc(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 |
| 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 |
| 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 |
| 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 |
| 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 |
| 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 |
| 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 |
| 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 |
| 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 |
| 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 |
| 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 |
| 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 | } |