diff --git a/include/libvirt/virterror.h b/include/libvirt/virterror.h
index 15ba4f1792..85dd74cbbe 100644
--- a/include/libvirt/virterror.h
+++ b/include/libvirt/virterror.h
@@ -124,6 +124,8 @@ typedef enum {
VIR_FROM_CRYPTO = 58, /* Error from crypto code */
VIR_FROM_FIREWALL = 59, /* Error from firewall */
+ VIR_FROM_POLKIT = 60, /* Error from polkit code */
+
# ifdef VIR_ENUM_SENTINELS
VIR_ERR_DOMAIN_LAST
# endif
diff --git a/po/POTFILES.in b/po/POTFILES.in
index f17b35fcaa..1a0b75ed9f 100644
--- a/po/POTFILES.in
+++ b/po/POTFILES.in
@@ -194,6 +194,7 @@ src/util/virnuma.c
src/util/virobject.c
src/util/virpci.c
src/util/virpidfile.c
+src/util/virpolkit.c
src/util/virportallocator.c
src/util/virprocess.c
src/util/virrandom.c
diff --git a/src/Makefile.am b/src/Makefile.am
index fa741a809d..3afae7b9d1 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -141,6 +141,7 @@ UTIL_SOURCES = \
util/virobject.c util/virobject.h \
util/virpci.c util/virpci.h \
util/virpidfile.c util/virpidfile.h \
+ util/virpolkit.c util/virpolkit.h \
util/virportallocator.c util/virportallocator.h \
util/virprobe.h \
util/virprocess.c util/virprocess.h \
@@ -988,11 +989,13 @@ libvirt_util_la_SOURCES = \
libvirt_util_la_CFLAGS = $(CAPNG_CFLAGS) $(YAJL_CFLAGS) $(LIBNL_CFLAGS) \
$(AM_CFLAGS) $(AUDIT_CFLAGS) $(DEVMAPPER_CFLAGS) \
$(DBUS_CFLAGS) $(LDEXP_LIBM) $(NUMACTL_CFLAGS) \
- $(SYSTEMD_DAEMON_CFLAGS) -I$(top_srcdir)/src/conf
+ $(SYSTEMD_DAEMON_CFLAGS) $(POLKIT_CFLAGS) \
+ -I$(top_srcdir)/src/conf
libvirt_util_la_LIBADD = $(CAPNG_LIBS) $(YAJL_LIBS) $(LIBNL_LIBS) \
$(THREAD_LIBS) $(AUDIT_LIBS) $(DEVMAPPER_LIBS) \
$(LIB_CLOCK_GETTIME) $(DBUS_LIBS) $(MSCOM_LIBS) $(LIBXML_LIBS) \
- $(SECDRIVER_LIBS) $(NUMACTL_LIBS) $(SYSTEMD_DAEMON_LIBS)
+ $(SECDRIVER_LIBS) $(NUMACTL_LIBS) $(SYSTEMD_DAEMON_LIBS) \
+ $(POLKIT_LIBS)
noinst_LTLIBRARIES += libvirt_conf.la
diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms
index a339ced69d..02ac4e9352 100644
--- a/src/libvirt_private.syms
+++ b/src/libvirt_private.syms
@@ -1794,6 +1794,10 @@ virPidFileWrite;
virPidFileWritePath;
+# util/virpolkit.h
+virPolkitCheckAuth;
+
+
# util/virportallocator.h
virPortAllocatorAcquire;
virPortAllocatorNew;
diff --git a/src/util/virerror.c b/src/util/virerror.c
index 6bd3d09cfd..4aa6d04088 100644
--- a/src/util/virerror.c
+++ b/src/util/virerror.c
@@ -130,6 +130,8 @@ VIR_ENUM_IMPL(virErrorDomain, VIR_ERR_DOMAIN_LAST,
"Bhyve",
"Crypto",
"Firewall",
+
+ "Polkit", /* 60 */
)
diff --git a/src/util/virpolkit.c b/src/util/virpolkit.c
new file mode 100644
index 0000000000..620bfda25e
--- /dev/null
+++ b/src/util/virpolkit.c
@@ -0,0 +1,263 @@
+/*
+ * virpolkit.c: helpers for using polkit APIs
+ *
+ * Copyright (C) 2013 Red Hat, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see
+ * .
+ *
+ */
+
+#include
+
+#if WITH_POLKIT0
+# include
+# include
+#endif
+
+#include "virpolkit.h"
+#include "vircommand.h"
+#include "virerror.h"
+#include "virlog.h"
+#include "virstring.h"
+#include "virprocess.h"
+#include "viralloc.h"
+#include "virdbus.h"
+
+#define VIR_FROM_THIS VIR_FROM_POLKIT
+
+VIR_LOG_INIT("util.polkit");
+
+#if WITH_POLKIT1
+/*
+ * virPolkitCheckAuth:
+ * @actionid: permission to check
+ * @pid: client process ID
+ * @startTime: process start time, or 0
+ * @uid: client process user ID
+ * @details: NULL terminated (key, value) pair list
+ * @allowInteraction: true if auth prompts are allowed
+ *
+ * Check if a client is authenticated with polkit
+ *
+ * Returns 0 on success, -1 on failure, -2 on auth denied
+ */
+int virPolkitCheckAuth(const char *actionid,
+ pid_t pid,
+ unsigned long long startTime,
+ uid_t uid,
+ const char **details,
+ bool allowInteraction)
+{
+ int status = -1;
+ bool authdismissed = 0;
+ bool supportsuid = 0;
+ char *pkout = NULL;
+ virCommandPtr cmd = NULL;
+ int ret = -1;
+ static bool polkitInsecureWarned = false;
+
+ VIR_DEBUG("Checking PID %lld UID %d startTime %llu",
+ (long long)pid, (int)uid, startTime);
+
+ if (startTime == 0) {
+ virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
+ _("Start time is required for polkit auth"));
+ return -1;
+ }
+
+ cmd = virCommandNewArgList(PKCHECK_PATH, "--action-id", actionid, NULL);
+ virCommandSetOutputBuffer(cmd, &pkout);
+ virCommandSetErrorBuffer(cmd, &pkout);
+
+ virCommandAddArg(cmd, "--process");
+# ifdef PKCHECK_SUPPORTS_UID
+ supportsuid = 1;
+# endif
+ if (supportsuid) {
+ virCommandAddArgFormat(cmd, "%lld,%llu,%lu",
+ (long long)pid, startTime, (unsigned long)uid);
+ } else {
+ if (!polkitInsecureWarned) {
+ VIR_WARN("No support for caller UID with pkcheck. This deployment is known to be insecure.");
+ polkitInsecureWarned = true;
+ }
+ virCommandAddArgFormat(cmd, "%lld,%llu",
+ (long long)pid, startTime);
+ }
+ if (allowInteraction)
+ virCommandAddArg(cmd, "--allow-user-interaction");
+
+ while (details && details[0] && details[1]) {
+ virCommandAddArgList(cmd, "--detail", details[0], details[1], NULL);
+ details += 2;
+ }
+
+ if (virCommandRun(cmd, &status) < 0)
+ goto cleanup;
+
+ authdismissed = (pkout && strstr(pkout, "dismissed=true"));
+ if (status != 0) {
+ char *tmp = virProcessTranslateStatus(status);
+ VIR_DEBUG("Policy kit denied action %s from pid %lld, uid %d: %s",
+ actionid, (long long)pid, (int)uid, NULLSTR(tmp));
+ VIR_FREE(tmp);
+ ret = -2;
+ goto cleanup;
+ }
+
+ VIR_DEBUG("Policy allowed action %s from pid %lld, uid %d",
+ actionid, (long long)pid, (int)uid);
+
+ ret = 0;
+
+ cleanup:
+ if (ret < 0) {
+ virResetLastError();
+
+ if (authdismissed) {
+ virReportError(VIR_ERR_AUTH_CANCELLED, "%s",
+ _("authentication cancelled by user"));
+ } else if (pkout && *pkout) {
+ virReportError(VIR_ERR_AUTH_FAILED, _("polkit: %s"), pkout);
+ } else {
+ virReportError(VIR_ERR_AUTH_FAILED, "%s", _("authentication failed"));
+ }
+ }
+
+ virCommandFree(cmd);
+ VIR_FREE(pkout);
+ return ret;
+}
+
+
+#elif WITH_POLKIT0
+int virPolkitCheckAuth(const char *actionid,
+ pid_t pid,
+ unsigned long long startTime ATTRIBUTE_UNUSED,
+ uid_t uid,
+ const char **details,
+ bool allowInteraction ATTRIBUTE_UNUSED)
+{
+ PolKitCaller *pkcaller = NULL;
+ PolKitAction *pkaction = NULL;
+ PolKitContext *pkcontext = NULL;
+ PolKitError *pkerr = NULL;
+ PolKitResult pkresult;
+ DBusError err;
+ DBusConnection *sysbus;
+ int ret = -1;
+
+ if (details) {
+ virReportError(VIR_ERR_AUTH_FAILED, "%s",
+ _("Details not supported with polkit v0"));
+ return -1;
+ }
+
+ if (!(sysbus = virDBusGetSystemBus()))
+ goto cleanup;
+
+ VIR_INFO("Checking PID %lld running as %d",
+ (long long) pid, uid);
+ dbus_error_init(&err);
+ if (!(pkcaller = polkit_caller_new_from_pid(sysbus,
+ pid, &err))) {
+ VIR_DEBUG("Failed to lookup policy kit caller: %s", err.message);
+ dbus_error_free(&err);
+ goto cleanup;
+ }
+
+ if (!(pkaction = polkit_action_new())) {
+ char ebuf[1024];
+ VIR_DEBUG("Failed to create polkit action %s",
+ virStrerror(errno, ebuf, sizeof(ebuf)));
+ goto cleanup;
+ }
+ polkit_action_set_action_id(pkaction, actionid);
+
+ if (!(pkcontext = polkit_context_new()) ||
+ !polkit_context_init(pkcontext, &pkerr)) {
+ char ebuf[1024];
+ VIR_DEBUG("Failed to create polkit context %s",
+ (pkerr ? polkit_error_get_error_message(pkerr)
+ : virStrerror(errno, ebuf, sizeof(ebuf))));
+ if (pkerr)
+ polkit_error_free(pkerr);
+ dbus_error_free(&err);
+ goto cleanup;
+ }
+
+# if HAVE_POLKIT_CONTEXT_IS_CALLER_AUTHORIZED
+ pkresult = polkit_context_is_caller_authorized(pkcontext,
+ pkaction,
+ pkcaller,
+ 0,
+ &pkerr);
+ if (pkerr && polkit_error_is_set(pkerr)) {
+ VIR_DEBUG("Policy kit failed to check authorization %d %s",
+ polkit_error_get_error_code(pkerr),
+ polkit_error_get_error_message(pkerr));
+ goto cleanup;
+ }
+# else
+ pkresult = polkit_context_can_caller_do_action(pkcontext,
+ pkaction,
+ pkcaller);
+# endif
+ if (pkresult != POLKIT_RESULT_YES) {
+ VIR_DEBUG("Policy kit denied action %s from pid %lld, uid %d, result: %s",
+ actionid, (long long) pid, uid,
+ polkit_result_to_string_representation(pkresult));
+ ret = -2;
+ goto cleanup;
+ }
+
+ VIR_DEBUG("Policy allowed action %s from pid %lld, uid %d",
+ actionid, (long long)pid, (int)uid);
+
+ ret = 0;
+
+ cleanup:
+ if (ret < 0) {
+ virResetLastError();
+ virReportError(VIR_ERR_AUTH_FAILED, "%s",
+ _("authentication failed"));
+ }
+ if (pkcontext)
+ polkit_context_unref(pkcontext);
+ if (pkcaller)
+ polkit_caller_unref(pkcaller);
+ if (pkaction)
+ polkit_action_unref(pkaction);
+ return ret;
+}
+
+
+#else /* ! WITH_POLKIT1 && ! WITH_POLKIT0 */
+
+int virPolkitCheckAuth(const char *actionid,
+ pid_t pid,
+ unsigned long long startTime,
+ uid_t uid,
+ const char **details,
+ bool allowInteraction)
+{
+ VIR_ERROR(_("Polkit auth attempted, even though polkit is not available"));
+ virReportError(VIR_ERR_AUTH_FAILED, "%s",
+ _("authentication failed"));
+ return -1;
+}
+
+
+#endif /* WITH_POLKIT1 */
diff --git a/src/util/virpolkit.h b/src/util/virpolkit.h
new file mode 100644
index 0000000000..36122d04ed
--- /dev/null
+++ b/src/util/virpolkit.h
@@ -0,0 +1,34 @@
+/*
+ * virpolkit.h: helpers for using polkit APIs
+ *
+ * Copyright (C) 2013 Red Hat, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see
+ * .
+ *
+ */
+
+#ifndef __VIR_POLKIT_H__
+# define __VIR_POLKIT_H__
+
+# include "internal.h"
+
+int virPolkitCheckAuth(const char *actionid,
+ pid_t pid,
+ unsigned long long startTime,
+ uid_t uid,
+ const char **details,
+ bool allowInteraction);
+
+#endif /* __VIR_POLKIT_H__ */