/** * @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 #include #include #include #include #include #include #include #include /* these are needed for nodecons and IPv4 and IPv6 */ #include #include #include #include #include /* 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); }