Allows run with restricted privileges.

Adds option -u and -g to specify user and group.
When different user (than root) is specified,
linux capabilities CAP_NET_* are kept.
This commit is contained in:
Ondrej Zajicek 2011-05-10 02:42:17 +02:00
parent 46bb7e0d17
commit 1bc2695744
6 changed files with 150 additions and 2 deletions

View file

@ -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_MULTIPLE_TABLES The kernel supports multiple routing tables
CONFIG_ALL_TABLES_AT_ONCE Kernel scanner wants to process all tables at once 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_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_IFACE Use Unix interface scanner
CONFIG_UNIX_SET Use Unix route setting 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_MREQN Linux: Use struct mreqn for multicasting
CONFIG_LINUX_MC_MREQ Linux: Use struct mreq CONFIG_LINUX_MC_MREQ Linux: Use struct mreq
CONFIG_LINUX_MC_MREQ_BIND Linux: Use struct mreq and SO_BINDTODEVICE CONFIG_LINUX_MC_MREQ_BIND Linux: Use struct mreq and SO_BINDTODEVICE

View file

@ -17,6 +17,8 @@
#define CONFIG_LINUX_MC_MREQN #define CONFIG_LINUX_MC_MREQN
#define CONFIG_UNIX_DONTROUTE #define CONFIG_UNIX_DONTROUTE
#define CONFIG_RESTRICTED_PRIVILEGES
/* /*
Link: sysdep/linux/netlink Link: sysdep/linux/netlink
Link: sysdep/linux Link: sysdep/linux

View file

@ -19,6 +19,8 @@
#define CONFIG_MULTIPLE_TABLES #define CONFIG_MULTIPLE_TABLES
#define CONFIG_ALL_TABLES_AT_ONCE #define CONFIG_ALL_TABLES_AT_ONCE
#define CONFIG_RESTRICTED_PRIVILEGES
/* /*
Link: sysdep/linux/netlink Link: sysdep/linux/netlink
Link: sysdep/linux Link: sysdep/linux

View file

@ -3,3 +3,4 @@ krt-scan.c
krt-scan.h krt-scan.h
#endif #endif
sysio.h sysio.h
syspriv.h

62
sysdep/linux/syspriv.h Normal file
View file

@ -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");
}

View file

@ -8,11 +8,15 @@
#undef LOCAL_DEBUG #undef LOCAL_DEBUG
#define _GNU_SOURCE 1
#include <stdio.h> #include <stdio.h>
#include <stdlib.h> #include <stdlib.h>
#include <fcntl.h> #include <fcntl.h>
#include <unistd.h> #include <unistd.h>
#include <signal.h> #include <signal.h>
#include <pwd.h>
#include <grp.h>
#include "nest/bird.h" #include "nest/bird.h"
#include "lib/lists.h" #include "lib/lists.h"
@ -58,6 +62,29 @@ async_dump(void)
debug("\n"); debug("\n");
} }
/*
* 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 * Reading the Configuration
*/ */
@ -444,14 +471,16 @@ signal_init(void)
* Parsing of command-line arguments * 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; static int parse_and_exit;
char *bird_name; char *bird_name;
static char *use_user;
static char *use_group;
static void static void
usage(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); exit(1);
} }
@ -469,6 +498,44 @@ get_bird_name(char *s, char *def)
return t+1; 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 static void
parse_args(int argc, char **argv) parse_args(int argc, char **argv)
{ {
@ -504,6 +571,12 @@ parse_args(int argc, char **argv)
case 's': case 's':
path_control_socket = optarg; path_control_socket = optarg;
break; break;
case 'u':
use_user = optarg;
break;
case 'g':
use_group = optarg;
break;
default: default:
usage(); usage();
} }
@ -528,6 +601,12 @@ main(int argc, char **argv)
log_init_debug(""); log_init_debug("");
log_switch(debug_flag, NULL, NULL); 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) if (!parse_and_exit)
test_old_bird(path_control_socket); test_old_bird(path_control_socket);