summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--sysdep/cf/README2
-rw-r--r--sysdep/cf/linux-22.h2
-rw-r--r--sysdep/cf/linux-v6.h2
-rw-r--r--sysdep/linux/Modules1
-rw-r--r--sysdep/linux/syspriv.h62
-rw-r--r--sysdep/unix/main.c83
6 files changed, 150 insertions, 2 deletions
diff --git a/sysdep/cf/README b/sysdep/cf/README
index 15a45a65..3b5bcd4f 100644
--- a/sysdep/cf/README
+++ b/sysdep/cf/README
@@ -6,6 +6,7 @@ CONFIG_SELF_CONSCIOUS We're able to recognize whether route was installed by us
CONFIG_MULTIPLE_TABLES The kernel supports multiple routing tables
CONFIG_ALL_TABLES_AT_ONCE Kernel scanner wants to process all tables at once
CONFIG_MC_PROPER_SRC Multicast packets have source address according to socket saddr field
+CONFIG_RESTRICTED_PRIVILEGES Implements restricted privileges using drop_uid()
CONFIG_UNIX_IFACE Use Unix interface scanner
CONFIG_UNIX_SET Use Unix route setting
@@ -19,3 +20,4 @@ CONFIG_UNNUM_MULTICAST krt-iface: We support multicasts on unnumbered PtP device
CONFIG_LINUX_MC_MREQN Linux: Use struct mreqn for multicasting
CONFIG_LINUX_MC_MREQ Linux: Use struct mreq
CONFIG_LINUX_MC_MREQ_BIND Linux: Use struct mreq and SO_BINDTODEVICE
+
diff --git a/sysdep/cf/linux-22.h b/sysdep/cf/linux-22.h
index 9ccab648..51b339d1 100644
--- a/sysdep/cf/linux-22.h
+++ b/sysdep/cf/linux-22.h
@@ -17,6 +17,8 @@
#define CONFIG_LINUX_MC_MREQN
#define CONFIG_UNIX_DONTROUTE
+#define CONFIG_RESTRICTED_PRIVILEGES
+
/*
Link: sysdep/linux/netlink
Link: sysdep/linux
diff --git a/sysdep/cf/linux-v6.h b/sysdep/cf/linux-v6.h
index ef52ee46..467d7728 100644
--- a/sysdep/cf/linux-v6.h
+++ b/sysdep/cf/linux-v6.h
@@ -19,6 +19,8 @@
#define CONFIG_MULTIPLE_TABLES
#define CONFIG_ALL_TABLES_AT_ONCE
+#define CONFIG_RESTRICTED_PRIVILEGES
+
/*
Link: sysdep/linux/netlink
Link: sysdep/linux
diff --git a/sysdep/linux/Modules b/sysdep/linux/Modules
index 1b867d81..09f4a470 100644
--- a/sysdep/linux/Modules
+++ b/sysdep/linux/Modules
@@ -3,3 +3,4 @@ krt-scan.c
krt-scan.h
#endif
sysio.h
+syspriv.h
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");
+}
diff --git a/sysdep/unix/main.c b/sysdep/unix/main.c
index fd921ace..744062b4 100644
--- a/sysdep/unix/main.c
+++ b/sysdep/unix/main.c
@@ -8,11 +8,15 @@
#undef LOCAL_DEBUG
+#define _GNU_SOURCE 1
+
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <unistd.h>
#include <signal.h>
+#include <pwd.h>
+#include <grp.h>
#include "nest/bird.h"
#include "lib/lists.h"
@@ -59,6 +63,29 @@ async_dump(void)
}
/*
+ * Dropping privileges
+ */
+
+#ifdef CONFIG_RESTRICTED_PRIVILEGES
+#include "lib/syspriv.h"
+#else
+
+static inline void
+drop_uid(uid_t uid)
+{
+ die("Cannot change user on this platform");
+}
+
+#endif
+
+static inline void
+drop_gid(gid_t gid)
+{
+ if (setgid(gid) < 0)
+ die("setgid: %m");
+}
+
+/*
* Reading the Configuration
*/
@@ -444,14 +471,16 @@ signal_init(void)
* Parsing of command-line arguments
*/
-static char *opt_list = "c:dD:ps:";
+static char *opt_list = "c:dD:ps:u:g:";
static int parse_and_exit;
char *bird_name;
+static char *use_user;
+static char *use_group;
static void
usage(void)
{
- fprintf(stderr, "Usage: %s [-c <config-file>] [-d] [-D <debug-file>] [-p] [-s <control-socket>]\n", bird_name);
+ fprintf(stderr, "Usage: %s [-c <config-file>] [-d] [-D <debug-file>] [-p] [-s <control-socket>] [-u <user>] [-g <group>]\n", bird_name);
exit(1);
}
@@ -469,6 +498,44 @@ get_bird_name(char *s, char *def)
return t+1;
}
+static inline uid_t
+get_uid(const char *s)
+{
+ struct passwd *pw;
+ char *endptr;
+
+ errno = 0;
+ long int rv = strtol(s, &endptr, 10);
+
+ if (!errno && !*endptr)
+ return rv;
+
+ pw = getpwnam(s);
+ if (!pw)
+ die("Cannot find user '%s'", s);
+
+ return pw->pw_uid;
+}
+
+static inline gid_t
+get_gid(const char *s)
+{
+ struct group *gr;
+ char *endptr;
+
+ errno = 0;
+ long int rv = strtol(s, &endptr, 10);
+
+ if (!errno && !*endptr)
+ return rv;
+
+ gr = getgrnam(s);
+ if (!gr)
+ die("Cannot find group '%s'", s);
+
+ return gr->gr_gid;
+}
+
static void
parse_args(int argc, char **argv)
{
@@ -504,6 +571,12 @@ parse_args(int argc, char **argv)
case 's':
path_control_socket = optarg;
break;
+ case 'u':
+ use_user = optarg;
+ break;
+ case 'g':
+ use_group = optarg;
+ break;
default:
usage();
}
@@ -528,6 +601,12 @@ main(int argc, char **argv)
log_init_debug("");
log_switch(debug_flag, NULL, NULL);
+ if (use_group)
+ drop_gid(get_gid(use_group));
+
+ if (use_user)
+ drop_uid(get_uid(use_user));
+
if (!parse_and_exit)
test_old_bird(path_control_socket);