1
1
use std:: fs:: File ;
2
2
use std:: os:: fd:: AsFd ;
3
- use std:: os :: unix :: fs :: MetadataExt ;
3
+ use std:: path :: Path ;
4
4
5
- use anyhow:: Result ;
5
+ use anyhow:: { Context , Result } ;
6
6
use rustix:: fs:: { Gid , Uid } ;
7
7
use rustix:: process:: Pid ;
8
8
use rustix:: thread:: { CapabilitiesSecureBits , LinkNameSpaceType , UnshareFlags } ;
9
9
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
+
10
41
pub struct MntNamespace {
11
- fd : File ,
42
+ mnt_fd : File ,
43
+ uid_map : IdMap ,
44
+ gid_map : IdMap ,
12
45
}
13
46
14
47
impl MntNamespace {
15
48
/// Open the mount namespace of a process.
16
49
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" ) ?) } )
20
68
}
21
69
22
70
/// Enter the mount namespace.
@@ -32,7 +80,7 @@ impl MntNamespace {
32
80
33
81
// Switch this particular thread to the container's mount namespace.
34
82
rustix:: thread:: move_into_link_name_space (
35
- self . fd . as_fd ( ) ,
83
+ self . mnt_fd . as_fd ( ) ,
36
84
Some ( LinkNameSpaceType :: Mount ) ,
37
85
) ?;
38
86
@@ -51,18 +99,15 @@ impl MntNamespace {
51
99
//
52
100
// https://elixir.bootlin.com/linux/v6.11.1/source/fs/namei.c#L4073
53
101
// 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 ( ) ;
57
102
58
103
// By default `setuid` will drop capabilities when transitioning from root
59
104
// to non-root user. This bit prevents it so our code still have superpower.
60
105
rustix:: thread:: set_capabilities_secure_bits (
61
106
CapabilitiesSecureBits :: NO_SETUID_FIXUP ,
62
107
) ?;
63
108
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 ) ? ) ?;
66
111
67
112
Ok ( f ( ) )
68
113
} )
0 commit comments