summaryrefslogtreecommitdiffstats
path: root/libapol/src/util.c
diff options
context:
space:
mode:
Diffstat (limited to 'libapol/src/util.c')
-rw-r--r--libapol/src/util.c659
1 files changed, 659 insertions, 0 deletions
diff --git a/libapol/src/util.c b/libapol/src/util.c
new file mode 100644
index 0000000..dd6d300
--- /dev/null
+++ b/libapol/src/util.c
@@ -0,0 +1,659 @@
+/**
+ * @file
+ *
+ * Implementation of utility functions.
+ *
+ * @author Jeremy A. Mowery jmowery@tresys.com
+ * @author Jason Tang jtang@tresys.com
+ *
+ * Copyright (C) 2001-2007 Tresys Technology, LLC
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include <config.h>
+
+#include <apol/util.h>
+
+#include <assert.h>
+#include <ctype.h>
+#include <errno.h>
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+#include <stdbool.h>
+
+/* these are needed for nodecons and IPv4 and IPv6 */
+#include <qpol/nodecon_query.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <arpa/inet.h>
+#include <netinet/in.h> /* needed for portcon's protocol */
+
+/* use 8k line size */
+#define APOL_LINE_SZ 8192
+#define APOL_ENVIRON_VAR_NAME "APOL_INSTALL_DIR"
+
+const char *libapol_get_version(void)
+{
+ return LIBAPOL_VERSION_STRING;
+}
+
+int apol_str_to_internal_ip(const char *str, uint32_t ip[4])
+{
+ bool ipv4 = false;
+ bool ipv6 = false;
+
+ if (!str || !ip) {
+ errno = EINVAL;
+ return -1;
+ }
+
+ ip[0] = ip[1] = ip[2] = ip[3] = 0;
+
+ if (strchr(str, '.'))
+ ipv4 = true;
+
+ if (strchr(str, ':'))
+ ipv6 = true;
+
+ if (ipv4 == ipv6) {
+ errno = EINVAL;
+ return -1;
+ }
+
+ if (ipv4) {
+ unsigned char *p = (unsigned char *)&(ip[0]);
+ int seg = 0;
+ uint32_t val = 0; /* value of current segment of address */
+ size_t len = strlen(str), i;
+ for (i = 0; i <= len; i++) {
+ if (str[i] == '.' || str[i] == '\0') {
+ if (val > 255) {
+ errno = EINVAL;
+ return -1;
+ }
+
+ p[seg] = (unsigned char)(0xff & val);
+ seg++;
+ val = 0;
+ if (seg == 4)
+ break;
+ } else if (isdigit(str[i])) {
+ char tmp[2] = { str[i], 0 };
+ val = val * 10 + atoi(tmp);
+ } else {
+ errno = EINVAL;
+ return -1;
+ }
+ }
+ } else {
+ struct in6_addr addr;
+ if (inet_pton(AF_INET6, str, &addr) <= 0) {
+ return -1;
+ }
+ memcpy(ip, addr.s6_addr32, 16);
+ }
+
+ return ipv4 ? QPOL_IPV4 : QPOL_IPV6;
+}
+
+const char *apol_objclass_to_str(uint32_t objclass)
+{
+ switch (objclass) {
+ case QPOL_CLASS_BLK_FILE:
+ return "block";
+ case QPOL_CLASS_CHR_FILE:
+ return "char";
+ case QPOL_CLASS_DIR:
+ return "dir";
+ case QPOL_CLASS_FIFO_FILE:
+ return "fifo";
+ case QPOL_CLASS_FILE:
+ return "file";
+ case QPOL_CLASS_LNK_FILE:
+ return "link";
+ case QPOL_CLASS_SOCK_FILE:
+ return "sock";
+ case QPOL_CLASS_ALL:
+ return "any";
+ }
+ return NULL;
+}
+
+uint32_t apol_str_to_objclass(const char *objclass)
+{
+ if (objclass == NULL) {
+ errno = EINVAL;
+ return 0;
+ }
+ if (strcmp(objclass, "block") == 0) {
+ return QPOL_CLASS_BLK_FILE;
+ }
+ if (strcmp(objclass, "char") == 0) {
+ return QPOL_CLASS_CHR_FILE;
+ }
+ if (strcmp(objclass, "dir") == 0) {
+ return QPOL_CLASS_DIR;
+ }
+ if (strcmp(objclass, "fifo") == 0) {
+ return QPOL_CLASS_FIFO_FILE;
+ }
+ if (strcmp(objclass, "file") == 0) {
+ return QPOL_CLASS_FILE;
+ }
+ if (strcmp(objclass, "link") == 0) {
+ return QPOL_CLASS_LNK_FILE;
+ }
+ if (strcmp(objclass, "sock") == 0) {
+ return QPOL_CLASS_SOCK_FILE;
+ }
+ if (strcmp(objclass, "any") == 0) {
+ return QPOL_CLASS_ALL;
+ }
+ return 0;
+}
+
+const char *apol_protocol_to_str(uint8_t protocol)
+{
+ switch (protocol) {
+ case IPPROTO_TCP:
+ return "tcp";
+ case IPPROTO_UDP:
+ return "udp";
+ default:
+ errno = EPROTONOSUPPORT;
+ return NULL;
+ }
+}
+
+uint8_t apol_str_to_protocol(const char *protocol_str)
+{
+ if (protocol_str == NULL) {
+ errno = EINVAL;
+ return 0;
+ }
+ if (strcmp(protocol_str, "tcp") == 0 || strcmp(protocol_str, "TCP") == 0) {
+ return IPPROTO_TCP;
+ }
+ if (strcmp(protocol_str, "udp") == 0 || strcmp(protocol_str, "UDP") == 0) {
+ return IPPROTO_UDP;
+ }
+ errno = EPROTONOSUPPORT;
+ return 0;
+}
+
+const char *apol_fs_use_behavior_to_str(uint32_t behavior)
+{
+ switch (behavior) {
+ case QPOL_FS_USE_XATTR:
+ return "fs_use_xattr";
+ case QPOL_FS_USE_TASK:
+ return "fs_use_task";
+ case QPOL_FS_USE_TRANS:
+ return "fs_use_trans";
+ case QPOL_FS_USE_GENFS:
+ return "fs_use_genfs";
+ case QPOL_FS_USE_NONE:
+ return "fs_use_none";
+ case QPOL_FS_USE_PSID:
+ return "fs_use_psid";
+ }
+ return NULL;
+}
+
+int apol_str_to_fs_use_behavior(const char *behavior)
+{
+ if (strcmp(behavior, "fs_use_xattr") == 0) {
+ return QPOL_FS_USE_XATTR;
+ } else if (strcmp(behavior, "fs_use_task") == 0) {
+ return QPOL_FS_USE_TASK;
+ } else if (strcmp(behavior, "fs_use_trans") == 0) {
+ return QPOL_FS_USE_TRANS;
+ } else if (strcmp(behavior, "fs_use_genfs") == 0) {
+ return QPOL_FS_USE_GENFS;
+ } else if (strcmp(behavior, "fs_use_none") == 0) {
+ return QPOL_FS_USE_NONE;
+ } else if (strcmp(behavior, "fs_use_psid") == 0) {
+ return QPOL_FS_USE_PSID;
+ }
+ return -1;
+}
+
+const char *apol_rule_type_to_str(uint32_t rule_type)
+{
+ switch (rule_type) {
+ case QPOL_RULE_ALLOW:
+ return "allow";
+ case QPOL_RULE_NEVERALLOW:
+ return "neverallow";
+ case QPOL_RULE_AUDITALLOW:
+ return "auditallow";
+ case QPOL_RULE_DONTAUDIT:
+ return "dontaudit";
+ case QPOL_RULE_TYPE_TRANS:
+ return "type_transition";
+ case QPOL_RULE_TYPE_CHANGE:
+ return "type_change";
+ case QPOL_RULE_TYPE_MEMBER:
+ return "type_member";
+ }
+ return NULL;
+}
+
+const char *apol_cond_expr_type_to_str(uint32_t expr_type)
+{
+ switch (expr_type) {
+ case QPOL_COND_EXPR_BOOL:
+ return "";
+ case QPOL_COND_EXPR_NOT:
+ return "!";
+ case QPOL_COND_EXPR_OR:
+ return "||";
+ case QPOL_COND_EXPR_AND:
+ return "&&";
+ case QPOL_COND_EXPR_XOR:
+ return "^";
+ case QPOL_COND_EXPR_EQ:
+ return "==";
+ case QPOL_COND_EXPR_NEQ:
+ return "!=";
+ }
+ return NULL;
+}
+
+char *apol_file_find(const char *file_name)
+{
+ char *file = NULL, *var = NULL, *dirs[3];
+ size_t i;
+ int rt;
+
+ if (file_name == NULL) {
+ errno = EINVAL;
+ return NULL;
+ }
+
+ /* check current directory, environment variable, and then
+ * installed directory */
+ dirs[0] = ".";
+ dirs[1] = getenv(APOL_ENVIRON_VAR_NAME);
+ dirs[2] = APOL_INSTALL_DIR;
+ for (i = 0; i < 3; i++) {
+ if ((var = dirs[i]) != NULL) {
+ if (asprintf(&file, "%s/%s", var, file_name) < 0) {
+ return NULL;
+ }
+ rt = access(file, R_OK);
+ free(file);
+ if (rt == 0) {
+ return strdup(var);
+ }
+ }
+ }
+
+ /* didn't find it */
+ return NULL;
+}
+
+char *apol_file_find_path(const char *file_name)
+{
+ char *file = NULL, *var = NULL, *dirs[3];
+ size_t i;
+ int rt;
+
+ if (file_name == NULL) {
+ errno = EINVAL;
+ return NULL;
+ }
+
+ /* check current directory, environment variable, and then
+ * installed directory */
+ dirs[0] = ".";
+ dirs[1] = getenv(APOL_ENVIRON_VAR_NAME);
+ dirs[2] = APOL_INSTALL_DIR;
+ for (i = 0; i < 3; i++) {
+ if ((var = dirs[i]) != NULL) {
+ if (asprintf(&file, "%s/%s", var, file_name) < 0) {
+ return NULL;
+ }
+ rt = access(file, R_OK);
+ if (rt == 0) {
+ return file;
+ }
+ free(file);
+ }
+ }
+
+ /* didn't find it */
+ return NULL;
+}
+
+char *apol_file_find_user_config(const char *file_name)
+{
+ char *file, *var;
+ int rt;
+
+ if (file_name == NULL) {
+ errno = EINVAL;
+ return NULL;
+ }
+ var = getenv("HOME");
+ if (var) {
+ if (asprintf(&file, "%s/%s", var, file_name) < 0) {
+ return NULL;
+ }
+ rt = access(file, R_OK);
+ if (rt == 0) {
+ return file;
+ } else {
+ free(file);
+ return NULL;
+ }
+ }
+ return NULL;
+}
+
+int apol_file_read_to_buffer(const char *fname, char **buf, size_t * len)
+{
+ FILE *file = NULL;
+ const size_t BUF_SIZE = 1024;
+ size_t size = 0, r;
+ char *bufp, *b;
+
+ assert(*buf == NULL);
+ assert(len);
+ *len = 0;
+ while (1) {
+ size += BUF_SIZE;
+ r = 0;
+ b = (char *)realloc(*buf, size * sizeof(char));
+ if (b == NULL) {
+ free(*buf);
+ *buf = NULL;
+ *len = 0;
+ if (file)
+ fclose(file);
+ return -1;
+ }
+ *buf = b;
+ if (!file) {
+ file = fopen(fname, "rb");
+ if (!file) {
+ free(*buf);
+ *buf = NULL;
+ *len = 0;
+ return -1;
+ }
+ }
+ bufp = &((*buf)[size - BUF_SIZE]);
+ r = fread(bufp, sizeof(char), BUF_SIZE, file);
+ *len += r;
+ if (r < BUF_SIZE) {
+ if (feof(file)) {
+ fclose(file);
+ break;
+ } else {
+ free(*buf);
+ *buf = NULL;
+ *len = 0;
+ fclose(file);
+ return -1;
+ }
+ }
+ }
+ return 0;
+}
+
+char *apol_config_get_var(const char *var, FILE * fp)
+{
+ char line[APOL_LINE_SZ], t1[APOL_LINE_SZ], t2[APOL_LINE_SZ];
+ char *line_ptr = NULL;
+
+ if (var == NULL || fp == NULL) {
+ errno = EINVAL;
+ return NULL;
+ }
+
+ rewind(fp);
+ while (fgets(line, APOL_LINE_SZ, fp) != NULL) {
+ if ((line_ptr = strdup(line)) == NULL) {
+ return NULL;
+ }
+ apol_str_trim(line_ptr);
+ if (line_ptr[0] == '#' || sscanf(line_ptr, "%s %[^\n]", t1, t2) != 2 || strcasecmp(var, t1) != 0) {
+ free(line_ptr);
+ continue;
+ } else {
+ free(line_ptr);
+ return strdup(t2);
+ }
+ }
+ return NULL;
+}
+
+apol_vector_t *apol_str_split(const char *s, const char *delim)
+{
+ char *orig_s = NULL, *dup_s = NULL, *v, *token;
+ apol_vector_t *list = NULL;
+ int error = 0;
+
+ if (s == NULL || delim == NULL) {
+ error = EINVAL;
+ goto cleanup;
+ }
+ if ((list = apol_vector_create(free)) == NULL || (orig_s = strdup(s)) == NULL) {
+ error = errno;
+ goto cleanup;
+ }
+ v = orig_s;
+ while ((token = strsep(&v, delim)) != NULL) {
+ if (strcmp(token, "") != 0 && !apol_str_is_only_white_space(token)) {
+ if ((dup_s = strdup(token)) == NULL || apol_vector_append(list, dup_s) < 0) {
+ error = errno;
+ free(dup_s);
+ goto cleanup;
+ }
+ }
+ }
+ cleanup:
+ free(orig_s);
+ if (error != 0) {
+ apol_vector_destroy(&list);
+ errno = error;
+ return NULL;
+ }
+ return list;
+}
+
+char *apol_str_join(const apol_vector_t * list, const char *delim)
+{
+ char *val, *s;
+ size_t i, len;
+
+ if (list == NULL || delim == NULL) {
+ errno = EINVAL;
+ return NULL;
+ }
+ if (apol_vector_get_size(list) == 0) {
+ return strdup("");
+ }
+ s = apol_vector_get_element(list, 0);
+ if ((val = strdup(s)) == NULL) {
+ return NULL;
+ }
+ len = strlen(val) + 1;
+ for (i = 1; i < apol_vector_get_size(list); i++) {
+ s = apol_vector_get_element(list, i);
+ if (apol_str_appendf(&val, &len, "%s%s", delim, s) < 0) {
+ return NULL;
+ }
+ }
+ return val;
+}
+
+/**
+ * Given a string, if the string begins with whitespace then allocate
+ * a new string that does not contain those whitespaces.
+ *
+ * @param str String to modify.
+ */
+static void trim_leading_whitespace(char *str)
+{
+ size_t i, len;
+ for (i = 0; str[i] != '\0' && isspace(str[i]); i++) ;
+ len = strlen(str + i);
+ memmove(str, str + i, len + 1);
+}
+
+/**
+ * Given a mutable string, replace trailing whitespace characters with
+ * null characters.
+ *
+ * @param str String to modify.
+ */
+static void trim_trailing_whitespace(char *str)
+{
+ size_t length;
+ length = strlen(str);
+ while (length > 0 && isspace(str[length - 1])) {
+ str[length - 1] = '\0';
+ length--;
+ }
+}
+
+void apol_str_trim(char *str)
+{
+ if (str == NULL) {
+ errno = EINVAL;
+ return;
+ }
+ trim_leading_whitespace(str);
+ trim_trailing_whitespace(str);
+}
+
+int apol_str_append(char **tgt, size_t * tgt_sz, const char *str)
+{
+ size_t str_len;
+ if (str == NULL || (str_len = strlen(str)) == 0)
+ return 0;
+ if (tgt == NULL) {
+ errno = EINVAL;
+ return -1;
+ }
+ str_len++;
+ /* target is currently empty */
+ if (*tgt == NULL || *tgt_sz == 0) {
+ *tgt = (char *)malloc(str_len);
+ if (*tgt == NULL) {
+ *tgt_sz = 0;
+ return -1;
+ }
+ *tgt_sz = str_len;
+ strcpy(*tgt, str);
+ return 0;
+ } else {
+ /* tgt has some memory */
+ char *t = (char *)realloc(*tgt, *tgt_sz + str_len);
+ if (t == NULL) {
+ int error = errno;
+ free(*tgt);
+ *tgt = NULL;
+ *tgt_sz = 0;
+ errno = error;
+ return -1;
+ }
+ *tgt = t;
+ *tgt_sz += str_len;
+ strcat(*tgt, str);
+ return 0;
+ }
+}
+
+int apol_str_appendf(char **tgt, size_t * tgt_sz, const char *fmt, ...)
+{
+ va_list ap;
+ int error;
+ if (fmt == NULL || strlen(fmt) == 0)
+ return 0;
+ if (tgt == NULL) {
+ errno = EINVAL;
+ return -1;
+ }
+ va_start(ap, fmt);
+ /* target is currently empty */
+ if (*tgt == NULL || *tgt_sz == 0) {
+ if (vasprintf(tgt, fmt, ap) < 0) {
+ error = errno;
+ *tgt = NULL;
+ *tgt_sz = 0;
+ va_end(ap);
+ errno = error;
+ return -1;
+ }
+ *tgt_sz = strlen(*tgt) + 1;
+ va_end(ap);
+ return 0;
+ } else {
+ /* tgt has some memory */
+ char *t, *u;
+ size_t str_len;
+ if (vasprintf(&t, fmt, ap) < 0) {
+ error = errno;
+ free(*tgt);
+ *tgt_sz = 0;
+ va_end(ap);
+ errno = error;
+ return -1;
+ }
+ va_end(ap);
+ str_len = strlen(t);
+ if ((u = (char *)realloc(*tgt, *tgt_sz + str_len)) == NULL) {
+ error = errno;
+ free(t);
+ free(*tgt);
+ *tgt_sz = 0;
+ errno = error;
+ return -1;
+ }
+ *tgt = u;
+ *tgt_sz += str_len;
+ strcat(*tgt, t);
+ free(t);
+ return 0;
+ }
+}
+
+int apol_str_is_only_white_space(const char *str)
+{
+ size_t len, i;
+ if (str == NULL)
+ return 0;
+ len = strlen(str);
+ for (i = 0; i < len; i++) {
+ if (!isspace(str[i]))
+ return 0;
+ }
+ return 1;
+}
+
+int apol_str_strcmp(const void *a, const void *b, void *unused __attribute__ ((unused)))
+{
+ return strcmp((const char *)a, (const char *)b);
+}
+
+void *apol_str_strdup(const void *elem, void *unused __attribute__ ((unused)))
+{
+ return strdup((const char *)elem);
+}