Skip to content

Commit 8ee2a50

Browse files
committed
Implement privilege check when moving tasks
When writing pids to a tasks file in lxcfs, lxcfs was checking for privilege over the tasks file but not over the pid being moved. Since the cgm_movepid request is done as root on the host, not with the requestor's credentials, we must copy the check which cgmanager was doing to ensure that the requesting task is allowed to change the victim task's cgroup membership. This is CVE-2015-1344 https://bugs.launchpad.net/ubuntu/+source/lxcfs/+bug/1512854 Signed-off-by: Serge Hallyn <serge.hallyn@ubuntu.com>
1 parent a8b6c3e commit 8ee2a50

File tree

1 file changed

+94
-2
lines changed

1 file changed

+94
-2
lines changed

Diff for: lxcfs.c

+94-2
Original file line numberDiff line numberDiff line change
@@ -1336,7 +1336,95 @@ static void pid_from_ns_wrapper(int sock, pid_t tpid)
13361336
goto loop;
13371337
}
13381338

1339-
static bool do_write_pids(pid_t tpid, const char *contrl, const char *cg, const char *file, const char *buf)
1339+
/*
1340+
* Given host @uid, return the uid to which it maps in
1341+
* @pid's user namespace, or -1 if none.
1342+
*/
1343+
bool hostuid_to_ns(uid_t uid, pid_t pid, uid_t *answer)
1344+
{
1345+
FILE *f;
1346+
char line[400];
1347+
1348+
sprintf(line, "/proc/%d/uid_map", pid);
1349+
if ((f = fopen(line, "r")) == NULL) {
1350+
return false;
1351+
}
1352+
1353+
*answer = convert_id_to_ns(f, uid);
1354+
fclose(f);
1355+
1356+
if (*answer == -1)
1357+
return false;
1358+
return true;
1359+
}
1360+
1361+
/*
1362+
* get_pid_creds: get the real uid and gid of @pid from
1363+
* /proc/$$/status
1364+
* (XXX should we use euid here?)
1365+
*/
1366+
void get_pid_creds(pid_t pid, uid_t *uid, gid_t *gid)
1367+
{
1368+
char line[400];
1369+
uid_t u;
1370+
gid_t g;
1371+
FILE *f;
1372+
1373+
*uid = -1;
1374+
*gid = -1;
1375+
sprintf(line, "/proc/%d/status", pid);
1376+
if ((f = fopen(line, "r")) == NULL) {
1377+
fprintf(stderr, "Error opening %s: %s\n", line, strerror(errno));
1378+
return;
1379+
}
1380+
while (fgets(line, 400, f)) {
1381+
if (strncmp(line, "Uid:", 4) == 0) {
1382+
if (sscanf(line+4, "%u", &u) != 1) {
1383+
fprintf(stderr, "bad uid line for pid %u\n", pid);
1384+
fclose(f);
1385+
return;
1386+
}
1387+
*uid = u;
1388+
} else if (strncmp(line, "Gid:", 4) == 0) {
1389+
if (sscanf(line+4, "%u", &g) != 1) {
1390+
fprintf(stderr, "bad gid line for pid %u\n", pid);
1391+
fclose(f);
1392+
return;
1393+
}
1394+
*gid = g;
1395+
}
1396+
}
1397+
fclose(f);
1398+
}
1399+
1400+
/*
1401+
* May the requestor @r move victim @v to a new cgroup?
1402+
* This is allowed if
1403+
* . they are the same task
1404+
* . they are ownedy by the same uid
1405+
* . @r is root on the host, or
1406+
* . @v's uid is mapped into @r's where @r is root.
1407+
*/
1408+
bool may_move_pid(pid_t r, uid_t r_uid, pid_t v)
1409+
{
1410+
uid_t v_uid, tmpuid;
1411+
gid_t v_gid;
1412+
1413+
if (r == v)
1414+
return true;
1415+
if (r_uid == 0)
1416+
return true;
1417+
get_pid_creds(v, &v_uid, &v_gid);
1418+
if (r_uid == v_uid)
1419+
return true;
1420+
if (hostuid_to_ns(r_uid, r, &tmpuid) && tmpuid == 0
1421+
&& hostuid_to_ns(v_uid, r, &tmpuid))
1422+
return true;
1423+
return false;
1424+
}
1425+
1426+
static bool do_write_pids(pid_t tpid, uid_t tuid, const char *contrl, const char *cg,
1427+
const char *file, const char *buf)
13401428
{
13411429
int sock[2] = {-1, -1};
13421430
pid_t qpid, cpid = -1;
@@ -1378,6 +1466,10 @@ static bool do_write_pids(pid_t tpid, const char *contrl, const char *cg, const
13781466

13791467
if (recv_creds(sock[0], &cred, &v)) {
13801468
if (v == '0') {
1469+
if (!may_move_pid(tpid, tuid, cred.pid)) {
1470+
fail = true;
1471+
break;
1472+
}
13811473
if (fprintf(pids_file, "%d", (int) cred.pid) < 0)
13821474
fail = true;
13831475
}
@@ -1450,7 +1542,7 @@ int cg_write(const char *path, const char *buf, size_t size, off_t offset,
14501542
strcmp(f->file, "/cgroup.procs") == 0 ||
14511543
strcmp(f->file, "cgroup.procs") == 0)
14521544
// special case - we have to translate the pids
1453-
r = do_write_pids(fc->pid, f->controller, f->cgroup, f->file, localbuf);
1545+
r = do_write_pids(fc->pid, fc->uid, f->controller, f->cgroup, f->file, localbuf);
14541546
else
14551547
r = cgfs_set_value(f->controller, f->cgroup, f->file, localbuf);
14561548

0 commit comments

Comments
 (0)