redmine

Added cgroups support (to forbid access to extra devices)

... ... @@ -56,6 +56,10 @@ endif
if HAVE_TRE
clsync_CFLAGS += -DTRE_SUPPORT
endif
if HAVE_LIBCGROUP
clsync_CFLAGS += -DCGROUP_SUPPORT
clsync_SOURCES += cgroup.c cgroup.h
endif
if HLLOCKS
clsync_CFLAGS += -DHL_LOCKS
... ...
/*
clsync - file tree sync utility based on inotify/kqueue/bsm
Copyright (C) 2014 Dmitry Yu Okunev <dyokunev@ut.mephi.ru> 0x8E30679C
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "common.h"
#include "error.h"
#include <libcgroup.h>
static struct cgroup *cgroup = NULL;
int clsync_cgroup_init() {
char cgroup_name[BUFSIZ+1];
snprintf(cgroup_name, BUFSIZ, "clsync/%u", getpid());
debug(2, "cgroup_name == \"%s\"", cgroup_name);
SAFE( cgroup_init(), return -1; );
SAFE( (cgroup = cgroup_new_cgroup(cgroup_name)) == NULL, return -1; );
return 0;
}
int clsync_cgroup_forbid_extra_devices() {
int rc;
char *allowed_devices[] = CG_ALLOWED_DEVICES, **allowed_device_i;
/*
* Unfortunately, libcgroup doesn't allow multiple values for one key, and cgroups doesn't allow multiple devices for one set. So I was been have to write this hack. It adds character '/' to start of "devices.allow" for every new entry. So libclsync thinks that it's different keys, "/sys/fs/cgroup/devices/clsync/123/devices.allow" == "/sys/fs/cgroup/devices/clsync/123//devices.allow".
*/
char control_name_buf[BUFSIZ+BUFSIZ]={[0 ... BUFSIZ-1] = '/', 'd', 'e', 'v', 'i', 'c', 'e', 's', '.', 'a', 'l', 'l', 'o', 'w'}, *control_name = &control_name_buf[BUFSIZ];
debug(2, "");
struct cgroup_controller *cgc;
SAFE( (cgc = cgroup_add_controller(cgroup, "devices")) == NULL, return -1; );
debug(8, "Deny device: \"a\"");
SAFE( cgroup_add_value_string(cgc, "devices.deny", "a"), return -1; );
allowed_device_i = allowed_devices;
while (*allowed_device_i != NULL) {
critical_on (control_name < control_name_buf);
debug(8, "Allow device: \"%s\" (\"%s\" = \"%s\")", *allowed_device_i, control_name, *allowed_device_i);
SAFE( cgroup_add_value_string(cgc, control_name, *allowed_device_i),return -1; );
control_name--;
allowed_device_i++;
}
if ((rc=cgroup_create_cgroup(cgroup, 1))) {
error("Got error while cgroup_create_cgroup(): %s", cgroup_strerror(rc));
return -1;
}
return 0;
}
int clsync_cgroup_attach() {
int rc;
debug(2, "");
if ((rc=cgroup_attach_task_pid(cgroup, getpid()))) {
error("Got error while cgroup_attach_task_pid(): %s", cgroup_strerror(rc));
return -1;
}
return 0;
}
int clsync_cgroup_deinit() {
debug(2, "");
pid_t pid = getpid();
setuid(0);
error_on(cgroup_delete_cgroup_ext(cgroup, CGFLAG_DELETE_IGNORE_MIGRATION | CGFLAG_DELETE_RECURSIVE));
cgroup_free(&cgroup);
if (pid != 0)
setuid(pid);
return 0;
}
... ...
/*
clsync - file tree sync utility based on inotify/kqueue/bsm
Copyright (C) 2014 Dmitry Yu Okunev <dyokunev@ut.mephi.ru> 0x8E30679C
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
extern int clsync_cgroup_init();
extern int clsync_cgroup_forbid_extra_devices();
extern int clsync_cgroup_attach();
extern int clsync_cgroup_deinit();
... ...
... ... @@ -75,7 +75,7 @@
#define COUNTER_LIMIT (1<<10)
#define SLEEP_SECONDS 0
#define SLEEP_SECONDS 1
#define KILL_TIMEOUT 60
... ... @@ -180,3 +180,17 @@ filesz:1M\n\
//#define READWRITE_SIGNALLING
#define CG_DEV_CONSOLE "c 5:1"
#define CG_DEV_ZERO "c 1:5"
#define CG_DEV_RANDOM "c 1:8"
#define CG_DEV_URANDOM "c 1:9"
#define CG_DEV_NULL "c 1:3"
#define CG_ALLOWED_DEVICES { \
CG_DEV_CONSOLE " rw", \
CG_DEV_ZERO " r", \
CG_DEV_URANDOM " r", \
CG_DEV_RANDOM " r", \
CG_DEV_NULL " w", \
NULL \
}
... ...
... ... @@ -158,6 +158,41 @@ AC_CHECK_FUNC([pivot_root], [HAVE_PIVOTROOT=1])
dnl searching for unshare
AC_CHECK_FUNC([unshare], [HAVE_UNSHARE=1])
dnl libcgroup check
AC_ARG_WITH(libcgroup,
AS_HELP_STRING(--with-libcgroup,
[Enable cgroup support via libcgroup; values: no, check, yes; default: check]),
[],
[with_libcgroup=check]
)
case "$with_libcgroup" in
yes)
AC_CHECK_LIB([cgroup], [cgroup_init],
[
AC_CHECK_HEADER(libcgroup.h, [], [AC_MSG_FAILURE([Cannot find libcgroup.h])])
LDFLAGS="${LDFLAGS} -lcgroup"
HAVE_LIBCGROUP=1
],
[
AC_MSG_FAILURE(
[Cannot find libcgroup])
]
)
;;
check)
AC_CHECK_LIB([cgroup], [cgroup_init],
[
AC_CHECK_HEADER(libcgroup.h, [], [AC_MSG_FAILURE([Cannot find libcgroup.h])])
LDFLAGS="${LDFLAGS} -lcgroup"
HAVE_LIBCGROUP=1
],
[]
)
;;
esac
dnl capabilities check
AC_ARG_WITH(capabilities,
AS_HELP_STRING(--with-capabilities,
... ... @@ -384,6 +419,7 @@ AM_CONDITIONAL([HAVE_PIVOTROOT], [test "x$HAVE_PIVOTROOT" != "x"])
AM_CONDITIONAL([HAVE_UNSHARE], [test "x$HAVE_UNSHARE" != "x"])
AM_CONDITIONAL([HAVE_SECCOMP], [test "x$HAVE_SECCOMP" != "x"])
AM_CONDITIONAL([HAVE_TRE], [test "x$HAVE_TRE" != "x"])
AM_CONDITIONAL([HAVE_LIBCGROUP], [test "x$HAVE_LIBCGROUP" != "x"])
AS_IF([test "$HAVE_KQUEUE" = '' -a "$HAVE_INOTIFY" = '' -a "$HAVE_FANOTIFY" = '' -a "$HAVE_BSM" = '' ], [AC_MSG_FAILURE([kqueue, inotify and bsm are not supported on this system])])
... ...
... ... @@ -112,6 +112,7 @@ enum flags_enum {
FORGET_PRIVTHREAD_INFO = 36|OPTION_LONGOPTONLY,
SECURETHREADSPLITTING = 37|OPTION_LONGOPTONLY,
FTS_EXPERIMENTAL_OPTIMIZATION = 38|OPTION_LONGOPTONLY,
FORBIDDEVICES = 39|OPTION_LONGOPTONLY,
};
typedef enum flags_enum flags_t;
... ...
... ... @@ -34,6 +34,7 @@ extern void _critical( const char *const function_name, const char *fmt, ...);
extern void _error(const char *const function_name, const char *fmt, ...);
#define error(...) _error(__FUNCTION__, __VA_ARGS__)
#define error_on(cond) {if (cond) {error("Error: ("TOSTR(cond)") != 0");}}
extern void _warning(const char *const function_name, const char *fmt, ...);
#define warning(...) _warning(__FUNCTION__, __VA_ARGS__)
... ...
... ... @@ -48,6 +48,9 @@
#include "socket.h"
#include "syscalls.h"
#include "rules.h"
#if CGROUP_SUPPORT
# include "cgroup.h"
#endif
//#include "revision.h"
... ... @@ -103,6 +106,9 @@ static const struct option long_options[] =
{"preserve-capabilities",required_argument, NULL, CAP_PRESERVE},
{"inherit-capabilities",optional_argument, NULL, CAPS_INHERIT},
#endif
#ifdef CGROUP_SUPPORT
{"forbid-devices", optional_argument, NULL, FORBIDDEVICES},
#endif
{"threading", required_argument, NULL, THREADING},
{"retries", optional_argument, NULL, RETRIES},
{"ignore-failures", optional_argument, NULL, IGNOREFAILURES},
... ... @@ -2393,6 +2399,14 @@ int main(int argc, char *argv[]) {
if (ctx_p->destproto != NULL)
ctx_p->destdirwslash = ctx_p->destdir;
#ifdef CGROUP_SUPPORT
if (ctx_p->flags[FORBIDDEVICES]) {
error_on(clsync_cgroup_init());
error_on(clsync_cgroup_forbid_extra_devices());
error_on(clsync_cgroup_attach());
}
#endif
if (ctx_p->flags[GID]) {
int rc;
debug(3, "Trying to drop gid to %i", ctx_p->gid);
... ...
... ... @@ -1457,6 +1457,33 @@ on everything not listed above.
Is not set by default.
.RE
.B \-\-forbid\-devices
.RS
.B [Linux only]
Forbid any access to all devices except listed ones:
.RS
read access to:
.RS
/dev/console
.br
/dev/zero
.br
/dev/urandom
.br
/dev/random
.RE
write access to:
.RS
/dev/console
.br
/dev/null
.RE
.RE
Is not set by default.
.RE
.SH SYNC HANDLER MODES
.B clsync
executes
... ...
... ... @@ -31,7 +31,10 @@
# include <fts.h> // fts_open()
# include <errno.h> // errno
# include <sys/capability.h> // capset()
# include "malloc.h" // strdup_protect();
# include "malloc.h" // strdup_protect()
# ifdef CGROUP_SUPPORT
# include "cgroup.h" // clsync_cgroup_deinit()
# endif
#endif
#include <unistd.h> // execvp()
... ... @@ -159,6 +162,8 @@ enum privileged_action {
PA_FORK_EXECVP,
PA_KILL_CHILD,
PA_CLSYNC_CGROUP_DEINIT,
};
# ifdef HL_LOCKS
... ... @@ -279,6 +284,8 @@ int (*_privileged_inotify_rm_watch) (
int wd
);
int (*_privileged_clsync_cgroup_deinit) ();
int cap_enable(__u32 caps) {
debug(1, "Enabling Linux capabilities 0x%x", caps);
... ... @@ -801,6 +808,12 @@ void *privileged_handler(void *_ctx_p)
cmd.ret = (void *)(long)__privileged_kill_child_itself(arg_p->pid, arg_p->signal);
break;
}
# ifdef CGROUP_SUPPORT
case PA_CLSYNC_CGROUP_DEINIT: {
cmd.ret = (void *)(long)clsync_cgroup_deinit();
break;
}
# endif
default:
critical("Unknown command type \"%u\". It's a buffer overflow (which means a security problem) or just an internal error.");
}
... ... @@ -1130,6 +1143,25 @@ int __privileged_inotify_rm_watch(
return (long)ret;
}
# ifdef CGROUP_SUPPORT
int __privileged_clsync_cgroup_deinit()
{
void *ret = (void *)(long)-1;
privileged_action(
# ifdef HL_LOCK_TRIES_AUTO
PC_DEFAULT,
# endif
PA_CLSYNC_CGROUP_DEINIT,
NULL,
&ret
);
return (long)ret;
}
# endif
int __privileged_fork_setuid_execvp(
const char *file,
char *const argv[]
... ... @@ -1223,6 +1255,9 @@ int privileged_init(ctx_t *ctx_p)
_privileged_inotify_init1 = (typeof(_privileged_inotify_init1)) inotify_init1;
_privileged_inotify_add_watch = (typeof(_privileged_inotify_add_watch)) inotify_add_watch;
_privileged_inotify_rm_watch = (typeof(_privileged_inotify_rm_watch)) inotify_rm_watch;
# ifdef CGROUP_SUPPORT
_privileged_clsync_cgroup_deinit= (typeof(_privileged_clsync_cgroup_deinit)) clsync_cgroup_deinit;
# endif
cap_drop(ctx_p, ctx_p->caps);
#endif
... ... @@ -1241,6 +1276,7 @@ int privileged_init(ctx_t *ctx_p)
_privileged_inotify_rm_watch = __privileged_inotify_rm_watch;
_privileged_fork_execvp = __privileged_fork_setuid_execvp;
_privileged_kill_child = __privileged_kill_child_wrapper;
_privileged_clsync_cgroup_deinit= __privileged_clsync_cgroup_deinit;
SAFE ( pthread_mutex_init(&pthread_mutex_privileged, NULL), return errno;);
SAFE ( pthread_mutex_init(&pthread_mutex_action_entrance,NULL), return errno;);
... ...
... ... @@ -78,6 +78,10 @@ extern int (*_privileged_inotify_rm_watch) (
int wd
);
#ifdef CGROUP_SUPPORT
extern int (*_privileged_clsync_cgroup_deinit) ();
#endif
# ifdef HL_LOCK_TRIES_AUTO
# define privileged_fts_open(a,b,c,d) _privileged_fts_open(a,b,c,d)
# define privileged_fts_read(a,b) _privileged_fts_read(a,b)
... ... @@ -93,6 +97,7 @@ extern int (*_privileged_inotify_rm_watch) (
# define privileged_inotify_init _privileged_inotify_init
# define privileged_inotify_init1 _privileged_inotify_init1
# define privileged_inotify_rm_watch _privileged_inotify_rm_watch
# define privileged_clsync_cgroup_deinit _privileged_clsync_cgroup_deinit
#else
... ... @@ -103,7 +108,9 @@ extern int (*_privileged_inotify_rm_watch) (
# define privileged_inotify_init1 inotify_init1
# define privileged_inotify_add_watch(a,b,c,d) inotify_add_watch(a,b,c)
# define privileged_inotify_rm_watch inotify_rm_watch
# ifdef CGROUP_SUPPORT
# define privileged_clsync_cgroup_deinit clsync_cgroup_deinit
# endif
#endif
extern int (*_privileged_kill_child)(
... ...
... ... @@ -46,6 +46,9 @@
#include "indexes.h"
#include "privileged.h"
#include "rules.h"
#if CGROUP_SUPPORT
# include "cgroup.h"
#endif
#include <stdio.h>
#include <dlfcn.h>
... ... @@ -1356,7 +1359,7 @@ int sync_initialsync_walk(ctx_t *ctx_p, const char *dirpath, indexes_t *indexes_
switch (ctx_p->flags[MODE]) {
case MODE_SIMPLE:
SAFE(sync_dosync(node->fts_path, evinfo.evmask, ctx_p, indexes_p), critical("fpath == \"%s\"; evmask == 0x%o", node->fts_path, evinfo.evmask));
SAFE(sync_dosync(node->fts_path, evinfo.evmask, ctx_p, indexes_p), debug(1, "fpath == \"%s\"; evmask == 0x%o", node->fts_path, evinfo.evmask); return -1;);
continue;
default:
break;
... ... @@ -1981,7 +1984,7 @@ int sync_prequeue_loadmark
switch (ctx_p->flags[MODE]) {
case MODE_SIMPLE:
return SAFE(sync_dosync(path_rel, event_mask, ctx_p, indexes_p), critical("fpath == \"%s\"; evmask == 0x%o", path_rel, event_mask));
return SAFE(sync_dosync(path_rel, event_mask, ctx_p, indexes_p), debug(1, "fpath == \"%s\"; evmask == 0x%o", path_rel, event_mask); return -1;);
default:
break;
}
... ... @@ -2136,7 +2139,7 @@ void _sync_idle_dosync_collectedevents(gpointer fpath_gp, gpointer evinfo_gp, gp
if ((ctx_p->listoutdir == NULL) && (!(ctx_p->synchandler_argf & SHFL_INCLUDE_LIST)) && (!(ctx_p->flags[MODE]==MODE_SO))) {
debug(3, "calling sync_dosync()");
SAFE(sync_dosync(fpath, evinfo->evmask, ctx_p, indexes_p), critical("fpath == \"%s\"; evmask == 0x%o", fpath, evinfo->evmask));
SAFE(sync_dosync(fpath, evinfo->evmask, ctx_p, indexes_p), debug(1, "fpath == \"%s\"; evmask == 0x%o", fpath, evinfo->evmask); exit(errno ? errno : -1)); // TODO: remove exit() from here
return;
}
... ... @@ -3878,6 +3881,12 @@ int sync_run(ctx_t *ctx_p) {
exec_argv(argv, NULL);
}
#ifdef CGROUP_SUPPORT
// Cleaning up cgroups staff
if (ctx_p->flags[FORBIDDEVICES])
error_on(privileged_clsync_cgroup_deinit());
#endif
ret |= privileged_deinit(ctx_p);
return ret;
... ...