diff options
Diffstat (limited to 'lib/libc/posix1e/mac.c')
-rw-r--r-- | lib/libc/posix1e/mac.c | 448 |
1 files changed, 448 insertions, 0 deletions
diff --git a/lib/libc/posix1e/mac.c b/lib/libc/posix1e/mac.c new file mode 100644 index 0000000..ded9059 --- /dev/null +++ b/lib/libc/posix1e/mac.c @@ -0,0 +1,448 @@ +/* + * Copyright (c) 1999, 2000, 2001, 2002 Robert N. M. Watson + * Copyright (c) 2002, 2003 Networks Associates Technology, Inc. + * All rights reserved. + * + * This software was developed by Robert Watson for the TrustedBSD Project. + * + * This software was developed for the FreeBSD Project in part by Network + * Associates Laboratories, the Security Research Division of Network + * Associates, Inc. under DARPA/SPAWAR contract N66001-01-C-8035 ("CBOSS"), + * as part of the DARPA CHATS research program. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $FreeBSD$ + */ + +#include <sys/types.h> +#include <sys/queue.h> +#include <sys/sysctl.h> + +#include <dlfcn.h> +#include <errno.h> +#include <limits.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +#include <sys/mac.h> + +static int internal_initialized; + +/* + * Maintain a list of default label preparations for various object + * types. Each name will appear only once in the list. + * + * XXXMAC: Not thread-safe. + */ +static LIST_HEAD(, label_default) label_default_head; +struct label_default { + char *ld_name; + char *ld_labels; + LIST_ENTRY(label_default) ld_entries; +}; + +static void +mac_destroy_labels(void) +{ + struct label_default *ld; + + while ((ld = LIST_FIRST(&label_default_head))) { + free(ld->ld_name); + free(ld->ld_labels); + LIST_REMOVE(ld, ld_entries); + free(ld); + } +} + +static void +mac_destroy_internal(void) +{ + + mac_destroy_labels(); + + internal_initialized = 0; +} + +static int +mac_add_type(const char *name, const char *labels) +{ + struct label_default *ld, *ld_new; + char *name_dup, *labels_dup; + + /* + * Speculatively allocate all the memory now to avoid allocating + * later when we will someday hold a mutex. + */ + name_dup = strdup(name); + if (name_dup == NULL) { + errno = ENOMEM; + return (-1); + } + labels_dup = strdup(labels); + if (labels_dup == NULL) { + free(name_dup); + errno = ENOMEM; + return (-1); + } + ld_new = malloc(sizeof(*ld)); + if (ld_new == NULL) { + free(name_dup); + free(labels_dup); + errno = ENOMEM; + return (-1); + } + + /* + * If the type is already present, replace the current entry + * rather than add a new instance. + */ + for (ld = LIST_FIRST(&label_default_head); ld != NULL; + ld = LIST_NEXT(ld, ld_entries)) { + if (strcmp(name, ld->ld_name) == 0) + break; + } + + if (ld != NULL) { + free(ld->ld_labels); + ld->ld_labels = labels_dup; + labels_dup = NULL; + } else { + ld = ld_new; + ld->ld_name = name_dup; + ld->ld_labels = labels_dup; + + ld_new = NULL; + name_dup = NULL; + labels_dup = NULL; + + LIST_INSERT_HEAD(&label_default_head, ld, ld_entries); + } + + if (name_dup != NULL) + free(name_dup); + if (labels_dup != NULL) + free(labels_dup); + if (ld_new != NULL) + free(ld_new); + + return (0); +} + +static char * +next_token(char **string) +{ + char *token; + + token = strsep(string, " \t"); + while (token != NULL && *token == '\0') + token = strsep(string, " \t"); + + return (token); +} + +static int +mac_init_internal(int ignore_errors) +{ + const char *filename; + char line[LINE_MAX]; + FILE *file; + int error; + + error = 0; + + LIST_INIT(&label_default_head); + + if (!issetugid() && getenv("MAC_CONFFILE") != NULL) + filename = getenv("MAC_CONFFILE"); + else + filename = MAC_CONFFILE; + file = fopen(filename, "r"); + if (file == NULL) + return (0); + + while (fgets(line, LINE_MAX, file)) { + char *comment, *parse, *statement; + + if (line[strlen(line)-1] == '\n') + line[strlen(line)-1] = '\0'; + else { + if (ignore_errors) + continue; + fclose(file); + error = EINVAL; + goto just_return; + } + + /* Remove any comment. */ + comment = line; + parse = strsep(&comment, "#"); + + /* Blank lines OK. */ + statement = next_token(&parse); + if (statement == NULL) + continue; + + if (strcmp(statement, "default_labels") == 0) { + char *name, *labels; + + name = next_token(&parse); + labels = next_token(&parse); + if (name == NULL || labels == NULL || + next_token(&parse) != NULL) { + if (ignore_errors) + continue; + error = EINVAL; + fclose(file); + goto just_return; + } + + if (mac_add_type(name, labels) == -1) { + if (ignore_errors) + continue; + fclose(file); + goto just_return; + } + } else if (strcmp(statement, "default_ifnet_labels") == 0 || + strcmp(statement, "default_file_labels") == 0 || + strcmp(statement, "default_process_labels") == 0) { + char *labels, *type; + + if (strcmp(statement, "default_ifnet_labels") == 0) + type = "ifnet"; + else if (strcmp(statement, "default_file_labels") == 0) + type = "file"; + else if (strcmp(statement, "default_process_labels") == + 0) + type = "process"; + + labels = next_token(&parse); + if (labels == NULL || next_token(&parse) != NULL) { + if (ignore_errors) + continue; + error = EINVAL; + fclose(file); + goto just_return; + } + + if (mac_add_type(type, labels) == -1) { + if (ignore_errors) + continue; + fclose(file); + goto just_return; + } + } else { + if (ignore_errors) + continue; + fclose(file); + error = EINVAL; + goto just_return; + } + } + + fclose(file); + + internal_initialized = 1; + +just_return: + if (error != 0) + mac_destroy_internal(); + return (error); +} + +static int +mac_maybe_init_internal(void) +{ + + if (!internal_initialized) + return (mac_init_internal(1)); + else + return (0); +} + +int +mac_reload(void) +{ + + if (internal_initialized) + mac_destroy_internal(); + return (mac_init_internal(0)); +} + +int +mac_free(struct mac *mac) +{ + + if (mac->m_string != NULL) + free(mac->m_string); + free(mac); + + return (0); +} + +int +mac_from_text(struct mac **mac, const char *text) +{ + + *mac = (struct mac *) malloc(sizeof(**mac)); + if (*mac == NULL) + return (ENOMEM); + + (*mac)->m_string = strdup(text); + if ((*mac)->m_string == NULL) { + free(*mac); + *mac = NULL; + return (ENOMEM); + } + + (*mac)->m_buflen = strlen((*mac)->m_string)+1; + + return (0); +} + +int +mac_to_text(struct mac *mac, char **text) +{ + + *text = strdup(mac->m_string); + if (*text == NULL) + return (ENOMEM); + return (0); +} + +int +mac_prepare(struct mac **mac, const char *elements) +{ + + if (strlen(elements) >= MAC_MAX_LABEL_BUF_LEN) + return (EINVAL); + + *mac = (struct mac *) malloc(sizeof(**mac)); + if (*mac == NULL) + return (ENOMEM); + + (*mac)->m_string = malloc(MAC_MAX_LABEL_BUF_LEN); + if ((*mac)->m_string == NULL) { + free(*mac); + *mac = NULL; + return (ENOMEM); + } + + strcpy((*mac)->m_string, elements); + (*mac)->m_buflen = MAC_MAX_LABEL_BUF_LEN; + + return (0); +} + +int +mac_prepare_type(struct mac **mac, const char *name) +{ + struct label_default *ld; + int error; + + error = mac_maybe_init_internal(); + if (error != 0) + return (error); + + for (ld = LIST_FIRST(&label_default_head); ld != NULL; + ld = LIST_NEXT(ld, ld_entries)) { + if (strcmp(name, ld->ld_name) == 0) + return (mac_prepare(mac, ld->ld_labels)); + } + + errno = ENOENT; + return (-1); /* XXXMAC: ENOLABEL */ +} + +int +mac_prepare_ifnet_label(struct mac **mac) +{ + + return (mac_prepare_type(mac, "ifnet")); +} + +int +mac_prepare_file_label(struct mac **mac) +{ + + return (mac_prepare_type(mac, "file")); +} + +int +mac_prepare_packet_label(struct mac **mac) +{ + + return (mac_prepare_type(mac, "packet")); +} + +int +mac_prepare_process_label(struct mac **mac) +{ + + return (mac_prepare_type(mac, "process")); +} + +/* + * Simply test whether the TrustedBSD/MAC MIB tree is present; if so, + * return 1 to indicate that the system has MAC enabled overall or for + * a given policy. + */ +int +mac_is_present(const char *policyname) +{ + int mib[5]; + size_t siz; + char *mibname; + int error; + + if (policyname != NULL) { + if (policyname[strcspn(policyname, ".=")] != '\0') { + errno = EINVAL; + return (-1); + } + mibname = malloc(sizeof("security.mac.") - 1 + + strlen(policyname) + sizeof(".enabled")); + if (mibname == NULL) + return (-1); + strcpy(mibname, "security.mac."); + strcat(mibname, policyname); + strcat(mibname, ".enabled"); + siz = 5; + error = sysctlnametomib(mibname, mib, &siz); + free(mibname); + } else { + siz = 3; + error = sysctlnametomib("security.mac", mib, &siz); + } + if (error == -1) { + switch (errno) { + case ENOTDIR: + case ENOENT: + return (0); + default: + return (error); + } + } + return (1); +} |