summaryrefslogtreecommitdiffstats
path: root/lib/libc/posix1e
diff options
context:
space:
mode:
authorrwatson <rwatson@FreeBSD.org>2000-10-13 18:24:58 +0000
committerrwatson <rwatson@FreeBSD.org>2000-10-13 18:24:58 +0000
commit9ddccb55057ef3b64d80b6d90cb2b25b56238ba3 (patch)
treecb45cb7b58d4cd59fb06e463e4a19e14fe69befe /lib/libc/posix1e
parent16ec4a91f179c9d047fe1cb7b7d68c657df986fc (diff)
downloadFreeBSD-src-9ddccb55057ef3b64d80b6d90cb2b25b56238ba3.zip
FreeBSD-src-9ddccb55057ef3b64d80b6d90cb2b25b56238ba3.tar.gz
o Introduce cap_from_text() and cap_to_text() implementations.
Reviewed by: green Obtained from: TrustedBSD Project Security audited by: imp, green
Diffstat (limited to 'lib/libc/posix1e')
-rw-r--r--lib/libc/posix1e/Makefile3
-rw-r--r--lib/libc/posix1e/cap_text.c571
2 files changed, 573 insertions, 1 deletions
diff --git a/lib/libc/posix1e/Makefile b/lib/libc/posix1e/Makefile
index b441adf..3a1afd6 100644
--- a/lib/libc/posix1e/Makefile
+++ b/lib/libc/posix1e/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/libc/posix1e/cap_text.c b/lib/libc/posix1e/cap_text.c
new file mode 100644
index 0000000..9748a2c
--- /dev/null
+++ b/lib/libc/posix1e/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 <sys/types.h>
+#include <sys/capability.h>
+#include <sys/errno.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+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);
+}
+
OpenPOWER on IntegriCloud