#endif
#if WITH_CAPNG
# include <cap-ng.h>
+# include <sys/prctl.h>
#endif
#if defined HAVE_MNTENT_H && defined HAVE_GETMNTENT_R
# include <mntent.h>
}
#endif /* HAVE_GETPWUID_R */
+#if WITH_CAPNG
+/* Set the real and effective uid and gid to the given values, while
+ * maintaining the capabilities indicated by bits in @capBits. Return
+ * 0 on success, -1 on failure (the original system error remains in
+ * errno).
+ */
+int
+virSetUIDGIDWithCaps(uid_t uid, gid_t gid, unsigned long long capBits)
+{
+ int ii, capng_ret, ret = -1;
+ bool need_setgid = false, need_setuid = false;
+ bool need_prctl = false, need_setpcap = false;
+
+ /* First drop all caps except those in capBits + the extra ones we
+ * need to change uid/gid and change the capabilities bounding
+ * set.
+ */
+
+ capng_clear(CAPNG_SELECT_BOTH);
+
+ for (ii = 0; ii <= CAP_LAST_CAP; ii++) {
+ if (capBits & (1ULL << ii)) {
+ capng_update(CAPNG_ADD,
+ CAPNG_EFFECTIVE|CAPNG_INHERITABLE|
+ CAPNG_PERMITTED|CAPNG_BOUNDING_SET,
+ ii);
+ }
+ }
+
+ if (gid != (gid_t)-1 &&
+ !capng_have_capability(CAPNG_EFFECTIVE, CAP_SETGID)) {
+ need_setgid = true;
+ capng_update(CAPNG_ADD, CAPNG_EFFECTIVE|CAPNG_PERMITTED, CAP_SETGID);
+ }
+ if (uid != (uid_t)-1 &&
+ !capng_have_capability(CAPNG_EFFECTIVE, CAP_SETUID)) {
+ need_setuid = true;
+ capng_update(CAPNG_ADD, CAPNG_EFFECTIVE|CAPNG_PERMITTED, CAP_SETUID);
+ }
+# ifdef PR_CAPBSET_DROP
+ /* If newer kernel, we need also need setpcap to change the bounding set */
+ if ((capBits || need_setgid || need_setuid) &&
+ !capng_have_capability(CAPNG_EFFECTIVE, CAP_SETPCAP)) {
+ need_setpcap = true;
+ }
+ if (need_setpcap)
+ capng_update(CAPNG_ADD, CAPNG_EFFECTIVE|CAPNG_PERMITTED, CAP_SETPCAP);
+# endif
+
+ need_prctl = capBits || need_setgid || need_setuid || need_setpcap;
+
+ /* Tell system we want to keep caps across uid change */
+ if (need_prctl && prctl(PR_SET_KEEPCAPS, 1, 0, 0, 0)) {
+ virReportSystemError(errno, "%s",
+ _("prctl failed to set KEEPCAPS"));
+ goto cleanup;
+ }
+
+ /* Change to the temp capabilities */
+ if ((capng_ret = capng_apply(CAPNG_SELECT_BOTH)) < 0) {
+ virReportError(VIR_ERR_INTERNAL_ERROR,
+ _("cannot apply process capabilities %d"), capng_ret);
+ goto cleanup;
+ }
+
+ if (virSetUIDGID(uid, gid) < 0)
+ goto cleanup;
+
+ /* Tell it we are done keeping capabilities */
+ if (need_prctl && prctl(PR_SET_KEEPCAPS, 0, 0, 0, 0)) {
+ virReportSystemError(errno, "%s",
+ _("prctl failed to reset KEEPCAPS"));
+ goto cleanup;
+ }
+
+ /* Drop the caps that allow setuid/gid (unless they were requested) */
+ if (need_setgid)
+ capng_update(CAPNG_DROP, CAPNG_EFFECTIVE|CAPNG_PERMITTED, CAP_SETGID);
+ if (need_setuid)
+ capng_update(CAPNG_DROP, CAPNG_EFFECTIVE|CAPNG_PERMITTED, CAP_SETUID);
+ /* Throw away CAP_SETPCAP so no more changes */
+ if (need_setpcap)
+ capng_update(CAPNG_DROP, CAPNG_EFFECTIVE|CAPNG_PERMITTED, CAP_SETPCAP);
+
+ if (need_prctl && ((capng_ret = capng_apply(CAPNG_SELECT_BOTH)) < 0)) {
+ virReportError(VIR_ERR_INTERNAL_ERROR,
+ _("cannot apply process capabilities %d"), capng_ret);
+ ret = -1;
+ goto cleanup;
+ }
+
+ ret = 0;
+cleanup:
+ return ret;
+}
+
+#else
+/*
+ * On platforms without libcapng, the capabilities setting is treated
+ * as a NOP.
+ */
+
+int
+virSetUIDGIDWithCaps(uid_t uid, gid_t gid,
+ unsigned long long capBits ATTRIBUTE_UNUSED)
+{
+ return virSetUIDGID(uid, gid);
+}
+#endif
+
#if defined HAVE_MNTENT_H && defined HAVE_GETMNTENT_R
/* search /proc/mounts for mount point of *type; return pointer to