Skip to content

Commit

Permalink
check capabilities, not effective user ID
Browse files Browse the repository at this point in the history
Under Linux, a process may not be run under root, yet it may have a permission
to do what a superuser may do given specific capabilities are granted.

This commit makes iodine not depend on EUID being 0 in order to run
properly. Instead, in presence of libcap-ng, the following capabilities
are being checked:

* `CAP_NET_BIND_SERVICES` for server to bind to a port, lower than
  `/proc/sys/net/ipv4/ip_unprivileged_port_start`
* `CAP_NET_ADMIN` to operate on a TUN device
* `CAP_SETUID` and `CAP_SETGID` in case server is configured to change
  the user it runs on behalf of

This change is handy if iodine is being run under a non-root user, provided
`AmbientCapabilities=` and `CapabilityBoundingSet=` of systemd are employed
in the first place.

Fixes: #80
Signed-off-by: Oleksandr Natalenko <[email protected]>
  • Loading branch information
pfactum committed Oct 17, 2022
1 parent 11dd73a commit c25eed6
Show file tree
Hide file tree
Showing 5 changed files with 53 additions and 5 deletions.
48 changes: 47 additions & 1 deletion src/common.c
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,10 @@
# include <selinux/selinux.h>
#endif

#ifdef HAVE_LIBCAPNG
#include <cap-ng.h>
#endif

#include "common.h"

/* The raw header used when not using DNS protocol */
Expand Down Expand Up @@ -103,12 +107,54 @@ int setgroups(int count, int *groups)

#ifndef WINDOWS32
void
check_privileges(void)
check_privileges(char *username, int port)
{
#if defined HAVE_LIBCAPNG
if (capng_get_caps_process() == -1) {
warnx("Unable to get capabilities");
exit(-1);
}

if (!capng_have_capability(CAPNG_EFFECTIVE, CAP_NET_ADMIN)) {
warnx("capabilities: CAP_NET_ADMIN required");
exit(-1);
}

if (port) {
unsigned short int ip_unprivileged_port_start = 1024;

FILE *file = fopen("/proc/sys/net/ipv4/ip_unprivileged_port_start", "r");
if (!file) {
warnx("sysctl: unable to get ip_unprivileged_port_start value");
// do not bail out here in case systemd.service has ProcSubset=pid set
} else {
fscanf(file, "%hu", &ip_unprivileged_port_start);
fclose(file);
}

if (port < ip_unprivileged_port_start &&
!capng_have_capability(CAPNG_EFFECTIVE, CAP_NET_BIND_SERVICE)) {
warnx("capabilities: CAP_NET_BIND_SERVICE required");
exit(-1);
}
}

if (username) {
if (!capng_have_capability(CAPNG_EFFECTIVE, CAP_SETUID)) {
warnx("capabilities: CAP_SETUID required");
exit(-1);
}
if (!capng_have_capability(CAPNG_EFFECTIVE, CAP_SETGID)) {
warnx("capabilities: CAP_SETGID required");
exit(-1);
}
}
#else
if (geteuid() != 0) {
warnx("Run as root and you'll be happy.");
exit(-1);
}
#endif
}
#endif

Expand Down
4 changes: 2 additions & 2 deletions src/common.h
Original file line number Diff line number Diff line change
Expand Up @@ -105,11 +105,11 @@ enum connection {
};

#ifdef WINDOWS32
static inline void check_privileges(void)
static inline void check_privileges(char *, int)
{
}
#else
void check_privileges(void);
void check_privileges(char *, int);
#endif
char *format_addr(struct sockaddr_storage *sockaddr, int sockaddr_len);
int get_addr(char *, int, int, int, struct sockaddr_storage *);
Expand Down
2 changes: 1 addition & 1 deletion src/iodine.c
Original file line number Diff line number Diff line change
Expand Up @@ -279,7 +279,7 @@ int main(int argc, char **argv)
}
}

check_privileges();
check_privileges(username, 0);

argc -= optind;
argv += optind;
Expand Down
2 changes: 1 addition & 1 deletion src/iodined.c
Original file line number Diff line number Diff line change
Expand Up @@ -2519,7 +2519,7 @@ main(int argc, char **argv)
argc -= optind;
argv += optind;

check_privileges();
check_privileges(username, port);

if (argc != 2)
usage();
Expand Down
2 changes: 2 additions & 0 deletions src/osflags
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ link)
[ -e /usr/include/selinux/selinux.h ] && FLAGS="$FLAGS -lselinux";
"$PKG_CONFIG" --exists libsystemd-daemon && FLAGS="$FLAGS $($PKG_CONFIG --libs libsystemd-daemon)";
"$PKG_CONFIG" --exists libsystemd && FLAGS="$FLAGS $($PKG_CONFIG --libs libsystemd)";
"$PKG_CONFIG" --exists libcap-ng && FLAGS="$FLAGS $($PKG_CONFIG --libs libcap-ng)";
echo $FLAGS;
;;
esac
Expand All @@ -43,6 +44,7 @@ cflags)
[ -e /usr/include/selinux/selinux.h ] && FLAGS="$FLAGS -DHAVE_SETCON";
"$PKG_CONFIG" --exists libsystemd-daemon && FLAGS="$FLAGS -DHAVE_SYSTEMD";
"$PKG_CONFIG" --exists libsystemd && FLAGS="$FLAGS -DHAVE_SYSTEMD";
"$PKG_CONFIG" --exists libcap-ng && FLAGS="$FLAGS -DHAVE_LIBCAPNG";
echo $FLAGS;
;;
GNU/kFreeBSD|GNU)
Expand Down

0 comments on commit c25eed6

Please sign in to comment.