[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]

[libvirt] [PATCH 1/7] Add common API for doing polkit authentication



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 |   2 +
 po/POTFILES.in              |   1 +
 src/Makefile.am             |   1 +
 src/libvirt_private.syms    |   4 +
 src/util/virerror.c         |   2 +
 src/util/virpolkit.c        | 263 ++++++++++++++++++++++++++++++++++++++++++++
 src/util/virpolkit.h        |  34 ++++++
 7 files changed, 307 insertions(+)
 create mode 100644 src/util/virpolkit.c
 create mode 100644 src/util/virpolkit.h

diff --git a/include/libvirt/virterror.h b/include/libvirt/virterror.h
index 15ba4f1..85dd74c 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 f17b35f..1a0b75e 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 46e411e..a05b2da 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		\
diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms
index 10ebd12..de334bb 100644
--- a/src/libvirt_private.syms
+++ b/src/libvirt_private.syms
@@ -1787,6 +1787,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 6bd3d09..4aa6d04 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 0000000..620bfda
--- /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
+ * <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
index 0000000..36122d0
--- /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
+ * <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__ */
-- 
1.9.3


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]