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