1 /* Author: Mark Goldman <mgoldman (at) tresys.com> 2 * Paul Rosenfeld <prosenfeld (at) tresys.com> 3 * Todd C. Miller <tmiller (at) tresys.com> 4 * 5 * Copyright (C) 2007 Tresys Technology, LLC 6 * 7 * This library is free software; you can redistribute it and/or modify 8 * it under the terms of the GNU Lesser General Public License as 9 * published by the Free Software Foundation; either version 2.1 of the 10 * License, or (at your option) any later version. 11 * 12 * This library is distributed in the hope that it will be useful, but 13 * WITHOUT ANY WARRANTY; without even the implied warranty of 14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 15 * Lesser General Public License for more details. 16 * 17 * You should have received a copy of the GNU Lesser General Public 18 * License along with this library; if not, write to the Free Software 19 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 20 * 02110-1301 USA 21 */ 22 23 #include <semanage/handle.h> 24 #include <semanage/seusers_policy.h> 25 #include <semanage/users_policy.h> 26 #include <semanage/user_record.h> 27 #include <semanage/fcontext_record.h> 28 #include <semanage/fcontexts_policy.h> 29 #include <sepol/context.h> 30 #include <sepol/context_record.h> 31 #include "semanage_store.h" 32 #include "seuser_internal.h" 33 #include "debug.h" 34 35 #include "utilities.h" 36 #include "genhomedircon.h" 37 #include <ustr.h> 38 39 #include <assert.h> 40 #include <limits.h> 41 #include <stdio.h> 42 #include <stdlib.h> 43 #include <string.h> 44 #include <sys/types.h> 45 #include <sys/stat.h> 46 #include <fcntl.h> 47 #include <pwd.h> 48 #include <errno.h> 49 #include <unistd.h> 50 #include <regex.h> 51 52 /* paths used in get_home_dirs() */ 53 #define PATH_ETC_USERADD "/etc/default/useradd" 54 #define PATH_ETC_LIBUSER "/etc/libuser.conf" 55 #define PATH_DEFAULT_HOME "/home" 56 #define PATH_EXPORT_HOME "/export/home" 57 #define PATH_ETC_LOGIN_DEFS "/etc/login.defs" 58 59 /* other paths */ 60 #define PATH_SHELLS_FILE "/etc/shells" 61 #define PATH_NOLOGIN_SHELL "/sbin/nologin" 62 63 /* comments written to context file */ 64 #define COMMENT_FILE_CONTEXT_HEADER "#\n#\n# " \ 65 "User-specific file contexts, generated via libsemanage\n" \ 66 "# use semanage command to manage system users to change" \ 67 " the file_context\n#\n#\n" 68 69 #define COMMENT_USER_HOME_CONTEXT "\n\n#\n# Home Context for user %s" \ 70 "\n#\n\n" 71 72 /* placeholders used in the template file 73 which are searched for and replaced */ 74 #define TEMPLATE_HOME_ROOT "HOME_ROOT" 75 #define TEMPLATE_HOME_DIR "HOME_DIR" 76 #define TEMPLATE_USER "USER" 77 #define TEMPLATE_ROLE "ROLE" 78 #define TEMPLATE_SEUSER "system_u" 79 #define TEMPLATE_LEVEL "s0" 80 81 #define FALLBACK_USER "user_u" 82 #define FALLBACK_USER_PREFIX "user" 83 #define FALLBACK_USER_LEVEL "s0" 84 #define DEFAULT_LOGIN "__default__" 85 86 typedef struct { 87 const char *fcfilepath; 88 int usepasswd; 89 const char *homedir_template_path; 90 char *fallback_user; 91 char *fallback_user_prefix; 92 char *fallback_user_level; 93 semanage_handle_t *h_semanage; 94 sepol_policydb_t *policydb; 95 } genhomedircon_settings_t; 96 97 typedef struct user_entry { 98 char *name; 99 char *sename; 100 char *prefix; 101 char *home; 102 char *level; 103 struct user_entry *next; 104 } genhomedircon_user_entry_t; 105 106 typedef struct { 107 const char *search_for; 108 const char *replace_with; 109 } replacement_pair_t; 110 111 typedef struct { 112 const char *dir; 113 int matched; 114 } fc_match_handle_t; 115 116 typedef struct IgnoreDir { 117 struct IgnoreDir *next; 118 char *dir; 119 } ignoredir_t; 120 121 ignoredir_t *ignore_head = NULL; 122 123 static void ignore_free(void) { 124 ignoredir_t *next; 125 126 while (ignore_head) { 127 next = ignore_head->next; 128 free(ignore_head->dir); 129 free(ignore_head); 130 ignore_head = next; 131 } 132 } 133 134 static int ignore_setup(char *ignoredirs) { 135 char *tok; 136 ignoredir_t *ptr = NULL; 137 138 tok = strtok(ignoredirs, ";"); 139 while(tok) { 140 ptr = calloc(sizeof(ignoredir_t),1); 141 if (!ptr) 142 goto err; 143 ptr->dir = strdup(tok); 144 if (!ptr->dir) 145 goto err; 146 147 ptr->next = ignore_head; 148 ignore_head = ptr; 149 150 tok = strtok(NULL, ";"); 151 } 152 153 return 0; 154 err: 155 free(ptr); 156 ignore_free(); 157 return -1; 158 } 159 160 static int ignore(const char *homedir) { 161 ignoredir_t *ptr = ignore_head; 162 while (ptr) { 163 if (strcmp(ptr->dir, homedir) == 0) { 164 return 1; 165 } 166 ptr = ptr->next; 167 } 168 return 0; 169 } 170 171 static semanage_list_t *default_shell_list(void) 172 { 173 semanage_list_t *list = NULL; 174 175 if (semanage_list_push(&list, "/bin/csh") 176 || semanage_list_push(&list, "/bin/tcsh") 177 || semanage_list_push(&list, "/bin/ksh") 178 || semanage_list_push(&list, "/bin/bsh") 179 || semanage_list_push(&list, "/bin/ash") 180 || semanage_list_push(&list, "/usr/bin/ksh") 181 || semanage_list_push(&list, "/usr/bin/pdksh") 182 || semanage_list_push(&list, "/bin/zsh") 183 || semanage_list_push(&list, "/bin/sh") 184 || semanage_list_push(&list, "/bin/bash")) 185 goto fail; 186 187 return list; 188 189 fail: 190 semanage_list_destroy(&list); 191 return NULL; 192 } 193 194 static semanage_list_t *get_shell_list(void) 195 { 196 FILE *shells; 197 char *temp = NULL; 198 semanage_list_t *list = NULL; 199 size_t buff_len = 0; 200 ssize_t len; 201 202 shells = fopen(PATH_SHELLS_FILE, "r"); 203 if (!shells) 204 return default_shell_list(); 205 while ((len = getline(&temp, &buff_len, shells)) > 0) { 206 if (temp[len-1] == '\n') temp[len-1] = 0; 207 if (strcmp(temp, PATH_NOLOGIN_SHELL)) { 208 if (semanage_list_push(&list, temp)) { 209 free(temp); 210 semanage_list_destroy(&list); 211 return default_shell_list(); 212 } 213 } 214 } 215 free(temp); 216 217 return list; 218 } 219 220 /* Helper function called via semanage_fcontext_iterate() */ 221 static int fcontext_matches(const semanage_fcontext_t *fcontext, void *varg) 222 { 223 const char *oexpr = semanage_fcontext_get_expr(fcontext); 224 fc_match_handle_t *handp = varg; 225 struct Ustr *expr; 226 regex_t re; 227 int type, retval = -1; 228 229 /* Only match ALL or DIR */ 230 type = semanage_fcontext_get_type(fcontext); 231 if (type != SEMANAGE_FCONTEXT_ALL && type != SEMANAGE_FCONTEXT_ALL) 232 return 0; 233 234 /* Convert oexpr into a Ustr and anchor it at the beginning */ 235 expr = ustr_dup_cstr("^"); 236 if (expr == USTR_NULL) 237 goto done; 238 if (!ustr_add_cstr(&expr, oexpr)) 239 goto done; 240 241 /* Strip off trailing ".+" or ".*" */ 242 if (ustr_cmp_suffix_cstr_eq(expr, ".+") || 243 ustr_cmp_suffix_cstr_eq(expr, ".*")) { 244 if (!ustr_del(&expr, 2)) 245 goto done; 246 } 247 248 /* Strip off trailing "(/.*)?" */ 249 if (ustr_cmp_suffix_cstr_eq(expr, "(/.*)?")) { 250 if (!ustr_del(&expr, 6)) 251 goto done; 252 } 253 254 if (ustr_cmp_suffix_cstr_eq(expr, "/")) { 255 if (!ustr_del(&expr, 1)) 256 goto done; 257 } 258 259 /* Append pattern to eat up trailing slashes */ 260 if (!ustr_add_cstr(&expr, "/*$")) 261 goto done; 262 263 /* Check dir against expr */ 264 if (regcomp(&re, ustr_cstr(expr), REG_EXTENDED) != 0) 265 goto done; 266 if (regexec(&re, handp->dir, 0, NULL, 0) == 0) 267 handp->matched = 1; 268 regfree(&re); 269 270 retval = 0; 271 272 done: 273 ustr_free(expr); 274 275 return retval; 276 } 277 278 static semanage_list_t *get_home_dirs(genhomedircon_settings_t * s) 279 { 280 semanage_list_t *homedir_list = NULL; 281 semanage_list_t *shells = NULL; 282 fc_match_handle_t hand; 283 char *rbuf = NULL; 284 char *path = NULL; 285 long rbuflen; 286 uid_t temp, minuid = 500, maxuid = 60000; 287 int minuid_set = 0; 288 struct passwd pwstorage, *pwbuf; 289 struct stat buf; 290 int retval; 291 292 path = semanage_findval(PATH_ETC_USERADD, "HOME", "="); 293 if (path && *path) { 294 if (semanage_list_push(&homedir_list, path)) 295 goto fail; 296 } 297 free(path); 298 299 path = semanage_findval(PATH_ETC_LIBUSER, "LU_HOMEDIRECTORY", "="); 300 if (path && *path) { 301 if (semanage_list_push(&homedir_list, path)) 302 goto fail; 303 } 304 free(path); 305 path = NULL; 306 307 if (!homedir_list) { 308 if (semanage_list_push(&homedir_list, PATH_DEFAULT_HOME)) { 309 goto fail; 310 } 311 } 312 313 if (!stat(PATH_EXPORT_HOME, &buf)) { 314 if (S_ISDIR(buf.st_mode)) { 315 if (semanage_list_push(&homedir_list, PATH_EXPORT_HOME)) { 316 goto fail; 317 } 318 } 319 } 320 321 if (!(s->usepasswd)) 322 return homedir_list; 323 324 shells = get_shell_list(); 325 assert(shells); 326 327 path = semanage_findval(PATH_ETC_LOGIN_DEFS, "UID_MIN", NULL); 328 if (path && *path) { 329 temp = atoi(path); 330 minuid = temp; 331 minuid_set = 1; 332 } 333 free(path); 334 path = NULL; 335 336 path = semanage_findval(PATH_ETC_LOGIN_DEFS, "UID_MAX", NULL); 337 if (path && *path) { 338 temp = atoi(path); 339 maxuid = temp; 340 } 341 free(path); 342 path = NULL; 343 344 path = semanage_findval(PATH_ETC_LIBUSER, "LU_UIDNUMBER", "="); 345 if (path && *path) { 346 temp = atoi(path); 347 if (!minuid_set || temp < minuid) { 348 minuid = temp; 349 minuid_set = 1; 350 } 351 } 352 free(path); 353 path = NULL; 354 355 rbuflen = sysconf(_SC_GETPW_R_SIZE_MAX); 356 if (rbuflen <= 0) 357 goto fail; 358 rbuf = malloc(rbuflen); 359 if (rbuf == NULL) 360 goto fail; 361 setpwent(); 362 while ((retval = getpwent_r(&pwstorage, rbuf, rbuflen, &pwbuf)) == 0) { 363 if (pwbuf->pw_uid < minuid || pwbuf->pw_uid > maxuid) 364 continue; 365 if (!semanage_list_find(shells, pwbuf->pw_shell)) 366 continue; 367 int len = strlen(pwbuf->pw_dir) -1; 368 for(; len > 0 && pwbuf->pw_dir[len] == '/'; len--) { 369 pwbuf->pw_dir[len] = '\0'; 370 } 371 if (strcmp(pwbuf->pw_dir, "/") == 0) 372 continue; 373 if (ignore(pwbuf->pw_dir)) 374 continue; 375 if (semanage_str_count(pwbuf->pw_dir, '/') <= 1) 376 continue; 377 if (!(path = strdup(pwbuf->pw_dir))) { 378 break; 379 } 380 381 semanage_rtrim(path, '/'); 382 383 if (!semanage_list_find(homedir_list, path)) { 384 /* 385 * Now check for an existing file context that matches 386 * so we don't label a non-homedir as a homedir. 387 */ 388 hand.dir = path; 389 hand.matched = 0; 390 if (semanage_fcontext_iterate(s->h_semanage, 391 fcontext_matches, &hand) == STATUS_ERR) 392 goto fail; 393 394 /* NOTE: old genhomedircon printed a warning on match */ 395 if (hand.matched) { 396 WARN(s->h_semanage, "%s homedir %s or its parent directory conflicts with a file context already specified in the policy. This usually indicates an incorrectly defined system account. If it is a system account please make sure its uid is less than %u or greater than %u or its login shell is /sbin/nologin.", pwbuf->pw_name, pwbuf->pw_dir, minuid, maxuid); 397 } else { 398 if (semanage_list_push(&homedir_list, path)) 399 goto fail; 400 } 401 } 402 free(path); 403 path = NULL; 404 } 405 406 if (retval && retval != ENOENT) { 407 WARN(s->h_semanage, "Error while fetching users. " 408 "Returning list so far."); 409 } 410 411 if (semanage_list_sort(&homedir_list)) 412 goto fail; 413 414 endpwent(); 415 free(rbuf); 416 semanage_list_destroy(&shells); 417 418 return homedir_list; 419 420 fail: 421 endpwent(); 422 free(rbuf); 423 free(path); 424 semanage_list_destroy(&homedir_list); 425 semanage_list_destroy(&shells); 426 return NULL; 427 } 428 429 /** 430 * @param out the FILE to put all the output in. 431 * @return 0 on success 432 */ 433 static int write_file_context_header(FILE * out) 434 { 435 if (fprintf(out, COMMENT_FILE_CONTEXT_HEADER) < 0) { 436 return STATUS_ERR; 437 } 438 439 return STATUS_SUCCESS; 440 } 441 442 /* Predicates for use with semanage_slurp_file_filter() the homedir_template 443 * file currently contains lines that serve as the template for a user's 444 * homedir. 445 * 446 * It also contains lines that are the template for the parent of a 447 * user's home directory. 448 * 449 * Currently, the only lines that apply to the the root of a user's home 450 * directory are all prefixed with the string "HOME_ROOT". All other 451 * lines apply to a user's home directory. If this changes the 452 * following predicates need to change to reflect that. 453 */ 454 static int HOME_ROOT_PRED(const char *string) 455 { 456 return semanage_is_prefix(string, TEMPLATE_HOME_ROOT); 457 } 458 459 static int HOME_DIR_PRED(const char *string) 460 { 461 return semanage_is_prefix(string, TEMPLATE_HOME_DIR); 462 } 463 464 static int USER_CONTEXT_PRED(const char *string) 465 { 466 return (int)(strstr(string, TEMPLATE_USER) != NULL); 467 } 468 469 /* make_tempate 470 * @param s the settings holding the paths to various files 471 * @param pred function pointer to function to use as filter for slurp 472 * file filter 473 * @return a list of lines from the template file with inappropriate 474 * lines filtered out. 475 */ 476 static semanage_list_t *make_template(genhomedircon_settings_t * s, 477 int (*pred) (const char *)) 478 { 479 FILE *template_file = NULL; 480 semanage_list_t *template_data = NULL; 481 482 template_file = fopen(s->homedir_template_path, "r"); 483 if (!template_file) 484 return NULL; 485 template_data = semanage_slurp_file_filter(template_file, pred); 486 fclose(template_file); 487 488 return template_data; 489 } 490 491 static Ustr *replace_all(const char *str, const replacement_pair_t * repl) 492 { 493 Ustr *retval = USTR_NULL; 494 int i; 495 496 if (!str || !repl) 497 goto done; 498 if (!(retval = ustr_dup_cstr(str))) 499 goto done; 500 501 for (i = 0; repl[i].search_for; i++) { 502 ustr_replace_cstr(&retval, repl[i].search_for, 503 repl[i].replace_with, 0); 504 } 505 if (ustr_enomem(retval)) 506 ustr_sc_free(&retval); 507 508 done: 509 return retval; 510 } 511 512 static const char * extract_context(Ustr *line) 513 { 514 const char whitespace[] = " \t\n"; 515 size_t off, len; 516 517 /* check for trailing whitespace */ 518 off = ustr_spn_chrs_rev(line, 0, whitespace, strlen(whitespace)); 519 520 /* find the length of the last field in line */ 521 len = ustr_cspn_chrs_rev(line, off, whitespace, strlen(whitespace)); 522 523 if (len == 0) 524 return NULL; 525 return ustr_cstr(line) + ustr_len(line) - (len + off); 526 } 527 528 static int check_line(genhomedircon_settings_t * s, Ustr *line) 529 { 530 sepol_context_t *ctx_record = NULL; 531 const char *ctx_str; 532 int result; 533 534 ctx_str = extract_context(line); 535 if (!ctx_str) 536 return STATUS_ERR; 537 538 result = sepol_context_from_string(s->h_semanage->sepolh, 539 ctx_str, &ctx_record); 540 if (result == STATUS_SUCCESS && ctx_record != NULL) { 541 sepol_msg_set_callback(s->h_semanage->sepolh, NULL, NULL); 542 result = sepol_context_check(s->h_semanage->sepolh, 543 s->policydb, ctx_record); 544 sepol_msg_set_callback(s->h_semanage->sepolh, 545 semanage_msg_relay_handler, s->h_semanage); 546 sepol_context_free(ctx_record); 547 } 548 return result; 549 } 550 551 static int write_home_dir_context(genhomedircon_settings_t * s, FILE * out, 552 semanage_list_t * tpl, const char *user, 553 const char *seuser, const char *home, 554 const char *role_prefix, const char *level) 555 { 556 replacement_pair_t repl[] = { 557 {.search_for = TEMPLATE_SEUSER,.replace_with = seuser}, 558 {.search_for = TEMPLATE_HOME_DIR,.replace_with = home}, 559 {.search_for = TEMPLATE_ROLE,.replace_with = role_prefix}, 560 {.search_for = TEMPLATE_LEVEL,.replace_with = level}, 561 {NULL, NULL} 562 }; 563 Ustr *line = USTR_NULL; 564 565 if (fprintf(out, COMMENT_USER_HOME_CONTEXT, user) < 0) 566 return STATUS_ERR; 567 568 for (; tpl; tpl = tpl->next) { 569 line = replace_all(tpl->data, repl); 570 if (!line) 571 goto fail; 572 if (check_line(s, line) == STATUS_SUCCESS) { 573 if (!ustr_io_putfileline(&line, out)) 574 goto fail; 575 } 576 ustr_sc_free(&line); 577 } 578 return STATUS_SUCCESS; 579 580 fail: 581 ustr_sc_free(&line); 582 return STATUS_ERR; 583 } 584 585 static int write_home_root_context(genhomedircon_settings_t * s, FILE * out, 586 semanage_list_t * tpl, char *homedir) 587 { 588 replacement_pair_t repl[] = { 589 {.search_for = TEMPLATE_HOME_ROOT,.replace_with = homedir}, 590 {NULL, NULL} 591 }; 592 Ustr *line = USTR_NULL; 593 594 for (; tpl; tpl = tpl->next) { 595 line = replace_all(tpl->data, repl); 596 if (!line) 597 goto fail; 598 if (check_line(s, line) == STATUS_SUCCESS) { 599 if (!ustr_io_putfileline(&line, out)) 600 goto fail; 601 } 602 ustr_sc_free(&line); 603 } 604 return STATUS_SUCCESS; 605 606 fail: 607 ustr_sc_free(&line); 608 return STATUS_ERR; 609 } 610 611 static int write_user_context(genhomedircon_settings_t * s, FILE * out, 612 semanage_list_t * tpl, const char *user, 613 const char *seuser, const char *role_prefix) 614 { 615 replacement_pair_t repl[] = { 616 {.search_for = TEMPLATE_USER,.replace_with = user}, 617 {.search_for = TEMPLATE_ROLE,.replace_with = role_prefix}, 618 {.search_for = TEMPLATE_SEUSER,.replace_with = seuser}, 619 {NULL, NULL} 620 }; 621 Ustr *line = USTR_NULL; 622 623 for (; tpl; tpl = tpl->next) { 624 line = replace_all(tpl->data, repl); 625 if (!line) 626 goto fail; 627 if (check_line(s, line) == STATUS_SUCCESS) { 628 if (!ustr_io_putfileline(&line, out)) 629 goto fail; 630 } 631 ustr_sc_free(&line); 632 } 633 return STATUS_SUCCESS; 634 635 fail: 636 ustr_sc_free(&line); 637 return STATUS_ERR; 638 } 639 640 static int user_sort_func(semanage_user_t ** arg1, semanage_user_t ** arg2) 641 { 642 return strcmp(semanage_user_get_name(*arg1), 643 semanage_user_get_name(*arg2)); 644 } 645 646 static int name_user_cmp(char *key, semanage_user_t ** val) 647 { 648 return strcmp(key, semanage_user_get_name(*val)); 649 } 650 651 static int push_user_entry(genhomedircon_user_entry_t ** list, const char *n, 652 const char *sen, const char *pre, const char *h, 653 const char *l) 654 { 655 genhomedircon_user_entry_t *temp = NULL; 656 char *name = NULL; 657 char *sename = NULL; 658 char *prefix = NULL; 659 char *home = NULL; 660 char *level = NULL; 661 662 temp = malloc(sizeof(genhomedircon_user_entry_t)); 663 if (!temp) 664 goto cleanup; 665 name = strdup(n); 666 if (!name) 667 goto cleanup; 668 sename = strdup(sen); 669 if (!sename) 670 goto cleanup; 671 prefix = strdup(pre); 672 if (!prefix) 673 goto cleanup; 674 home = strdup(h); 675 if (!home) 676 goto cleanup; 677 level = strdup(l); 678 if (!level) 679 goto cleanup; 680 681 temp->name = name; 682 temp->sename = sename; 683 temp->prefix = prefix; 684 temp->home = home; 685 temp->level = level; 686 temp->next = (*list); 687 (*list) = temp; 688 689 return STATUS_SUCCESS; 690 691 cleanup: 692 free(name); 693 free(sename); 694 free(prefix); 695 free(home); 696 free(level); 697 free(temp); 698 return STATUS_ERR; 699 } 700 701 static void pop_user_entry(genhomedircon_user_entry_t ** list) 702 { 703 genhomedircon_user_entry_t *temp; 704 705 if (!list || !(*list)) 706 return; 707 708 temp = *list; 709 *list = temp->next; 710 free(temp->name); 711 free(temp->sename); 712 free(temp->prefix); 713 free(temp->home); 714 free(temp->level); 715 free(temp); 716 } 717 718 static int set_fallback_user(genhomedircon_settings_t *s, const char *user, 719 const char *prefix, const char *level) 720 { 721 char *fallback_user = strdup(user); 722 char *fallback_user_prefix = strdup(prefix); 723 char *fallback_user_level = NULL; 724 if (level) 725 fallback_user_level = strdup(level); 726 727 if (fallback_user == NULL || fallback_user_prefix == NULL || 728 (fallback_user_level == NULL && level != NULL)) { 729 free(fallback_user); 730 free(fallback_user_prefix); 731 free(fallback_user_level); 732 return STATUS_ERR; 733 } 734 735 free(s->fallback_user); 736 free(s->fallback_user_prefix); 737 free(s->fallback_user_level); 738 s->fallback_user = fallback_user; 739 s->fallback_user_prefix = fallback_user_prefix; 740 s->fallback_user_level = fallback_user_level; 741 return STATUS_SUCCESS; 742 } 743 744 static int setup_fallback_user(genhomedircon_settings_t * s) 745 { 746 semanage_seuser_t **seuser_list = NULL; 747 unsigned int nseusers = 0; 748 semanage_user_key_t *key = NULL; 749 semanage_user_t *u = NULL; 750 const char *name = NULL; 751 const char *seuname = NULL; 752 const char *prefix = NULL; 753 const char *level = NULL; 754 unsigned int i; 755 int retval; 756 int errors = 0; 757 758 retval = semanage_seuser_list(s->h_semanage, &seuser_list, &nseusers); 759 if (retval < 0 || (nseusers < 1)) { 760 /* if there are no users, this function can't do any other work */ 761 return errors; 762 } 763 764 for (i = 0; i < nseusers; i++) { 765 name = semanage_seuser_get_name(seuser_list[i]); 766 if (strcmp(name, DEFAULT_LOGIN) == 0) { 767 seuname = semanage_seuser_get_sename(seuser_list[i]); 768 769 /* find the user structure given the name */ 770 if (semanage_user_key_create(s->h_semanage, seuname, 771 &key) < 0) { 772 errors = STATUS_ERR; 773 break; 774 } 775 if (semanage_user_query(s->h_semanage, key, &u) < 0) 776 { 777 prefix = name; 778 level = FALLBACK_USER_LEVEL; 779 } 780 else 781 { 782 prefix = semanage_user_get_prefix(u); 783 level = semanage_user_get_mlslevel(u); 784 if (!level) 785 level = FALLBACK_USER_LEVEL; 786 } 787 788 if (set_fallback_user(s, seuname, prefix, level) != 0) 789 errors = STATUS_ERR; 790 semanage_user_key_free(key); 791 if (u) 792 semanage_user_free(u); 793 break; 794 } 795 } 796 797 for (i = 0; i < nseusers; i++) 798 semanage_seuser_free(seuser_list[i]); 799 free(seuser_list); 800 801 return errors; 802 } 803 804 static genhomedircon_user_entry_t *get_users(genhomedircon_settings_t * s, 805 int *errors) 806 { 807 genhomedircon_user_entry_t *head = NULL; 808 semanage_seuser_t **seuser_list = NULL; 809 unsigned int nseusers = 0; 810 semanage_user_t **user_list = NULL; 811 unsigned int nusers = 0; 812 semanage_user_t **u = NULL; 813 const char *name = NULL; 814 const char *seuname = NULL; 815 const char *prefix = NULL; 816 const char *level = NULL; 817 struct passwd pwstorage, *pwent = NULL; 818 unsigned int i; 819 long rbuflen; 820 char *rbuf = NULL; 821 int retval; 822 823 *errors = 0; 824 retval = semanage_seuser_list(s->h_semanage, &seuser_list, &nseusers); 825 if (retval < 0 || (nseusers < 1)) { 826 /* if there are no users, this function can't do any other work */ 827 return NULL; 828 } 829 830 if (semanage_user_list(s->h_semanage, &user_list, &nusers) < 0) { 831 nusers = 0; 832 } 833 834 qsort(user_list, nusers, sizeof(semanage_user_t *), 835 (int (*)(const void *, const void *))&user_sort_func); 836 837 /* Allocate space for the getpwnam_r buffer */ 838 rbuflen = sysconf(_SC_GETPW_R_SIZE_MAX); 839 if (rbuflen <= 0) 840 goto cleanup; 841 rbuf = malloc(rbuflen); 842 if (rbuf == NULL) 843 goto cleanup; 844 845 for (i = 0; i < nseusers; i++) { 846 seuname = semanage_seuser_get_sename(seuser_list[i]); 847 name = semanage_seuser_get_name(seuser_list[i]); 848 849 if (strcmp(name,"root") && strcmp(seuname, s->fallback_user) == 0) 850 continue; 851 852 if (strcmp(name, DEFAULT_LOGIN) == 0) 853 continue; 854 855 if (strcmp(name, TEMPLATE_SEUSER) == 0) 856 continue; 857 858 /* %groupname syntax */ 859 if (name[0] == '%') 860 continue; 861 862 /* find the user structure given the name */ 863 u = bsearch(seuname, user_list, nusers, sizeof(semanage_user_t *), 864 (int (*)(const void *, const void *)) 865 &name_user_cmp); 866 if (u) { 867 prefix = semanage_user_get_prefix(*u); 868 level = semanage_user_get_mlslevel(*u); 869 if (!level) 870 level = FALLBACK_USER_LEVEL; 871 } else { 872 prefix = name; 873 level = FALLBACK_USER_LEVEL; 874 } 875 876 retval = getpwnam_r(name, &pwstorage, rbuf, rbuflen, &pwent); 877 if (retval != 0 || pwent == NULL) { 878 if (retval != 0 && retval != ENOENT) { 879 *errors = STATUS_ERR; 880 goto cleanup; 881 } 882 883 WARN(s->h_semanage, 884 "user %s not in password file", name); 885 continue; 886 } 887 888 int len = strlen(pwent->pw_dir) -1; 889 for(; len > 0 && pwent->pw_dir[len] == '/'; len--) { 890 pwent->pw_dir[len] = '\0'; 891 } 892 893 if (strcmp(pwent->pw_dir, "/") == 0) { 894 /* don't relabel / genhomdircon checked to see if root 895 * was the user and if so, set his home directory to 896 * /root */ 897 continue; 898 } 899 if (ignore(pwent->pw_dir)) 900 continue; 901 if (push_user_entry(&head, name, seuname, 902 prefix, pwent->pw_dir, level) != STATUS_SUCCESS) { 903 *errors = STATUS_ERR; 904 break; 905 } 906 } 907 908 cleanup: 909 free(rbuf); 910 if (*errors) { 911 for (; head; pop_user_entry(&head)) { 912 /* the pop function takes care of all the cleanup 913 so the loop body is just empty */ 914 } 915 } 916 for (i = 0; i < nseusers; i++) { 917 semanage_seuser_free(seuser_list[i]); 918 } 919 free(seuser_list); 920 921 for (i = 0; i < nusers; i++) { 922 semanage_user_free(user_list[i]); 923 } 924 free(user_list); 925 926 return head; 927 } 928 929 static int write_gen_home_dir_context(genhomedircon_settings_t * s, FILE * out, 930 semanage_list_t * user_context_tpl, 931 semanage_list_t * homedir_context_tpl) 932 { 933 genhomedircon_user_entry_t *users; 934 int errors = 0; 935 936 users = get_users(s, &errors); 937 if (!users && errors) { 938 return STATUS_ERR; 939 } 940 941 for (; users; pop_user_entry(&users)) { 942 if (write_home_dir_context(s, out, homedir_context_tpl, 943 users->name, 944 users->sename, users->home, 945 users->prefix, users->level)) 946 goto err; 947 if (write_user_context(s, out, user_context_tpl, users->name, 948 users->sename, users->prefix)) 949 goto err; 950 } 951 952 return STATUS_SUCCESS; 953 err: 954 for (; users; pop_user_entry(&users)) { 955 /* the pop function takes care of all the cleanup 956 * so the loop body is just empty */ 957 } 958 959 return STATUS_ERR; 960 } 961 962 /** 963 * @param s settings structure, stores various paths etc. Must never be NULL 964 * @param out the FILE to put all the output in. 965 * @return 0 on success 966 */ 967 static int write_context_file(genhomedircon_settings_t * s, FILE * out) 968 { 969 semanage_list_t *homedirs = NULL; 970 semanage_list_t *h = NULL; 971 semanage_list_t *user_context_tpl = NULL; 972 semanage_list_t *homedir_context_tpl = NULL; 973 semanage_list_t *homeroot_context_tpl = NULL; 974 int retval = STATUS_SUCCESS; 975 976 homedir_context_tpl = make_template(s, &HOME_DIR_PRED); 977 homeroot_context_tpl = make_template(s, &HOME_ROOT_PRED); 978 user_context_tpl = make_template(s, &USER_CONTEXT_PRED); 979 980 if (!homedir_context_tpl && !homeroot_context_tpl && !user_context_tpl) 981 goto done; 982 983 if (write_file_context_header(out) != STATUS_SUCCESS) { 984 retval = STATUS_ERR; 985 goto done; 986 } 987 988 if (setup_fallback_user(s) != 0) { 989 retval = STATUS_ERR; 990 goto done; 991 } 992 993 if (homedir_context_tpl || homeroot_context_tpl) { 994 homedirs = get_home_dirs(s); 995 if (!homedirs) { 996 WARN(s->h_semanage, 997 "no home directories were available, exiting without writing"); 998 goto done; 999 } 1000 1001 for (h = homedirs; h; h = h->next) { 1002 Ustr *temp = ustr_dup_cstr(h->data); 1003 1004 if (!temp || !ustr_add_cstr(&temp, "/[^/]*")) { 1005 ustr_sc_free(&temp); 1006 retval = STATUS_ERR; 1007 goto done; 1008 } 1009 1010 if (write_home_dir_context(s, out, 1011 homedir_context_tpl, 1012 s->fallback_user, s->fallback_user, 1013 ustr_cstr(temp), 1014 s->fallback_user_prefix, s->fallback_user_level) != 1015 STATUS_SUCCESS) { 1016 ustr_sc_free(&temp); 1017 retval = STATUS_ERR; 1018 goto done; 1019 } 1020 if (write_home_root_context(s, out, 1021 homeroot_context_tpl, 1022 h->data) != STATUS_SUCCESS) { 1023 ustr_sc_free(&temp); 1024 retval = STATUS_ERR; 1025 goto done; 1026 } 1027 1028 ustr_sc_free(&temp); 1029 } 1030 } 1031 if (user_context_tpl) { 1032 if (write_user_context(s, out, user_context_tpl, 1033 ".*", s->fallback_user, 1034 s->fallback_user_prefix) != STATUS_SUCCESS) { 1035 retval = STATUS_ERR; 1036 goto done; 1037 } 1038 1039 if (write_gen_home_dir_context(s, out, user_context_tpl, 1040 homedir_context_tpl) != STATUS_SUCCESS) { 1041 retval = STATUS_ERR; 1042 } 1043 } 1044 1045 done: 1046 /* Cleanup */ 1047 semanage_list_destroy(&homedirs); 1048 semanage_list_destroy(&user_context_tpl); 1049 semanage_list_destroy(&homedir_context_tpl); 1050 semanage_list_destroy(&homeroot_context_tpl); 1051 1052 return retval; 1053 } 1054 1055 int semanage_genhomedircon(semanage_handle_t * sh, 1056 sepol_policydb_t * policydb, 1057 int usepasswd, 1058 char *ignoredirs) 1059 { 1060 genhomedircon_settings_t s; 1061 FILE *out = NULL; 1062 int retval = 0; 1063 1064 assert(sh); 1065 1066 s.homedir_template_path = 1067 semanage_path(SEMANAGE_TMP, SEMANAGE_HOMEDIR_TMPL); 1068 s.fcfilepath = semanage_final_path(SEMANAGE_FINAL_TMP, 1069 SEMANAGE_FC_HOMEDIRS); 1070 1071 s.fallback_user = strdup(FALLBACK_USER); 1072 s.fallback_user_prefix = strdup(FALLBACK_USER_PREFIX); 1073 s.fallback_user_level = strdup(FALLBACK_USER_LEVEL); 1074 if (s.fallback_user == NULL || s.fallback_user_prefix == NULL || s.fallback_user_level == NULL) { 1075 retval = STATUS_ERR; 1076 goto done; 1077 } 1078 1079 if (ignoredirs) ignore_setup(ignoredirs); 1080 1081 s.usepasswd = usepasswd; 1082 s.h_semanage = sh; 1083 s.policydb = policydb; 1084 1085 if (!(out = fopen(s.fcfilepath, "w"))) { 1086 /* couldn't open output file */ 1087 ERR(sh, "Could not open the file_context file for writing"); 1088 retval = STATUS_ERR; 1089 goto done; 1090 } 1091 1092 retval = write_context_file(&s, out); 1093 1094 done: 1095 if (out != NULL) 1096 fclose(out); 1097 1098 free(s.fallback_user); 1099 free(s.fallback_user_prefix); 1100 free(s.fallback_user_level); 1101 ignore_free(); 1102 1103 return retval; 1104 } 1105