|
| 1 | +// SPDX-License-Identifier: BSD-3-Clause |
| 2 | + |
| 3 | +use crate::oslib; |
| 4 | +use crate::passthrough::util::einval; |
| 5 | +use std::io; |
| 6 | + |
| 7 | +pub struct UnixCredentials { |
| 8 | + uid: libc::uid_t, |
| 9 | + gid: libc::gid_t, |
| 10 | + sup_gid: Option<u32>, |
| 11 | + keep_capability: bool, |
| 12 | +} |
| 13 | + |
| 14 | +impl UnixCredentials { |
| 15 | + pub fn new(uid: libc::uid_t, gid: libc::gid_t) -> Self { |
| 16 | + UnixCredentials { |
| 17 | + uid, |
| 18 | + gid, |
| 19 | + sup_gid: None, |
| 20 | + keep_capability: false, |
| 21 | + } |
| 22 | + } |
| 23 | + |
| 24 | + /// Set a supplementary group. Set `supported_extension` to `false` to signal that a |
| 25 | + /// supplementary group maybe required, but the guest was not able to tell us which, |
| 26 | + /// so we have to rely on keeping the DAC_OVERRIDE capability. |
| 27 | + pub fn supplementary_gid(self, supported_extension: bool, sup_gid: Option<u32>) -> Self { |
| 28 | + UnixCredentials { |
| 29 | + uid: self.uid, |
| 30 | + gid: self.gid, |
| 31 | + sup_gid, |
| 32 | + keep_capability: !supported_extension, |
| 33 | + } |
| 34 | + } |
| 35 | + |
| 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. |
| 38 | + pub fn set(self) -> io::Result<Option<UnixCredentialsGuard>> { |
| 39 | + let change_uid = self.uid != 0; |
| 40 | + let change_gid = self.gid != 0; |
| 41 | + |
| 42 | + // We have to change the gid before we change the uid because if we |
| 43 | + // change the uid first then we lose the capability to change the gid. |
| 44 | + // However changing back can happen in any order. |
| 45 | + if let Some(sup_gid) = self.sup_gid { |
| 46 | + oslib::setsupgroup(sup_gid)?; |
| 47 | + } |
| 48 | + |
| 49 | + if change_gid { |
| 50 | + oslib::seteffgid(self.gid)?; |
| 51 | + } |
| 52 | + |
| 53 | + if change_uid { |
| 54 | + oslib::seteffuid(self.uid)?; |
| 55 | + } |
| 56 | + |
| 57 | + if change_uid && self.keep_capability { |
| 58 | + // Before kernel 6.3, we don't have access to process supplementary groups. |
| 59 | + // To work around this we can set the `DAC_OVERRIDE` in the effective set. |
| 60 | + // We are allowed to set the capability because we only change the effective |
| 61 | + // user ID, so we still have the 'DAC_OVERRIDE' in the permitted set. |
| 62 | + // After switching back to root the permitted set is copied to the effective set, |
| 63 | + // so no additional steps are required. |
| 64 | + if let Err(e) = crate::util::add_cap_to_eff("DAC_OVERRIDE") { |
| 65 | + warn!("failed to add 'DAC_OVERRIDE' to the effective set of capabilities: {e}"); |
| 66 | + } |
| 67 | + } |
| 68 | + |
| 69 | + if !change_uid && !change_gid { |
| 70 | + return Ok(None); |
| 71 | + } |
| 72 | + |
| 73 | + Ok(Some(UnixCredentialsGuard { |
| 74 | + reset_uid: change_uid, |
| 75 | + reset_gid: change_gid, |
| 76 | + drop_sup_gid: self.sup_gid.is_some(), |
| 77 | + })) |
| 78 | + } |
| 79 | +} |
| 80 | + |
| 81 | +pub struct UnixCredentialsGuard { |
| 82 | + reset_uid: bool, |
| 83 | + reset_gid: bool, |
| 84 | + drop_sup_gid: bool, |
| 85 | +} |
| 86 | + |
| 87 | +impl Drop for UnixCredentialsGuard { |
| 88 | + fn drop(&mut self) { |
| 89 | + if self.reset_uid { |
| 90 | + oslib::seteffuid(0).unwrap_or_else(|e| { |
| 91 | + error!("failed to change uid back to root: {e}"); |
| 92 | + }); |
| 93 | + } |
| 94 | + |
| 95 | + if self.reset_gid { |
| 96 | + oslib::seteffgid(0).unwrap_or_else(|e| { |
| 97 | + error!("failed to change gid back to root: {e}"); |
| 98 | + }); |
| 99 | + } |
| 100 | + |
| 101 | + if self.drop_sup_gid { |
| 102 | + oslib::dropsupgroups().unwrap_or_else(|e| { |
| 103 | + error!("failed to drop supplementary groups: {e}"); |
| 104 | + }); |
| 105 | + } |
| 106 | + } |
| 107 | +} |
| 108 | + |
| 109 | +pub struct ScopedCaps { |
| 110 | + cap: capng::Capability, |
| 111 | +} |
| 112 | + |
| 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 | + } |
| 147 | + } |
| 148 | +} |
| 149 | + |
| 150 | +impl Drop for ScopedCaps { |
| 151 | + fn drop(&mut self) { |
| 152 | + use capng::{Action, CUpdate, Set, Type}; |
| 153 | + |
| 154 | + let req = vec![CUpdate { |
| 155 | + action: Action::ADD, |
| 156 | + cap_type: Type::EFFECTIVE, |
| 157 | + capability: self.cap, |
| 158 | + }]; |
| 159 | + |
| 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 | + } |
| 170 | +} |
| 171 | + |
| 172 | +pub fn drop_effective_cap(cap_name: &str) -> io::Result<Option<ScopedCaps>> { |
| 173 | + ScopedCaps::new(cap_name) |
| 174 | +} |
0 commit comments