]> xenbits.xensource.com Git - libvirt.git/commitdiff
Add common API for doing polkit authentication
authorDaniel P. Berrange <berrange@redhat.com>
Thu, 22 Aug 2013 13:27:19 +0000 (14:27 +0100)
committerDaniel P. Berrange <berrange@redhat.com>
Wed, 24 Sep 2014 14:29:22 +0000 (15:29 +0100)
There are now two places in libvirt which use polkit. Currently
they use pkexec, which is set to be replaced by direct DBus API
calls. Add a common API which they will both be able to use for
this purpose.

No tests are added at this time, since the impl will be gutted
in favour of a DBus API call shortly.

Signed-off-by: Daniel P. Berrange <berrange@redhat.com>
include/libvirt/virterror.h
po/POTFILES.in
src/Makefile.am
src/libvirt_private.syms
src/util/virerror.c
src/util/virpolkit.c [new file with mode: 0644]
src/util/virpolkit.h [new file with mode: 0644]

index 15ba4f17922cf0769b142fc568bef1485a7636ef..85dd74cbbe957c6ea37ddf0449e2d4ab05251c21 100644 (file)
@@ -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
index f17b35fcaa1140d955f3d5fa746bc989cc68f644..1a0b75ed9fac8226225c44317095960a96876b4c 100644 (file)
@@ -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
index fa741a809d78bc47c3ed01ce30a9c8db85098611..3afae7b9d1cabf6ec48fff93a805c76402b7d878 100644 (file)
@@ -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
index a339ced69dad931ddc08d88703e0ec605b822e0f..02ac4e9352a033ee6c2d20871c10276d5862ce58 100644 (file)
@@ -1794,6 +1794,10 @@ virPidFileWrite;
 virPidFileWritePath;
 
 
+# util/virpolkit.h
+virPolkitCheckAuth;
+
+
 # util/virportallocator.h
 virPortAllocatorAcquire;
 virPortAllocatorNew;
index 6bd3d09cfdeb884ccb5a0c72b1f5f4cb9eb4c880..4aa6d04088847e51fe23dc64118440a529e18896 100644 (file)
@@ -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 (file)
index 0000000..620bfda
--- /dev/null
@@ -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
+ * <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include <config.h>
+
+#if WITH_POLKIT0
+# include <polkit/polkit.h>
+# include <polkit-dbus/polkit-dbus.h>
+#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 (file)
index 0000000..36122d0
--- /dev/null
@@ -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
+ * <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#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__ */