1 /* 2 * Author: Karl MacMillan <kmacmillan (at) tresys.com> 3 * 4 * Modified: 5 * Dan Walsh <dwalsh (at) redhat.com> - Added security_load_booleans(). 6 */ 7 8 #ifndef DISABLE_BOOL 9 10 #include <assert.h> 11 #include <sys/types.h> 12 #include <sys/stat.h> 13 #include <fcntl.h> 14 #include <stdlib.h> 15 #include <dirent.h> 16 #include <string.h> 17 #include <stdio.h> 18 #include <stdio_ext.h> 19 #include <unistd.h> 20 #include <fnmatch.h> 21 #include <limits.h> 22 #include <ctype.h> 23 #include <errno.h> 24 25 #include "selinux_internal.h" 26 #include "policy.h" 27 28 #define SELINUX_BOOL_DIR "/booleans/" 29 30 static int filename_select(const struct dirent *d) 31 { 32 if (d->d_name[0] == '.' 33 && (d->d_name[1] == '\0' 34 || (d->d_name[1] == '.' && d->d_name[2] == '\0'))) 35 return 0; 36 return 1; 37 } 38 39 int security_get_boolean_names(char ***names, int *len) 40 { 41 char path[PATH_MAX]; 42 int i, rc; 43 struct dirent **namelist; 44 char **n; 45 46 if (!len || names == NULL) { 47 errno = EINVAL; 48 return -1; 49 } 50 if (!selinux_mnt) { 51 errno = ENOENT; 52 return -1; 53 } 54 55 snprintf(path, sizeof path, "%s%s", selinux_mnt, SELINUX_BOOL_DIR); 56 *len = scandir(path, &namelist, &filename_select, alphasort); 57 if (*len <= 0) { 58 errno = ENOENT; 59 return -1; 60 } 61 62 n = (char **)malloc(sizeof(char *) * *len); 63 if (!n) { 64 rc = -1; 65 goto bad; 66 } 67 68 for (i = 0; i < *len; i++) { 69 n[i] = strdup(namelist[i]->d_name); 70 if (!n[i]) { 71 rc = -1; 72 goto bad_freen; 73 } 74 } 75 rc = 0; 76 *names = n; 77 out: 78 for (i = 0; i < *len; i++) { 79 free(namelist[i]); 80 } 81 free(namelist); 82 return rc; 83 bad_freen: 84 for (--i; i >= 0; --i) 85 free(n[i]); 86 free(n); 87 bad: 88 goto out; 89 } 90 91 char *selinux_boolean_sub(const char *name) 92 { 93 char *sub = NULL; 94 char *line_buf = NULL; 95 size_t line_len; 96 FILE *cfg; 97 98 if (!name) 99 return NULL; 100 101 cfg = fopen(selinux_booleans_subs_path(), "re"); 102 if (!cfg) 103 goto out; 104 105 while (getline(&line_buf, &line_len, cfg) != -1) { 106 char *ptr; 107 char *src = line_buf; 108 char *dst; 109 while (*src && isspace(*src)) 110 src++; 111 if (!*src) 112 continue; 113 if (src[0] == '#') 114 continue; 115 116 ptr = src; 117 while (*ptr && !isspace(*ptr)) 118 ptr++; 119 *ptr++ = '\0'; 120 if (strcmp(src, name) != 0) 121 continue; 122 123 dst = ptr; 124 while (*dst && isspace(*dst)) 125 dst++; 126 if (!*dst) 127 continue; 128 ptr=dst; 129 while (*ptr && !isspace(*ptr)) 130 ptr++; 131 *ptr='\0'; 132 133 sub = strdup(dst); 134 135 break; 136 } 137 free(line_buf); 138 fclose(cfg); 139 out: 140 if (!sub) 141 sub = strdup(name); 142 return sub; 143 } 144 145 static int bool_open(const char *name, int flag) { 146 char *fname = NULL; 147 char *alt_name = NULL; 148 int len; 149 int fd = -1; 150 int ret; 151 char *ptr; 152 153 if (!name) { 154 errno = EINVAL; 155 return -1; 156 } 157 158 /* note the 'sizeof' gets us enough room for the '\0' */ 159 len = strlen(name) + strlen(selinux_mnt) + sizeof(SELINUX_BOOL_DIR); 160 fname = malloc(sizeof(char) * len); 161 if (!fname) 162 return -1; 163 164 ret = snprintf(fname, len, "%s%s%s", selinux_mnt, SELINUX_BOOL_DIR, name); 165 if (ret < 0) 166 goto out; 167 assert(ret < len); 168 169 fd = open(fname, flag); 170 if (fd >= 0 || errno != ENOENT) 171 goto out; 172 173 alt_name = selinux_boolean_sub(name); 174 if (!alt_name) 175 goto out; 176 177 /* note the 'sizeof' gets us enough room for the '\0' */ 178 len = strlen(alt_name) + strlen(selinux_mnt) + sizeof(SELINUX_BOOL_DIR); 179 ptr = realloc(fname, len); 180 if (!ptr) 181 goto out; 182 fname = ptr; 183 184 ret = snprintf(fname, len, "%s%s%s", selinux_mnt, SELINUX_BOOL_DIR, alt_name); 185 if (ret < 0) 186 goto out; 187 assert(ret < len); 188 189 fd = open(fname, flag); 190 out: 191 free(fname); 192 free(alt_name); 193 194 return fd; 195 } 196 197 #define STRBUF_SIZE 3 198 static int get_bool_value(const char *name, char **buf) 199 { 200 int fd, len; 201 int errno_tmp; 202 203 if (!selinux_mnt) { 204 errno = ENOENT; 205 return -1; 206 } 207 208 *buf = malloc(sizeof(char) * (STRBUF_SIZE + 1)); 209 if (!*buf) 210 return -1; 211 212 (*buf)[STRBUF_SIZE] = 0; 213 214 fd = bool_open(name, O_RDONLY | O_CLOEXEC); 215 if (fd < 0) 216 goto out_err; 217 218 len = read(fd, *buf, STRBUF_SIZE); 219 errno_tmp = errno; 220 close(fd); 221 errno = errno_tmp; 222 if (len != STRBUF_SIZE) 223 goto out_err; 224 225 return 0; 226 out_err: 227 free(*buf); 228 return -1; 229 } 230 231 int security_get_boolean_pending(const char *name) 232 { 233 char *buf; 234 int val; 235 236 if (get_bool_value(name, &buf)) 237 return -1; 238 239 if (atoi(&buf[1])) 240 val = 1; 241 else 242 val = 0; 243 free(buf); 244 return val; 245 } 246 247 int security_get_boolean_active(const char *name) 248 { 249 char *buf; 250 int val; 251 252 if (get_bool_value(name, &buf)) 253 return -1; 254 255 buf[1] = '\0'; 256 if (atoi(buf)) 257 val = 1; 258 else 259 val = 0; 260 free(buf); 261 return val; 262 } 263 264 int security_set_boolean(const char *name, int value) 265 { 266 int fd, ret; 267 char buf[2]; 268 269 if (!selinux_mnt) { 270 errno = ENOENT; 271 return -1; 272 } 273 if (value < 0 || value > 1) { 274 errno = EINVAL; 275 return -1; 276 } 277 278 fd = bool_open(name, O_WRONLY | O_CLOEXEC); 279 if (fd < 0) 280 return -1; 281 282 if (value) 283 buf[0] = '1'; 284 else 285 buf[0] = '0'; 286 buf[1] = '\0'; 287 288 ret = write(fd, buf, 2); 289 close(fd); 290 291 if (ret > 0) 292 return 0; 293 else 294 return -1; 295 } 296 297 int security_commit_booleans(void) 298 { 299 int fd, ret; 300 char buf[2]; 301 char path[PATH_MAX]; 302 303 if (!selinux_mnt) { 304 errno = ENOENT; 305 return -1; 306 } 307 308 snprintf(path, sizeof path, "%s/commit_pending_bools", selinux_mnt); 309 fd = open(path, O_WRONLY | O_CLOEXEC); 310 if (fd < 0) 311 return -1; 312 313 buf[0] = '1'; 314 buf[1] = '\0'; 315 316 ret = write(fd, buf, 2); 317 close(fd); 318 319 if (ret > 0) 320 return 0; 321 else 322 return -1; 323 } 324 325 static char *strtrim(char *dest, char *source, int size) 326 { 327 int i = 0; 328 char *ptr = source; 329 i = 0; 330 while (isspace(*ptr) && i < size) { 331 ptr++; 332 i++; 333 } 334 strncpy(dest, ptr, size); 335 for (i = strlen(dest) - 1; i > 0; i--) { 336 if (!isspace(dest[i])) 337 break; 338 } 339 dest[i + 1] = '\0'; 340 return dest; 341 } 342 static int process_boolean(char *buffer, char *name, int namesize, int *val) 343 { 344 char name1[BUFSIZ]; 345 char *ptr = NULL; 346 char *tok; 347 348 /* Skip spaces */ 349 while (isspace(buffer[0])) 350 buffer++; 351 /* Ignore comments */ 352 if (buffer[0] == '#') 353 return 0; 354 355 tok = strtok_r(buffer, "=", &ptr); 356 if (!tok) { 357 errno = EINVAL; 358 return -1; 359 } 360 strncpy(name1, tok, BUFSIZ - 1); 361 strtrim(name, name1, namesize - 1); 362 363 tok = strtok_r(NULL, "\0", &ptr); 364 if (!tok) { 365 errno = EINVAL; 366 return -1; 367 } 368 369 while (isspace(*tok)) 370 tok++; 371 372 *val = -1; 373 if (isdigit(tok[0])) 374 *val = atoi(tok); 375 else if (!strncasecmp(tok, "true", sizeof("true") - 1)) 376 *val = 1; 377 else if (!strncasecmp(tok, "false", sizeof("false") - 1)) 378 *val = 0; 379 if (*val != 0 && *val != 1) { 380 errno = EINVAL; 381 return -1; 382 } 383 return 1; 384 } 385 static int save_booleans(size_t boolcnt, SELboolean * boollist) 386 { 387 ssize_t len; 388 size_t i; 389 char outbuf[BUFSIZ]; 390 char *inbuf = NULL; 391 392 /* Open file */ 393 const char *bool_file = selinux_booleans_path(); 394 char local_bool_file[PATH_MAX]; 395 char tmp_bool_file[PATH_MAX]; 396 FILE *boolf; 397 int fd; 398 int *used = (int *)malloc(sizeof(int) * boolcnt); 399 if (!used) { 400 return -1; 401 } 402 /* zero out used field */ 403 for (i = 0; i < boolcnt; i++) 404 used[i] = 0; 405 406 snprintf(tmp_bool_file, sizeof(tmp_bool_file), "%s.XXXXXX", bool_file); 407 fd = mkstemp(tmp_bool_file); 408 if (fd < 0) { 409 free(used); 410 return -1; 411 } 412 413 snprintf(local_bool_file, sizeof(local_bool_file), "%s.local", 414 bool_file); 415 boolf = fopen(local_bool_file, "re"); 416 if (boolf != NULL) { 417 ssize_t ret; 418 size_t size = 0; 419 int val; 420 char boolname[BUFSIZ-3]; 421 char *buffer; 422 inbuf = NULL; 423 __fsetlocking(boolf, FSETLOCKING_BYCALLER); 424 while ((len = getline(&inbuf, &size, boolf)) > 0) { 425 buffer = strdup(inbuf); 426 if (!buffer) 427 goto close_remove_fail; 428 ret = 429 process_boolean(inbuf, boolname, sizeof(boolname), 430 &val); 431 if (ret != 1) { 432 ret = write(fd, buffer, len); 433 free(buffer); 434 if (ret != len) 435 goto close_remove_fail; 436 } else { 437 free(buffer); 438 for (i = 0; i < boolcnt; i++) { 439 if (strcmp(boollist[i].name, boolname) 440 == 0) { 441 snprintf(outbuf, sizeof(outbuf), 442 "%s=%d\n", boolname, 443 boollist[i].value); 444 len = strlen(outbuf); 445 used[i] = 1; 446 if (write(fd, outbuf, len) != 447 len) 448 goto close_remove_fail; 449 else 450 break; 451 } 452 } 453 if (i == boolcnt) { 454 val = !!val; 455 snprintf(outbuf, sizeof(outbuf), 456 "%s=%d\n", boolname, val); 457 len = strlen(outbuf); 458 if (write(fd, outbuf, len) != len) 459 goto close_remove_fail; 460 } 461 } 462 free(inbuf); 463 inbuf = NULL; 464 } 465 fclose(boolf); 466 } 467 468 for (i = 0; i < boolcnt; i++) { 469 if (used[i] == 0) { 470 snprintf(outbuf, sizeof(outbuf), "%s=%d\n", 471 boollist[i].name, boollist[i].value); 472 len = strlen(outbuf); 473 if (write(fd, outbuf, len) != len) { 474 close_remove_fail: 475 free(inbuf); 476 close(fd); 477 remove_fail: 478 unlink(tmp_bool_file); 479 free(used); 480 return -1; 481 } 482 } 483 484 } 485 if (fchmod(fd, S_IRUSR | S_IWUSR) != 0) 486 goto close_remove_fail; 487 close(fd); 488 if (rename(tmp_bool_file, local_bool_file) != 0) 489 goto remove_fail; 490 491 free(used); 492 return 0; 493 } 494 static void rollback(SELboolean * boollist, int end) 495 { 496 int i; 497 498 for (i = 0; i < end; i++) 499 security_set_boolean(boollist[i].name, 500 security_get_boolean_active(boollist[i]. 501 name)); 502 } 503 504 int security_set_boolean_list(size_t boolcnt, SELboolean * boollist, 505 int permanent) 506 { 507 508 size_t i; 509 for (i = 0; i < boolcnt; i++) { 510 boollist[i].value = !!boollist[i].value; 511 if (security_set_boolean(boollist[i].name, boollist[i].value)) { 512 rollback(boollist, i); 513 return -1; 514 } 515 } 516 517 /* OK, let's do the commit */ 518 if (security_commit_booleans()) { 519 return -1; 520 } 521 522 if (permanent) 523 return save_booleans(boolcnt, boollist); 524 525 return 0; 526 } 527 int security_load_booleans(char *path) 528 { 529 FILE *boolf; 530 char *inbuf; 531 char localbools[BUFSIZ]; 532 size_t len = 0, errors = 0; 533 int val; 534 char name[BUFSIZ]; 535 536 boolf = fopen(path ? path : selinux_booleans_path(), "re"); 537 if (boolf == NULL) 538 goto localbool; 539 540 __fsetlocking(boolf, FSETLOCKING_BYCALLER); 541 while (getline(&inbuf, &len, boolf) > 0) { 542 int ret = process_boolean(inbuf, name, sizeof(name), &val); 543 if (ret == -1) 544 errors++; 545 if (ret == 1) 546 if (security_set_boolean(name, val) < 0) { 547 errors++; 548 } 549 } 550 fclose(boolf); 551 localbool: 552 snprintf(localbools, sizeof(localbools), "%s.local", 553 (path ? path : selinux_booleans_path())); 554 boolf = fopen(localbools, "re"); 555 556 if (boolf != NULL) { 557 int ret; 558 __fsetlocking(boolf, FSETLOCKING_BYCALLER); 559 while (getline(&inbuf, &len, boolf) > 0) { 560 ret = process_boolean(inbuf, name, sizeof(name), &val); 561 if (ret == -1) 562 errors++; 563 if (ret == 1) 564 if (security_set_boolean(name, val) < 0) { 565 errors++; 566 } 567 } 568 fclose(boolf); 569 } 570 if (security_commit_booleans() < 0) 571 return -1; 572 573 if (errors) 574 errno = EINVAL; 575 return errors ? -1 : 0; 576 } 577 578 #else 579 580 #include <stdlib.h> 581 #include "selinux_internal.h" 582 583 int security_set_boolean_list(size_t boolcnt __attribute__((unused)), 584 SELboolean * boollist __attribute__((unused)), 585 int permanent __attribute__((unused))) 586 { 587 return -1; 588 } 589 590 int security_load_booleans(char *path __attribute__((unused))) 591 { 592 return -1; 593 } 594 595 int security_get_boolean_names(char ***names __attribute__((unused)), 596 int *len __attribute__((unused))) 597 { 598 return -1; 599 } 600 601 int security_get_boolean_pending(const char *name __attribute__((unused))) 602 { 603 return -1; 604 } 605 606 int security_get_boolean_active(const char *name __attribute__((unused))) 607 { 608 return -1; 609 } 610 611 int security_set_boolean(const char *name __attribute__((unused)), 612 int value __attribute__((unused))) 613 { 614 return -1; 615 } 616 617 int security_commit_booleans(void) 618 { 619 return -1; 620 } 621 622 char *selinux_boolean_sub(const char *name __attribute__((unused))) 623 { 624 return NULL; 625 } 626 #endif 627 628 hidden_def(security_get_boolean_names) 629 hidden_def(selinux_boolean_sub) 630 hidden_def(security_get_boolean_active) 631 hidden_def(security_set_boolean) 632 hidden_def(security_commit_booleans) 633