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__ */