11// SPDX-License-Identifier: BSD-3-Clause
22
3- use crate :: oslib;
4- use crate :: passthrough:: util:: einval;
53use std:: io;
64
5+ use super :: util:: { dropsupgroups, seteffgid, seteffuid, setsupgroup} ;
6+
77pub struct UnixCredentials {
88 uid : libc:: uid_t ,
99 gid : libc:: gid_t ,
@@ -24,6 +24,7 @@ impl UnixCredentials {
2424 /// Set a supplementary group. Set `supported_extension` to `false` to signal that a
2525 /// supplementary group maybe required, but the guest was not able to tell us which,
2626 /// so we have to rely on keeping the DAC_OVERRIDE capability.
27+ #[ allow( dead_code) ]
2728 pub fn supplementary_gid ( self , supported_extension : bool , sup_gid : Option < u32 > ) -> Self {
2829 UnixCredentials {
2930 uid : self . uid ,
@@ -33,8 +34,9 @@ impl UnixCredentials {
3334 }
3435 }
3536
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.
3840 pub fn set ( self ) -> io:: Result < Option < UnixCredentialsGuard > > {
3941 let change_uid = self . uid != 0 ;
4042 let change_gid = self . gid != 0 ;
@@ -43,15 +45,15 @@ impl UnixCredentials {
4345 // change the uid first then we lose the capability to change the gid.
4446 // However changing back can happen in any order.
4547 if let Some ( sup_gid) = self . sup_gid {
46- oslib :: setsupgroup ( sup_gid) ?;
48+ setsupgroup ( sup_gid) ?;
4749 }
4850
4951 if change_gid {
50- oslib :: seteffgid ( self . gid ) ?;
52+ seteffgid ( self . gid ) ?;
5153 }
5254
5355 if change_uid {
54- oslib :: seteffuid ( self . uid ) ?;
56+ seteffuid ( self . uid ) ?;
5557 }
5658
5759 if change_uid && self . keep_capability {
@@ -61,7 +63,7 @@ impl UnixCredentials {
6163 // user ID, so we still have the 'DAC_OVERRIDE' in the permitted set.
6264 // After switching back to root the permitted set is copied to the effective set,
6365 // 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 ) {
6567 warn ! ( "failed to add 'DAC_OVERRIDE' to the effective set of capabilities: {e}" ) ;
6668 }
6769 }
@@ -87,88 +89,100 @@ pub struct UnixCredentialsGuard {
8789impl Drop for UnixCredentialsGuard {
8890 fn drop ( & mut self ) {
8991 if self . reset_uid {
90- oslib :: seteffuid ( 0 ) . unwrap_or_else ( |e| {
92+ seteffuid ( 0 ) . unwrap_or_else ( |e| {
9193 error ! ( "failed to change uid back to root: {e}" ) ;
9294 } ) ;
9395 }
9496
9597 if self . reset_gid {
96- oslib :: seteffgid ( 0 ) . unwrap_or_else ( |e| {
98+ seteffgid ( 0 ) . unwrap_or_else ( |e| {
9799 error ! ( "failed to change gid back to root: {e}" ) ;
98100 } ) ;
99101 }
100102
101103 if self . drop_sup_gid {
102- oslib :: dropsupgroups ( ) . unwrap_or_else ( |e| {
104+ dropsupgroups ( ) . unwrap_or_else ( |e| {
103105 error ! ( "failed to drop supplementary groups: {e}" ) ;
104106 } ) ;
105107 }
106108 }
107109}
108110
109111pub struct ScopedCaps {
110- cap : capng :: Capability ,
112+ capability : caps :: Capability ,
111113}
112114
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+ } ;
147120 }
148121}
149122
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+ }
153134
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+ }
159138
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- ) ;
168- }
169- }
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+ } )
170156}
171157
172- pub fn drop_effective_cap ( cap_name : & str ) -> io:: Result < Option < ScopedCaps > > {
173- ScopedCaps :: new ( cap_name)
158+ #[ cfg( test) ]
159+ mod tests {
160+ use super :: * ;
161+
162+ #[ test]
163+ fn test_unix_credentials_set ( ) {
164+ let cred = UnixCredentials :: new ( 0 , 0 ) . set ( ) . unwrap ( ) ;
165+ assert ! ( cred. is_none( ) ) ;
166+ drop ( cred) ;
167+
168+ let cred = UnixCredentials :: new ( 1 , 1 ) ;
169+ let cred = cred. supplementary_gid ( false , Some ( 2 ) ) ;
170+ let guard = cred. set ( ) . unwrap ( ) ;
171+ assert ! ( guard. is_some( ) ) ;
172+ drop ( guard) ;
173+ }
174+
175+ #[ test]
176+ fn test_drop_cap_fssetid ( ) {
177+ let cap = drop_cap_fssetid ( ) . unwrap ( ) ;
178+ let has_cap =
179+ caps:: has_cap ( None , caps:: CapSet :: Effective , caps:: Capability :: CAP_FSETID ) . unwrap ( ) ;
180+ assert_eq ! ( has_cap, false ) ;
181+ drop ( cap) ;
182+ }
183+
184+ #[ test]
185+ fn test_add_cap_to_eff ( ) {
186+ add_cap_to_eff ( caps:: Capability :: CAP_DAC_OVERRIDE ) . unwrap ( ) ;
187+ }
174188}
0 commit comments