blob: 14facd20e55b24fe8ca549146c64886701ed8edb [file] [log] [blame] [raw]
/*
Copyright 2015-2021 Rivoreo
This Source Code Form is subject to the terms of the Mozilla Public
License, v. 2.0. If a copy of the MPL was not distributed with this
file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
#include "common.h"
#include <sys/types.h>
#include <sys/mac.h>
#include <sys/priv.h>
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
#include <bash/command.h>
/* Being user-space program, trying to define every possible privilege to
* maximize compatibility between different kernel releases */
#ifndef PRIV_NET_PPP
#define PRIV_NET_PPP 392
#endif
#ifndef PRIV_NET_SETVLANPCP
#define PRIV_NET_SETVLANPCP 421
#endif
#ifndef PRIV_NETATALK_RESERVEDPORT
#define PRIV_NETATALK_RESERVEDPORT 450
#endif
#ifndef PRIV_NETINET_HASHKEY
#define PRIV_NETINET_HASHKEY 507
#endif
#ifndef PRIV_NETIPX_RESERVEDPORT
#define PRIV_NETIPX_RESERVEDPORT 520
#endif
#ifndef PRIV_NETIPX_RAW
#define PRIV_NETIPX_RAW 521
#endif
static struct privilege_description {
char *name, *desc;
} privileges[] = {
#define PRIVILEGE(N,D) [PRIV_##N] = { #N, D }
PRIVILEGE(ACCT, "Manage process accounting"),
PRIVILEGE(MAXFILES, "Exceed system open files limit"),
PRIVILEGE(MAXPROC, "Exceed system processes limit"),
PRIVILEGE(KTRACE, "Set/clear KTRFAC_ROOT on ktrace"),
PRIVILEGE(SETDUMPER, "Configure dump device"),
PRIVILEGE(REBOOT, "Can reboot system"),
PRIVILEGE(SWAPON, "Can swapon()"),
PRIVILEGE(SWAPOFF, "Can swapoff()"),
PRIVILEGE(MSGBUF, "Can read kernel message buffer"),
PRIVILEGE(IO, "Can perform low-level I/O"),
PRIVILEGE(KEYBOARD, "Reprogram keyboard"),
PRIVILEGE(DRIVER, "Low-level driver privilege"),
PRIVILEGE(ADJTIME, "Set time adjustment"),
PRIVILEGE(NTP_ADJTIME, "Set NTP time adjustment"),
PRIVILEGE(CLOCK_SETTIME, "Can call clock_settime"),
PRIVILEGE(SETTIMEOFDAY, "Can call settimeofday"),
PRIVILEGE(AUDIT_CONTROL, "Can configure audit"),
PRIVILEGE(AUDIT_FAILSTOP, "Can run during audit fail stop"),
PRIVILEGE(AUDIT_GETAUDIT, "Can get proc audit properties"),
PRIVILEGE(AUDIT_SETAUDIT, "Can set proc audit properties"),
PRIVILEGE(AUDIT_SUBMIT, "Can submit an audit record"),
PRIVILEGE(CRED_SETUID, "setuid"),
PRIVILEGE(CRED_SETEUID, "seteuid to !ruid and !svuid"),
PRIVILEGE(CRED_SETGID, "setgid"),
PRIVILEGE(CRED_SETEGID, "setgid to !rgid and !svgid"),
PRIVILEGE(CRED_SETGROUPS, "Set process additional groups"),
PRIVILEGE(CRED_SETREUID, "setreuid"),
PRIVILEGE(CRED_SETREGID, "setregid"),
PRIVILEGE(CRED_SETRESUID, "setresuid"),
PRIVILEGE(CRED_SETRESGID, "setresgid"),
PRIVILEGE(SEEOTHERGIDS, "Exempt bsd.seeothergids"),
PRIVILEGE(SEEOTHERUIDS, "Exempt bsd.seeotheruids"),
PRIVILEGE(DEBUG_DIFFCRED, "Exempt debugging other users"),
PRIVILEGE(DEBUG_SUGID, "Exempt debugging setuid proc"),
PRIVILEGE(DEBUG_UNPRIV, "Exempt unprivileged debug limit"),
PRIVILEGE(DEBUG_DENIED, "Exempt P2_NOTRACE"),
PRIVILEGE(DTRACE_KERNEL, "Allow use of DTrace on the kernel"),
PRIVILEGE(DTRACE_PROC, "Allow attaching DTrace to process"),
PRIVILEGE(DTRACE_USER, "Process may submit DTrace events"),
PRIVILEGE(FIRMWARE_LOAD, "Can load firmware"),
PRIVILEGE(JAIL_ATTACH, "Attach to a jail"),
PRIVILEGE(JAIL_SET, "Set jail parameters"),
PRIVILEGE(JAIL_REMOVE, "Remove a jail"),
PRIVILEGE(KENV_SET, "Set kernel env. variables"),
PRIVILEGE(KENV_UNSET, "Unset kernel env. variables"),
PRIVILEGE(KLD_LOAD, "Load a kernel module"),
PRIVILEGE(KLD_UNLOAD, "Unload a kernel module"),
PRIVILEGE(MAC_PARTITION, "Privilege in mac_partition policy"),
PRIVILEGE(MAC_PRIVS, "Privilege in the mac_privs policy"),
PRIVILEGE(PROC_LIMIT, "Exceed user process limit"),
PRIVILEGE(PROC_SETLOGIN, "Can call setlogin"),
PRIVILEGE(PROC_SETRLIMIT, "Can raise resources limits"),
PRIVILEGE(PROC_SETLOGINCLASS, "Can call setloginclass(2)"),
PRIVILEGE(IPC_READ, "Can override IPC read perm"),
PRIVILEGE(IPC_WRITE, "Can override IPC write perm"),
PRIVILEGE(IPC_ADMIN, "Can override IPC owner-only perm"),
PRIVILEGE(IPC_MSGSIZE, "Exempt IPC message queue limit"),
PRIVILEGE(MQ_ADMIN, "Can override msgq owner-only perm"),
PRIVILEGE(PMC_MANAGE, "Can administer PMC"),
PRIVILEGE(PMC_SYSTEM, "Can allocate a system-wide PMC"),
PRIVILEGE(SCHED_DIFFCRED, "Exempt scheduling other users"),
PRIVILEGE(SCHED_SETPRIORITY, "Can set lower nice value for proc"),
PRIVILEGE(SCHED_RTPRIO, "Can set real time scheduling"),
PRIVILEGE(SCHED_SETPOLICY, "Can set scheduler policy"),
PRIVILEGE(SCHED_SET, "Can set thread scheduler"),
PRIVILEGE(SCHED_SETPARAM, "Can set thread scheduler params"),
PRIVILEGE(SCHED_CPUSET, "Can manipulate cpusets"),
PRIVILEGE(SCHED_CPUSET_INTR, "Can adjust IRQ to CPU binding"),
PRIVILEGE(SEM_WRITE, "Can override sem write perm"),
PRIVILEGE(SIGNAL_DIFFCRED, "Exempt signalling other users"),
PRIVILEGE(SIGNAL_SUGID, "Non-conserv signal setuid proc"),
PRIVILEGE(SYSCTL_DEBUG, "Can invoke sysctl.debug"),
PRIVILEGE(SYSCTL_WRITE, "Can write sysctls"),
PRIVILEGE(SYSCTL_WRITEJAIL, "Can write sysctls, jail permitted"),
PRIVILEGE(TTY_CONSOLE, "Set console to tty"),
PRIVILEGE(TTY_DRAINWAIT, "Set tty drain wait time"),
PRIVILEGE(TTY_DTRWAIT, "Set DTR wait on tty"),
PRIVILEGE(TTY_EXCLUSIVE, "Override tty exclusive flag"),
PRIVILEGE(TTY_STI, "Simulate input on another tty"),
PRIVILEGE(TTY_SETA, "Set tty termios structure"),
PRIVILEGE(UFS_EXTATTRCTL, "Can configure EAs on UFS1"),
PRIVILEGE(UFS_QUOTAOFF, "quotaoff()"),
PRIVILEGE(UFS_QUOTAON, "quotaon()"),
PRIVILEGE(UFS_SETUSE, "setuse()"),
PRIVILEGE(ZFS_POOL_CONFIG, "Can configure ZFS pools"),
PRIVILEGE(ZFS_INJECT, "Can inject faults in the ZFS fault injection framework"),
PRIVILEGE(ZFS_JAIL, "Can attach/detach ZFS to/from jails"),
PRIVILEGE(NFS_DAEMON, "Can become the NFS daemon"),
PRIVILEGE(NFS_LOCKD, "Can become NFS lock daemon"),
PRIVILEGE(VFS_READ, "Override vnode DAC read perm"),
PRIVILEGE(VFS_WRITE, "Override vnode DAC write perm"),
PRIVILEGE(VFS_ADMIN, "Override vnode DAC admin perm"),
PRIVILEGE(VFS_EXEC, "Override vnode DAC exec perm"),
PRIVILEGE(VFS_LOOKUP, "Override vnode DAC lookup perm"),
PRIVILEGE(VFS_BLOCKRESERVE, "Can use free block reserve"),
PRIVILEGE(VFS_CHFLAGS_DEV, "Can chflags() a device node"),
PRIVILEGE(VFS_CHOWN, "Can set user; group to non-member"),
PRIVILEGE(VFS_CHROOT, "chroot()"),
PRIVILEGE(VFS_RETAINSUGID, "Can retain sugid bits on change"),
PRIVILEGE(VFS_EXCEEDQUOTA, "Exempt from quota restrictions"),
PRIVILEGE(VFS_EXTATTR_SYSTEM, "Operate on system EA namespace"),
PRIVILEGE(VFS_FCHROOT, "fchroot()"),
PRIVILEGE(VFS_FHOPEN, "Can fhopen()"),
PRIVILEGE(VFS_FHSTAT, "Can fhstat()"),
PRIVILEGE(VFS_FHSTATFS, "Can fhstatfs()"),
PRIVILEGE(VFS_GENERATION, "stat() returns generation number"),
PRIVILEGE(VFS_GETFH, "Can retrieve file handles"),
PRIVILEGE(VFS_GETQUOTA, "getquota()"),
PRIVILEGE(VFS_LINK, "bsd.hardlink_check_uid"),
PRIVILEGE(VFS_MKNOD_BAD, "Can mknod() to mark bad inodes"),
PRIVILEGE(VFS_MKNOD_DEV, "Can mknod() to create dev nodes"),
PRIVILEGE(VFS_MKNOD_WHT, "Can mknod() to create whiteout"),
PRIVILEGE(VFS_MOUNT, "Can mount()"),
PRIVILEGE(VFS_MOUNT_OWNER, "Can manage other users' file systems"),
PRIVILEGE(VFS_MOUNT_EXPORTED, "Can set MNT_EXPORTED on mount"),
PRIVILEGE(VFS_MOUNT_PERM, "Override dev node perms at mount"),
PRIVILEGE(VFS_MOUNT_SUIDDIR, "Can set MNT_SUIDDIR on mount"),
PRIVILEGE(VFS_MOUNT_NONUSER, "Can perform a non-user mount"),
PRIVILEGE(VFS_SETGID, "Can setgid if not in group"),
PRIVILEGE(VFS_SETQUOTA, "setquota()"),
PRIVILEGE(VFS_STICKYFILE, "Can set sticky bit on file"),
PRIVILEGE(VFS_SYSFLAGS, "Can modify system flags"),
PRIVILEGE(VFS_UNMOUNT, "Can unmount()"),
PRIVILEGE(VFS_STAT, "Override vnode MAC stat perm"),
PRIVILEGE(VM_MADV_PROTECT, "Can set MADV_PROTECT"),
PRIVILEGE(VM_MLOCK, "Can mlock(), mlockall()"),
PRIVILEGE(VM_MUNLOCK, "Can munlock(), munlockall()"),
PRIVILEGE(VM_SWAP_NOQUOTA, "Can override the global swap reservation limits"),
PRIVILEGE(VM_SWAP_NORLIMIT, "Can override the per-uid swap reservation limits"),
PRIVILEGE(DEVFS_RULE, "Can manage devfs rules"),
PRIVILEGE(DEVFS_SYMLINK, "Can create symlinks in devfs"),
PRIVILEGE(RANDOM_RESEED, "Closing /dev/random reseeds"),
PRIVILEGE(NET_BRIDGE, "Administer bridge"),
PRIVILEGE(NET_GRE, "Administer GRE"),
PRIVILEGE(NET_PPP, "Administer PPP interface"),
PRIVILEGE(NET_BPF, "Monitor BPF"),
PRIVILEGE(NET_RAW, "Open raw socket"),
PRIVILEGE(NET_ROUTE, "Administer routing"),
PRIVILEGE(NET_TAP, "Can open tap device"),
PRIVILEGE(NET_SETIFMTU, "Set interface MTU"),
PRIVILEGE(NET_SETIFFLAGS, "Set interface flags"),
PRIVILEGE(NET_SETIFCAP, "Set interface capabilities"),
PRIVILEGE(NET_SETIFNAME, "Set interface name"),
PRIVILEGE(NET_SETIFMETRIC, "Set interface metrics"),
PRIVILEGE(NET_SETIFPHYS, "Set interface physical layer prop"),
PRIVILEGE(NET_SETIFMAC, "Set interface MAC label"),
PRIVILEGE(NET_ADDMULTI, "Add multicast addr. to ifnet"),
PRIVILEGE(NET_DELMULTI, "Delete multicast addr. from ifnet"),
PRIVILEGE(NET_HWIOCTL, "Issue hardware ioctl on ifnet"),
PRIVILEGE(NET_SETLLADDR, "Set interface link-level address"),
PRIVILEGE(NET_ADDIFGROUP, "Add new interface group"),
PRIVILEGE(NET_DELIFGROUP, "Delete interface group"),
PRIVILEGE(NET_IFCREATE, "Create cloned interface"),
PRIVILEGE(NET_IFDESTROY, "Destroy cloned interface"),
PRIVILEGE(NET_ADDIFADDR, "Add protocol addr to interface"),
PRIVILEGE(NET_DELIFADDR, "Delete protocol addr on interface"),
PRIVILEGE(NET_LAGG, "Administer lagg interface"),
PRIVILEGE(NET_GIF, "Administer gif interface"),
PRIVILEGE(NET_SETIFVNET, "Move interface to vnet"),
PRIVILEGE(NET_SETIFDESCR, "Set interface description"),
PRIVILEGE(NET_SETIFFIB, "Set interface fib"),
PRIVILEGE(NET_VXLAN, "Administer vxlan"),
PRIVILEGE(NET_SETVLANPCP, "Set VLAN priority"),
PRIVILEGE(NET80211_GETKEY, "Query 802.11 keys"),
PRIVILEGE(NET80211_MANAGE, "Administer 802.11"),
PRIVILEGE(NETATALK_RESERVEDPORT, "Bind low port number"),
PRIVILEGE(NETATM_CFG, ""),
PRIVILEGE(NETATM_ADD, ""),
PRIVILEGE(NETATM_DEL, ""),
PRIVILEGE(NETATM_SET, ""),
PRIVILEGE(NETBLUETOOTH_RAW, "Open raw bluetooth socket"),
PRIVILEGE(NETGRAPH_CONTROL, "Open netgraph control socket"),
PRIVILEGE(NETGRAPH_TTY, "Configure tty for netgraph"),
PRIVILEGE(NETINET_RESERVEDPORT, "Bind low port number"),
PRIVILEGE(NETINET_IPFW, "Administer IPFW firewall"),
PRIVILEGE(NETINET_DIVERT, "Open IP divert socket"),
PRIVILEGE(NETINET_PF, "Administer pf firewall"),
PRIVILEGE(NETINET_DUMMYNET, "Administer DUMMYNET"),
PRIVILEGE(NETINET_CARP, "Administer CARP"),
PRIVILEGE(NETINET_MROUTE, "Administer multicast routing"),
PRIVILEGE(NETINET_RAW, "Open netinet raw socket"),
PRIVILEGE(NETINET_GETCRED, "Query netinet pcb credentials"),
PRIVILEGE(NETINET_ADDRCTRL6, "Administer IPv6 address scopes"),
PRIVILEGE(NETINET_ND6, "Administer IPv6 neighbor disc"),
PRIVILEGE(NETINET_SCOPE6, "Administer IPv6 address scopes"),
PRIVILEGE(NETINET_ALIFETIME6, "Administer IPv6 address lifetimes"),
PRIVILEGE(NETINET_IPSEC, "Administer IPSEC"),
PRIVILEGE(NETINET_REUSEPORT, "Allow [rapid] port/address reuse"),
PRIVILEGE(NETINET_SETHDROPTS, "Set certain IPv4/6 header options"),
PRIVILEGE(NETINET_BINDANY, "Allow bind to any address"),
PRIVILEGE(NETINET_HASHKEY, "Get and set hash keys for IPv4/6"),
PRIVILEGE(NETIPX_RESERVEDPORT, "Bind low port number"),
PRIVILEGE(NETIPX_RAW, "Open netipx raw socket"),
PRIVILEGE(NETNCP, "Use another user's connection"),
PRIVILEGE(NETSMB, "Use another user's connection"),
PRIVILEGE(VM86_INTCALL, "Allow invoking vm86 int handlers"),
PRIVILEGE(MODULE0, ""),
PRIVILEGE(MODULE1, ""),
PRIVILEGE(MODULE2, ""),
PRIVILEGE(MODULE3, ""),
PRIVILEGE(MODULE4, ""),
PRIVILEGE(MODULE5, ""),
PRIVILEGE(MODULE6, ""),
PRIVILEGE(MODULE7, ""),
PRIVILEGE(MODULE8, ""),
PRIVILEGE(MODULE9, ""),
PRIVILEGE(MODULE10, ""),
PRIVILEGE(MODULE11, ""),
PRIVILEGE(MODULE12, ""),
PRIVILEGE(MODULE13, ""),
PRIVILEGE(MODULE14, ""),
PRIVILEGE(MODULE15, ""),
PRIVILEGE(DDB_CAPTURE, "Allow reading of DDB capture log"),
PRIVILEGE(NNPFS_DEBUG, "Perforn ARLA_VIOC_NNPFSDEBUG"),
PRIVILEGE(CPUCTL_WRMSR, "Write model-specific register"),
PRIVILEGE(CPUCTL_UPDATE, "Update cpu microcode"),
PRIVILEGE(C4B_RESET_CTLR, "Load firmware, reset controller"),
PRIVILEGE(C4B_TRACE, "Unrestricted CAPI message tracing"),
PRIVILEGE(AFS_ADMIN, "Can change AFS client settings"),
PRIVILEGE(AFS_DAEMON, "Can become the AFS daemon"),
PRIVILEGE(RCTL_GET_RACCT, ""),
PRIVILEGE(RCTL_GET_RULES, ""),
PRIVILEGE(RCTL_GET_LIMITS, ""),
PRIVILEGE(RCTL_ADD_RULE, ""),
PRIVILEGE(RCTL_REMOVE_RULE, ""),
PRIVILEGE(KMEM_READ, "Open mem/kmem for reading"),
PRIVILEGE(KMEM_WRITE, "Open mem/kmem for writing"),
#undef PRIVILEGE
};
static int get_privilege_by_name(const char *name) {
unsigned int i = _PRIV_LOWEST;
do {
const char *s = privileges[i].name;
if(s && strcasecmp(s, name) == 0) return i;
} while(++i < _PRIV_HIGHEST);
return -1;
}
static char *get_priv_label_text() {
if(!mac_is_present("priv")) {
fputs("MAC/Privilege not available\n", stderr);
return NULL;
}
mac_t mac;
if(mac_prepare(&mac, "priv") < 0) {
perror("mac_prepare");
return NULL;
}
if(mac_get_proc(mac) < 0) {
perror("mac_get_proc");
mac_free(mac);
return NULL;
}
char *label_text;
if(mac_to_text(mac, &label_text) < 0) {
perror("mac_to_text");
label_text = NULL;
}
mac_free(mac);
return label_text;
}
int get_privileges(char ***names, char ***descriptions, unsigned int *count) {
int r = -1;
char *label_text = get_priv_label_text();
if(!label_text) return -1;
char *p = strchr(label_text, '/');
if(!p) {
fprintf(stderr, "Malformed MAC label string '%s'\n", label_text);
goto cleanup;
}
p++;
char *end_p;
if(names) *names = NULL;
if(descriptions) *descriptions = NULL;
*count = 0;
size_t current_size = 0;
while(*p) {
int n = strtol(p, &end_p, 0);
if(*end_p && *end_p != '/') {
end_p = strchr(p, '/');
if(end_p) *end_p = 0;
n = get_privilege_by_name(p);
} else if(end_p == p) {
fputs("Failed to parse privilege set\n", stderr);
goto failed;
} else if(*end_p) {
*end_p = 0;
} else {
end_p = NULL;
}
if((names || descriptions) && (*count + 1) * sizeof(char *) > current_size) {
current_size += 2 * sizeof(char *);
if(names) {
char **new_p = realloc(*names, current_size);
if(!new_p) {
fputs("Out of memory\n", stderr);
goto failed;
}
*names = new_p;
}
if(descriptions) {
char **new_p = realloc(*descriptions, current_size);
if(!new_p) {
fputs("Out of memory\n", stderr);
goto failed;
}
*descriptions = new_p;
}
}
if(names) {
(*names)[*count] = strdup(n < 0 ? p : privileges[n].name);
if(!(*names)[*count]) {
fputs("Out of memory\n", stderr);
goto failed;
}
}
if(descriptions) {
(*descriptions)[*count] = strdup(n < 0 ? "Unknown" : privileges[n].desc);
if(!(*descriptions)[*count]) {
fputs("Out of memory\n", stderr);
if(names) free((*names)[*count]);
goto failed;
}
}
(*count)++;
if(!end_p) break;
p = end_p + 1;
}
r = 0;
cleanup:
free(label_text);
return r;
failed:
r = -1;
if(names && *names) {
unsigned int i = 0;
while(i < *count) free((*names)[i++]);
free(*names);
}
if(descriptions && *descriptions) {
unsigned int i = 0;
while(i < *count) free((*descriptions)[i++]);
free(*descriptions);
}
goto cleanup;
}
static int is_in_list(const char *s, const struct word_list *list) {
while(list) {
if(strcasecmp(list->word->word, s) == 0) return 1;
list = list->next;
}
return 0;
}
static int walk_all_privileges(int (*walk_func)(const char *, int, const char *), const struct word_list *filter) {
int r = -1;
char *label_text = get_priv_label_text();
if(!label_text) return -1;
char *next_p = strchr(label_text, '/');
if(!next_p) {
fprintf(stderr, "Malformed MAC label string '%s'\n", label_text);
goto cleanup;
}
char *p = NULL, *end_p;
long int n;
unsigned int i = _PRIV_LOWEST;
do {
if(!p && next_p) {
p = next_p + 1;
next_p = strchr(p, '/');
if(next_p) *next_p = 0;
n = strtol(p, &end_p, 0);
if(*end_p) n = -1;
}
const struct privilege_description *priv = privileges + i;
if(priv->name) {
int is_granted = p && strcmp(priv->name, p) == 0;
if(is_granted) p = NULL;
if(filter && !is_in_list(priv->name, filter)) continue;
if(walk_func(priv->name, is_granted, priv->desc) < 0) goto cleanup;
} else if(p && n >= _PRIV_LOWEST && n <= i) {
p = NULL;
if(filter && !is_in_list(priv->name, filter)) continue;
if(walk_func(p, 1, "Unknown") < 0) goto cleanup;
}
} while(++i < _PRIV_HIGHEST);
r = 0;
cleanup:
free(label_text);
return r;
}
int get_all_privileges(char ***names, char **grants, char ***descriptions, unsigned int *count, const struct word_list *filter) {
if(names) *names = NULL;
if(grants) *grants = NULL;
if(descriptions) *descriptions = NULL;
*count = 0;
unsigned int current_count = 0;
int store(const char *name, int is_granted, const char *desc) {
if((names || grants || descriptions) && (*count + 1) > current_count) {
current_count += 2;
if(names) {
char **new_p = realloc(*names, current_count * sizeof(char *));
if(!new_p) {
fputs("Out of memory\n", stderr);
return -1;
}
*names = new_p;
}
if(grants) {
char *new_p = realloc(*grants, current_count);
if(!new_p) {
fputs("Out of memory\n", stderr);
return -1;
}
*grants = new_p;
}
if(descriptions) {
char **new_p = realloc(*descriptions, current_count * sizeof(char *));
if(!new_p) {
fputs("Out of memory\n", stderr);
return -1;
}
*descriptions = new_p;
}
}
if(names) {
(*names)[*count] = strdup(name);
if(!(*names)[*count]) {
fputs("Out of memory\n", stderr);
return -1;
}
}
if(grants) (*grants)[*count] = is_granted;
if(descriptions) {
(*descriptions)[*count] = strdup(desc);
if(!(*descriptions)[*count]) {
fputs("Out of memory\n", stderr);
if(names) free((*names)[*count]);
return -1;
}
}
(*count)++;
return 0;
}
if(walk_all_privileges(store, filter) < 0) {
if(names && *names) {
unsigned int i = 0;
while(i < *count) free((*names)[i++]);
free(*names);
}
if(grants) free(*grants);
if(descriptions && *descriptions) {
unsigned int i = 0;
while(i < *count) free((*descriptions)[i++]);
free(*descriptions);
}
return -1;
}
return 0;
}
static int list_prepend(struct word_list **list, const char *s) {
struct word_desc *word = malloc(sizeof(struct word_desc));
if(!word) return -1;
word->word = strdup(s);
if(!word->word) {
free(word);
return -1;
}
word->flags = 0;
struct word_list *node = malloc(sizeof(struct word_list));
if(!node) {
free(word->word);
free(word);
return -1;
}
node->word = word;
node->next = *list;
*list = node;
return 0;
}
static void list_remove(struct word_list **list, const char *s) {
struct word_list *node = *list, *prev_node = NULL;
while(node) {
if(strcmp(node->word->word, s) == 0) {
struct word_list *next_node = node->next;
free(node->word->word);
free(node->word);
free(node);
if(prev_node) prev_node->next = next_node;
else *list = next_node;
node = next_node;
} else {
prev_node = node;
node = node->next;
}
}
}
static void list_destroy(struct word_list **list) {
while(*list) {
struct word_list *node = *list;
*list = node->next;
free(node->word->word);
free(node->word);
free(node);
}
}
int set_privileges(const struct word_list *priv_list, int how) {
struct word_list *final_priv_list = NULL;
int add_to_list(const char *name, int is_granted, const char *desc) {
if(list_prepend(&final_priv_list, name) < 0) {
fputs("Out of memory\n", stderr);
return -1;
}
return 0;
}
int check_add_to_list(const char *name, int is_granted, const char *desc) {
if(!is_granted) return 0;
return add_to_list(name, is_granted, desc);
}
switch(how) {
case ADJUST:
if(!priv_list) return 0;
if(walk_all_privileges(check_add_to_list, NULL) < 0) goto failed;
break;
case ALLOW_OTHER:
if(walk_all_privileges(add_to_list, NULL) < 0) goto failed;
break;
case DENY_OTHER:
break;
default:
abort();
}
while(priv_list) {
const char *s = priv_list->word->word;
if(*s == '!') list_remove(&final_priv_list, s + 1);
else if(list_prepend(&final_priv_list, s) < 0) {
fputs("Out of memory\n", stderr);
goto failed;
}
priv_list = priv_list->next;
}
size_t label_text_buffer_size = 6;
char *label_text_buffer = malloc(label_text_buffer_size);
if(!label_text_buffer) {
fputs("Out of memory\n", stderr);
goto failed;
}
memcpy(label_text_buffer, "priv/", 6);
priv_list = final_priv_list;
while(priv_list) {
const char *s = priv_list->word->word;
size_t len = strlen(s);
size_t i = label_text_buffer_size - 1;
label_text_buffer_size += (priv_list != final_priv_list) + len;
char *p = realloc(label_text_buffer, label_text_buffer_size);
if(!p) {
fputs("Out of memory\n", stderr);
free(label_text_buffer);
goto failed;
}
label_text_buffer = p;
if(priv_list != final_priv_list) label_text_buffer[i++] = '/';
memcpy(label_text_buffer + i, s, len + 1);
priv_list = priv_list->next;
}
mac_t label;
if(mac_from_text(&label, label_text_buffer) < 0) {
perror("mac_from_text");
free(label_text_buffer);
goto failed;
}
list_destroy(&final_priv_list);
int r = 0;
if(mac_set_proc(label) < 0) {
perror("mac_set_proc");
r = -1;
}
free(label);
return r;
failed:
list_destroy(&final_priv_list);
return -1;
}