/*
 * Copyright (C) 2022 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

//! A rust library wrapping the libcap functionality.

use anyhow::{bail, Result};
use cap_bindgen::{
    cap_clear_flag, cap_drop_bound, cap_flag_t, cap_free, cap_get_proc, cap_set_proc, cap_value_t,
    CAP_LAST_CAP,
};
use nix::errno::Errno;

/// Removes inheritable capabilities set for this process.
/// See: https://man7.org/linux/man-pages/man7/capabilities.7.html
pub fn drop_inheritable_caps() -> Result<()> {
    // SAFETY: we do not manipulate memory handled by libcap.
    unsafe {
        let caps = cap_get_proc();
        scopeguard::defer! {
            cap_free(caps as *mut std::os::raw::c_void);
        }
        if cap_clear_flag(caps, cap_flag_t::CAP_INHERITABLE) < 0 {
            let e = Errno::last();
            bail!("cap_clear_flag failed: {:?}", e)
        }
        if cap_set_proc(caps) < 0 {
            let e = Errno::last();
            bail!("cap_set_proc failed: {:?}", e)
        }
    }
    Ok(())
}

/// Drop bounding set capabitilies for this process.
/// See: https://man7.org/linux/man-pages/man7/capabilities.7.html
pub fn drop_bounding_set() -> Result<()> {
    let mut cap_id: cap_value_t = 0;
    while cap_id <= CAP_LAST_CAP.try_into().unwrap() {
        // SAFETY: we do not manipulate memory handled by libcap.
        unsafe {
            if cap_drop_bound(cap_id) == -1 {
                let e = Errno::last();
                bail!("cap_drop_bound failed for {}: {:?}", cap_id, e);
            }
        }
        cap_id += 1;
    }
    Ok(())
}

#[cfg(test)]
mod tests {
    use super::*;

    // Basic test to verify that calling drop_inheritable_caps doesn't fail
    #[test]
    fn test_drop_inheritable_caps() {
        let result = drop_inheritable_caps();
        assert!(result.is_ok(), "failed with: {:?}", result)
    }
}