Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Gracefully handle removing capabilities that this library does not know about #87

Open
Alex-Rockliff opened this issue Oct 9, 2024 · 1 comment

Comments

@Alex-Rockliff
Copy link

In the future it is possible that this library lags behind the introduction of new capabilities. If this library does not know about a capability, the only option it has for dropping that capability is to clear the entire set.

Consider the following:

fn required_caps() -> CapsHashSet {
    // Any caps you might want here
    HashSet::from([
        Capability::CAP_KILL,
        Capability::CAP_SETGID,
        Capability::CAP_SETUID,
        Capability::CAP_CHOWN,
        Capability::CAP_FOWNER,
    ])
}

fn drop_caps(set: CapSet) -> Result<()> {
    let required = required_caps();

    let current = caps::read(None, set)?;
    let missing = required.difference(&current);
    let missing: Vec<_> = missing.collect();
    if !missing.is_empty() {
        return Err(anyhow!(
            "missing required capabilities: {:?} {:?}",
            set,
            missing
        ));
    }

    // Take the current capabilities that we have but don't want, and clear them
    let to_drop = current.difference(&required);

    warn!("{:?} caps to drop: {:?}", set, to_drop);
    for cap in to_drop {
        caps::drop(None, set, cap.to_owned())
            .unwrap_or_else(|_| panic!("failed to drop {:?} cap {:?}", set, cap));
    }

    Ok(())
}

In this case, if caps::read doesn't contain a new capability as it's not been added to the enum, the capability will still exist at the end of drop_caps function.

capctl does handle this, and to me it seems like it'd be a pretty simple function (clear_unknown) to add here too: https://docs.rs/capctl/latest/capctl/#handling-of-newly-added-capabilities

@Alex-Rockliff
Copy link
Author

Reading the implementation a bit closer, it looks like this may already be happening when you drop a cap in the base library:

If I have a thread with caps [a, b, c, d], but this crate only knows about [a, b, c], and I want to drop my caps back to [a, b], then when I call drop(tid, cset, c) it appears as though it will do this:

  • Get all current caps for the thread as raw data
  • Convert to HashSet of enum values
  • Remove c from the hashset
  • Convert hashset back to raw data
  • Set thread caps from hashset

In this case, the conversion from raw data to a hashset of enum values will automatically result in a set that doesn't contain caps this library doesn't know about. When converting back to raw data, the new data won't contain the unknown caps, and the call to capset will get rid of them.

This doesn't seem to apply to ambient/bounding caps, which have a different code path, but it does solve my immediate problem.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant