@@ -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