summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/cgroup-util.c47
-rw-r--r--src/cgroup-util.h2
-rw-r--r--src/cgroup.c19
-rw-r--r--src/cgroup.h8
-rw-r--r--src/conf-parser.c30
-rw-r--r--src/conf-parser.h1
-rw-r--r--src/dbus-execute.c1
-rw-r--r--src/execute.c17
-rw-r--r--src/execute.h1
-rw-r--r--src/load-fragment-gperf.gperf.m43
-rw-r--r--src/login/logind-session.c2
-rw-r--r--src/util.c24
-rw-r--r--src/util.h2
13 files changed, 120 insertions, 37 deletions
diff --git a/src/cgroup-util.c b/src/cgroup-util.c
index f74280f495..904d300952 100644
--- a/src/cgroup-util.c
+++ b/src/cgroup-util.c
@@ -173,7 +173,7 @@ int cg_rmdir(const char *controller, const char *path, bool honour_sticky) {
return -ENOMEM;
}
- r = file_is_sticky(tasks);
+ r = file_is_priv_sticky(tasks);
free(tasks);
if (r > 0) {
@@ -571,7 +571,7 @@ static int trim_cb(const char *path, const struct stat *sb, int typeflag, struct
return 1;
}
- is_sticky = file_is_sticky(p) > 0;
+ is_sticky = file_is_priv_sticky(p) > 0;
free(p);
if (is_sticky)
@@ -606,7 +606,7 @@ int cg_trim(const char *controller, const char *path, bool delete_root) {
return -ENOMEM;
}
- is_sticky = file_is_sticky(p) > 0;
+ is_sticky = file_is_priv_sticky(p) > 0;
free(p);
if (!is_sticky)
@@ -712,7 +712,11 @@ int cg_set_group_access(const char *controller, const char *path, mode_t mode, u
assert(controller);
assert(path);
- if ((r = cg_get_path(controller, path, NULL, &fs)) < 0)
+ if (mode != (mode_t) -1)
+ mode &= 0777;
+
+ r = cg_get_path(controller, path, NULL, &fs);
+ if (r < 0)
return r;
r = chmod_and_chown(fs, mode, uid, gid);
@@ -721,16 +725,47 @@ int cg_set_group_access(const char *controller, const char *path, mode_t mode, u
return r;
}
-int cg_set_task_access(const char *controller, const char *path, mode_t mode, uid_t uid, gid_t gid) {
+int cg_set_task_access(const char *controller, const char *path, mode_t mode, uid_t uid, gid_t gid, int sticky) {
char *fs;
int r;
assert(controller);
assert(path);
- if ((r = cg_get_path(controller, path, "tasks", &fs)) < 0)
+ if (mode == (mode_t) -1 && uid == (uid_t) -1 && gid == (gid_t) -1 && sticky < 0)
+ return 0;
+
+ if (mode != (mode_t) -1)
+ mode &= 0666;
+
+ r = cg_get_path(controller, path, "tasks", &fs);
+ if (r < 0)
return r;
+ if (sticky >= 0 && mode != (mode_t) -1)
+ /* Both mode and sticky param are passed */
+ mode |= (sticky ? S_ISVTX : 0);
+ else if ((sticky >= 0 && mode == (mode_t) -1) ||
+ (mode != (mode_t) -1 && sticky < 0)) {
+ struct stat st;
+
+ /* Only one param is passed, hence read the current
+ * mode from the file itself */
+
+ r = lstat(fs, &st);
+ if (r < 0) {
+ free(fs);
+ return -errno;
+ }
+
+ if (mode == (mode_t) -1)
+ /* No mode set, we just shall set the sticky bit */
+ mode = (st.st_mode & ~S_ISVTX) | (sticky ? S_ISVTX : 0);
+ else
+ /* Only mode set, leave sticky bit untouched */
+ mode = (st.st_mode & ~0777) | mode;
+ }
+
r = chmod_and_chown(fs, mode, uid, gid);
free(fs);
diff --git a/src/cgroup-util.h b/src/cgroup-util.h
index f09373bd06..37e4255a9c 100644
--- a/src/cgroup-util.h
+++ b/src/cgroup-util.h
@@ -60,7 +60,7 @@ int cg_attach(const char *controller, const char *path, pid_t pid);
int cg_create_and_attach(const char *controller, const char *path, pid_t pid);
int cg_set_group_access(const char *controller, const char *path, mode_t mode, uid_t uid, gid_t gid);
-int cg_set_task_access(const char *controller, const char *path, mode_t mode, uid_t uid, gid_t gid);
+int cg_set_task_access(const char *controller, const char *path, mode_t mode, uid_t uid, gid_t gid, int sticky);
int cg_install_release_agent(const char *controller, const char *agent);
diff --git a/src/cgroup.c b/src/cgroup.c
index e141b4153d..9aff02e7bc 100644
--- a/src/cgroup.c
+++ b/src/cgroup.c
@@ -60,7 +60,7 @@ int cgroup_bonding_realize_list(CGroupBonding *first) {
return 0;
}
-void cgroup_bonding_free(CGroupBonding *b, bool remove_or_trim) {
+void cgroup_bonding_free(CGroupBonding *b, bool trim) {
assert(b);
if (b->unit) {
@@ -79,13 +79,8 @@ void cgroup_bonding_free(CGroupBonding *b, bool remove_or_trim) {
}
}
- if (b->realized && b->ours && remove_or_trim) {
-
- if (cgroup_bonding_is_empty(b) > 0)
- cg_delete(b->controller, b->path);
- else
- cg_trim(b->controller, b->path, false);
- }
+ if (b->realized && b->ours && trim)
+ cg_trim(b->controller, b->path, false);
free(b->controller);
free(b->path);
@@ -159,21 +154,21 @@ int cgroup_bonding_set_group_access_list(CGroupBonding *first, mode_t mode, uid_
return 0;
}
-int cgroup_bonding_set_task_access(CGroupBonding *b, mode_t mode, uid_t uid, gid_t gid) {
+int cgroup_bonding_set_task_access(CGroupBonding *b, mode_t mode, uid_t uid, gid_t gid, int sticky) {
assert(b);
if (!b->realized)
return -EINVAL;
- return cg_set_task_access(b->controller, b->path, mode, uid, gid);
+ return cg_set_task_access(b->controller, b->path, mode, uid, gid, sticky);
}
-int cgroup_bonding_set_task_access_list(CGroupBonding *first, mode_t mode, uid_t uid, gid_t gid) {
+int cgroup_bonding_set_task_access_list(CGroupBonding *first, mode_t mode, uid_t uid, gid_t gid, int sticky) {
CGroupBonding *b;
int r;
LIST_FOREACH(by_unit, b, first) {
- r = cgroup_bonding_set_task_access(b, mode, uid, gid);
+ r = cgroup_bonding_set_task_access(b, mode, uid, gid, sticky);
if (r < 0)
return r;
}
diff --git a/src/cgroup.h b/src/cgroup.h
index f33d8440e6..db4feb916e 100644
--- a/src/cgroup.h
+++ b/src/cgroup.h
@@ -53,8 +53,8 @@ struct CGroupBonding {
int cgroup_bonding_realize(CGroupBonding *b);
int cgroup_bonding_realize_list(CGroupBonding *first);
-void cgroup_bonding_free(CGroupBonding *b, bool remove_or_trim);
-void cgroup_bonding_free_list(CGroupBonding *first, bool remove_or_trim);
+void cgroup_bonding_free(CGroupBonding *b, bool trim);
+void cgroup_bonding_free_list(CGroupBonding *first, bool trim);
int cgroup_bonding_install(CGroupBonding *b, pid_t pid);
int cgroup_bonding_install_list(CGroupBonding *first, pid_t pid);
@@ -62,8 +62,8 @@ int cgroup_bonding_install_list(CGroupBonding *first, pid_t pid);
int cgroup_bonding_set_group_access(CGroupBonding *b, mode_t mode, uid_t uid, gid_t gid);
int cgroup_bonding_set_group_access_list(CGroupBonding *b, mode_t mode, uid_t uid, gid_t gid);
-int cgroup_bonding_set_task_access(CGroupBonding *b, mode_t mode, uid_t uid, gid_t gid);
-int cgroup_bonding_set_task_access_list(CGroupBonding *b, mode_t mode, uid_t uid, gid_t gid);
+int cgroup_bonding_set_task_access(CGroupBonding *b, mode_t mode, uid_t uid, gid_t gid, int sticky);
+int cgroup_bonding_set_task_access_list(CGroupBonding *b, mode_t mode, uid_t uid, gid_t gid, int sticky);
int cgroup_bonding_kill(CGroupBonding *b, int sig, bool sigcont, Set *s);
int cgroup_bonding_kill_list(CGroupBonding *first, int sig, bool sigcont, Set *s);
diff --git a/src/conf-parser.c b/src/conf-parser.c
index 3ccd1c067a..ac8d9f5ac5 100644
--- a/src/conf-parser.c
+++ b/src/conf-parser.c
@@ -509,6 +509,36 @@ int config_parse_bool(
return 0;
}
+int config_parse_tristate(
+ const char *filename,
+ unsigned line,
+ const char *section,
+ const char *lvalue,
+ int ltype,
+ const char *rvalue,
+ void *data,
+ void *userdata) {
+
+ int k;
+ int *b = data;
+
+ assert(filename);
+ assert(lvalue);
+ assert(rvalue);
+ assert(data);
+
+ /* Tristates are like booleans, but can also take the 'default' value, i.e. "-1" */
+
+ k = parse_boolean(rvalue);
+ if (k < 0) {
+ log_error("[%s:%u] Failed to parse boolean value, ignoring: %s", filename, line, rvalue);
+ return 0;
+ }
+
+ *b = !!k;
+ return 0;
+}
+
int config_parse_string(
const char *filename,
unsigned line,
diff --git a/src/conf-parser.h b/src/conf-parser.h
index e970ee2834..35edcb63af 100644
--- a/src/conf-parser.h
+++ b/src/conf-parser.h
@@ -95,6 +95,7 @@ int config_parse_long(const char *filename, unsigned line, const char *section,
int config_parse_uint64(const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
int config_parse_size(const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
int config_parse_bool(const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
+int config_parse_tristate(const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
int config_parse_string(const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
int config_parse_path(const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
int config_parse_strv(const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
diff --git a/src/dbus-execute.c b/src/dbus-execute.c
index caeaf77931..e02d61ca8b 100644
--- a/src/dbus-execute.c
+++ b/src/dbus-execute.c
@@ -416,5 +416,6 @@ const BusProperty bus_exec_context_properties[] = {
{ "KillSignal", bus_property_append_int, "i", offsetof(ExecContext, kill_signal) },
{ "UtmpIdentifier", bus_property_append_string, "s", offsetof(ExecContext, utmp_id), true },
{ "ControlGroupModify", bus_property_append_bool, "b", offsetof(ExecContext, control_group_modify) },
+ { "ControlGroupModify", bus_property_append_bool, "b", offsetof(ExecContext, control_group_persistant) },
{ NULL, }
};
diff --git a/src/execute.c b/src/execute.c
index cacc8a73c5..650c6c1436 100644
--- a/src/execute.c
+++ b/src/execute.c
@@ -993,7 +993,7 @@ int exec_spawn(ExecCommand *command,
char **our_env = NULL, **pam_env = NULL, **final_env = NULL, **final_argv = NULL;
unsigned n_env = 0;
int saved_stdout = -1, saved_stdin = -1;
- bool keep_stdout = false, keep_stdin = false;
+ bool keep_stdout = false, keep_stdin = false, set_access = false;
/* child */
@@ -1218,11 +1218,21 @@ int exec_spawn(ExecCommand *command,
if (cgroup_bondings && context->control_group_modify) {
err = cgroup_bonding_set_group_access_list(cgroup_bondings, 0755, uid, gid);
if (err >= 0)
- err = cgroup_bonding_set_task_access_list(cgroup_bondings, 0644, uid, gid);
+ err = cgroup_bonding_set_task_access_list(cgroup_bondings, 0644, uid, gid, context->control_group_persistant);
if (err < 0) {
r = EXIT_CGROUP;
goto fail_child;
}
+
+ set_access = true;
+ }
+ }
+
+ if (cgroup_bondings && !set_access && context->control_group_persistant >= 0) {
+ err = cgroup_bonding_set_task_access_list(cgroup_bondings, (mode_t) -1, (uid_t) -1, (uid_t) -1, context->control_group_persistant);
+ if (err < 0) {
+ r = EXIT_CGROUP;
+ goto fail_child;
}
}
@@ -1488,6 +1498,7 @@ void exec_context_init(ExecContext *c) {
c->mount_flags = MS_SHARED;
c->kill_signal = SIGTERM;
c->send_sigkill = true;
+ c->control_group_persistant = -1;
}
void exec_context_done(ExecContext *c) {
@@ -1673,6 +1684,7 @@ void exec_context_dump(ExecContext *c, FILE* f, const char *prefix) {
"%sNonBlocking: %s\n"
"%sPrivateTmp: %s\n"
"%sControlGroupModify: %s\n"
+ "%sControlGroupPersistant: %s\n"
"%sPrivateNetwork: %s\n",
prefix, c->umask,
prefix, c->working_directory ? c->working_directory : "/",
@@ -1680,6 +1692,7 @@ void exec_context_dump(ExecContext *c, FILE* f, const char *prefix) {
prefix, yes_no(c->non_blocking),
prefix, yes_no(c->private_tmp),
prefix, yes_no(c->control_group_modify),
+ prefix, yes_no(c->control_group_persistant),
prefix, yes_no(c->private_network));
STRV_FOREACH(e, c->environment)
diff --git a/src/execute.h b/src/execute.h
index 187e8d2274..ff33fa92df 100644
--- a/src/execute.h
+++ b/src/execute.h
@@ -163,6 +163,7 @@ struct ExecContext {
bool private_network;
bool control_group_modify;
+ int control_group_persistant;
/* This is not exposed to the user but available
* internally. We need it to make sure that whenever we spawn
diff --git a/src/load-fragment-gperf.gperf.m4 b/src/load-fragment-gperf.gperf.m4
index c3f295710d..8adedc59e0 100644
--- a/src/load-fragment-gperf.gperf.m4
+++ b/src/load-fragment-gperf.gperf.m4
@@ -86,7 +86,8 @@ $1.KillMode, config_parse_kill_mode, 0,
$1.KillSignal, config_parse_kill_signal, 0, offsetof($1, exec_context.kill_signal)
$1.SendSIGKILL, config_parse_bool, 0, offsetof($1, exec_context.send_sigkill)
$1.UtmpIdentifier, config_parse_unit_string_printf, 0, offsetof($1, exec_context.utmp_id)
-$1.ControlGroupModify, config_parse_bool, 0, offsetof($1, exec_context.control_group_modify)'
+$1.ControlGroupModify, config_parse_bool, 0, offsetof($1, exec_context.control_group_modify)
+$1.ControlGroupPersistant, config_parse_tristate, 0, offsetof($1, exec_context.control_group_persistant)'
)m4_dnl
Unit.Names, config_parse_unit_names, 0, 0
Unit.Description, config_parse_unit_string_printf, 0, offsetof(Unit, description)
diff --git a/src/login/logind-session.c b/src/login/logind-session.c
index bb802e5fc2..5ea7e260a7 100644
--- a/src/login/logind-session.c
+++ b/src/login/logind-session.c
@@ -434,7 +434,7 @@ static int session_create_one_group(Session *s, const char *controller, const ch
if (r < 0)
return r;
- r = cg_set_task_access(controller, path, 0644, s->user->uid, s->user->gid);
+ r = cg_set_task_access(controller, path, 0644, s->user->uid, s->user->gid, -1);
if (r >= 0)
r = cg_set_group_access(controller, path, 0755, s->user->uid, s->user->gid);
diff --git a/src/util.c b/src/util.c
index b6e490d457..8004bebbd1 100644
--- a/src/util.c
+++ b/src/util.c
@@ -3483,7 +3483,9 @@ static int rm_rf_children(int fd, bool only_dirs, bool honour_sticky) {
}
if (honour_sticky)
- keep_around = st.st_uid == 0 && (st.st_mode & S_ISVTX);
+ keep_around =
+ (st.st_uid == 0 || st.st_uid == getuid()) &&
+ (st.st_mode & S_ISVTX);
is_dir = S_ISDIR(st.st_mode);
@@ -3497,7 +3499,9 @@ static int rm_rf_children(int fd, bool only_dirs, bool honour_sticky) {
continue;
}
- keep_around = st.st_uid == 0 && (st.st_mode & S_ISVTX);
+ keep_around =
+ (st.st_uid == 0 || st.st_uid == getuid()) &&
+ (st.st_mode & S_ISVTX);
}
is_dir = de->d_type == DT_DIR;
@@ -3559,7 +3563,7 @@ int rm_rf(const char *path, bool only_dirs, bool delete_root, bool honour_sticky
if (delete_root) {
- if (honour_sticky && file_is_sticky(path) > 0)
+ if (honour_sticky && file_is_priv_sticky(path) > 0)
return r;
if (rmdir(path) < 0 && errno != ENOENT) {
@@ -3578,11 +3582,13 @@ int chmod_and_chown(const char *path, mode_t mode, uid_t uid, gid_t gid) {
* first change the access mode and only then hand out
* ownership to avoid a window where access is too open. */
- if (chmod(path, mode) < 0)
- return -errno;
+ if (mode != (mode_t) -1)
+ if (chmod(path, mode) < 0)
+ return -errno;
- if (chown(path, uid, gid) < 0)
- return -errno;
+ if (uid != (uid_t) -1 || gid != (gid_t) -1)
+ if (chown(path, uid, gid) < 0)
+ return -errno;
return 0;
}
@@ -5810,7 +5816,7 @@ int block_get_whole_disk(dev_t d, dev_t *ret) {
return -ENOENT;
}
-int file_is_sticky(const char *p) {
+int file_is_priv_sticky(const char *p) {
struct stat st;
assert(p);
@@ -5819,7 +5825,7 @@ int file_is_sticky(const char *p) {
return -errno;
return
- st.st_uid == 0 &&
+ (st.st_uid == 0 || st.st_uid == getuid()) &&
(st.st_mode & S_ISVTX);
}
diff --git a/src/util.h b/src/util.h
index 590dc1781e..6acfcc8373 100644
--- a/src/util.h
+++ b/src/util.h
@@ -478,7 +478,7 @@ bool in_charset(const char *s, const char* charset);
int block_get_whole_disk(dev_t d, dev_t *ret);
-int file_is_sticky(const char *p);
+int file_is_priv_sticky(const char *p);
int strdup_or_null(const char *a, char **b);