3
* Copyright © 2013 Stphane Graber
4
* Author: Stphane Graber <stgraber@ubuntu.com>
6
* This program is free software; you can redistribute it and/or modify
7
* it under the terms of the GNU General Public License version 2, as
8
* published by the Free Software Foundation.
10
* This program is distributed in the hope that it will be useful,
11
* but WITHOUT ANY WARRANTY; without even the implied warranty of
12
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13
* GNU General Public License for more details.
15
* You should have received a copy of the GNU General Public License along
16
* with this program; if not, write to the Free Software Foundation, Inc.,
17
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
23
* Maximum depth of directories we allow in Create
24
* Default is 16. Figure 4 directories per level of container
25
* nesting (/user/1000.user/c2.session/c1), that lets us nest
28
static int maxdepth = 16;
31
int get_pid_cgroup_main(void *parent, const char *controller,struct ucred p,
32
struct ucred r, struct ucred v, char **output)
34
char rcgpath[MAXPATHLEN], vcgpath[MAXPATHLEN];
36
// Get r's current cgroup in rcgpath
37
if (!compute_pid_cgroup(r.pid, controller, "", rcgpath, NULL)) {
38
nih_error("Could not determine the requestor cgroup");
42
// Get v's cgroup in vcgpath
43
if (!compute_pid_cgroup(v.pid, controller, "", vcgpath, NULL)) {
44
nih_error("Could not determine the victim cgroup");
48
// Make sure v's cgroup is under r's
49
int rlen = strlen(rcgpath);
50
if (strncmp(rcgpath, vcgpath, rlen) != 0) {
51
nih_error("v (%d)'s cgroup is not below r (%d)'s",
55
if (strlen(vcgpath) == rlen)
56
*output = NIH_MUST (nih_strdup(parent, "/") );
58
*output = NIH_MUST (nih_strdup(parent, vcgpath + rlen + 1) );
63
static bool victim_under_proxy_cgroup(char *rcgpath, pid_t v,
64
const char *controller)
66
char vcgpath[MAXPATHLEN];
68
if (!compute_pid_cgroup(v, controller, "", vcgpath, NULL)) {
69
nih_error("Could not determine the victim's cgroup");
72
if (strncmp(vcgpath, rcgpath, strlen(rcgpath)) != 0)
77
int do_move_pid_main(const char *controller, const char *cgroup, struct ucred p,
78
struct ucred r, struct ucred v, bool escape)
80
char rcgpath[MAXPATHLEN], path[MAXPATHLEN];
84
if (!sane_cgroup(cgroup)) {
85
nih_error("unsafe cgroup");
89
// verify that ucred.pid may move target pid
90
if (!may_move_pid(r.pid, r.uid, v.pid)) {
91
nih_error("%d may not move %d", r.pid, v.pid);
95
// Get r's current cgroup in rcgpath
98
if (!compute_pid_cgroup(query, controller, "", rcgpath, NULL)) {
99
nih_error("Could not determine the requested cgroup");
103
// If the victim is not under proxy's cgroup, refuse
104
if (!victim_under_proxy_cgroup(rcgpath, v.pid, controller)) {
105
nih_error("victim's cgroup is not under proxy's (p.uid %u)", p.uid);
109
/* rcgpath + / + cgroup + /tasks + \0 */
110
if (strlen(rcgpath) + strlen(cgroup) > MAXPATHLEN - 8) {
111
nih_error("Path name too long");
114
strcpy(path, rcgpath);
115
strncat(path, "/", MAXPATHLEN-1);
116
strncat(path, cgroup, MAXPATHLEN-1);
117
if (realpath_escapes(path, rcgpath)) {
118
nih_error("Invalid path %s", path);
121
// is r allowed to descend under the parent dir?
122
if (!may_access(r.pid, r.uid, r.gid, path, O_RDONLY)) {
123
nih_error("pid %d (uid %u gid %u) may not read under %s",
124
r.pid, r.uid, r.gid, path);
127
// is r allowed to write to tasks file?
128
strncat(path, "/tasks", MAXPATHLEN-1);
129
if (!may_access(r.pid, r.uid, r.gid, path, O_WRONLY)) {
130
nih_error("pid %d (uid %u gid %u) may not write to %s",
131
r.pid, r.uid, r.gid, path);
134
f = fopen(path, "w");
136
nih_error("Failed to open %s", path);
139
if (fprintf(f, "%d\n", v.pid) < 0) {
141
nih_error("Failed to open %s", path);
144
if (fclose(f) != 0) {
145
nih_error("Failed to write %d to %s", v.pid, path);
148
nih_info(_("%d moved to %s:%s by %d's request"), v.pid,
149
controller, cgroup, r.pid);
153
int move_pid_main(const char *controller, const char *cgroup, struct ucred p,
154
struct ucred r, struct ucred v)
156
if (cgroup[0] == '/') {
157
// We could try to be accomodating, but let's not fool around right now
158
nih_error("Bad requested cgroup path: %s", cgroup);
162
return do_move_pid_main(controller, cgroup, p, r, v, false);
165
int move_pid_abs_main(const char *controller, const char *cgroup, struct ucred p,
166
struct ucred r, struct ucred v)
168
return do_move_pid_main(controller, cgroup, p, r, v, true);
171
int create_main(const char *controller, const char *cgroup, struct ucred p,
172
struct ucred r, int32_t *existed)
175
char rcgpath[MAXPATHLEN], path[MAXPATHLEN], dirpath[MAXPATHLEN];
176
nih_local char *copy = NULL;
178
char *p1, *p2, oldp2;
181
if (!cgroup || ! *cgroup) // nothing to do
184
if (!sane_cgroup(cgroup)) {
185
nih_error("unsafe cgroup");
189
// Get r's current cgroup in rcgpath
190
if (!compute_pid_cgroup(r.pid, controller, "", rcgpath, &depth)) {
191
nih_error("Could not determine the requested cgroup");
195
depth += get_path_depth(cgroup);
196
if (depth > maxdepth) {
197
nih_error("Cgroup too deep: %s/%s", rcgpath, cgroup);
201
cgroup_len = strlen(cgroup);
203
if (strlen(rcgpath) + cgroup_len > MAXPATHLEN) {
204
nih_error("Path name too long");
207
copy = NIH_MUST( nih_strndup(NULL, cgroup, cgroup_len) );
209
strcpy(path, rcgpath);
210
strcpy(dirpath, rcgpath);
211
for (p1=copy; *p1; p1 = p2) {
213
for (p2=p1; *p2 && *p2 != '/'; p2++);
216
if (strcmp(p1, "..") == 0) {
217
nih_error("Invalid cgroup path at create: %s", p1);
220
strncat(path, "/", MAXPATHLEN-1);
221
strncat(path, p1, MAXPATHLEN-1);
222
if (dir_exists(path)) {
224
// TODO - properly use execute perms
225
if (!may_access(r.pid, r.uid, r.gid, path, O_RDONLY)) {
226
nih_error("pid %d (uid %u gid %u) may not look under %s",
227
r.pid, r.uid, r.gid, path);
232
if (!may_access(r.pid, r.uid, r.gid, dirpath, O_RDWR)) {
233
nih_error("pid %d (uid %u gid %u) may not create under %s",
234
r.pid, r.uid, r.gid, dirpath);
237
ret = mkdir(path, 0755);
238
if (ret < 0) { // Should we ignore EEXIST? Ok, but don't chown.
239
if (errno == EEXIST) {
243
nih_error("failed to create %s", path);
246
if (!chown_cgroup_path(path, r.uid, r.gid, true)) {
247
nih_error("Failed to change ownership on %s to %u:%u",
254
strncat(dirpath, "/", MAXPATHLEN-1);
255
strncat(dirpath, p1, MAXPATHLEN-1);
262
nih_info(_("Created %s for %d (%u:%u)"), path, r.pid,
267
int chown_main(const char *controller, const char *cgroup, struct ucred p,
268
struct ucred r, struct ucred v)
270
char rcgpath[MAXPATHLEN];
271
nih_local char *path = NULL;
274
/* If caller is not root in his userns, then he can't chown, as
275
* that requires privilege over two uids */
277
if (!hostuid_to_ns(r.uid, r.pid, &uid) || uid != 0) {
278
nih_error("Chown requested by non-root uid %u", r.uid);
283
if (!sane_cgroup(cgroup)) {
284
nih_error("unsafe cgroup");
288
// Get r's current cgroup in rcgpath
289
if (!compute_pid_cgroup(r.pid, controller, "", rcgpath, NULL)) {
290
nih_error("Could not determine the requested cgroup");
293
/* rcgpath + / + cgroup + \0 */
294
if (strlen(rcgpath) + strlen(cgroup) > MAXPATHLEN+2) {
295
nih_error("Path name too long");
298
path = NIH_MUST( nih_sprintf(NULL, "%s/%s", rcgpath, cgroup) );
299
if (realpath_escapes(path, rcgpath)) {
300
nih_error("Invalid path %s", path);
303
// is r allowed to descend under the parent dir?
304
if (!may_access(r.pid, r.uid, r.gid, path, O_RDONLY)) {
305
nih_error("pid %d (uid %u gid %u) may not read under %s",
306
r.pid, r.uid, r.gid, path);
310
// does r have privilege over the cgroup dir?
311
if (!may_access(r.pid, r.uid, r.gid, path, O_RDWR)) {
312
nih_error("Pid %d may not chown %s\n", r.pid, path);
316
// go ahead and chown it.
317
if (!chown_cgroup_path(path, v.uid, v.gid, false)) {
318
nih_error("Failed to change ownership on %s to %u:%u",
326
int chmod_main(const char *controller, const char *cgroup, const char *file,
327
struct ucred p, struct ucred r, int mode)
329
char rcgpath[MAXPATHLEN];
330
nih_local char *path = NULL;
332
if (!sane_cgroup(cgroup)) {
333
nih_error("unsafe cgroup");
337
if (file && ( strchr(file, '/') || strchr(file, '\\')) ) {
338
nih_dbus_error_raise_printf (DBUS_ERROR_INVALID_ARGS,
343
// Get r's current cgroup in rcgpath
344
if (!compute_pid_cgroup(r.pid, controller, "", rcgpath, NULL)) {
345
nih_error("Could not determine the requested cgroup");
349
path = NIH_MUST( nih_sprintf(NULL, "%s/%s", rcgpath, cgroup) );
350
if (file && strlen(file))
351
NIH_MUST( nih_strcat_sprintf(&path, NULL, "/%s", file) );
352
if (realpath_escapes(path, rcgpath)) {
353
nih_error("Invalid path %s", path);
356
// is r allowed to descend under the parent dir?
357
if (!may_access(r.pid, r.uid, r.gid, path, O_RDONLY)) {
358
nih_error("pid %d (uid %u gid %u) may not read under %s",
359
r.pid, r.uid, r.gid, path);
363
// does r have privilege over the cgroup dir?
364
if (!may_access(r.pid, r.uid, r.gid, path, O_RDWR)) {
365
nih_error("Pid %d may not chmod %s\n", r.pid, path);
369
// go ahead and chmod it.
370
if (!chmod_cgroup_path(path, mode)) {
371
nih_error("Failed to change mode on %s to %d",
379
int get_value_main(void *parent, const char *controller, const char *cgroup,
380
const char *key, struct ucred p, struct ucred r, char **value)
382
char path[MAXPATHLEN];
384
if (!sane_cgroup(cgroup)) {
385
nih_error("unsafe cgroup");
389
if (!compute_pid_cgroup(r.pid, controller, cgroup, path, NULL)) {
390
nih_error("Could not determine the requested cgroup");
394
/* Check access rights to the cgroup directory */
395
if (!may_access(r.pid, r.uid, r.gid, path, O_RDONLY)) {
396
nih_error("Pid %d may not access %s\n", r.pid, path);
400
/* append the filename */
401
if (strlen(path) + strlen(key) + 2 > MAXPATHLEN) {
402
nih_error("filename too long for cgroup %s key %s", path, key);
406
strncat(path, "/", MAXPATHLEN-1);
407
strncat(path, key, MAXPATHLEN-1);
409
/* Check access rights to the file itself */
410
if (!may_access(r.pid, r.uid, r.gid, path, O_RDONLY)) {
411
nih_error("Pid %d may not access %s\n", r.pid, path);
415
/* read and return the value */
416
*value = file_read_string(parent, path);
418
nih_error("Failed to read value from %s", path);
422
nih_info(_("Sending to client: %s"), *value);
426
int set_value_main(const char *controller, const char *cgroup,
427
const char *key, const char *value, struct ucred p,
431
char path[MAXPATHLEN];
433
if (!sane_cgroup(cgroup)) {
434
nih_error("unsafe cgroup");
438
if (!compute_pid_cgroup(r.pid, controller, cgroup, path, NULL)) {
439
nih_error("Could not determine the requested cgroup");
443
/* Check access rights to the cgroup directory */
444
if (!may_access(r.pid, r.uid, r.gid, path, O_RDONLY)) {
445
nih_error("Pid %d may not access %s\n", r.pid, path);
449
/* append the filename */
450
if (strlen(path) + strlen(key) + 2 > MAXPATHLEN) {
451
nih_error("filename too long for cgroup %s key %s", path, key);
455
strncat(path, "/", MAXPATHLEN-1);
456
strncat(path, key, MAXPATHLEN-1);
458
/* Check access rights to the file itself */
459
if (!may_access(r.pid, r.uid, r.gid, path, O_RDWR)) {
460
nih_error("Pid %d may not access %s\n", r.pid, path);
464
/* read and return the value */
465
if (!set_value(path, value)) {
466
nih_error("Failed to set value %s to %s", path, value);
474
* Refuse any '..', and consolidate any '//'
476
static bool normalize_path(char *path)
478
if (strstr(path, ".."))
480
while ((path = strstr(path, "//")) != NULL) {
484
memmove(path, p2, strlen(p2)+1);
491
* Recursively delete a cgroup.
492
* Cgroup files can't be deleted, but are cleaned up when you remove the
493
* containing directory. A directory cannot be removed until all its
494
* children are removed, and can't be removed if any tasks remain.
496
* We allow any task which may write under /a/b to delete any cgroups
497
* under that, even if, say, it technically is not allowed to remove
500
static int recursive_rmdir(char *path)
502
struct dirent dirent, *direntp;
504
char pathname[MAXPATHLEN];
509
nih_error("Failed to open dir %s for recursive deletion", path);
513
while (!readdir_r(dir, &dirent, &direntp)) {
519
if (!strcmp(direntp->d_name, ".") ||
520
!strcmp(direntp->d_name, ".."))
522
rc = snprintf(pathname, MAXPATHLEN, "%s/%s", path, direntp->d_name);
523
if (rc < 0 || rc >= MAXPATHLEN) {
527
rc = lstat(pathname, &mystat);
532
if (S_ISDIR(mystat.st_mode)) {
533
if (recursive_rmdir(pathname) < 0)
538
if (closedir(dir) < 0)
543
return failed ? -1 : 0;
546
int remove_main(const char *controller, const char *cgroup, struct ucred p,
547
struct ucred r, int recursive, int32_t *existed)
549
char rcgpath[MAXPATHLEN], path[MAXPATHLEN];
551
nih_local char *working = NULL, *copy = NULL, *wcgroup = NULL;
556
if (!sane_cgroup(cgroup)) {
557
nih_error("unsafe cgroup");
561
// Get r's current cgroup in rcgpath
562
if (!compute_pid_cgroup(r.pid, controller, "", rcgpath, NULL)) {
563
nih_error("Could not determine the requested cgroup");
567
cgroup_len = strlen(cgroup);
569
if (strlen(rcgpath) + cgroup_len > MAXPATHLEN) {
570
nih_error("Path name too long");
574
wcgroup = NIH_MUST( nih_strdup(NULL, cgroup) );
575
if (!normalize_path(wcgroup))
578
working = NIH_MUST( nih_strdup(NULL, rcgpath) );
579
NIH_MUST( nih_strcat(&working, NULL, "/") );
580
NIH_MUST( nih_strcat(&working, NULL, wcgroup) );
582
if (!dir_exists(working)) {
586
// must have write access to the parent dir
587
copy = NIH_MUST( nih_strdup(NULL, working) );
588
if (!(p1 = strrchr(copy, '/')))
591
if (!may_access(r.pid, r.uid, r.gid, copy, O_WRONLY)) {
592
nih_error("pid %d (%u:%u) may not remove %s",
593
r.pid, r.uid, r.gid, copy);
598
if (rmdir(working) < 0) {
599
nih_error("Failed to remove %s: %s", working, strerror(errno));
602
} else if (recursive_rmdir(working) < 0)
605
nih_info(_("Removed %s for %d (%u:%u)"), path, r.pid,
610
int get_tasks_main(void *parent, const char *controller, const char *cgroup,
611
struct ucred p, struct ucred r, int32_t **pids)
613
char path[MAXPATHLEN];
614
const char *key = "tasks";
616
if (!sane_cgroup(cgroup)) {
617
nih_error("unsafe cgroup");
621
if (!compute_pid_cgroup(r.pid, controller, cgroup, path, NULL)) {
622
nih_error("Could not determine the requested cgroup");
626
/* Check access rights to the cgroup directory */
627
if (!may_access(r.pid, r.uid, r.gid, path, O_RDONLY)) {
628
nih_error("Pid %d may not access %s\n", r.pid, path);
632
/* append the filename */
633
if (strlen(path) + strlen(key) + 2 > MAXPATHLEN) {
634
nih_error("filename too long for cgroup %s key %s", path, key);
638
strncat(path, "/", MAXPATHLEN-1);
639
strncat(path, key, MAXPATHLEN-1);
641
return file_read_pids(parent, path, pids);
644
char *extra_cgroup_mounts;
647
my_setter (NihOption *option, const char *arg)
649
extra_cgroup_mounts = NIH_MUST( strdup(arg) );
657
* Command-line options accepted by this program.
659
static NihOption options[] = {
660
{ 0, "max-depth", N_("Maximum cgroup depth"),
661
NULL, NULL, &maxdepth, NULL },
662
{ 'm', "mount", N_("Extra subsystems to mount"),
663
NULL, "subsystems to mount", NULL, my_setter },
664
{ 0, "daemon", N_("Detach and run in the background"),
665
NULL, NULL, &daemonise, NULL },
670
static inline int mkdir_cgmanager_dir(void)
672
if (mkdir(CGMANAGER_DIR, 0755) == -1 && errno != EEXIST) {
673
nih_error("Could not create %s", CGMANAGER_DIR);
679
static bool daemon_running(void)
681
DBusConnection *server_conn;
684
server_conn = nih_dbus_connect(CGMANAGER_DBUS_PATH, NULL);
686
dbus_connection_unref (server_conn);
689
err = nih_error_get();
695
* We may decide to make the socket path customizable. For now
696
* just assume it is in /sys/fs/cgroup/ which has some special
699
static bool setup_cgroup_dir(void)
702
if (!dir_exists(CGDIR)) {
703
nih_debug(CGDIR " does not exist");
706
if (daemon_running()) {
707
nih_error("cgmanager is already running");
710
if (file_exists(CGMANAGER_SOCK)) {
711
if (unlink(CGMANAGER_SOCK) < 0) {
712
nih_error("failed to delete stale cgmanager socket");
716
/* Check that /sys/fs/cgroup is writeable, else mount a tmpfs */
718
ret = creat(CGPROBE, O_RDWR);
722
return mkdir_cgmanager_dir();
724
ret = mount("cgroup", CGDIR, "tmpfs", 0, "size=10000");
726
nih_debug("Failed to mount tmpfs on %s: %s",
727
CGDIR, strerror(errno));
730
nih_debug("Mounted tmpfs onto %s", CGDIR);
731
return mkdir_cgmanager_dir();
735
main (int argc, char *argv[])
742
nih_main_init (argv[0]);
744
nih_option_set_synopsis (_("Control group manager"));
745
nih_option_set_help (_("The cgroup manager daemon"));
747
args = nih_option_parser (NULL, argc, argv, options, FALSE);
751
if (!setup_cgroup_dir()) {
752
nih_fatal("Failed to set up cgmanager socket");
756
/* Setup the DBus server */
757
server = nih_dbus_server (CGMANAGER_DBUS_PATH, client_connect,
759
nih_assert (server != NULL);
761
if (setup_cgroup_mounts(extra_cgroup_mounts) < 0) {
762
nih_fatal ("Failed to set up cgroup mounts");
766
if (!move_self_to_root()) {
767
nih_fatal ("Failed to move self to root cgroup");
771
if (stat("/proc/self/ns/pid", &sb) == 0) {
772
mypidns = read_pid_ns_link(getpid());
773
setns_pid_supported = true;
776
if (stat("/proc/self/ns/user", &sb) == 0) {
777
myuserns = read_user_ns_link(getpid());
778
setns_user_supported = true;
783
if (nih_main_daemonise () < 0) {
786
err = nih_error_get ();
787
nih_fatal ("%s: %s", _("Unable to become daemon"),
795
ret = nih_main_loop ();
797
/* Destroy any PID file we may have created */
799
nih_main_unlink_pidfile();