11use std:: fs:: File ;
22use std:: os:: fd:: AsFd ;
3- use std:: os :: unix :: fs :: MetadataExt ;
3+ use std:: path :: Path ;
44
5- use anyhow:: Result ;
5+ use anyhow:: { Context , Result } ;
66use rustix:: fs:: { Gid , Uid } ;
77use rustix:: process:: Pid ;
88use rustix:: thread:: { CapabilitiesSecureBits , LinkNameSpaceType , UnshareFlags } ;
99
10+ pub struct IdMap {
11+ map : Vec < ( u32 , u32 , u32 ) > ,
12+ }
13+
14+ impl IdMap {
15+ fn read ( path : & Path ) -> Result < Self > {
16+ Self :: parse ( & std:: fs:: read_to_string ( path) ?)
17+ }
18+
19+ fn parse ( content : & str ) -> Result < Self > {
20+ let mut map = Vec :: new ( ) ;
21+ for line in content. lines ( ) {
22+ let mut words = line. split_ascii_whitespace ( ) ;
23+ let inside = words. next ( ) . context ( "unexpected id_map" ) ?. parse ( ) ?;
24+ let outside = words. next ( ) . context ( "unexpected id_map" ) ?. parse ( ) ?;
25+ let count = words. next ( ) . context ( "unexpected id_map" ) ?. parse ( ) ?;
26+ map. push ( ( inside, outside, count) ) ;
27+ }
28+ Ok ( Self { map } )
29+ }
30+
31+ fn translate ( & self , id : u32 ) -> Option < u32 > {
32+ for & ( inside, outside, count) in self . map . iter ( ) {
33+ if ( inside..inside. checked_add ( count) ?) . contains ( & id) {
34+ return ( id - inside) . checked_add ( outside) ;
35+ }
36+ }
37+ None
38+ }
39+ }
40+
1041pub struct MntNamespace {
11- fd : File ,
42+ mnt_fd : File ,
43+ uid_map : IdMap ,
44+ gid_map : IdMap ,
1245}
1346
1447impl MntNamespace {
1548 /// Open the mount namespace of a process.
1649 pub fn of_pid ( pid : Pid ) -> Result < MntNamespace > {
17- let path = format ! ( "/proc/{}/ns/mnt" , pid. as_raw_nonzero( ) ) ;
18- let fd = File :: open ( path) ?;
19- Ok ( MntNamespace { fd } )
50+ let mnt_fd = File :: open ( format ! ( "/proc/{}/ns/mnt" , pid. as_raw_nonzero( ) ) ) ?;
51+ let uid_map = IdMap :: read ( format ! ( "/proc/{}/uid_map" , pid. as_raw_nonzero( ) ) . as_ref ( ) ) ?;
52+ let gid_map = IdMap :: read ( format ! ( "/proc/{}/gid_map" , pid. as_raw_nonzero( ) ) . as_ref ( ) ) ?;
53+ Ok ( MntNamespace {
54+ mnt_fd,
55+ uid_map,
56+ gid_map,
57+ } )
58+ }
59+
60+ /// Translate user ID into a UID in the namespace.
61+ pub fn uid ( & self , uid : u32 ) -> Result < Uid > {
62+ Ok ( unsafe { Uid :: from_raw ( self . uid_map . translate ( uid) . context ( "UID overflows" ) ?) } )
63+ }
64+
65+ /// Translate group ID into a GID in the namespace.
66+ pub fn gid ( & self , gid : u32 ) -> Result < Gid > {
67+ Ok ( unsafe { Gid :: from_raw ( self . gid_map . translate ( gid) . context ( "GID overflows" ) ?) } )
2068 }
2169
2270 /// Enter the mount namespace.
@@ -32,7 +80,7 @@ impl MntNamespace {
3280
3381 // Switch this particular thread to the container's mount namespace.
3482 rustix:: thread:: move_into_link_name_space (
35- self . fd . as_fd ( ) ,
83+ self . mnt_fd . as_fd ( ) ,
3684 Some ( LinkNameSpaceType :: Mount ) ,
3785 ) ?;
3886
@@ -51,18 +99,15 @@ impl MntNamespace {
5199 //
52100 // https://elixir.bootlin.com/linux/v6.11.1/source/fs/namei.c#L4073
53101 // https://elixir.bootlin.com/linux/v6.11.1/source/include/linux/cred.h#L111
54- let metadata = std:: fs:: metadata ( "/" ) ?;
55- let uid = metadata. uid ( ) ;
56- let gid = metadata. gid ( ) ;
57102
58103 // By default `setuid` will drop capabilities when transitioning from root
59104 // to non-root user. This bit prevents it so our code still have superpower.
60105 rustix:: thread:: set_capabilities_secure_bits (
61106 CapabilitiesSecureBits :: NO_SETUID_FIXUP ,
62107 ) ?;
63108
64- rustix:: thread:: set_thread_uid ( unsafe { Uid :: from_raw ( uid) } ) ?;
65- rustix:: thread:: set_thread_gid ( unsafe { Gid :: from_raw ( gid) } ) ?;
109+ rustix:: thread:: set_thread_uid ( self . uid ( 0 ) ? ) ?;
110+ rustix:: thread:: set_thread_gid ( self . gid ( 0 ) ? ) ?;
66111
67112 Ok ( f ( ) )
68113 } )
0 commit comments