in antlir/nspawn_in_subvol/clonecaps/clonecaps.c [242:354]
int main(int argc, char** argv) {
if (argc < 3 || strcmp("--", argv[2]) != 0) {
fprintf(stderr, "Usage: clonecaps /proc/PID/status -- cmd argv1 ...\n");
return 1;
}
char* target_procfs_path = argv[1];
argv += 3; // Skip "our" args, this is now ready to `execv`.
argc -= 3;
// The running kernel may not match our compile-time headers.
int last_cap = find_last_cap();
if (last_cap == -1) { // The function already printed the error
return 1;
}
// We read this to check that `libcap-ng` worked correctly, since
// `check_all_caps` cannot.
char my_procfs_path[64]; // `/proc/PID/status` fits even with 64-bit PIDs
if (snprintf(
my_procfs_path,
sizeof(my_procfs_path),
"/proc/%d/status",
getpid()) >= sizeof(my_procfs_path)) {
fprintf(stderr, "PID too long??? %d\n", getpid());
return 1;
}
if (is_debug()) {
cap_bits_t cur_bits;
if (!read_procfs_cap_bits(my_procfs_path, &cur_bits)) {
return 1; // An error was already printed
}
fprint_cap_bits(stderr, "Initial procfs for getpid()", cur_bits);
}
cap_bits_t target_bits;
if (!read_procfs_cap_bits(target_procfs_path, &target_bits)) {
return 1; // An error was already printed
}
if (is_debug()) {
fprint_cap_bits(stderr, "Procfs for target PID", target_bits);
}
capng_clear(CAPNG_SELECT_ALL); // Clear traditional, bounding, ambient
// Clone the target's values
if (!(add_all_caps(last_cap, CAPNG_INHERITABLE, target_bits.inheritable) &&
add_all_caps(last_cap, CAPNG_PERMITTED, target_bits.permitted) &&
add_all_caps(last_cap, CAPNG_EFFECTIVE, target_bits.effective) &&
add_all_caps(last_cap, CAPNG_BOUNDING_SET, target_bits.bounding_set)
#ifdef CAPNG_SUPPORTS_AMBIENT
&& add_all_caps(last_cap, CAPNG_AMBIENT, target_bits.ambient)
#endif
)) {
return 1; // `add_all_caps` already printed an error
}
// Apply traditional & bounding
if (capng_apply(CAPNG_SELECT_ALL) != 0) {
fprint_cap_bits(stderr, "Failed to apply capabilities", target_bits);
return 1;
}
#ifdef CAPNG_SUPPORTS_AMBIENT
// Due to the following bug, ambient capabilities only get applied the
// second time around: https://github.com/stevegrubb/libcap-ng/issues/18
//
// This can be removed once both the OSS and FB versions of `libcap-ng`
// are guaranteed to include b6ff250a71a1f0a11b2917186155d2426080293d
// from https://github.com/stevegrubb/libcap-ng
if (capng_apply(CAPNG_SELECT_ALL) != 0) {
fprint_cap_bits(stderr, "Failed to re-apply capabilities", target_bits);
return 1;
}
#endif
if (!(check_all_caps(last_cap, CAPNG_INHERITABLE, target_bits.inheritable) &&
check_all_caps(last_cap, CAPNG_PERMITTED, target_bits.permitted) &&
check_all_caps(last_cap, CAPNG_EFFECTIVE, target_bits.effective) &&
check_all_caps(last_cap, CAPNG_BOUNDING_SET, target_bits.bounding_set)
#ifdef CAPNG_SUPPORTS_AMBIENT
&& check_all_caps(last_cap, CAPNG_AMBIENT, target_bits.ambient)
#endif
)) {
return 1; // `check_all_caps` already printed an error
}
cap_bits_t final_bits;
if (!read_procfs_cap_bits(my_procfs_path, &final_bits)) {
return 1; // An error was already printed
}
// Note that this will fail if the target proc has ambient caps that do
// not match ours, and our `libcap-ng` is old.
//
// This also detects an `libcap-ng` bug:
// https://github.com/stevegrubb/libcap-ng/issues/19
if (final_bits.inheritable != target_bits.inheritable ||
final_bits.permitted != target_bits.permitted ||
final_bits.effective != target_bits.effective ||
final_bits.bounding_set != target_bits.bounding_set ||
final_bits.ambient != target_bits.ambient) {
fprint_cap_bits(stderr, "After applying new capabilities", target_bits);
fprint_cap_bits(stderr, "Aborting, procfs does not match", final_bits);
return 1;
} else if (is_debug()) {
fprint_cap_bits(stderr, "Final procfs for getpid()", final_bits);
}
execv(argv[0], argv);
perror("execv");
return 1;
}