summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMatthias Schiffer <mschiffer@universe-factory.net>2012-12-24 23:52:18 +0100
committerMatthias Schiffer <mschiffer@universe-factory.net>2012-12-24 23:52:18 +0100
commit78440eab81959ec7a95effd579fd87b7c56dbe3d (patch)
tree23a962d528fa2ac50b7c4fba92c36a63df25b479
parenteaac49427339a365aac2d3505f567572cfbdbb96 (diff)
downloadfastd-78440eab81959ec7a95effd579fd87b7c56dbe3d.tar
fastd-78440eab81959ec7a95effd579fd87b7c56dbe3d.zip
Add user switching and capability support
-rw-r--r--src/CMakeLists.txt1
-rw-r--r--src/capabilities.c114
-rw-r--r--src/config.c70
-rw-r--r--src/config.l5
-rw-r--r--src/config.y42
-rw-r--r--src/fastd.c68
-rw-r--r--src/fastd.h16
-rw-r--r--src/types.h6
8 files changed, 317 insertions, 5 deletions
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index 0fb0ebd..ccee0d9 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -16,6 +16,7 @@ BISON_TARGET(fastd_config_parse config.y ${CMAKE_CURRENT_BINARY_DIR}/config.yy.c
add_executable(fastd
fastd.c
+ capabilities.c
config.c
crypto.c
crypto_linux.c
diff --git a/src/capabilities.c b/src/capabilities.c
new file mode 100644
index 0000000..415cce6
--- /dev/null
+++ b/src/capabilities.c
@@ -0,0 +1,114 @@
+/*
+ Copyright (c) 2012, Matthias Schiffer <mschiffer@universe-factory.net>
+ All rights reserved.
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions are met:
+
+ 1. Redistributions of source code must retain the above copyright notice,
+ this list of conditions and the following disclaimer.
+ 2. Redistributions in binary form must reproduce the above copyright notice,
+ this list of conditions and the following disclaimer in the documentation
+ and/or other materials provided with the distribution.
+
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+ FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+
+#include "fastd.h"
+
+#ifdef WITH_CAPABILITIES
+
+#include <linux/securebits.h>
+
+#include <sys/capability.h>
+#include <sys/prctl.h>
+
+
+static void try_cap(fastd_context_t *ctx, cap_value_t cap) {
+ char *name = cap_to_name(cap);
+
+ if (!name)
+ return;
+
+ pr_debug(ctx, "Trying to acquire %s", name);
+
+ cap_t caps = cap_get_proc();
+
+ if (cap_set_flag(caps, CAP_EFFECTIVE, 1, &cap, CAP_SET) < 0) {
+ pr_debug_errno(ctx, "cap_set_flags");
+ goto end_free;
+ }
+
+ if (cap_set_proc(caps) < 0) {
+ pr_debug_errno(ctx, "cap_set_proc");
+ goto end_free;
+ }
+
+ pr_verbose(ctx, "Acquired capability %s.", name);
+
+ end_free:
+ cap_free(caps);
+ cap_free(name);
+}
+
+void fastd_cap_init(fastd_context_t *ctx) {
+ /* interface creation */
+ try_cap(ctx, CAP_NET_ADMIN);
+
+ /* privileged binds */
+ try_cap(ctx, CAP_NET_BIND_SERVICE);
+
+ /* for device binds */
+ try_cap(ctx, CAP_NET_RAW);
+}
+
+void fastd_cap_lock(fastd_context_t *ctx) {
+ if (prctl(PR_SET_SECUREBITS,
+ SECBIT_KEEP_CAPS_LOCKED |
+ SECBIT_NO_SETUID_FIXUP |
+ SECBIT_NO_SETUID_FIXUP_LOCKED |
+ SECBIT_NOROOT |
+ SECBIT_NOROOT_LOCKED) < 0) {
+ pr_debug_errno(ctx, "prctl");
+ }
+}
+
+void fastd_cap_drop(fastd_context_t *ctx) {
+ cap_t caps = cap_init();
+
+ if (cap_set_proc(caps) < 0) {
+ pr_debug_errno(ctx, "cap_set_proc");
+ }
+ else {
+ pr_verbose(ctx, "Dropped capabilities.");
+ }
+
+ cap_free(caps);
+
+}
+
+
+#else /* WITH_CAPABILITIES */
+
+void fastd_cap_init(fastd_context_t *ctx) {
+}
+
+void fastd_cap_lock(fastd_context_t *ctx) {
+}
+
+void fastd_cap_drop(fastd_context_t *ctx) {
+}
+
+#endif /* WITH_CAPABILITIES */
+
+
diff --git a/src/config.c b/src/config.c
index 57059e5..2d387f3 100644
--- a/src/config.c
+++ b/src/config.c
@@ -31,11 +31,15 @@
#include <config.ll.h>
#include <config.yy.h>
-#include <arpa/inet.h>
#include <dirent.h>
+#include <grp.h>
#include <libgen.h>
+#include <pwd.h>
#include <stdarg.h>
#include <strings.h>
+
+#include <arpa/inet.h>
+
#include <sys/stat.h>
#include <sys/types.h>
@@ -104,6 +108,8 @@ static void default_config(fastd_config_t *conf) {
conf->mtu = 1500;
conf->mode = MODE_TAP;
+ conf->drop_caps = DROP_CAPS_ON;
+
conf->protocol = &fastd_protocol_ec25519_fhmqvc;
conf->method_default = &fastd_method_null;
conf->key_valid = 3600; /* 60 minutes */
@@ -522,6 +528,8 @@ static void count_peers(fastd_context_t *ctx, fastd_config_t *conf) {
OPTION(usage, "--help" OR "-h", "Shows this help text") \
OPTION(version, "--version" OR "-v", "Shows the fastd version") \
OPTION(option_daemon, "--daemon" OR "-d", "Runs fastd in the background") \
+ OPTION_ARG(option_user, "--user", "<user>", "Sets the user to run fastd as") \
+ OPTION_ARG(option_group, "--group", "<group>", "Sets the group to run fastd as") \
OPTION_ARG(option_pid_file, "--pid-file", "<filename>", "Writes fastd's PID to the specified file") \
OPTION_ARG(option_log_level, "--log-level", "error|warn|info|verbose|debug", "Sets the stderr log level; default is info, if no alternative log destination ist configured") \
OPTION_ARG(option_syslog_level, "--syslog-level", "error|warn|info|verbose|debug", "Sets the log level for syslog output; default is not to use syslog") \
@@ -600,6 +608,16 @@ static int parse_log_level(fastd_context_t *ctx, const char *arg) {
+static void option_user(fastd_context_t *ctx, fastd_config_t *conf, const char *arg) {
+ free(conf->user);
+ conf->user = strdup(arg);
+}
+
+static void option_group(fastd_context_t *ctx, fastd_config_t *conf, const char *arg) {
+ free(conf->group);
+ conf->group = strdup(arg);
+}
+
static void option_log_level(fastd_context_t *ctx, fastd_config_t *conf, const char *arg) {
conf->log_stderr_level = parse_log_level(ctx, arg);
}
@@ -786,6 +804,52 @@ static void option_machine_readable(fastd_context_t *ctx, fastd_config_t *conf)
}
+static void configure_user(fastd_context_t *ctx, fastd_config_t *conf) {
+ conf->uid = getuid();
+ conf->gid = getgid();
+
+ if (conf->user) {
+ struct passwd pwd, *pwdr;
+ size_t bufspace = 1024;
+ int error;
+
+ do {
+ char buf[bufspace];
+ error = getpwnam_r(conf->user, &pwd, buf, bufspace, &pwdr);
+ bufspace *= 2;
+ } while(error == ERANGE);
+
+ if (error)
+ exit_errno(ctx, "getpwnam_r");
+
+ if (!pwdr)
+ exit_error(ctx, "Unable to find user `%s'.", conf->user);
+
+ conf->uid = pwdr->pw_uid;
+ conf->gid = pwdr->pw_gid;
+ }
+
+ if (conf->group) {
+ struct group grp, *grpr;
+ size_t bufspace = 1024;
+ int error;
+
+ do {
+ char buf[bufspace];
+ error = getgrnam_r(conf->group, &grp, buf, bufspace, &grpr);
+ bufspace *= 2;
+ } while(error == ERANGE);
+
+ if (error)
+ exit_errno(ctx, "getgrnam_r");
+
+ if (!grpr)
+ exit_error(ctx, "Unable to find group `%s'.", conf->group);
+
+ conf->gid = grpr->gr_gid;
+ }
+}
+
void fastd_configure(fastd_context_t *ctx, fastd_config_t *conf, int argc, char *const argv[]) {
#define OR ,
#define OPTION(func, options, message) \
@@ -831,6 +895,8 @@ void fastd_configure(fastd_context_t *ctx, fastd_config_t *conf, int argc, char
if (!conf->peers && !has_peer_group_peer_dirs(conf->peer_group))
exit_error(ctx, "config error: neither fixed peers nor peer dirs have been configured");
+ configure_user(ctx, conf);
+
count_peers(ctx, conf);
#undef OR
@@ -968,6 +1034,8 @@ void fastd_config_release(fastd_context_t *ctx, fastd_config_t *conf) {
free_peer_group(conf->peer_group);
+ free(conf->user);
+ free(conf->group);
free(conf->ifname);
free(conf->secret);
free(conf->on_up);
diff --git a/src/config.l b/src/config.l
index afe37dd..1881575 100644
--- a/src/config.l
+++ b/src/config.l
@@ -100,7 +100,12 @@ float { TOKEN(TOK_FLOAT); }
crypto { TOKEN(TOK_CRYPTO); }
use { TOKEN(TOK_USE); }
default { TOKEN(TOK_DEFAULT); }
+user { TOKEN(TOK_USER); }
group { TOKEN(TOK_GROUP); }
+drop { TOKEN(TOK_DROP); }
+capabilities { TOKEN(TOK_CAPABILITIES); }
+early { TOKEN(TOK_EARLY); }
+lock { TOKEN(TOK_LOCK); }
limit { TOKEN(TOK_LIMIT); }
[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3} {
diff --git a/src/config.y b/src/config.y
index d91dd61..5794124 100644
--- a/src/config.y
+++ b/src/config.y
@@ -100,7 +100,12 @@
%token TOK_CRYPTO
%token TOK_USE
%token TOK_DEFAULT
+%token TOK_USER
%token TOK_GROUP
+%token TOK_DROP
+%token TOK_CAPABILITIES
+%token TOK_EARLY
+%token TOK_LOCK
%token TOK_LIMIT
%token <addr4> TOK_ADDR4
@@ -129,6 +134,8 @@
%type <str> maybe_bind_interface
%type <num> maybe_bind_default
%type <num> bind_default
+%type <num> drop_capabilities_enabled
+%type <boolean> drop_capabilities_lock
%%
start: START_CONFIG config
@@ -146,6 +153,9 @@ peer_group_config:
;
statement: peer_group_statement
+ | TOK_USER user ';'
+ | TOK_GROUP group ';'
+ | TOK_DROP TOK_CAPABILITIES drop_capabilities ';'
| TOK_LOG log ';'
| TOK_INTERFACE interface ';'
| TOK_BIND bind ';'
@@ -169,6 +179,38 @@ peer_group_statement:
| TOK_INCLUDE include ';'
;
+user: TOK_STRING {
+ free(conf->user);
+ conf->user = strdup($1->str);
+ }
+
+group: TOK_STRING {
+ free(conf->group);
+ conf->group = strdup($1->str);
+ }
+
+drop_capabilities:
+ drop_capabilities_enabled drop_capabilities_lock {
+ conf->drop_caps = $1;
+ conf->lock_caps = $2;
+ }
+
+drop_capabilities_enabled:
+ TOK_EARLY {
+ $$ = DROP_CAPS_EARLY;
+ }
+ | boolean {
+ $$ = $1 ? DROP_CAPS_ON : DROP_CAPS_OFF;
+ }
+
+drop_capabilities_lock:
+ TOK_LOCK {
+ $$ = true;
+ }
+ | {
+ $$ = false;
+ }
+
log: TOK_LEVEL log_level {
conf->log_stderr_level = $2;
}
diff --git a/src/fastd.c b/src/fastd.c
index 18a0c91..b7a7a83 100644
--- a/src/fastd.c
+++ b/src/fastd.c
@@ -33,6 +33,7 @@
#include "task.h"
#include <fcntl.h>
+#include <grp.h>
#include <linux/if_tun.h>
#include <net/if.h>
#include <poll.h>
@@ -94,6 +95,15 @@ static void init_pipes(fastd_context_t *ctx) {
}
static void init_log(fastd_context_t *ctx) {
+ uid_t uid = geteuid();
+ gid_t gid = getegid();
+
+ if (ctx->conf->user || ctx->conf->group) {
+ /* We don't care about errors here */
+ setegid(ctx->conf->gid);
+ seteuid(ctx->conf->uid);
+ }
+
if (ctx->conf->log_syslog_level >= 0)
openlog(ctx->conf->log_syslog_ident, LOG_PID, LOG_DAEMON);
@@ -107,6 +117,9 @@ static void init_log(fastd_context_t *ctx) {
file->next = ctx->log_files;
ctx->log_files = file;
}
+
+ seteuid(uid);
+ setegid(gid);
}
static void close_log(fastd_context_t *ctx) {
@@ -993,10 +1006,19 @@ static void write_pid(fastd_context_t *ctx, pid_t pid) {
if (!ctx->conf->pid_file)
return;
+ uid_t uid = geteuid();
+ gid_t gid = getegid();
+
+ if (ctx->conf->user || ctx->conf->group) {
+ /* We don't care about errors here */
+ setegid(ctx->conf->gid);
+ seteuid(ctx->conf->uid);
+ }
+
int fd = open(ctx->conf->pid_file, O_WRONLY|O_CREAT|O_TRUNC, 0666);
if (fd < 0) {
pr_error_errno(ctx, "can't write PID file: open");
- return;
+ goto end;
}
if (dprintf(fd, "%i", pid) < 0)
@@ -1004,6 +1026,36 @@ static void write_pid(fastd_context_t *ctx, pid_t pid) {
if (close(fd) < 0)
pr_warn_errno(ctx, "close");
+
+ end:
+ seteuid(uid);
+ setegid(gid);
+}
+
+static void set_user(fastd_context_t *ctx) {
+ if (ctx->conf->user || ctx->conf->group) {
+ if (setgid(ctx->conf->gid) < 0)
+ exit_errno(ctx, "setgid");
+
+ if (setgroups(1, &ctx->conf->gid) < 0) {
+ if (errno != EPERM)
+ pr_debug_errno(ctx, "setgroups");
+ }
+
+ if (setuid(ctx->conf->uid) < 0)
+ exit_errno(ctx, "setuid");
+
+ pr_info(ctx, "Changed to UID %i, GID %i.", ctx->conf->uid, ctx->conf->gid);
+ }
+}
+
+static void drop_caps(fastd_context_t *ctx) {
+ if (ctx->conf->lock_caps)
+ fastd_cap_lock(ctx);
+
+ set_user(ctx);
+
+ fastd_cap_drop(ctx);
}
int main(int argc, char *argv[]) {
@@ -1023,8 +1075,6 @@ int main(int argc, char *argv[]) {
init_log(&ctx);
- crypto_init(&ctx);
-
if (conf.generate_key) {
conf.protocol->generate_key(&ctx);
exit(0);
@@ -1041,6 +1091,10 @@ int main(int argc, char *argv[]) {
pr_info(&ctx, "fastd " FASTD_VERSION " starting");
+ fastd_cap_init(&ctx);
+
+ crypto_init(&ctx);
+
init_sockets(&ctx);
bind_sockets(&ctx);
@@ -1066,8 +1120,16 @@ int main(int argc, char *argv[]) {
write_pid(&ctx, getpid());
}
+ if (conf.drop_caps == DROP_CAPS_EARLY)
+ drop_caps(&ctx);
+
on_up(&ctx);
+ if (conf.drop_caps == DROP_CAPS_ON)
+ drop_caps(&ctx);
+ else if (conf.drop_caps == DROP_CAPS_OFF)
+ set_user(&ctx);
+
while (!terminate) {
handle_tasks(&ctx);
diff --git a/src/fastd.h b/src/fastd.h
index fd2a668..dc24579 100644
--- a/src/fastd.h
+++ b/src/fastd.h
@@ -195,6 +195,14 @@ struct fastd_config {
bool forward;
+ fastd_drop_caps_t drop_caps;
+ bool lock_caps;
+
+ char *user;
+ char *group;
+ uid_t uid;
+ gid_t gid;
+
const fastd_protocol_t *protocol;
const fastd_method_t *methods[MAX_METHODS];
const fastd_method_t *method_default;
@@ -316,6 +324,10 @@ void fastd_config_release(fastd_context_t *ctx, fastd_config_t *conf);
void fastd_configure(fastd_context_t *ctx, fastd_config_t *conf, int argc, char *const argv[]);
void fastd_reconfigure(fastd_context_t *ctx, fastd_config_t *conf);
+void fastd_cap_init(fastd_context_t *ctx);
+void fastd_cap_lock(fastd_context_t *ctx);
+void fastd_cap_drop(fastd_context_t *ctx);
+
void fastd_random_bytes(fastd_context_t *ctx, void *buffer, size_t len, bool secure);
static inline int fastd_rand(fastd_context_t *ctx, int min, int max) {
@@ -334,8 +346,10 @@ static inline int fastd_rand(fastd_context_t *ctx, int min, int max) {
#define pr_verbose(ctx, args...) fastd_logf(ctx, LOG_INFO, args)
#define pr_debug(ctx, args...) fastd_logf(ctx, LOG_DEBUG, args)
+#define pr_error_errno(ctx, message) pr_error(ctx, "%s: %s", message, strerror(errno))
#define pr_warn_errno(ctx, message) pr_warn(ctx, "%s: %s", message, strerror(errno))
-#define pr_error_errno(ctx, message) pr_warn(ctx, "%s: %s", message, strerror(errno))
+#define pr_debug_errno(ctx, message) pr_debug(ctx, "%s: %s", message, strerror(errno))
+
#define exit_fatal(ctx, args...) do { pr_fatal(ctx, args); abort(); } while(0)
#define exit_bug(ctx, message) exit_fatal(ctx, "BUG: %s", message)
#define exit_error(ctx, args...) do { pr_error(ctx, args); exit(1); } while(0)
diff --git a/src/types.h b/src/types.h
index cda16ec..a4da2b8 100644
--- a/src/types.h
+++ b/src/types.h
@@ -41,6 +41,12 @@ typedef enum fastd_mode {
MODE_TUN,
} fastd_mode_t;
+typedef enum fastd_drop_caps {
+ DROP_CAPS_OFF,
+ DROP_CAPS_ON,
+ DROP_CAPS_EARLY,
+} fastd_drop_caps_t;
+
typedef struct fastd_buffer fastd_buffer_t;
typedef union fastd_peer_address fastd_peer_address_t;