1 #include <unistd.h> 2 #include <errno.h> 3 #include <stdio.h> 4 #include <stdio_ext.h> 5 #include <stdlib.h> 6 #include <string.h> 7 #include <ctype.h> 8 #include <pwd.h> 9 #include "selinux_internal.h" 10 #include "context_internal.h" 11 #include "get_context_list_internal.h" 12 13 int get_default_context_with_role(const char *user, 14 const char *role, 15 char * fromcon, 16 char ** newcon) 17 { 18 char **conary; 19 char **ptr; 20 context_t con; 21 const char *role2; 22 int rc; 23 24 rc = get_ordered_context_list(user, fromcon, &conary); 25 if (rc <= 0) 26 return -1; 27 28 for (ptr = conary; *ptr; ptr++) { 29 con = context_new(*ptr); 30 if (!con) 31 continue; 32 role2 = context_role_get(con); 33 if (role2 && !strcmp(role, role2)) { 34 context_free(con); 35 break; 36 } 37 context_free(con); 38 } 39 40 rc = -1; 41 if (!(*ptr)) { 42 errno = EINVAL; 43 goto out; 44 } 45 *newcon = strdup(*ptr); 46 if (!(*newcon)) 47 goto out; 48 rc = 0; 49 out: 50 freeconary(conary); 51 return rc; 52 } 53 54 hidden_def(get_default_context_with_role) 55 56 int get_default_context_with_rolelevel(const char *user, 57 const char *role, 58 const char *level, 59 char * fromcon, 60 char ** newcon) 61 { 62 63 int rc = 0; 64 int freefrom = 0; 65 context_t con; 66 char *newfromcon; 67 if (!level) 68 return get_default_context_with_role(user, role, fromcon, 69 newcon); 70 71 if (!fromcon) { 72 rc = getcon(&fromcon); 73 if (rc < 0) 74 return rc; 75 freefrom = 1; 76 } 77 78 rc = -1; 79 con = context_new(fromcon); 80 if (!con) 81 goto out; 82 83 if (context_range_set(con, level)) 84 goto out; 85 86 newfromcon = context_str(con); 87 if (!newfromcon) 88 goto out; 89 90 rc = get_default_context_with_role(user, role, newfromcon, newcon); 91 92 out: 93 context_free(con); 94 if (freefrom) 95 freecon(fromcon); 96 return rc; 97 98 } 99 100 int get_default_context(const char *user, 101 char * fromcon, char ** newcon) 102 { 103 char **conary; 104 int rc; 105 106 rc = get_ordered_context_list(user, fromcon, &conary); 107 if (rc <= 0) 108 return -1; 109 110 *newcon = strdup(conary[0]); 111 freeconary(conary); 112 if (!(*newcon)) 113 return -1; 114 return 0; 115 } 116 117 static int find_partialcon(char ** list, 118 unsigned int nreach, char *part) 119 { 120 const char *conrole, *contype; 121 char *partrole, *parttype, *ptr; 122 context_t con; 123 unsigned int i; 124 125 partrole = part; 126 ptr = part; 127 while (*ptr && !isspace(*ptr) && *ptr != ':') 128 ptr++; 129 if (*ptr != ':') 130 return -1; 131 *ptr++ = 0; 132 parttype = ptr; 133 while (*ptr && !isspace(*ptr) && *ptr != ':') 134 ptr++; 135 *ptr = 0; 136 137 for (i = 0; i < nreach; i++) { 138 con = context_new(list[i]); 139 if (!con) 140 return -1; 141 conrole = context_role_get(con); 142 contype = context_type_get(con); 143 if (!conrole || !contype) { 144 context_free(con); 145 return -1; 146 } 147 if (!strcmp(conrole, partrole) && !strcmp(contype, parttype)) { 148 context_free(con); 149 return i; 150 } 151 context_free(con); 152 } 153 154 return -1; 155 } 156 157 static int get_context_order(FILE * fp, 158 char * fromcon, 159 char ** reachable, 160 unsigned int nreach, 161 unsigned int *ordering, unsigned int *nordered) 162 { 163 char *start, *end = NULL; 164 char *line = NULL; 165 size_t line_len = 0; 166 ssize_t len; 167 int found = 0; 168 const char *fromrole, *fromtype; 169 char *linerole, *linetype; 170 unsigned int i; 171 context_t con; 172 int rc; 173 174 errno = -EINVAL; 175 176 /* Extract the role and type of the fromcon for matching. 177 User identity and MLS range can be variable. */ 178 con = context_new(fromcon); 179 if (!con) 180 return -1; 181 fromrole = context_role_get(con); 182 fromtype = context_type_get(con); 183 if (!fromrole || !fromtype) { 184 context_free(con); 185 return -1; 186 } 187 188 while ((len = getline(&line, &line_len, fp)) > 0) { 189 if (line[len - 1] == '\n') 190 line[len - 1] = 0; 191 192 /* Skip leading whitespace. */ 193 start = line; 194 while (*start && isspace(*start)) 195 start++; 196 if (!(*start)) 197 continue; 198 199 /* Find the end of the (partial) fromcon in the line. */ 200 end = start; 201 while (*end && !isspace(*end)) 202 end++; 203 if (!(*end)) 204 continue; 205 206 /* Check for a match. */ 207 linerole = start; 208 while (*start && !isspace(*start) && *start != ':') 209 start++; 210 if (*start != ':') 211 continue; 212 *start = 0; 213 linetype = ++start; 214 while (*start && !isspace(*start) && *start != ':') 215 start++; 216 if (!(*start)) 217 continue; 218 *start = 0; 219 if (!strcmp(fromrole, linerole) && !strcmp(fromtype, linetype)) { 220 found = 1; 221 break; 222 } 223 } 224 225 if (!found) { 226 errno = ENOENT; 227 rc = -1; 228 goto out; 229 } 230 231 start = ++end; 232 while (*start) { 233 /* Skip leading whitespace */ 234 while (*start && isspace(*start)) 235 start++; 236 if (!(*start)) 237 break; 238 239 /* Find the end of this partial context. */ 240 end = start; 241 while (*end && !isspace(*end)) 242 end++; 243 if (*end) 244 *end++ = 0; 245 246 /* Check for a match in the reachable list. */ 247 rc = find_partialcon(reachable, nreach, start); 248 if (rc < 0) { 249 /* No match, skip it. */ 250 start = end; 251 continue; 252 } 253 254 /* If a match is found and the entry is not already ordered 255 (e.g. due to prior match in prior config file), then set 256 the ordering for it. */ 257 i = rc; 258 if (ordering[i] == nreach) 259 ordering[i] = (*nordered)++; 260 start = end; 261 } 262 263 rc = 0; 264 265 out: 266 context_free(con); 267 free(line); 268 return rc; 269 } 270 271 static int get_failsafe_context(const char *user, char ** newcon) 272 { 273 FILE *fp; 274 char buf[255], *ptr; 275 size_t plen, nlen; 276 int rc; 277 278 fp = fopen(selinux_failsafe_context_path(), "r"); 279 if (!fp) 280 return -1; 281 282 ptr = fgets_unlocked(buf, sizeof buf, fp); 283 fclose(fp); 284 285 if (!ptr) 286 return -1; 287 plen = strlen(ptr); 288 if (buf[plen - 1] == '\n') 289 buf[plen - 1] = 0; 290 291 nlen = strlen(user) + 1 + plen + 1; 292 *newcon = malloc(nlen); 293 if (!(*newcon)) 294 return -1; 295 rc = snprintf(*newcon, nlen, "%s:%s", user, ptr); 296 if (rc < 0 || (size_t) rc >= nlen) { 297 free(*newcon); 298 *newcon = 0; 299 return -1; 300 } 301 302 /* If possible, check the context to catch 303 errors early rather than waiting until the 304 caller tries to use setexeccon on the context. 305 But this may not always be possible, e.g. if 306 selinuxfs isn't mounted. */ 307 if (security_check_context(*newcon) && errno != ENOENT) { 308 free(*newcon); 309 *newcon = 0; 310 return -1; 311 } 312 313 return 0; 314 } 315 316 struct context_order { 317 char * con; 318 unsigned int order; 319 }; 320 321 static int order_compare(const void *A, const void *B) 322 { 323 const struct context_order *c1 = A, *c2 = B; 324 if (c1->order < c2->order) 325 return -1; 326 else if (c1->order > c2->order) 327 return 1; 328 return strcmp(c1->con, c2->con); 329 } 330 331 int get_ordered_context_list_with_level(const char *user, 332 const char *level, 333 char * fromcon, 334 char *** list) 335 { 336 int rc; 337 int freefrom = 0; 338 context_t con; 339 char *newfromcon; 340 341 if (!level) 342 return get_ordered_context_list(user, fromcon, list); 343 344 if (!fromcon) { 345 rc = getcon(&fromcon); 346 if (rc < 0) 347 return rc; 348 freefrom = 1; 349 } 350 351 rc = -1; 352 con = context_new(fromcon); 353 if (!con) 354 goto out; 355 356 if (context_range_set(con, level)) 357 goto out; 358 359 newfromcon = context_str(con); 360 if (!newfromcon) 361 goto out; 362 363 rc = get_ordered_context_list(user, newfromcon, list); 364 365 out: 366 context_free(con); 367 if (freefrom) 368 freecon(fromcon); 369 return rc; 370 } 371 372 hidden_def(get_ordered_context_list_with_level) 373 374 int get_default_context_with_level(const char *user, 375 const char *level, 376 char * fromcon, 377 char ** newcon) 378 { 379 char **conary; 380 int rc; 381 382 rc = get_ordered_context_list_with_level(user, level, fromcon, &conary); 383 if (rc <= 0) 384 return -1; 385 386 *newcon = strdup(conary[0]); 387 freeconary(conary); 388 if (!(*newcon)) 389 return -1; 390 return 0; 391 } 392 393 int get_ordered_context_list(const char *user, 394 char * fromcon, 395 char *** list) 396 { 397 char **reachable = NULL; 398 unsigned int *ordering = NULL; 399 struct context_order *co = NULL; 400 char **ptr; 401 int rc = 0; 402 unsigned int nreach = 0, nordered = 0, freefrom = 0, i; 403 FILE *fp; 404 char *fname = NULL; 405 size_t fname_len; 406 const char *user_contexts_path = selinux_user_contexts_path(); 407 408 if (!fromcon) { 409 /* Get the current context and use it for the starting context */ 410 rc = getcon(&fromcon); 411 if (rc < 0) 412 return rc; 413 freefrom = 1; 414 } 415 416 /* Determine the set of reachable contexts for the user. */ 417 rc = security_compute_user(fromcon, user, &reachable); 418 if (rc < 0) 419 goto failsafe; 420 nreach = 0; 421 for (ptr = reachable; *ptr; ptr++) 422 nreach++; 423 if (!nreach) 424 goto failsafe; 425 426 /* Initialize ordering array. */ 427 ordering = malloc(nreach * sizeof(unsigned int)); 428 if (!ordering) 429 goto failsafe; 430 for (i = 0; i < nreach; i++) 431 ordering[i] = nreach; 432 433 /* Determine the ordering to apply from the optional per-user config 434 and from the global config. */ 435 fname_len = strlen(user_contexts_path) + strlen(user) + 2; 436 fname = malloc(fname_len); 437 if (!fname) 438 goto failsafe; 439 snprintf(fname, fname_len, "%s%s", user_contexts_path, user); 440 fp = fopen(fname, "r"); 441 if (fp) { 442 __fsetlocking(fp, FSETLOCKING_BYCALLER); 443 rc = get_context_order(fp, fromcon, reachable, nreach, ordering, 444 &nordered); 445 fclose(fp); 446 if (rc < 0 && errno != ENOENT) { 447 fprintf(stderr, 448 "%s: error in processing configuration file %s\n", 449 __FUNCTION__, fname); 450 /* Fall through, try global config */ 451 } 452 } 453 free(fname); 454 fp = fopen(selinux_default_context_path(), "r"); 455 if (fp) { 456 __fsetlocking(fp, FSETLOCKING_BYCALLER); 457 rc = get_context_order(fp, fromcon, reachable, nreach, ordering, 458 &nordered); 459 fclose(fp); 460 if (rc < 0 && errno != ENOENT) { 461 fprintf(stderr, 462 "%s: error in processing configuration file %s\n", 463 __FUNCTION__, selinux_default_context_path()); 464 /* Fall through */ 465 } 466 rc = 0; 467 } 468 469 if (!nordered) 470 goto failsafe; 471 472 /* Apply the ordering. */ 473 co = malloc(nreach * sizeof(struct context_order)); 474 if (!co) 475 goto failsafe; 476 for (i = 0; i < nreach; i++) { 477 co[i].con = reachable[i]; 478 co[i].order = ordering[i]; 479 } 480 qsort(co, nreach, sizeof(struct context_order), order_compare); 481 for (i = 0; i < nreach; i++) 482 reachable[i] = co[i].con; 483 free(co); 484 485 /* Only report the ordered entries to the caller. */ 486 if (nordered <= nreach) { 487 for (i = nordered; i < nreach; i++) 488 free(reachable[i]); 489 reachable[nordered] = NULL; 490 rc = nordered; 491 } 492 493 out: 494 if (rc > 0) 495 *list = reachable; 496 else 497 freeconary(reachable); 498 499 free(ordering); 500 if (freefrom) 501 freecon(fromcon); 502 503 return rc; 504 505 failsafe: 506 /* Unable to determine a reachable context list, try to fall back to 507 the "failsafe" context to at least permit root login 508 for emergency recovery if possible. */ 509 freeconary(reachable); 510 reachable = malloc(2 * sizeof(char *)); 511 if (!reachable) { 512 rc = -1; 513 goto out; 514 } 515 reachable[0] = reachable[1] = 0; 516 rc = get_failsafe_context(user, &reachable[0]); 517 if (rc < 0) { 518 freeconary(reachable); 519 reachable = NULL; 520 goto out; 521 } 522 rc = 1; /* one context in the list */ 523 goto out; 524 } 525 526 hidden_def(get_ordered_context_list) 527