1391b1d75SRobert Watson /* 2391b1d75SRobert Watson * Copyright (c) 1999, 2000, 2001, 2002 Robert N. M. Watson 3738824adSRobert Watson * Copyright (c) 2002, 2003 Networks Associates Technology, Inc. 4391b1d75SRobert Watson * All rights reserved. 5391b1d75SRobert Watson * 6391b1d75SRobert Watson * This software was developed by Robert Watson for the TrustedBSD Project. 7391b1d75SRobert Watson * 8f8d08150SRobert Watson * This software was developed for the FreeBSD Project in part by Network 9f8d08150SRobert Watson * Associates Laboratories, the Security Research Division of Network 10f8d08150SRobert Watson * Associates, Inc. under DARPA/SPAWAR contract N66001-01-C-8035 ("CBOSS"), 11f8d08150SRobert Watson * as part of the DARPA CHATS research program. 12391b1d75SRobert Watson * 13391b1d75SRobert Watson * Redistribution and use in source and binary forms, with or without 14391b1d75SRobert Watson * modification, are permitted provided that the following conditions 15391b1d75SRobert Watson * are met: 16391b1d75SRobert Watson * 1. Redistributions of source code must retain the above copyright 17391b1d75SRobert Watson * notice, this list of conditions and the following disclaimer. 18391b1d75SRobert Watson * 2. Redistributions in binary form must reproduce the above copyright 19391b1d75SRobert Watson * notice, this list of conditions and the following disclaimer in the 20391b1d75SRobert Watson * documentation and/or other materials provided with the distribution. 21391b1d75SRobert Watson * 22391b1d75SRobert Watson * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 23391b1d75SRobert Watson * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 24391b1d75SRobert Watson * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 25391b1d75SRobert Watson * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 26391b1d75SRobert Watson * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 27391b1d75SRobert Watson * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 28391b1d75SRobert Watson * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 29391b1d75SRobert Watson * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 30391b1d75SRobert Watson * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 31391b1d75SRobert Watson * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 32391b1d75SRobert Watson * SUCH DAMAGE. 33391b1d75SRobert Watson * 34391b1d75SRobert Watson * $FreeBSD$ 35391b1d75SRobert Watson */ 36391b1d75SRobert Watson 37391b1d75SRobert Watson #include <sys/types.h> 38391b1d75SRobert Watson #include <sys/queue.h> 39391b1d75SRobert Watson #include <sys/sysctl.h> 40391b1d75SRobert Watson 41391b1d75SRobert Watson #include <dlfcn.h> 42391b1d75SRobert Watson #include <errno.h> 43688dfe45SGarrett Wollman #include <limits.h> 44391b1d75SRobert Watson #include <stdio.h> 45391b1d75SRobert Watson #include <stdlib.h> 46391b1d75SRobert Watson #include <string.h> 4784d9142fSJacques Vidrine #include <unistd.h> 48391b1d75SRobert Watson 49391b1d75SRobert Watson #include <sys/mac.h> 50391b1d75SRobert Watson 51391b1d75SRobert Watson static int internal_initialized; 52391b1d75SRobert Watson 53738824adSRobert Watson /* 54738824adSRobert Watson * Maintain a list of default label preparations for various object 55738824adSRobert Watson * types. Each name will appear only once in the list. 56738824adSRobert Watson * 57738824adSRobert Watson * XXXMAC: Not thread-safe. 58738824adSRobert Watson */ 59a2f046e8SRobert Watson static LIST_HEAD(, label_default) label_default_head; 60738824adSRobert Watson struct label_default { 61738824adSRobert Watson char *ld_name; 62738824adSRobert Watson char *ld_labels; 63738824adSRobert Watson LIST_ENTRY(label_default) ld_entries; 64738824adSRobert Watson }; 65391b1d75SRobert Watson 66391b1d75SRobert Watson static void 67391b1d75SRobert Watson mac_destroy_labels(void) 68391b1d75SRobert Watson { 69738824adSRobert Watson struct label_default *ld; 70391b1d75SRobert Watson 71738824adSRobert Watson while ((ld = LIST_FIRST(&label_default_head))) { 72738824adSRobert Watson free(ld->ld_name); 73738824adSRobert Watson free(ld->ld_labels); 74738824adSRobert Watson LIST_REMOVE(ld, ld_entries); 75738824adSRobert Watson free(ld); 76391b1d75SRobert Watson } 77391b1d75SRobert Watson } 78391b1d75SRobert Watson 79391b1d75SRobert Watson static void 80391b1d75SRobert Watson mac_destroy_internal(void) 81391b1d75SRobert Watson { 82391b1d75SRobert Watson 83391b1d75SRobert Watson mac_destroy_labels(); 84391b1d75SRobert Watson 85391b1d75SRobert Watson internal_initialized = 0; 86391b1d75SRobert Watson } 87391b1d75SRobert Watson 88391b1d75SRobert Watson static int 89738824adSRobert Watson mac_add_type(const char *name, const char *labels) 90391b1d75SRobert Watson { 91738824adSRobert Watson struct label_default *ld, *ld_new; 92738824adSRobert Watson char *name_dup, *labels_dup; 93738824adSRobert Watson 94738824adSRobert Watson /* 95738824adSRobert Watson * Speculatively allocate all the memory now to avoid allocating 96738824adSRobert Watson * later when we will someday hold a mutex. 97738824adSRobert Watson */ 98738824adSRobert Watson name_dup = strdup(name); 99738824adSRobert Watson if (name_dup == NULL) { 100738824adSRobert Watson errno = ENOMEM; 101738824adSRobert Watson return (-1); 102738824adSRobert Watson } 103738824adSRobert Watson labels_dup = strdup(labels); 104738824adSRobert Watson if (labels_dup == NULL) { 105738824adSRobert Watson free(name_dup); 106738824adSRobert Watson errno = ENOMEM; 107738824adSRobert Watson return (-1); 108738824adSRobert Watson } 109738824adSRobert Watson ld_new = malloc(sizeof(*ld)); 110738824adSRobert Watson if (ld_new == NULL) { 111738824adSRobert Watson free(name_dup); 112738824adSRobert Watson free(labels_dup); 113738824adSRobert Watson errno = ENOMEM; 114738824adSRobert Watson return (-1); 115738824adSRobert Watson } 116738824adSRobert Watson 117738824adSRobert Watson /* 118738824adSRobert Watson * If the type is already present, replace the current entry 119738824adSRobert Watson * rather than add a new instance. 120738824adSRobert Watson */ 121738824adSRobert Watson for (ld = LIST_FIRST(&label_default_head); ld != NULL; 122738824adSRobert Watson ld = LIST_NEXT(ld, ld_entries)) { 123738824adSRobert Watson if (strcmp(name, ld->ld_name) == 0) 124738824adSRobert Watson break; 125738824adSRobert Watson } 126738824adSRobert Watson 127738824adSRobert Watson if (ld != NULL) { 128738824adSRobert Watson free(ld->ld_labels); 129738824adSRobert Watson ld->ld_labels = labels_dup; 130738824adSRobert Watson labels_dup = NULL; 131738824adSRobert Watson } else { 132738824adSRobert Watson ld = ld_new; 133738824adSRobert Watson ld->ld_name = name_dup; 134738824adSRobert Watson ld->ld_labels = labels_dup; 135738824adSRobert Watson 136738824adSRobert Watson ld_new = NULL; 137738824adSRobert Watson name_dup = NULL; 138738824adSRobert Watson labels_dup = NULL; 139738824adSRobert Watson 140738824adSRobert Watson LIST_INSERT_HEAD(&label_default_head, ld, ld_entries); 141738824adSRobert Watson } 142738824adSRobert Watson 143738824adSRobert Watson if (name_dup != NULL) 144738824adSRobert Watson free(name_dup); 145738824adSRobert Watson if (labels_dup != NULL) 146738824adSRobert Watson free(labels_dup); 147738824adSRobert Watson if (ld_new != NULL) 148738824adSRobert Watson free(ld_new); 149738824adSRobert Watson 150738824adSRobert Watson return (0); 151738824adSRobert Watson } 152738824adSRobert Watson 153738824adSRobert Watson static char * 154738824adSRobert Watson next_token(char **string) 155738824adSRobert Watson { 156738824adSRobert Watson char *token; 157738824adSRobert Watson 158738824adSRobert Watson token = strsep(string, " \t"); 159738824adSRobert Watson while (token != NULL && *token == '\0') 160738824adSRobert Watson token = strsep(string, " \t"); 161738824adSRobert Watson 162738824adSRobert Watson return (token); 163738824adSRobert Watson } 164738824adSRobert Watson 165738824adSRobert Watson static int 166738824adSRobert Watson mac_init_internal(int ignore_errors) 167738824adSRobert Watson { 168738824adSRobert Watson const char *filename; 169391b1d75SRobert Watson char line[LINE_MAX]; 170738824adSRobert Watson FILE *file; 171391b1d75SRobert Watson int error; 172391b1d75SRobert Watson 173391b1d75SRobert Watson error = 0; 174391b1d75SRobert Watson 175738824adSRobert Watson LIST_INIT(&label_default_head); 176738824adSRobert Watson 177738824adSRobert Watson if (!issetugid() && getenv("MAC_CONFFILE") != NULL) 178738824adSRobert Watson filename = getenv("MAC_CONFFILE"); 179738824adSRobert Watson else 180738824adSRobert Watson filename = MAC_CONFFILE; 181738824adSRobert Watson file = fopen(filename, "r"); 182391b1d75SRobert Watson if (file == NULL) 183391b1d75SRobert Watson return (0); 184391b1d75SRobert Watson 185391b1d75SRobert Watson while (fgets(line, LINE_MAX, file)) { 18684d9142fSJacques Vidrine char *comment, *parse, *statement; 187391b1d75SRobert Watson 188391b1d75SRobert Watson if (line[strlen(line)-1] == '\n') 189391b1d75SRobert Watson line[strlen(line)-1] = '\0'; 190391b1d75SRobert Watson else { 191738824adSRobert Watson if (ignore_errors) 192738824adSRobert Watson continue; 193391b1d75SRobert Watson fclose(file); 194391b1d75SRobert Watson error = EINVAL; 195391b1d75SRobert Watson goto just_return; 196391b1d75SRobert Watson } 197391b1d75SRobert Watson 198738824adSRobert Watson /* Remove any comment. */ 199738824adSRobert Watson comment = line; 200738824adSRobert Watson parse = strsep(&comment, "#"); 201391b1d75SRobert Watson 202738824adSRobert Watson /* Blank lines OK. */ 203738824adSRobert Watson statement = next_token(&parse); 204738824adSRobert Watson if (statement == NULL) 205391b1d75SRobert Watson continue; 206391b1d75SRobert Watson 207738824adSRobert Watson if (strcmp(statement, "default_labels") == 0) { 208738824adSRobert Watson char *name, *labels; 209738824adSRobert Watson 210738824adSRobert Watson name = next_token(&parse); 211738824adSRobert Watson labels = next_token(&parse); 212738824adSRobert Watson if (name == NULL || labels == NULL || 213738824adSRobert Watson next_token(&parse) != NULL) { 214738824adSRobert Watson if (ignore_errors) 215391b1d75SRobert Watson continue; 216738824adSRobert Watson error = EINVAL; 217391b1d75SRobert Watson fclose(file); 218391b1d75SRobert Watson goto just_return; 219391b1d75SRobert Watson } 220391b1d75SRobert Watson 221738824adSRobert Watson if (mac_add_type(name, labels) == -1) { 222738824adSRobert Watson if (ignore_errors) 223738824adSRobert Watson continue; 224391b1d75SRobert Watson fclose(file); 225391b1d75SRobert Watson goto just_return; 226391b1d75SRobert Watson } 227738824adSRobert Watson } else if (strcmp(statement, "default_ifnet_labels") == 0 || 228738824adSRobert Watson strcmp(statement, "default_file_labels") == 0 || 229738824adSRobert Watson strcmp(statement, "default_process_labels") == 0) { 230738824adSRobert Watson char *labels, *type; 231391b1d75SRobert Watson 232738824adSRobert Watson if (strcmp(statement, "default_ifnet_labels") == 0) 233738824adSRobert Watson type = "ifnet"; 234738824adSRobert Watson else if (strcmp(statement, "default_file_labels") == 0) 235738824adSRobert Watson type = "file"; 236738824adSRobert Watson else if (strcmp(statement, "default_process_labels") == 237738824adSRobert Watson 0) 238738824adSRobert Watson type = "process"; 239738824adSRobert Watson 240738824adSRobert Watson labels = next_token(&parse); 241738824adSRobert Watson if (labels == NULL || next_token(&parse) != NULL) { 242738824adSRobert Watson if (ignore_errors) 243738824adSRobert Watson continue; 244738824adSRobert Watson error = EINVAL; 245391b1d75SRobert Watson fclose(file); 246391b1d75SRobert Watson goto just_return; 247391b1d75SRobert Watson } 248738824adSRobert Watson 249738824adSRobert Watson if (mac_add_type(type, labels) == -1) { 250738824adSRobert Watson if (ignore_errors) 251738824adSRobert Watson continue; 252738824adSRobert Watson fclose(file); 253738824adSRobert Watson goto just_return; 254391b1d75SRobert Watson } 255391b1d75SRobert Watson } else { 256738824adSRobert Watson if (ignore_errors) 257738824adSRobert Watson continue; 258391b1d75SRobert Watson fclose(file); 259391b1d75SRobert Watson error = EINVAL; 260391b1d75SRobert Watson goto just_return; 261391b1d75SRobert Watson } 262391b1d75SRobert Watson } 263391b1d75SRobert Watson 264391b1d75SRobert Watson fclose(file); 265391b1d75SRobert Watson 266391b1d75SRobert Watson internal_initialized = 1; 267391b1d75SRobert Watson 268391b1d75SRobert Watson just_return: 269391b1d75SRobert Watson if (error != 0) 270391b1d75SRobert Watson mac_destroy_internal(); 271391b1d75SRobert Watson return (error); 272391b1d75SRobert Watson } 273391b1d75SRobert Watson 274391b1d75SRobert Watson static int 275391b1d75SRobert Watson mac_maybe_init_internal(void) 276391b1d75SRobert Watson { 277391b1d75SRobert Watson 278391b1d75SRobert Watson if (!internal_initialized) 279738824adSRobert Watson return (mac_init_internal(1)); 280391b1d75SRobert Watson else 281391b1d75SRobert Watson return (0); 282391b1d75SRobert Watson } 283391b1d75SRobert Watson 284391b1d75SRobert Watson int 285391b1d75SRobert Watson mac_reload(void) 286391b1d75SRobert Watson { 287391b1d75SRobert Watson 288391b1d75SRobert Watson if (internal_initialized) 289391b1d75SRobert Watson mac_destroy_internal(); 290738824adSRobert Watson return (mac_init_internal(0)); 291391b1d75SRobert Watson } 292391b1d75SRobert Watson 293391b1d75SRobert Watson int 294391b1d75SRobert Watson mac_free(struct mac *mac) 295391b1d75SRobert Watson { 296391b1d75SRobert Watson 297391b1d75SRobert Watson if (mac->m_string != NULL) 298391b1d75SRobert Watson free(mac->m_string); 299391b1d75SRobert Watson free(mac); 300391b1d75SRobert Watson 301391b1d75SRobert Watson return (0); 302391b1d75SRobert Watson } 303391b1d75SRobert Watson 304391b1d75SRobert Watson int 305391b1d75SRobert Watson mac_from_text(struct mac **mac, const char *text) 306391b1d75SRobert Watson { 307391b1d75SRobert Watson 308391b1d75SRobert Watson *mac = (struct mac *) malloc(sizeof(**mac)); 309391b1d75SRobert Watson if (*mac == NULL) 310391b1d75SRobert Watson return (ENOMEM); 311391b1d75SRobert Watson 312391b1d75SRobert Watson (*mac)->m_string = strdup(text); 313391b1d75SRobert Watson if ((*mac)->m_string == NULL) { 314391b1d75SRobert Watson free(*mac); 315391b1d75SRobert Watson *mac = NULL; 316391b1d75SRobert Watson return (ENOMEM); 317391b1d75SRobert Watson } 318391b1d75SRobert Watson 319391b1d75SRobert Watson (*mac)->m_buflen = strlen((*mac)->m_string)+1; 320391b1d75SRobert Watson 321391b1d75SRobert Watson return (0); 322391b1d75SRobert Watson } 323391b1d75SRobert Watson 324391b1d75SRobert Watson int 3254bae1674SChris Costello mac_to_text(struct mac *mac, char **text) 3264bae1674SChris Costello { 3274bae1674SChris Costello 3284bae1674SChris Costello *text = strdup(mac->m_string); 3294bae1674SChris Costello if (*text == NULL) 3304bae1674SChris Costello return (ENOMEM); 3314bae1674SChris Costello return (0); 3324bae1674SChris Costello } 3334bae1674SChris Costello 3344bae1674SChris Costello int 335930d4ffaSRobert Watson mac_prepare(struct mac **mac, const char *elements) 336391b1d75SRobert Watson { 337391b1d75SRobert Watson 338391b1d75SRobert Watson if (strlen(elements) >= MAC_MAX_LABEL_BUF_LEN) 339391b1d75SRobert Watson return (EINVAL); 340391b1d75SRobert Watson 341391b1d75SRobert Watson *mac = (struct mac *) malloc(sizeof(**mac)); 342391b1d75SRobert Watson if (*mac == NULL) 343391b1d75SRobert Watson return (ENOMEM); 344391b1d75SRobert Watson 345391b1d75SRobert Watson (*mac)->m_string = malloc(MAC_MAX_LABEL_BUF_LEN); 346391b1d75SRobert Watson if ((*mac)->m_string == NULL) { 347391b1d75SRobert Watson free(*mac); 348391b1d75SRobert Watson *mac = NULL; 349391b1d75SRobert Watson return (ENOMEM); 350391b1d75SRobert Watson } 351391b1d75SRobert Watson 352391b1d75SRobert Watson strcpy((*mac)->m_string, elements); 353391b1d75SRobert Watson (*mac)->m_buflen = MAC_MAX_LABEL_BUF_LEN; 354391b1d75SRobert Watson 355391b1d75SRobert Watson return (0); 356391b1d75SRobert Watson } 357391b1d75SRobert Watson 358391b1d75SRobert Watson int 359738824adSRobert Watson mac_prepare_type(struct mac **mac, const char *name) 360391b1d75SRobert Watson { 361738824adSRobert Watson struct label_default *ld; 3626e07ce26SRobert Watson int error; 3636e07ce26SRobert Watson 3646e07ce26SRobert Watson error = mac_maybe_init_internal(); 3656e07ce26SRobert Watson if (error != 0) 3666e07ce26SRobert Watson return (error); 367391b1d75SRobert Watson 368738824adSRobert Watson for (ld = LIST_FIRST(&label_default_head); ld != NULL; 369738824adSRobert Watson ld = LIST_NEXT(ld, ld_entries)) { 370738824adSRobert Watson if (strcmp(name, ld->ld_name) == 0) 371738824adSRobert Watson return (mac_prepare(mac, ld->ld_labels)); 372738824adSRobert Watson } 373391b1d75SRobert Watson 3747ea02dcdSRobert Watson errno = ENOENT; 3757ea02dcdSRobert Watson return (-1); /* XXXMAC: ENOLABEL */ 376391b1d75SRobert Watson } 377391b1d75SRobert Watson 378391b1d75SRobert Watson int 379391b1d75SRobert Watson mac_prepare_ifnet_label(struct mac **mac) 380391b1d75SRobert Watson { 381391b1d75SRobert Watson 382738824adSRobert Watson return (mac_prepare_type(mac, "ifnet")); 383391b1d75SRobert Watson } 384738824adSRobert Watson 385738824adSRobert Watson int 386738824adSRobert Watson mac_prepare_file_label(struct mac **mac) 387738824adSRobert Watson { 388738824adSRobert Watson 389738824adSRobert Watson return (mac_prepare_type(mac, "file")); 390738824adSRobert Watson } 391738824adSRobert Watson 392738824adSRobert Watson int 393738824adSRobert Watson mac_prepare_packet_label(struct mac **mac) 394738824adSRobert Watson { 395738824adSRobert Watson 396738824adSRobert Watson return (mac_prepare_type(mac, "packet")); 397738824adSRobert Watson } 398738824adSRobert Watson 399391b1d75SRobert Watson int 400391b1d75SRobert Watson mac_prepare_process_label(struct mac **mac) 401391b1d75SRobert Watson { 402391b1d75SRobert Watson 403738824adSRobert Watson return (mac_prepare_type(mac, "process")); 404391b1d75SRobert Watson } 405391b1d75SRobert Watson 406391b1d75SRobert Watson /* 407391b1d75SRobert Watson * Simply test whether the TrustedBSD/MAC MIB tree is present; if so, 408391b1d75SRobert Watson * return 1 to indicate that the system has MAC enabled overall or for 409391b1d75SRobert Watson * a given policy. 410391b1d75SRobert Watson */ 411391b1d75SRobert Watson int 412391b1d75SRobert Watson mac_is_present(const char *policyname) 413391b1d75SRobert Watson { 414391b1d75SRobert Watson int mib[5]; 415391b1d75SRobert Watson size_t siz; 416391b1d75SRobert Watson char *mibname; 417391b1d75SRobert Watson int error; 418391b1d75SRobert Watson 419391b1d75SRobert Watson if (policyname != NULL) { 420391b1d75SRobert Watson if (policyname[strcspn(policyname, ".=")] != '\0') { 421391b1d75SRobert Watson errno = EINVAL; 422391b1d75SRobert Watson return (-1); 423391b1d75SRobert Watson } 424391b1d75SRobert Watson mibname = malloc(sizeof("security.mac.") - 1 + 425391b1d75SRobert Watson strlen(policyname) + sizeof(".enabled")); 426391b1d75SRobert Watson if (mibname == NULL) 427391b1d75SRobert Watson return (-1); 428391b1d75SRobert Watson strcpy(mibname, "security.mac."); 429391b1d75SRobert Watson strcat(mibname, policyname); 430391b1d75SRobert Watson strcat(mibname, ".enabled"); 431391b1d75SRobert Watson siz = 5; 432391b1d75SRobert Watson error = sysctlnametomib(mibname, mib, &siz); 433391b1d75SRobert Watson free(mibname); 434391b1d75SRobert Watson } else { 435391b1d75SRobert Watson siz = 3; 436391b1d75SRobert Watson error = sysctlnametomib("security.mac", mib, &siz); 437391b1d75SRobert Watson } 438391b1d75SRobert Watson if (error == -1) { 439391b1d75SRobert Watson switch (errno) { 440391b1d75SRobert Watson case ENOTDIR: 441391b1d75SRobert Watson case ENOENT: 442391b1d75SRobert Watson return (0); 443391b1d75SRobert Watson default: 444391b1d75SRobert Watson return (error); 445391b1d75SRobert Watson } 446391b1d75SRobert Watson } 447391b1d75SRobert Watson return (1); 448391b1d75SRobert Watson } 449