int main()

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;
}