diff --git a/.gitignore b/.gitignore index b12d1ab3ae..f9168fc2e0 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/daemon/libvirtd.conf b/daemon/libvirtd.conf index 9d7879c982..af4493e633 100644 --- a/daemon/libvirtd.conf +++ b/daemon/libvirtd.conf @@ -163,7 +163,7 @@ # meaning no access control checks are done once a # client has authenticated with libvirtd # -#access_drivers = [ ] +#access_drivers = [ "polkit" ] ################################################################# # diff --git a/daemon/test_libvirtd.aug.in b/daemon/test_libvirtd.aug.in index b9df7117bd..4e3b8789fb 100644 --- a/daemon/test_libvirtd.aug.in +++ b/daemon/test_libvirtd.aug.in @@ -18,6 +18,7 @@ module Test_libvirtd = { "auth_tcp" = "sasl" } { "auth_tls" = "none" } { "access_drivers" + { "1" = "polkit" } } { "key_file" = "/etc/pki/libvirt/private/serverkey.pem" } { "cert_file" = "/etc/pki/libvirt/servercert.pem" } diff --git a/po/POTFILES.in b/po/POTFILES.in index b3a8ec19d5..af7fd7fbe3 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 fea4862474..9c118a9d84 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 0000000000..eb7069a6d1 --- /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 +# . +# + +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 +# +# +# +# Monitor local virtualized systems +# System policy prevents monitoring of local virtualized systems +# +# yes +# yes +# yes +# +# +# ...more rules... +# + +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 < + + + Libvirt Project + http://libvirt.org +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 < + $description + $message + + $allow_any + $allow_inactive + $allow_active + + +EOF + + } +} + +print < +EOF diff --git a/src/access/viraccessdriverpolkit.c b/src/access/viraccessdriverpolkit.c new file mode 100644 index 0000000000..6e503ba02e --- /dev/null +++ b/src/access/viraccessdriverpolkit.c @@ -0,0 +1,402 @@ +/* + * 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 + * . + */ + +#include + +#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 "), + 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; + + VIR_DEBUG("Check action '%s' for process '%s'", actionid, process); + + 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 = { + .privateDataLen = sizeof(virAccessDriverPolkitPrivate), + .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 0000000000..00b044f458 --- /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 + * . + */ + +#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 8b1f1509b5..559a8445ed 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 };