redmine

Capabilities fixes:

* Added option "--inherit-capabilities"
* setuid()/setgid() to original user (not always to root)
* Fixed building without capabilities support
* Try "nobody"/"nogroup" before "65534" on default values
... ... @@ -5,9 +5,9 @@ if CLSYNC
bin_PROGRAMS = clsync
clsync_SOURCES = calc.c cluster.c error.c fileutils.c glibex.c \
indexes.c main.c malloc.c stringex.c sync.c calc.h cluster.h \
fileutils.h glibex.h main.h port-hacks.h stringex.h sync.h \
common.h control.h privileged.h
indexes.c main.c malloc.c stringex.c sync.c privileged.c calc.h \
cluster.h fileutils.h glibex.h main.h port-hacks.h stringex.h \
sync.h common.h control.h privileged.h
clsync_CFLAGS = $(AM_CFLAGS)
... ... @@ -39,7 +39,6 @@ clsync_CFLAGS += -DBACKTRACE_SUPPORT
endif
if HAVE_CAPABILITIES
clsync_CFLAGS += -DCAPABILITIES_SUPPORT
clsync_SOURCES += privileged.c
endif
if HAVE_GETMNTENT
clsync_CFLAGS += -DGETMNTENT_SUPPORT
... ...
... ... @@ -147,9 +147,9 @@ filesz:1M\n\
#define DEFAULT_PRESERVE_CAPABILITIES ( CAP_TO_MASK(CAP_DAC_READ_SEARCH) | CAP_TO_MASK(CAP_SETUID) | CAP_TO_MASK(CAP_SETGID) )
#define DEFAULT_UID 65535
#define DEFAULT_GID 65535
#define DEFAULT_SYNCHANDLER_UID 0
#define DEFAULT_SYNCHANDLER_GID 0
#define DEFAULT_USER "nobody"
#define DEFAULT_GROUP "nogroup"
#define DEFAULT_UID 65534
#define DEFAULT_GID 65534
#define DEFAULT_CAPS_INHERIT CI_EMPTY
... ...
... ... @@ -49,7 +49,6 @@ enum flags_enum {
OUTPUT_METHOD = 'Y',
EXCLUDEMOUNTPOINTS= 'X',
PIDFILE = 'z',
#ifdef CLUSTER_SUPPORT
CLUSTERIFACE = 'c',
CLUSTERMCASTIPADDR='m',
CLUSTERMCASTIPPORT='P',
... ... @@ -57,8 +56,6 @@ enum flags_enum {
CLUSTERNODENAME = 'n',
CLUSTERHDLMIN = 'o',
CLUSTERHDLMAX = 'O',
CLUSTERSDLMAX = 11|OPTION_LONGOPTONLY,
#endif
DELAY = 't',
BFILEDELAY = 'T',
SYNCDELAY = 'w',
... ... @@ -87,36 +84,36 @@ enum flags_enum {
EXITONNOEVENTS = 8|OPTION_LONGOPTONLY,
STANDBYFILE = 9|OPTION_LONGOPTONLY,
EXITHOOK = 10|OPTION_LONGOPTONLY,
CLUSTERSDLMAX = 11|OPTION_LONGOPTONLY,
PREEXITHOOK = 12|OPTION_LONGOPTONLY,
SOCKETAUTH = 13|OPTION_LONGOPTONLY,
SOCKETMOD = 14|OPTION_LONGOPTONLY,
SOCKETOWN = 15|OPTION_LONGOPTONLY,
MAXITERATIONS = 16|OPTION_LONGOPTONLY,
IGNOREFAILURES = 17|OPTION_LONGOPTONLY,
DUMPDIR = 18|OPTION_LONGOPTONLY,
CONFIGBLOCKINHERITS = 19|OPTION_LONGOPTONLY,
MONITOR = 20|OPTION_LONGOPTONLY,
SYNCHANDLERARGS0 = 21|OPTION_LONGOPTONLY,
SYNCHANDLERARGS1 = 22|OPTION_LONGOPTONLY,
CUSTOMSIGNALS = 23|OPTION_LONGOPTONLY,
CHROOT = 24|OPTION_LONGOPTONLY,
#ifdef GETMNTENT_SUPPORT
MOUNTPOINTS = 25|OPTION_LONGOPTONLY,
#endif
NOTHREADSPLITTING = 26|OPTION_LONGOPTONLY,
SYNCHANDLERUID = 27|OPTION_LONGOPTONLY,
SYNCHANDLERGID = 28|OPTION_LONGOPTONLY,
CAPS_INHERIT = 29|OPTION_LONGOPTONLY,
};
typedef enum flags_enum flags_t;
enum capsinherit {
CI_DONTTOUCH = 0,
CI_PERMITTED,
CI_CLSYNC,
CI_EMPTY,
};
typedef enum capsinherit capsinherit_t;
enum mode_id {
MODE_UNSET = 0,
MODE_SIMPLE,
... ... @@ -236,9 +233,9 @@ struct ctx {
size_t pid_str_len;
uid_t uid;
gid_t gid;
#ifdef CAPABILITIES_SUPPORT
uid_t synchandler_uid;
gid_t synchandler_gid;
#ifdef CAPABILITIES_SUPPORT
__u32 caps;
#endif
pid_t child_pid[MAXCHILDREN]; // Used only for non-pthread mode
... ...
... ... @@ -78,6 +78,7 @@ static const struct option long_options[] =
#endif
#ifdef CAPABILITIES_SUPPORT
{"preserve-capabilities",required_argument, NULL, CAP_PRESERVE},
{"inherit-capabilities",optional_argument, NULL, CAPS_INHERIT},
#endif
{"threading", required_argument, NULL, THREADING},
{"retries", optional_argument, NULL, RETRIES},
... ... @@ -151,6 +152,14 @@ static char *const capabilities[] = {
NULL
};
#define XCAP_TO_CAP(x) (xcap_to_cap[x])
static char *const capsinherits[] = {
[CI_PERMITTED] = "permittied",
[CI_DONTTOUCH] = "dont-touch",
[CI_CLSYNC] = "clsync",
[CI_EMPTY] = "empty",
};
#endif
static char *const socketauth[] = {
... ... @@ -612,7 +621,7 @@ int parse_parameter(ctx_t *ctx_p, uint16_t param_id, char *arg, paramsource_t pa
ctx_p->flags_values_raw[param_id] = arg;
}
switch(param_id) {
switch (param_id) {
case '?':
case HELP:
syntax();
... ... @@ -698,6 +707,24 @@ int parse_parameter(ctx_t *ctx_p, uint16_t param_id, char *arg, paramsource_t pa
break;
}
case CAPS_INHERIT: {
char *value, *arg_orig = arg;
if (!*arg) {
ctx_p->flags_set[param_id] = 0;
return 0;
}
capsinherit_t capsinherit = getsubopt(&arg, capsinherits, &value);
if((int)capsinherit == -1) {
errno = EINVAL;
error("Invalid capabilities inheriting mode entered: \"%s\"", arg_orig);
return EINVAL;
}
ctx_p->flags[CAPS_INHERIT] = capsinherit;
break;
}
#endif
case CHROOT:
if (paramsource == PS_CONTROL) {
... ... @@ -1994,13 +2021,20 @@ int main(int argc, char *argv[]) {
ctx_p->flags[VERBOSE] = DEFAULT_VERBOSE;
#ifdef CAPABILITIES_SUPPORT
ctx_p->caps = DEFAULT_PRESERVE_CAPABILITIES;
ctx_p->synchandler_uid = DEFAULT_SYNCHANDLER_UID;
ctx_p->synchandler_gid = DEFAULT_SYNCHANDLER_GID;
ctx_p->synchandler_uid = getuid();
ctx_p->synchandler_gid = getgid();
ctx_p->flags[CAPS_INHERIT] = DEFAULT_CAPS_INHERIT;
ctx_p->flags[UID] = -1;
ctx_p->flags[GID] = -1;
ctx_p->uid = DEFAULT_UID;
ctx_p->gid = DEFAULT_GID;
{
struct passwd *pwd = getpwnam(DEFAULT_USER);
ctx_p->uid = (pwd != NULL) ? pwd->pw_uid : DEFAULT_UID;
ctx_p->flags[UID] = -1;
}
{
struct group *grp = getgrnam(DEFAULT_GROUP);
ctx_p->gid = (grp != NULL) ? grp->gr_gid : DEFAULT_GID;
ctx_p->flags[GID] = -1;
}
#endif
ctx_p->pid = getpid();
... ... @@ -2172,26 +2206,6 @@ int main(int argc, char *argv[]) {
}
#endif
if (ctx_p->flags[GID]) {
int rc;
debug(3, "Trying to drop gid to %i", ctx_p->gid);
if ((rc=setgid(ctx_p->gid)) && (ctx_p->flags[GID] != -1)) {
error("Cannot setgid(%u)", ctx_p->gid);
ret = errno;
}
if (!rc) debug(4, "success");
}
if (ctx_p->flags[UID]) {
int rc;
debug(3, "Trying to drop uid to %i", ctx_p->uid);
if ((rc=setuid(ctx_p->uid)) && (ctx_p->flags[UID] != -1)) {
error("Cannot setuid(%u)", ctx_p->uid);
ret = errno;
}
if (!rc) debug(4, "success");
}
if (ctx_p->watchdir != NULL) {
char *rwatchdir = realpath(ctx_p->watchdir, NULL);
if (rwatchdir == NULL) {
... ... @@ -2322,7 +2336,27 @@ int main(int argc, char *argv[]) {
} else
if (ctx_p->destproto != NULL)
ctx_p->destdirwslash = ctx_p->destdir;
/*
if (ctx_p->flags[GID]) {
int rc;
debug(3, "Trying to drop gid to %i", ctx_p->gid);
if ((rc=setgid(ctx_p->gid)) && (ctx_p->flags[GID] != -1)) {
error("Cannot setgid(%u)", ctx_p->gid);
ret = errno;
}
if (!rc) debug(4, "success");
}
if (ctx_p->flags[UID]) {
int rc;
debug(3, "Trying to drop uid to %i", ctx_p->uid);
if ((rc=setuid(ctx_p->uid)) && (ctx_p->flags[UID] != -1)) {
error("Cannot setuid(%u)", ctx_p->uid);
ret = errno;
}
if (!rc) debug(4, "success");
}
*/
debug(1, "%s [%s] (%p) -> %s [%s]", ctx_p->watchdir, ctx_p->watchdirwslash, ctx_p->watchdirwslash, ctx_p->destdir?ctx_p->destdir:"", ctx_p->destdirwslash?ctx_p->destdirwslash:"");
ret = ctx_check(ctx_p);
... ...
... ... @@ -310,7 +310,7 @@ with
If there's a
.BR capabilities (7)
support then the default value is "65535", otherwise the option is not set by default;
support then the default value is "nobody" (or "65534" if "nobody" not found), otherwise the option is not set by default;
.PP
.RE
... ... @@ -324,15 +324,13 @@ with
If there's a
.BR capabilities (7)
support then the default value is "65535", otherwise the option is not set by default;
support then the default value is "group" (or "65534" if "nogroup" not found), otherwise the option is not set by default;
.PP
.RE
.B \-\-sync\-handler\-uid
.I sync\-handler\-uid
.RS
.B [Linux only, requires capabilities]
An user ID to be used for
.IR sync\-handler .
... ... @@ -341,19 +339,17 @@ See
This option is ignored if the
.B clsync
instance have no CAP_SETUID capability or if option
instance have no CAP_SETUID capability (or analog) or if option
.B --no-thread-splitting
is enabled.
The default value is "0".
The default value is "$UID".
.PP
.RE
.B \-\-sync\-handler\-gid
.I sync\-handler\-gid
.RS
.B [Linux only, requires capabilities]
A group ID to be used for
.IR sync\-handler .
... ... @@ -362,11 +358,11 @@ See
This option is ignored if the
.B clsync
instance have no CAP_SETGID capability or if option
instance have no CAP_SETGID capability (or analog) or if option
.B --no-thread-splitting
is enabled.
The default value is "0".
The default value is "$GID".
.PP
.RE
... ... @@ -439,6 +435,37 @@ runner have such privileges.
.PP
.RE
.B \-\-inherit\-capabilities
.RS
.B [Linux only, requires capabilities]
Sets a mode for capabilities inheriting.
Possible values:
.RS
.B permitted
.RS
Inherits all permitted capabilities
.RE
.B dont-touch
.RS
Don't change inheritable capabilities set
.RE
.B clsync
.RS
Use
.BR clsync 's
effective capabilities set
.RE
.B empty
.RS
Reset all capabilities
.RE
.RE
The default value is "empty".
.RE
.B \-\-no\-thread\-splitting
.RS
.B [Linux only, requires capabilities]
... ...
... ... @@ -17,6 +17,15 @@
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <unistd.h> // execvp()
#include "common.h" // ctx.h
#include "ctx.h" // ctx_t
#include "error.h" // debug()
int (*privileged_fork_execvp)(const char *file, char *const argv[]);
#ifdef CAPABILITIES_SUPPORT
#include <pthread.h> // pthread_create()
#include <sys/inotify.h> // inotify_init()
#include <sys/types.h> // fts_open()
... ... @@ -24,11 +33,7 @@
#include <fts.h> // fts_open()
#include <errno.h> // errno
#include <sys/capability.h> // capset()
#include <unistd.h> // execvp()
#include "common.h" // ctx.h
#include "ctx.h" // ctx_t
#include "error.h" // debug()
pthread_t pthread_thread;
pthread_mutex_t pthread_mutex_privileged = PTHREAD_MUTEX_INITIALIZER;
... ... @@ -113,11 +118,9 @@ int (*privileged_inotify_rm_watch) (
int wd
);
int (*privileged_fork_execvp)(const char *file, char *const argv[]);
int cap_drop(__u32 caps) {
debug(1, "Dropping all Linux capabilites but 0x%o", caps);
int cap_drop(ctx_t *ctx_p, __u32 caps) {
debug(1, "Dropping all Linux capabilites but 0x%x", caps);
struct __user_cap_header_struct cap_hdr = {0};
struct __user_cap_data_struct cap_dat = {0};
... ... @@ -127,12 +130,26 @@ int cap_drop(__u32 caps) {
error("Cannot get capabilites with capget()");
return errno;
}
debug(3, "old: cap.eff == 0x%04x; cap.prm == 0x%04x; cap.inh == 0x%04x.",
cap_dat.effective, cap_dat.permitted, cap_dat.inheritable);
cap_dat.effective = caps;
cap_dat.permitted = cap_dat.effective;
cap_dat.inheritable = cap_dat.effective;
switch (ctx_p->flags[CAPS_INHERIT]) {
case CI_PERMITTED:
cap_dat.inheritable = cap_dat.permitted;
break;
case CI_DONTTOUCH:
break;
case CI_CLSYNC:
cap_dat.inheritable = caps;
break;
case CI_EMPTY:
cap_dat.inheritable = 0;
break;
}
cap_dat.effective = caps;
cap_dat.permitted = caps;
debug(3, "cap.eff == 0x%04x; cap.prm == 0x%04x; cap.inh == 0x%04x.",
debug(3, "new: cap.eff == 0x%04x; cap.prm == 0x%04x; cap.inh == 0x%04x.",
cap_dat.effective, cap_dat.permitted, cap_dat.inheritable);
if (capset(&cap_hdr, &cap_dat) < 0) {
... ... @@ -150,7 +167,7 @@ void *privileged_handler(void *_ctx_p)
uid_t exec_uid = 65535;
gid_t exec_gid = 65535;
cap_drop(ctx_p->caps);
cap_drop(ctx_p, ctx_p->caps);
debug(2, "Syncing with the runner");
pthread_mutex_lock(&pthread_mutex_privileged);
... ... @@ -364,6 +381,8 @@ int _privileged_fork_setuid_execvp(const char *file, char *const argv[])
return (long)ret;
}
#endif
uid_t _privileged_fork_execvp_uid;
gid_t _privileged_fork_execvp_gid;
int _privileged_fork_execvp(const char *file, char *const argv[])
... ... @@ -386,7 +405,16 @@ int _privileged_fork_execvp(const char *file, char *const argv[])
int privileged_init(ctx_t *ctx_p)
{
#ifdef CAPABILITIES_SUPPORT
if (ctx_p->flags[NOTHREADSPLITTING]) {
#endif
privileged_fork_execvp = _privileged_fork_execvp;
_privileged_fork_execvp_uid = ctx_p->synchandler_uid;
_privileged_fork_execvp_gid = ctx_p->synchandler_gid;
#ifdef CAPABILITIES_SUPPORT
privileged_fts_open = fts_open;
privileged_fts_read = fts_read;
privileged_fts_close = fts_close;
... ... @@ -394,13 +422,13 @@ int privileged_init(ctx_t *ctx_p)
privileged_inotify_init1 = inotify_init1;
privileged_inotify_add_watch = inotify_add_watch;
privileged_inotify_rm_watch = inotify_rm_watch;
privileged_fork_execvp = _privileged_fork_execvp;
_privileged_fork_execvp_uid = ctx_p->synchandler_uid;
_privileged_fork_execvp_gid = ctx_p->synchandler_gid;
cap_drop(ctx_p, ctx_p->caps);
#endif
cap_drop(ctx_p->caps);
return 0;
#ifdef CAPABILITIES_SUPPORT
}
privileged_fts_open = _privileged_fts_open;
... ... @@ -451,7 +479,7 @@ int privileged_init(ctx_t *ctx_p)
error("Cannot pthread_create().");
return errno;
}
cap_drop(0);
cap_drop(ctx_p, 0);
debug(4, "Waiting for the privileged thread to get prepared");
pthread_cond_wait(&pthread_cond_runner, &pthread_mutex_runner);
... ... @@ -476,12 +504,14 @@ int privileged_init(ctx_t *ctx_p)
debug(5, "Finish");
return 0;
#endif
}
int privileged_deinit(ctx_t *ctx_p)
{
int ret = 0;
#ifdef CAPABILITIES_SUPPORT
if (ctx_p->flags[NOTHREADSPLITTING])
return 0;
... ... @@ -514,6 +544,7 @@ int privileged_deinit(ctx_t *ctx_p)
ret = errno;
}
#endif
return ret;
}
... ...
... ... @@ -19,9 +19,6 @@
#ifdef CAPABILITIES_SUPPORT
extern int privileged_init(struct ctx *ctx_p);
extern int privileged_deinit(struct ctx *ctx_p);
extern FTS *(*privileged_fts_open) (
char * const *path_argv,
int options,
... ... @@ -58,3 +55,6 @@ extern int (*privileged_inotify_rm_watch) (
extern int (*privileged_fork_execvp)(const char *file, char *const argv[]);
extern int privileged_init(struct ctx *ctx_p);
extern int privileged_deinit(struct ctx *ctx_p);
... ...
... ... @@ -3664,10 +3664,8 @@ int sync_run(ctx_t *ctx_p) {
signal(SIGUSR_BLOPINT, sync_sig_int);
}
#ifdef CAPABILITIES_SUPPORT
if ((ret=privileged_init(ctx_p)))
return ret;
#endif
// Creating hash tables
{
... ... @@ -3860,9 +3858,7 @@ int sync_run(ctx_t *ctx_p) {
pthread_kill(pthread_sighandler, SIGINT);
pthread_join(pthread_sighandler, NULL);
#ifdef CAPABILITIES_SUPPORT
ret |= privileged_deinit(ctx_p);
#endif
// Killing children
... ...