| /* |
| 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 { |
| const 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 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_ignore_case(filter, priv->name)) 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_ignore_case(filter, priv->name)) 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: |
| case BASIC_SET: |
| 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; |
| } |