1
1
// SPDX-License-Identifier: BSD-3-Clause
2
2
3
- use crate :: oslib;
4
- use crate :: passthrough:: util:: einval;
5
3
use std:: io;
6
4
5
+ use super :: util:: { dropsupgroups, seteffgid, seteffuid, setsupgroup} ;
6
+
7
7
pub struct UnixCredentials {
8
8
uid : libc:: uid_t ,
9
9
gid : libc:: gid_t ,
@@ -24,6 +24,7 @@ impl UnixCredentials {
24
24
/// Set a supplementary group. Set `supported_extension` to `false` to signal that a
25
25
/// supplementary group maybe required, but the guest was not able to tell us which,
26
26
/// so we have to rely on keeping the DAC_OVERRIDE capability.
27
+ #[ allow( dead_code) ]
27
28
pub fn supplementary_gid ( self , supported_extension : bool , sup_gid : Option < u32 > ) -> Self {
28
29
UnixCredentials {
29
30
uid : self . uid ,
@@ -33,8 +34,9 @@ impl UnixCredentials {
33
34
}
34
35
}
35
36
36
- /// Changes the effective uid/gid of the current thread to `val`. Changes
37
- /// the thread's credentials back to root when the returned struct is dropped.
37
+ /// Changes the effective uid/gid of the current thread to `val`.
38
+ ///
39
+ /// Changes the thread's credentials back to root when the returned struct is dropped.
38
40
pub fn set ( self ) -> io:: Result < Option < UnixCredentialsGuard > > {
39
41
let change_uid = self . uid != 0 ;
40
42
let change_gid = self . gid != 0 ;
@@ -43,15 +45,15 @@ impl UnixCredentials {
43
45
// change the uid first then we lose the capability to change the gid.
44
46
// However changing back can happen in any order.
45
47
if let Some ( sup_gid) = self . sup_gid {
46
- oslib :: setsupgroup ( sup_gid) ?;
48
+ setsupgroup ( sup_gid) ?;
47
49
}
48
50
49
51
if change_gid {
50
- oslib :: seteffgid ( self . gid ) ?;
52
+ seteffgid ( self . gid ) ?;
51
53
}
52
54
53
55
if change_uid {
54
- oslib :: seteffuid ( self . uid ) ?;
56
+ seteffuid ( self . uid ) ?;
55
57
}
56
58
57
59
if change_uid && self . keep_capability {
@@ -61,7 +63,7 @@ impl UnixCredentials {
61
63
// user ID, so we still have the 'DAC_OVERRIDE' in the permitted set.
62
64
// After switching back to root the permitted set is copied to the effective set,
63
65
// so no additional steps are required.
64
- if let Err ( e) = crate :: util :: add_cap_to_eff ( "DAC_OVERRIDE" ) {
66
+ if let Err ( e) = add_cap_to_eff ( caps :: Capability :: CAP_DAC_OVERRIDE ) {
65
67
warn ! ( "failed to add 'DAC_OVERRIDE' to the effective set of capabilities: {e}" ) ;
66
68
}
67
69
}
@@ -87,88 +89,105 @@ pub struct UnixCredentialsGuard {
87
89
impl Drop for UnixCredentialsGuard {
88
90
fn drop ( & mut self ) {
89
91
if self . reset_uid {
90
- oslib :: seteffuid ( 0 ) . unwrap_or_else ( |e| {
92
+ seteffuid ( 0 ) . unwrap_or_else ( |e| {
91
93
error ! ( "failed to change uid back to root: {e}" ) ;
92
94
} ) ;
93
95
}
94
96
95
97
if self . reset_gid {
96
- oslib :: seteffgid ( 0 ) . unwrap_or_else ( |e| {
98
+ seteffgid ( 0 ) . unwrap_or_else ( |e| {
97
99
error ! ( "failed to change gid back to root: {e}" ) ;
98
100
} ) ;
99
101
}
100
102
101
103
if self . drop_sup_gid {
102
- oslib :: dropsupgroups ( ) . unwrap_or_else ( |e| {
104
+ dropsupgroups ( ) . unwrap_or_else ( |e| {
103
105
error ! ( "failed to drop supplementary groups: {e}" ) ;
104
106
} ) ;
105
107
}
106
108
}
107
109
}
108
110
109
111
pub struct ScopedCaps {
110
- cap : capng :: Capability ,
112
+ capability : caps :: Capability ,
111
113
}
112
114
113
- impl ScopedCaps {
114
- fn new ( cap_name : & str ) -> io:: Result < Option < Self > > {
115
- use capng:: { Action , CUpdate , Set , Type } ;
116
-
117
- let cap = capng:: name_to_capability ( cap_name) . map_err ( |_| {
118
- let err = io:: Error :: last_os_error ( ) ;
119
- error ! (
120
- "couldn't get the capability id for name {}: {:?}" ,
121
- cap_name, err
122
- ) ;
123
- err
124
- } ) ?;
125
-
126
- if capng:: have_capability ( Type :: EFFECTIVE , cap) {
127
- let req = vec ! [ CUpdate {
128
- action: Action :: DROP ,
129
- cap_type: Type :: EFFECTIVE ,
130
- capability: cap,
131
- } ] ;
132
- capng:: update ( req) . map_err ( |e| {
133
- error ! ( "couldn't drop {} capability: {:?}" , cap, e) ;
134
- einval ( )
135
- } ) ?;
136
- capng:: apply ( Set :: CAPS ) . map_err ( |e| {
137
- error ! (
138
- "couldn't apply capabilities after dropping {}: {:?}" ,
139
- cap, e
140
- ) ;
141
- einval ( )
142
- } ) ?;
143
- Ok ( Some ( Self { cap } ) )
144
- } else {
145
- Ok ( None )
146
- }
115
+ impl Drop for ScopedCaps {
116
+ fn drop ( & mut self ) {
117
+ if let Err ( e) = caps:: raise ( None , caps:: CapSet :: Effective , self . capability ) {
118
+ error ! ( "fail to restore thread cap_fsetid: {}" , e) ;
119
+ } ;
147
120
}
148
121
}
149
122
150
- impl Drop for ScopedCaps {
151
- fn drop ( & mut self ) {
152
- use capng:: { Action , CUpdate , Set , Type } ;
123
+ pub fn scoped_drop_capability ( capability : caps:: Capability ) -> io:: Result < Option < ScopedCaps > > {
124
+ if !caps:: has_cap ( None , caps:: CapSet :: Effective , capability)
125
+ . map_err ( |_e| io:: Error :: new ( io:: ErrorKind :: PermissionDenied , "no capability" ) ) ?
126
+ {
127
+ return Ok ( None ) ;
128
+ }
129
+ caps:: drop ( None , caps:: CapSet :: Effective , capability) . map_err ( |_e| {
130
+ io:: Error :: new ( io:: ErrorKind :: PermissionDenied , "failed to drop capability" )
131
+ } ) ?;
132
+ Ok ( Some ( ScopedCaps { capability } ) )
133
+ }
153
134
154
- let req = vec ! [ CUpdate {
155
- action: Action :: ADD ,
156
- cap_type: Type :: EFFECTIVE ,
157
- capability: self . cap,
158
- } ] ;
135
+ pub fn drop_cap_fssetid ( ) -> io:: Result < Option < ScopedCaps > > {
136
+ scoped_drop_capability ( caps:: Capability :: CAP_FSETID )
137
+ }
159
138
160
- if let Err ( e) = capng:: update ( req) {
161
- panic ! ( "couldn't restore {} capability: {:?}" , self . cap, e) ;
162
- }
163
- if let Err ( e) = capng:: apply ( Set :: CAPS ) {
164
- panic ! (
165
- "couldn't apply capabilities after restoring {}: {:?}" ,
166
- self . cap, e
167
- ) ;
139
+ /// Add a capability to the effective set
140
+ ///
141
+ /// # Errors
142
+ /// An error variant will be returned:
143
+ /// - if the input string does not match the name, without the 'CAP_' prefix,
144
+ /// of any of the capability defined in `linux/capabiliy.h`.
145
+ /// - if `capng::get_caps_process()` cannot get the capabilities and bounding set of the process.
146
+ /// - if `capng::update()` fails to update the internal posix capabilities settings.
147
+ /// - if `capng::apply()` fails to transfer the specified internal posix capabilities
148
+ /// settings to the kernel.
149
+ pub fn add_cap_to_eff ( capability : caps:: Capability ) -> io:: Result < ( ) > {
150
+ caps:: raise ( None , caps:: CapSet :: Effective , capability) . map_err ( |_e| {
151
+ io:: Error :: new (
152
+ io:: ErrorKind :: PermissionDenied ,
153
+ "failed to raise capability" ,
154
+ )
155
+ } )
156
+ }
157
+
158
+ #[ cfg( test) ]
159
+ mod tests {
160
+ use super :: * ;
161
+ use nix:: unistd:: getuid;
162
+
163
+ #[ test]
164
+ fn test_unix_credentials_set ( ) {
165
+ if getuid ( ) == 0 {
166
+ let cred = UnixCredentials :: new ( 0 , 0 ) . set ( ) . unwrap ( ) ;
167
+ assert ! ( cred. is_none( ) ) ;
168
+ drop ( cred) ;
169
+
170
+ let cred = UnixCredentials :: new ( 1 , 1 ) ;
171
+ let cred = cred. supplementary_gid ( false , Some ( 2 ) ) ;
172
+ let guard = cred. set ( ) . unwrap ( ) ;
173
+ assert ! ( guard. is_some( ) ) ;
174
+ drop ( guard) ;
168
175
}
169
176
}
170
- }
171
177
172
- pub fn drop_effective_cap ( cap_name : & str ) -> io:: Result < Option < ScopedCaps > > {
173
- ScopedCaps :: new ( cap_name)
178
+ #[ test]
179
+ fn test_drop_cap_fssetid ( ) {
180
+ let cap = drop_cap_fssetid ( ) . unwrap ( ) ;
181
+ let has_cap =
182
+ caps:: has_cap ( None , caps:: CapSet :: Effective , caps:: Capability :: CAP_FSETID ) . unwrap ( ) ;
183
+ assert_eq ! ( has_cap, false ) ;
184
+ drop ( cap) ;
185
+ }
186
+
187
+ #[ test]
188
+ fn test_add_cap_to_eff ( ) {
189
+ if getuid ( ) == 0 {
190
+ add_cap_to_eff ( caps:: Capability :: CAP_DAC_OVERRIDE ) . unwrap ( ) ;
191
+ }
192
+ }
174
193
}
0 commit comments