From 9ddccb55057ef3b64d80b6d90cb2b25b56238ba3 Mon Sep 17 00:00:00 2001 From: rwatson Date: Fri, 13 Oct 2000 18:24:58 +0000 Subject: o Introduce cap_from_text() and cap_to_text() implementations. Reviewed by: green Obtained from: TrustedBSD Project Security audited by: imp, green --- lib/libposix1e/Makefile | 3 +- lib/libposix1e/cap_text.c | 571 ++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 573 insertions(+), 1 deletion(-) create mode 100644 lib/libposix1e/cap_text.c (limited to 'lib/libposix1e') diff --git a/lib/libposix1e/Makefile b/lib/libposix1e/Makefile index b441adf..3a1afd6 100644 --- a/lib/libposix1e/Makefile +++ b/lib/libposix1e/Makefile @@ -24,7 +24,8 @@ SRCS+= acl_delete.c \ cap_set_fd.c \ cap_set_file.c \ cap_set_flag.c \ - cap_set_proc.c + cap_set_proc.c \ + cap_text.c MAN3= acl.3 \ diff --git a/lib/libposix1e/cap_text.c b/lib/libposix1e/cap_text.c new file mode 100644 index 0000000..9748a2c --- /dev/null +++ b/lib/libposix1e/cap_text.c @@ -0,0 +1,571 @@ +/*- + * Copyright (c) 2000 Robert N. M. Watson + * All rights reserved. + * + * 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$ + */ +/* + * TrustedBSD Project - support for POSIX.1e process capabilities + */ + +#include +#include +#include + +#include +#include +#include + +static const size_t CAP_MAX_BUF_LEN = 1024; +static const size_t CAP_MAX_SMALL_BUF_LEN = 64; + +static const char *CAP_FLAGS[8] = { + "", /* 000 */ + "e", /* 001 */ + "i", /* 010 */ + "ei", /* 011 */ + "p", /* 100 */ + "ep", /* 101 */ + "ip", /* 110 */ + "eip", /* 111 */ +}; + +static const char *CAP_SEP = ": \t"; +static const char *CAP_OPERATION = "=-+"; + +struct cap_info { + char *ci_name; + cap_value_t ci_cap; +}; + +static const struct cap_info cap_info_array[] = { +{"CAP_CHOWN", CAP_CHOWN}, +{"CAP_DAC_EXECUTE", CAP_DAC_EXECUTE}, +{"CAP_DAC_WRITE", CAP_DAC_WRITE}, +{"CAP_DAC_READ_SEARCH", CAP_DAC_READ_SEARCH}, +{"CAP_FOWNER", CAP_FOWNER}, +{"CAP_FSETID", CAP_FSETID}, +{"CAP_KILL", CAP_KILL}, +{"CAP_LINK_DIR", CAP_LINK_DIR}, +{"CAP_SETFCAP", CAP_SETFCAP}, +{"CAP_SETGID", CAP_SETGID}, +{"CAP_SETUID", CAP_SETUID}, +{"CAP_MAC_DOWNGRADE", CAP_MAC_DOWNGRADE}, +{"CAP_MAC_READ", CAP_MAC_READ}, +{"CAP_MAC_RELABEL_SUBJ", CAP_MAC_RELABEL_SUBJ}, +{"CAP_MAC_UPGRADE", CAP_MAC_UPGRADE}, +{"CAP_MAC_WRITE", CAP_MAC_WRITE}, +{"CAP_INF_NOFLOAT_OBJ", CAP_INF_NOFLOAT_OBJ}, +{"CAP_INF_NOFLOAT_SUBJ", CAP_INF_NOFLOAT_SUBJ}, +{"CAP_INF_RELABEL_OBJ", CAP_INF_RELABEL_OBJ}, +{"CAP_INF_RELABEL_SUBJ", CAP_INF_RELABEL_SUBJ}, +{"CAP_AUDIT_CONTROL", CAP_AUDIT_CONTROL}, +{"CAP_AUDIT_WRITE", CAP_AUDIT_WRITE}, +{"CAP_SETPCAP", CAP_SETPCAP}, +{"CAP_SYS_SETFFLAG", CAP_SYS_SETFFLAG}, +{"CAP_LINUX_IMMUTABLE", CAP_SYS_SETFFLAG}, +{"CAP_NET_BIND_SERVICE", CAP_NET_BIND_SERVICE}, +{"CAP_NET_BROADCAST", CAP_NET_BROADCAST}, +{"CAP_NET_ADMIN", CAP_NET_ADMIN}, +{"CAP_NET_RAW", CAP_NET_RAW}, +{"CAP_IPC_LOCK", CAP_IPC_LOCK}, +{"CAP_IPC_OWNER", CAP_IPC_OWNER}, +{"CAP_SYS_MODULE", CAP_SYS_MODULE}, +{"CAP_SYS_RAWIO", CAP_SYS_RAWIO}, +{"CAP_SYS_CHROOT", CAP_SYS_CHROOT}, +{"CAP_SYS_PTRACE", CAP_SYS_PTRACE}, +{"CAP_SYS_PACCT", CAP_SYS_PACCT}, +{"CAP_SYS_ADMIN", CAP_SYS_ADMIN}, +{"CAP_SYS_BOOT", CAP_SYS_BOOT}, +{"CAP_SYS_NICE", CAP_SYS_NICE}, +{"CAP_SYS_RESOURCE", CAP_SYS_RESOURCE}, +{"CAP_SYS_TIME", CAP_SYS_TIME}, +{"CAP_SYS_TTY_CONFIG", CAP_SYS_TTY_CONFIG}, +{"CAP_MKNOD", CAP_MKNOD}, +{"", CAP_ALL_OFF}, +{"all", CAP_ALL_ON}, +}; + +static const int cap_info_array_len = sizeof(cap_info_array) / + sizeof(cap_info_array[0]); + +static const cap_value_t cap_list[] = { +CAP_CHOWN, +CAP_DAC_EXECUTE, +CAP_DAC_WRITE, +CAP_DAC_READ_SEARCH, +CAP_FOWNER, +CAP_FSETID, +CAP_KILL, +CAP_LINK_DIR, +CAP_SETFCAP, +CAP_SETGID, +CAP_SETUID, +CAP_MAC_DOWNGRADE, +CAP_MAC_READ, +CAP_MAC_RELABEL_SUBJ, +CAP_MAC_UPGRADE, +CAP_MAC_WRITE, +CAP_INF_NOFLOAT_OBJ, +CAP_INF_NOFLOAT_SUBJ, +CAP_INF_RELABEL_OBJ, +CAP_INF_RELABEL_SUBJ, +CAP_AUDIT_CONTROL, +CAP_AUDIT_WRITE, +CAP_SETPCAP, +CAP_SYS_SETFFLAG, +CAP_NET_BIND_SERVICE, +CAP_NET_BROADCAST, +CAP_NET_ADMIN, +CAP_NET_RAW, +CAP_IPC_LOCK, +CAP_IPC_OWNER, +CAP_SYS_MODULE, +CAP_SYS_RAWIO, +CAP_SYS_CHROOT, +CAP_SYS_PTRACE, +CAP_SYS_PACCT, +CAP_SYS_ADMIN, +CAP_SYS_BOOT, +CAP_SYS_NICE, +CAP_SYS_RESOURCE, +CAP_SYS_TIME, +CAP_SYS_TTY_CONFIG, +CAP_MKNOD, +}; + +static const int cap_list_len = sizeof(cap_list) / sizeof(cap_list[0]); + +static void +cap_set(cap_t cap_p, cap_flag_t flags, cap_flag_value_t fvalue, + cap_value_t cap_value) +{ + + if (flags & CAP_EFFECTIVE) { + if (fvalue == CAP_SET) + cap_p->c_effective |= cap_value; + else + cap_p->c_effective &= ~cap_value; + } + if (flags & CAP_INHERITABLE) { + if (fvalue == CAP_SET) + cap_p->c_inheritable |= cap_value; + else + cap_p->c_inheritable &= ~cap_value; + } + if (flags & CAP_PERMITTED) { + if (fvalue == CAP_SET) + cap_p->c_permitted |= cap_value; + else + cap_p->c_permitted &= ~cap_value; + } +} + +static int +cap_is_set(cap_t cap_p, cap_flag_t cap_flag, cap_value_t cap_value) +{ + int seen = 0; + + if (cap_flag & CAP_EFFECTIVE) + seen |= (cap_p->c_effective & cap_value); + if (cap_flag & CAP_INHERITABLE) + seen |= (cap_p->c_inheritable & cap_value); + if (cap_flag & CAP_PERMITTED) + seen |= (cap_p->c_permitted & cap_value); + + return (seen); +} + +static cap_flag_value_t +cap_value_to_flags(cap_t cap_p, cap_value_t cap_value) +{ + cap_flag_t flags = 0; + + if (cap_p->c_effective & cap_value) + flags |= CAP_EFFECTIVE; + if (cap_p->c_inheritable & cap_value) + flags |= CAP_INHERITABLE; + if (cap_p->c_permitted & cap_value) + flags |= CAP_PERMITTED; + + return (flags); +} + +static const char * +cap_flags_to_string(cap_flag_t flags) +{ + + return (CAP_FLAGS[flags]); +} + +static int +cap_string_to_flags(const char *string, cap_flag_t *flags) +{ + const char *c = string; + + *flags = 0; + while (*c != '\0') { + switch (*c) { + case 'e': + *flags |= CAP_EFFECTIVE; + break; + case 'i': + *flags |= CAP_INHERITABLE; + break; + case 'p': + *flags |= CAP_PERMITTED; + break; + default: + return (EINVAL); + } + c++; + } + + return (0); +} + +static const char * +cap_to_string(cap_value_t cap) +{ + int i; + + for (i = 0; i < cap_info_array_len; i++) { + if (cap_info_array[i].ci_cap == cap) + return (cap_info_array[i].ci_name); + } + + return (NULL); +} + +static int +cap_from_string(const char *string, cap_value_t *cap) +{ + int i; + + for (i = 0; i < cap_info_array_len; i++) { + if (!strcasecmp(cap_info_array[i].ci_name, string)) { + *cap = cap_info_array[i].ci_cap; + return (0); + } + } + + return (EINVAL); +} + +char * +cap_to_text(cap_t cap_p, ssize_t *len_p) +{ + cap_value_t cap_value; + cap_flag_t cap_flag, most_flag; + const char *flag_s, *value_s, *prefix_s; + char *buf, minibuf[CAP_MAX_SMALL_BUF_LEN], operation; + + int num_effective, num_inheritable, num_permitted; + int most_effective, most_inheritable, most_permitted; + int count, any_so_far; + + buf = (char *)malloc(CAP_MAX_BUF_LEN); + if (buf == NULL) { + errno = ENOMEM; + return (NULL); + } + buf[0] = '\0'; + + /* + * For the sake of prettiness, first walk each flag to see if it's + * set for cap_list_len/2 or more. If so, list it as a plus, and + * do the remainder as negative, as needed. This will tend to + * collapse a lot of the common all= cases. + */ + num_effective = num_inheritable = num_permitted = 0; + for (count = 0; count < cap_list_len; count++) { + cap_value = cap_list[count]; + if (cap_is_set(cap_p, CAP_EFFECTIVE, cap_value)) + num_effective++; + if (cap_is_set(cap_p, CAP_INHERITABLE, cap_value)) + num_inheritable++; + if (cap_is_set(cap_p, CAP_PERMITTED, cap_value)) + num_permitted++; + } + + most_effective = (num_effective > cap_list_len / 2); + most_inheritable = (num_inheritable > cap_list_len / 2); + most_permitted = (num_permitted > cap_list_len / 2); + + most_flag = 0; + if (most_effective) + most_flag |= CAP_EFFECTIVE; + if (most_inheritable) + most_flag |= CAP_INHERITABLE; + if (most_permitted) + most_flag |= CAP_PERMITTED; + + any_so_far = 0; + if (most_flag != 0) { + if ((strlcat(buf, "all=", CAP_MAX_BUF_LEN) >= + CAP_MAX_BUF_LEN) || + (strlcat(buf, CAP_FLAGS[most_flag], + CAP_MAX_BUF_LEN) >= CAP_MAX_BUF_LEN)) { + free(buf); + errno = ENOMEM; + return (NULL); + } + any_so_far = 1; + } + + /* + * For each capability value, determine how that value relates + * to the most common case, and (depending on CAP_PRINT_RELATIVE) + * either print out the value's flag set relative to the most + * common, or its absolute flag set. + */ + for (count = 0; count < cap_list_len; count++) { + cap_value = cap_list[count]; + cap_flag = cap_value_to_flags(cap_p, cap_value); + /* + * Determine which, if any, flags need to be printed + * for this capability. First, if the flags on the + * capability are equal to the "most" flags, just skip + * it. + */ + if (cap_flag == most_flag) + continue; + +#if CAP_PRINT_RELATIVE + /* + * If the flags are a strict superset of the "most" + * flags, print it as a "+" case. If they're a + * strict subset, print as a "-" case. Otherwise, + * specify as an "=" case. + */ + if ((cap_flag | most_flag) == cap_flag) { + /* Strict superset, use "+". */ + operation = '+'; + cap_flag = cap_flag & ~most_flag; + flag_s = cap_flags_to_string(cap_flag); + } else if ((cap_flag | most_flag) == most_flag) { + /* Strict subset, use "-". */ + operation = '-'; + cap_flag = most_flag & ~cap_flag; + flag_s = cap_flags_to_string(cap_flag); + } else { +#endif + /* Mixed, use an "=" case */ + operation = '='; + flag_s = cap_flags_to_string(cap_flag); +#if CAP_PRINT_RELATIVE + } +#endif + /* + * Now assemble clause, and append to the string being + * built. + */ + if (any_so_far) + prefix_s = ":"; + else + prefix_s = ""; + value_s = cap_to_string(cap_value); + if ((snprintf(minibuf, sizeof(minibuf), "%s%s%c%s", prefix_s, + value_s, operation, flag_s) >= sizeof(minibuf)) || + (strlcat(buf, minibuf, CAP_MAX_BUF_LEN) >= + CAP_MAX_BUF_LEN)) { + free(buf); + errno = ENOMEM; + return (NULL); + } + } + + if (len_p) + *len_p = strlen(buf); + return (buf); +} + +cap_t +cap_from_text(const char *buf_p) +{ + cap_value_t cap_value_v, cap_value_set_v; + cap_flag_t cap_action_v; + cap_t cap; + char *mybuf, *cur; + char *clause_s, *cap_value_s, *cap_value_list_s; + char *cap_action_list_s, *cap_action_s; + char *next_operation_p, operation, next_operation; + + cap = cap_init(); + if (cap == NULL) + return ((cap_t)NULL); + + mybuf = strdup(buf_p); + if (mybuf == NULL) { + errno = ENOMEM; + goto err1; + } + + /* + * clase [SEP clause [SEP clause ...]] + * Split into "clauses", which are seperated by a : or whitespace. + * + * clause = [caplist]actionlist + * caplist = capabilityname[,capabilityname[, ...]] + * actionlist = op[flags][op[flags]] + * Split clauses into a (possibly null) capability name list, and a + * set of one or more {op,flags} pairs. + * + * Each assignment is then applied to a running "state" to + * produce an end-result in the internal representation. + * Parsing failure at any time releases resources and results + * in EINVAL. + */ + cur = mybuf; + while ((clause_s = strsep(&cur, CAP_SEP)) != NULL) { + /* + * Identify and NULL the first operation so that we + * can parse the capability name list, but save + * for later when we iterate over the operation list. + */ + cap_action_list_s = clause_s; + next_operation_p = strpbrk(cap_action_list_s, CAP_OPERATION); + if (next_operation_p == NULL) + goto err2; + operation = *next_operation_p; + cap_value_list_s = strsep(&cap_action_list_s, CAP_OPERATION); + if (cap_value_list_s == NULL || cap_action_list_s == NULL) + goto err2; + /* + * cap_value_list_s now points at the NULL-terminated list + * of capability values, if any. + * cap_action_list_s now points to the NULL-terminated list + * of actions. + * + * First, parse the value list to generate a value set + * refering to the combined contents of the value list. + */ + cap_value_set_v = 0; + while ((cap_value_s = strsep(&cap_value_list_s, ",")) != NULL) { + /* + * Convert value string into internal representation. + * Reject if not a valid capability identifier. + */ + if (cap_from_string(cap_value_s, &cap_value_v)) + goto err2; + cap_value_set_v |= cap_value_v; + } + + /* + * While the current operation is non-0, parse its flags, + * apply the actions, and then repeat. The first set + * is assured above when the capability list is split off. + */ + while (operation != 0) { + /* + * Identify and save the next operation, then NULL + * it to find the end of the current flags. + */ + next_operation_p = strpbrk(cap_action_list_s, + CAP_OPERATION); + if (next_operation_p) + next_operation = *next_operation_p; + else + next_operation = 0; + cap_action_s = strsep(&cap_action_list_s, + CAP_OPERATION); + /* + * Convert string form of flags to internal + * representation, reject if not possible. + */ + if (cap_string_to_flags(cap_action_s, &cap_action_v)) + goto err2; + + /* + * Now, based on operation apply actionlist flags + * to the capability value set built earlier from + * the capability list. + */ + switch (operation) { + case '=': + /* + * Remove current flags for the value set, + * replace with new flags. + * + * Spec requires that an "=" operation with + * no value set be treated as an "=" operation + * with a value set equivilent to "all". + */ + if (cap_value_set_v == CAP_ALL_OFF) { + cap_set(cap, CAP_EFFECTIVE| + CAP_INHERITABLE|CAP_PERMITTED, + CAP_CLEAR, CAP_ALL_ON); + cap_set(cap, cap_action_v, CAP_SET, + CAP_ALL_ON); + } else { + cap_set(cap, CAP_EFFECTIVE| + CAP_INHERITABLE|CAP_PERMITTED, + CAP_CLEAR, cap_value_set_v); + cap_set(cap, cap_action_v, CAP_SET, + cap_value_set_v); + } + break; + case '+': + /* + * Add current flags to value set. + * + * Spec requires that a "+" operation with + * no value set be rejected. + */ + if (cap_value_set_v == CAP_ALL_OFF) + goto err2; + cap_set(cap, cap_action_v, CAP_SET, + cap_value_set_v); + break; + case '-': + /* + * Subtract current flags from value set. + * + * Spec requires that a "-" operation with + * no value set be treated as a "-" operation + * with a value set equivilent to "all". + */ + if (cap_value_set_v == CAP_ALL_OFF) + cap_set(cap, cap_action_v, CAP_CLEAR, + CAP_ALL_ON); + else + cap_set(cap, cap_action_v, CAP_CLEAR, + cap_value_set_v); + break; + default: + goto err2; + } + operation = next_operation; + } + } + + return (cap); + err2: + errno = EINVAL; + free(mybuf); + err1: + cap_free(cap); + return ((cap_t)NULL); +} + -- cgit v1.1