freebsd-skq/lib/libposix1e/cap_text.c
rwatson 9ddccb5505 o Introduce cap_from_text() and cap_to_text() implementations.
Reviewed by:	green
Obtained from:	TrustedBSD Project
Security audited by:	imp, green
2000-10-13 18:24:58 +00:00

572 lines
14 KiB
C

/*-
* 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);
}