diff options
Diffstat (limited to 'sysdep/linux/syspriv.h')
-rw-r--r-- | sysdep/linux/syspriv.h | 62 |
1 files changed, 62 insertions, 0 deletions
diff --git a/sysdep/linux/syspriv.h b/sysdep/linux/syspriv.h new file mode 100644 index 00000000..bfe19ac3 --- /dev/null +++ b/sysdep/linux/syspriv.h @@ -0,0 +1,62 @@ + +#include <sys/prctl.h> +#include <linux/capability.h> + +#ifndef _LINUX_CAPABILITY_VERSION_3 +#define _LINUX_CAPABILITY_VERSION_3 0x20080522 +#define _LINUX_CAPABILITY_U32S_3 2 +#endif + +/* capset() prototype is missing ... */ +int capset(cap_user_header_t hdrp, const cap_user_data_t datap); + +static inline int +set_capabilities(u32 caps) +{ + struct __user_cap_header_struct cap_hdr; + struct __user_cap_data_struct cap_dat[_LINUX_CAPABILITY_U32S_3]; + int err; + + cap_hdr.version = _LINUX_CAPABILITY_VERSION_3; + cap_hdr.pid = 0; + + memset(cap_dat, 0, sizeof(cap_dat)); + cap_dat[0].effective = cap_dat[0].permitted = caps; + + err = capset(&cap_hdr, cap_dat); + if (!err) + return 0; + + /* Kernel may support do not support our version of capability interface. + The last call returned supported version so we just retry it. */ + if (errno == EINVAL) + { + err = capset(&cap_hdr, cap_dat); + if (!err) + return 0; + } + + return -1; +} + +static void +drop_uid(uid_t uid) +{ + u32 caps = + CAP_TO_MASK(CAP_NET_BIND_SERVICE) | + CAP_TO_MASK(CAP_NET_BROADCAST) | + CAP_TO_MASK(CAP_NET_ADMIN) | + CAP_TO_MASK(CAP_NET_RAW); + + if (seteuid(uid) < 0) + die("seteuid: %m"); + + if (set_capabilities(caps) < 0) + die("capset: %m"); + + if (prctl(PR_SET_KEEPCAPS, 1) < 0) + die("prctl: %m"); + + if (setresuid(uid, uid, uid) < 0) + die("setresuid: %m"); +} |