From: "Daniel P. Berrange" <berrange@xxxxxxxxxx> Add an access control driver that uses the pkcheck command to check authorization requests. This is fairly inefficient, particularly for cases where an API returns a list of objects and needs to check permission for each object. It would be desirable to use the polkit API but this links to glib with abort-on-OOM behaviour, so can't be used. The other alternative is to speak to dbus directly Signed-off-by: Daniel P. Berrange <berrange@xxxxxxxxxx> --- .gitignore | 1 + po/POTFILES.in | 1 + src/Makefile.am | 28 ++- src/access/genpolkit.pl | 119 +++++++++++ src/access/viraccessdriverpolkit.c | 399 +++++++++++++++++++++++++++++++++++++ src/access/viraccessdriverpolkit.h | 28 +++ src/access/viraccessmanager.c | 6 + 7 files changed, 581 insertions(+), 1 deletion(-) create mode 100755 src/access/genpolkit.pl create mode 100644 src/access/viraccessdriverpolkit.c create mode 100644 src/access/viraccessdriverpolkit.h diff --git a/.gitignore b/.gitignore index b12d1ab..f9168fc 100644 --- a/.gitignore +++ b/.gitignore @@ -103,6 +103,7 @@ /run /sc_* /src/.*.stamp +/src/access/org.libvirt.api.policy /src/esx/*.generated.* /src/hyperv/*.generated.* /src/libvirt*.def diff --git a/po/POTFILES.in b/po/POTFILES.in index b3a8ec1..af7fd7f 100644 --- a/po/POTFILES.in +++ b/po/POTFILES.in @@ -6,6 +6,7 @@ daemon/remote_dispatch.h daemon/stream.c gnulib/lib/gai_strerror.c gnulib/lib/regcomp.c +src/access/viraccessdriverpolkit.c src/access/viraccessmanager.c src/conf/cpu_conf.c src/conf/device_conf.c diff --git a/src/Makefile.am b/src/Makefile.am index fea4862..9c118a9 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -797,6 +797,13 @@ ACCESS_DRIVER_SOURCES = \ access/viraccessdrivernop.h access/viraccessdrivernop.c \ access/viraccessdriverstack.h access/viraccessdriverstack.c +ACCESS_DRIVER_POLKIT_SOURCES = \ + access/viraccessdriverpolkit.h access/viraccessdriverpolkit.c + +ACCESS_DRIVER_POLKIT_POLICY = \ + access/org.libvirt.api.policy + + NODE_DEVICE_DRIVER_SOURCES = \ node_device/node_device_driver.c \ node_device/node_device_driver.h \ @@ -1391,6 +1398,24 @@ libvirt_driver_access_la_CFLAGS = \ libvirt_driver_access_la_LDFLAGS = $(AM_LDFLAGS) libvirt_driver_access_la_LIBADD = +EXTRA_DIST += access/genpolkit.pl + +if WITH_POLKIT1 +libvirt_driver_access_la_SOURCES += $(ACCESS_DRIVER_POLKIT_SOURCES) + +polkitactiondir = $(datadir)/polkit-1/actions +polkitaction_DATA = $(ACCESS_DRIVER_POLKIT_POLICY) + +$(ACCESS_DRIVER_POLKIT_POLICY): $(srcdir)/access/viraccessperm.h \ + $(srcdir)/access/genpolkit.pl Makefile.am + $(AM_V_GEN)$(PERL) $(srcdir)/access/genpolkit.pl < $< > $@ || rm -f $@ + +CLEANFILES += $(ACCESS_DRIVER_POLKIT_POLICY) +BUILT_SOURCES += $(ACCESS_DRIVER_POLKIT_POLICY) +else +EXTRA_DIST += $(ACCESS_DRIVER_POLKIT_SOURCES) +endif + # Add all conditional sources just in case... EXTRA_DIST += \ @@ -1430,7 +1455,8 @@ EXTRA_DIST += \ $(SECRET_DRIVER_SOURCES) \ $(VBOX_DRIVER_EXTRA_DIST) \ $(VMWARE_DRIVER_SOURCES) \ - $(XENXS_SOURCES) + $(XENXS_SOURCES) \ + $(ACCESS_DRIVER_POLKIT_POLICY) check-local: check-augeas diff --git a/src/access/genpolkit.pl b/src/access/genpolkit.pl new file mode 100755 index 0000000..eb7069a --- /dev/null +++ b/src/access/genpolkit.pl @@ -0,0 +1,119 @@ +#!/usr/bin/perl +# +# Copyright (C) 2012-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/>. +# + +use strict; +use warnings; + +my @objects = ( + "CONNECT", "DOMAIN", "INTERFACE", + "NETWORK","NODE_DEVICE", "NWFILTER", + "SECRET", "STORAGE_POOL", "STORAGE_VOL", + ); + +my $objects = join ("|", @objects); + +# Data we're going to be generating looks like this +# +# <policyconfig> +# <action id="org.libvirt.unix.monitor"> +# <description>Monitor local virtualized systems</description> +# <message>System policy prevents monitoring of local virtualized systems</message> +# <defaults> +# <allow_any>yes</allow_any> +# <allow_inactive>yes</allow_inactive> +# <allow_active>yes</allow_active> +# </defaults> +# </action> +# ...more <action> rules... +# </policyconfig> + +my %opts; +my $in_opts = 0; + +my %perms; + +while (<>) { + if ($in_opts) { + if (m,\*/,) { + $in_opts = 0; + } elsif (/\*\s*\@(\w+):\s*(.*?)\s*$/) { + $opts{$1} = $2; + } + } elsif (m,/\*\*,) { + $in_opts = 1; + } elsif (/VIR_ACCESS_PERM_($objects)_((?:\w|_)+),/) { + my $object = lc $1; + my $perm = lc $2; + next if $perm eq "last"; + + $object =~ s/_/-/g; + $perm =~ s/_/-/g; + + $perms{$object} = {} unless exists $perms{$object}; + $perms{$object}->{$perm} = { + desc => $opts{desc}, + message => $opts{message}, + anonymous => $opts{anonymous} + }; + %opts = (); + } +} + +print <<EOF; +<?xml version="1.0" encoding="UTF-8"?> +<!DOCTYPE policyconfig PUBLIC "-//freedesktop//DTD polkit Policy Configuration 1.0//EN" + "http://www.freedesktop.org/software/polkit/policyconfig-1.dtd"> +<policyconfig> + <vendor>Libvirt Project</vendor> + <vendor_url>http://libvirt.org</vendor_url> +EOF + +foreach my $object (sort { $a cmp $b } keys %perms) { + foreach my $perm (sort { $a cmp $b } keys %{$perms{$object}}) { + my $description = $perms{$object}->{$perm}->{desc}; + my $message = $perms{$object}->{$perm}->{message}; + my $anonymous = $perms{$object}->{$perm}->{anonymous}; + + die "missing description for $object.$perm" unless + defined $description; + die "missing message for $object.$perm" unless + defined $message; + + my $allow_any = $anonymous ? "yes" : "no"; + my $allow_inactive = $allow_any; + my $allow_active = $allow_any; + + print <<EOF; + <action id="org.libvirt.api.$object.$perm"> + <description>$description</description> + <message>$message</message> + <defaults> + <allow_any>$allow_any</allow_any> + <allow_inactive>$allow_inactive</allow_inactive> + <allow_active>$allow_active</allow_active> + </defaults> + </action> +EOF + + } +} + +print <<EOF; +</policyconfig> +EOF diff --git a/src/access/viraccessdriverpolkit.c b/src/access/viraccessdriverpolkit.c new file mode 100644 index 0000000..a7ea439 --- /dev/null +++ b/src/access/viraccessdriverpolkit.c @@ -0,0 +1,399 @@ +/* + * viraccessdriverpolkit.c: polkited access control driver + * + * Copyright (C) 2012 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> + +#include "viraccessdriverpolkit.h" +#include "viralloc.h" +#include "vircommand.h" +#include "virlog.h" +#include "virprocess.h" +#include "virerror.h" +#include "virstring.h" + +#define VIR_FROM_THIS VIR_FROM_ACCESS +#define virAccessError(code, ...) \ + virReportErrorHelper(VIR_FROM_THIS, code, __FILE__, \ + __FUNCTION__, __LINE__, __VA_ARGS__) + +#define VIR_ACCESS_DRIVER_POLKIT_ACTION_PREFIX "org.libvirt.api" + +typedef struct _virAccessDriverPolkitPrivate virAccessDriverPolkitPrivate; +typedef virAccessDriverPolkitPrivate *virAccessDriverPolkitPrivatePtr; + +struct _virAccessDriverPolkitPrivate { + bool ignore; +}; + + +static void virAccessDriverPolkitCleanup(virAccessManagerPtr manager ATTRIBUTE_UNUSED) +{ +} + + +static char * +virAccessDriverPolkitFormatAction(const char *typename, + const char *permname) +{ + char *actionid = NULL; + size_t i; + + if (virAsprintf(&actionid, "%s.%s.%s", + VIR_ACCESS_DRIVER_POLKIT_ACTION_PREFIX, + typename, permname) < 0) { + virReportOOMError(); + return NULL; + } + + for (i = 0; actionid[i]; i++) + if (actionid[i] == '_') + actionid[i] = '-'; + + return actionid; +} + + +static char * +virAccessDriverPolkitFormatProcess(const char *actionid) +{ + virIdentityPtr identity = virIdentityGetCurrent(); + const char *process = NULL; + char *ret = NULL; + + if (!identity) { + virAccessError(VIR_ERR_ACCESS_DENIED, + _("Policy kit denied action %s from <anonymous>"), + actionid); + return NULL; + } + if (virIdentityGetAttr(identity, VIR_IDENTITY_ATTR_UNIX_PROCESS_ID, &process) < 0) + goto cleanup; + + if (!process) { + virAccessError(VIR_ERR_INTERNAL_ERROR, "%s", + _("No UNIX process ID available")); + goto cleanup; + } + + if (VIR_STRDUP(ret, process) < 0) + goto cleanup; + +cleanup: + virObjectUnref(identity); + return ret; +} + + +static int +virAccessDriverPolkitCheck(virAccessManagerPtr manager ATTRIBUTE_UNUSED, + const char *typename, + const char *permname, + const char **attrs) +{ + char *actionid = NULL; + char *process = NULL; + virCommandPtr cmd = NULL; + int status; + int ret = -1; + + if (!(actionid = virAccessDriverPolkitFormatAction(typename, permname))) + goto cleanup; + + if (!(process = virAccessDriverPolkitFormatProcess(actionid))) + goto cleanup; + + cmd = virCommandNewArgList(PKCHECK_PATH, + "--action-id", actionid, + "--process", process, + NULL); + + while (attrs && attrs[0] && attrs[1]) { + virCommandAddArgList(cmd, "--detail", attrs[0], attrs[1], NULL); + attrs += 2; + } + + if (virCommandRun(cmd, &status) < 0) + goto cleanup; + + if (status == 0) { + ret = 1; /* Allowed */ + } else { + if (status == 1 || + status == 2 || + status == 3) { + ret = 0; /* Denied */ + } else { + ret = -1; /* Error */ + char *tmp = virProcessTranslateStatus(status); + virAccessError(VIR_ERR_ACCESS_DENIED, + _("Policy kit denied action %s from %s: %s"), + actionid, process, NULLSTR(tmp)); + VIR_FREE(tmp); + } + goto cleanup; + } + +cleanup: + virCommandFree(cmd); + VIR_FREE(actionid); + VIR_FREE(process); + return ret; +} + + +static int +virAccessDriverPolkitCheckConnect(virAccessManagerPtr manager, + const char *driverName, + virAccessPermConnect perm) +{ + const char *attrs[] = { + "connect-driver", driverName, + NULL, + }; + + return virAccessDriverPolkitCheck(manager, + "connect", + virAccessPermConnectTypeToString(perm), + attrs); +} + +static int +virAccessDriverPolkitCheckDomain(virAccessManagerPtr manager, + const char *driverName, + virDomainDefPtr domain, + virAccessPermDomain perm) +{ + char uuidstr[VIR_UUID_STRING_BUFLEN]; + const char *attrs[] = { + "connect-driver", driverName, + "domain-name", domain->name, + "domain-uuid", uuidstr, + NULL, + }; + virUUIDFormat(domain->uuid, uuidstr); + + return virAccessDriverPolkitCheck(manager, + "domain", + virAccessPermDomainTypeToString(perm), + attrs); +} + +static int +virAccessDriverPolkitCheckInterface(virAccessManagerPtr manager, + const char *driverName, + virInterfaceDefPtr iface, + virAccessPermInterface perm) +{ + const char *attrs[] = { + "connect-driver", driverName, + "interface-name", iface->name, + "interface-macaddr", iface->mac, + NULL, + }; + + return virAccessDriverPolkitCheck(manager, + "interface", + virAccessPermInterfaceTypeToString(perm), + attrs); +} + +static int +virAccessDriverPolkitCheckNetwork(virAccessManagerPtr manager, + const char *driverName, + virNetworkDefPtr network, + virAccessPermNetwork perm) +{ + char uuidstr[VIR_UUID_STRING_BUFLEN]; + const char *attrs[] = { + "connect-driver", driverName, + "network-name", network->name, + "network-uuid", uuidstr, + NULL, + }; + virUUIDFormat(network->uuid, uuidstr); + + return virAccessDriverPolkitCheck(manager, + "network", + virAccessPermNetworkTypeToString(perm), + attrs); +} + +static int +virAccessDriverPolkitCheckNodeDevice(virAccessManagerPtr manager, + const char *driverName, + virNodeDeviceDefPtr nodedev, + virAccessPermNodeDevice perm) +{ + const char *attrs[] = { + "connect-driver", driverName, + "node-device-name", nodedev->name, + NULL, + }; + + return virAccessDriverPolkitCheck(manager, + "nodedevice", + virAccessPermNodeDeviceTypeToString(perm), + attrs); +} + +static int +virAccessDriverPolkitCheckNWFilter(virAccessManagerPtr manager, + const char *driverName, + virNWFilterDefPtr nwfilter, + virAccessPermNWFilter perm) +{ + char uuidstr[VIR_UUID_STRING_BUFLEN]; + const char *attrs[] = { + "connect-driver", driverName, + "nwfilter-name", nwfilter->name, + "nwfilter-uuid", uuidstr, + NULL, + }; + virUUIDFormat(nwfilter->uuid, uuidstr); + + return virAccessDriverPolkitCheck(manager, + "nwfilter", + virAccessPermNWFilterTypeToString(perm), + attrs); +} + +static int +virAccessDriverPolkitCheckSecret(virAccessManagerPtr manager, + const char *driverName, + virSecretDefPtr secret, + virAccessPermSecret perm) +{ + char uuidstr[VIR_UUID_STRING_BUFLEN]; + virUUIDFormat(secret->uuid, uuidstr); + + switch (secret->usage_type) { + default: + case VIR_SECRET_USAGE_TYPE_NONE: { + const char *attrs[] = { + "connect-driver", driverName, + "secret-uuid", uuidstr, + NULL, + }; + + return virAccessDriverPolkitCheck(manager, + "secret", + virAccessPermSecretTypeToString(perm), + attrs); + } break; + case VIR_SECRET_USAGE_TYPE_VOLUME: { + const char *attrs[] = { + "connect-driver", driverName, + "secret-uuid", uuidstr, + "secret-usage-volume", secret->usage.volume, + NULL, + }; + + return virAccessDriverPolkitCheck(manager, + "secret", + virAccessPermSecretTypeToString(perm), + attrs); + } break; + case VIR_SECRET_USAGE_TYPE_CEPH: { + const char *attrs[] = { + "connect-driver", driverName, + "secret-uuid", uuidstr, + "secret-usage-ceph", secret->usage.ceph, + NULL, + }; + + return virAccessDriverPolkitCheck(manager, + "secret", + virAccessPermSecretTypeToString(perm), + attrs); + } break; + case VIR_SECRET_USAGE_TYPE_ISCSI: { + const char *attrs[] = { + "connect-driver", driverName, + "secret-uuid", uuidstr, + "secret-usage-target", secret->usage.target, + NULL, + }; + + return virAccessDriverPolkitCheck(manager, + "secret", + virAccessPermSecretTypeToString(perm), + attrs); + } break; + } +} + +static int +virAccessDriverPolkitCheckStoragePool(virAccessManagerPtr manager, + const char *driverName, + virStoragePoolDefPtr pool, + virAccessPermStoragePool perm) +{ + char uuidstr[VIR_UUID_STRING_BUFLEN]; + const char *attrs[] = { + "connect-driver", driverName, + "pool-name", pool->name, + "pool-uuid", uuidstr, + NULL, + }; + virUUIDFormat(pool->uuid, uuidstr); + + return virAccessDriverPolkitCheck(manager, + "pool", + virAccessPermStoragePoolTypeToString(perm), + attrs); +} + +static int +virAccessDriverPolkitCheckStorageVol(virAccessManagerPtr manager, + const char *driverName, + virStoragePoolDefPtr pool, + virStorageVolDefPtr vol, + virAccessPermStorageVol perm) +{ + char uuidstr[VIR_UUID_STRING_BUFLEN]; + const char *attrs[] = { + "connect-driver", driverName, + "pool-name", pool->name, + "pool-uuid", uuidstr, + "vol-name", vol->name, + "vol-key", vol->key, + NULL, + }; + virUUIDFormat(pool->uuid, uuidstr); + + return virAccessDriverPolkitCheck(manager, + "vol", + virAccessPermStorageVolTypeToString(perm), + attrs); +} + +virAccessDriver accessDriverPolkit = { + .name = "polkit", + .cleanup = virAccessDriverPolkitCleanup, + .checkConnect = virAccessDriverPolkitCheckConnect, + .checkDomain = virAccessDriverPolkitCheckDomain, + .checkInterface = virAccessDriverPolkitCheckInterface, + .checkNetwork = virAccessDriverPolkitCheckNetwork, + .checkNodeDevice = virAccessDriverPolkitCheckNodeDevice, + .checkNWFilter = virAccessDriverPolkitCheckNWFilter, + .checkSecret = virAccessDriverPolkitCheckSecret, + .checkStoragePool = virAccessDriverPolkitCheckStoragePool, + .checkStorageVol = virAccessDriverPolkitCheckStorageVol, +}; diff --git a/src/access/viraccessdriverpolkit.h b/src/access/viraccessdriverpolkit.h new file mode 100644 index 0000000..00b044f --- /dev/null +++ b/src/access/viraccessdriverpolkit.h @@ -0,0 +1,28 @@ +/* + * viraccessdriverpolkit.h: polkited access control driver + * + * Copyright (C) 2012 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_ACCESS_DRIVER_POLKIT_H__ +# define __VIR_ACCESS_DRIVER_POLKIT_H__ + +# include "viraccessdriver.h" + +extern virAccessDriver accessDriverPolkit; + +#endif /* __VIR_ACCESS_DRIVER_POLKIT_H__ */ diff --git a/src/access/viraccessmanager.c b/src/access/viraccessmanager.c index 8b1f150..559a844 100644 --- a/src/access/viraccessmanager.c +++ b/src/access/viraccessmanager.c @@ -23,6 +23,9 @@ #include "viraccessmanager.h" #include "viraccessdrivernop.h" #include "viraccessdriverstack.h" +#if WITH_POLKIT1 +# include "viraccessdriverpolkit.h" +#endif #include "viralloc.h" #include "virerror.h" #include "virobject.h" @@ -108,6 +111,9 @@ static virAccessManagerPtr virAccessManagerNewDriver(virAccessDriverPtr drv) static virAccessDriverPtr accessDrivers[] = { &accessDriverNop, +#if WITH_POLKIT1 + &accessDriverPolkit, +#endif }; -- 1.8.1.4 -- libvir-list mailing list libvir-list@xxxxxxxxxx https://www.redhat.com/mailman/listinfo/libvir-list