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>
This commit is contained in:
Daniel P. Berrange 2013-08-22 14:27:19 +01:00
parent 2d79e1752a
commit 1b854c76c7
7 changed files with 311 additions and 2 deletions

View 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

View 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

View 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

View File

@ -1794,6 +1794,10 @@ virPidFileWrite;
virPidFileWritePath;
# util/virpolkit.h
virPolkitCheckAuth;
# util/virportallocator.h
virPortAllocatorAcquire;
virPortAllocatorNew;

View File

@ -130,6 +130,8 @@ VIR_ENUM_IMPL(virErrorDomain, VIR_ERR_DOMAIN_LAST,
"Bhyve",
"Crypto",
"Firewall",
"Polkit", /* 60 */
)

263
src/util/virpolkit.c Normal file
View File

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

34
src/util/virpolkit.h Normal file
View File

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