[libvirt] [PATCH RFC 00/12] Fine grained access control proof of concept

This series of patch is the minimal required to get a working proof of concept implementation of fine grained access control in libvirt. This demonstrates - Obtaining a client identity from a socket - Ensuring RPC calls are executed with the correct identity sset - A policykit access driver that checks based on access vector alone - A SELinux access driver that checks based on access vector + object - A set of hooks in the QEMU driver to protect virDomainObjPtr access Things that are not done - APIs for changing the real/effective identity post-connect - A simple RBAC access driver for doing (Access vector, object) checks - SELinux policy for the SELinux driver - Access control hooks on all other QEMU driver methods - Access control hooks in LXC, UML, other libvirtd side drivers - Access control hooks in storage, network, interface, etc drivers - Document WTF todo to propagate SELinux contexts across TCP sockets using IPSec. Any hints welcome... - Lots more I can't think of right now I should note that the policykit driver is mostly useless because it is unable to let you do checks on anything other than permission name and UNIX process ID at this time. In theory it is supposed to be extendable to allow other types of identity information besides the process ID, and to include some kind of object identiers in the permission check, but no one seems to be attacking this. So I expect the simple RBAC driver to be the most used one in the common case usage of libvirt

From: "Daniel P. Berrange" <berrange@redhat.com> Currently the server determines whether authentication of clients is complete, by checking whether an identity is set. This patch removes that lame hack and replaces it with an explicit method for changing the client auth code * daemon/remote.c: Update for new APis * src/libvirt_private.syms, src/rpc/virnetserverclient.c, src/rpc/virnetserverclient.h: Remove virNetServerClientGetIdentity and virNetServerClientSetIdentity, adding a new method virNetServerClientSetAuth. --- daemon/remote.c | 14 +++++--------- src/libvirt_private.syms | 2 +- src/rpc/virnetserverclient.c | 36 ++++++++---------------------------- src/rpc/virnetserverclient.h | 5 +---- 4 files changed, 15 insertions(+), 42 deletions(-) diff --git a/daemon/remote.c b/daemon/remote.c index 3b8ccd9..362a25a 100644 --- a/daemon/remote.c +++ b/daemon/remote.c @@ -2048,10 +2048,8 @@ remoteDispatchAuthList(virNetServerPtr server ATTRIBUTE_UNUSED, if (rv > 0 || rv < sizeof ident) { VIR_INFO("Bypass polkit auth for privileged client %s", ident); - if (virNetServerClientSetIdentity(client, ident) < 0) - virResetLastError(); - else - auth = VIR_NET_SERVER_SERVICE_AUTH_NONE; + virNetServerClientSetAuth(client, 0); + auth = VIR_NET_SERVER_SERVICE_AUTH_NONE; } rv = -1; } @@ -2191,9 +2189,7 @@ remoteSASLFinish(virNetServerClientPtr client) if (!virNetSASLContextCheckIdentity(saslCtxt, identity)) return -2; - if (virNetServerClientSetIdentity(client, identity) < 0) - goto error; - + virNetServerClientSetAuth(client, 0); virNetServerClientSetSASLSession(client, priv->sasl); VIR_DEBUG("Authentication successful %d", virNetServerClientGetFD(client)); @@ -2530,7 +2526,7 @@ remoteDispatchAuthPolkit(virNetServerPtr server ATTRIBUTE_UNUSED, action, callerPid, callerUid); ret->complete = 1; - virNetServerClientSetIdentity(client, ident); + virNetServerClientSetAuth(client, 0); virMutexUnlock(&priv->lock); return 0; @@ -2668,8 +2664,8 @@ remoteDispatchAuthPolkit(virNetServerPtr server, action, callerPid, callerUid, polkit_result_to_string_representation(pkresult)); ret->complete = 1; - virNetServerClientSetIdentity(client, ident); + virNetServerClientSetAuth(client, 0); virMutexUnlock(&priv->lock); return 0; diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms index a0530b1..363f316 100644 --- a/src/libvirt_private.syms +++ b/src/libvirt_private.syms @@ -1303,8 +1303,8 @@ virNetServerClientRef; virNetServerClientRemoteAddrString; virNetServerClientRemoveFilter; virNetServerClientSendMessage; +virNetServerClientSetAuth; virNetServerClientSetCloseHook; -virNetServerClientSetIdentity; virNetServerClientSetPrivateData; virNetServerClientStartKeepAlive; diff --git a/src/rpc/virnetserverclient.c b/src/rpc/virnetserverclient.c index 67600fd..81dbb32 100644 --- a/src/rpc/virnetserverclient.c +++ b/src/rpc/virnetserverclient.c @@ -67,7 +67,6 @@ struct _virNetServerClient virNetSocketPtr sock; int auth; bool readonly; - char *identity; virNetTLSContextPtr tlsCtxt; virNetTLSSessionPtr tls; #if HAVE_SASL @@ -408,6 +407,13 @@ int virNetServerClientGetAuth(virNetServerClientPtr client) return auth; } +void virNetServerClientSetAuth(virNetServerClientPtr client, int auth) +{ + virNetServerClientLock(client); + client->auth = auth; + virNetServerClientUnlock(client); +} + bool virNetServerClientGetReadonly(virNetServerClientPtr client) { bool readonly; @@ -492,31 +498,6 @@ void virNetServerClientSetSASLSession(virNetServerClientPtr client, #endif -int virNetServerClientSetIdentity(virNetServerClientPtr client, - const char *identity) -{ - int ret = -1; - virNetServerClientLock(client); - if (!(client->identity = strdup(identity))) { - virReportOOMError(); - goto error; - } - ret = 0; - -error: - virNetServerClientUnlock(client); - return ret; -} - -const char *virNetServerClientGetIdentity(virNetServerClientPtr client) -{ - const char *identity; - virNetServerClientLock(client); - identity = client->identity; - virNetServerClientLock(client); - return identity; -} - void virNetServerClientSetPrivateData(virNetServerClientPtr client, void *opaque, virNetServerClientFreeFunc ff) @@ -600,7 +581,6 @@ void virNetServerClientFree(virNetServerClientPtr client) client->privateDataFreeFunc) client->privateDataFreeFunc(client->privateData); - VIR_FREE(client->identity); #if HAVE_SASL virNetSASLSessionFree(client->sasl); #endif @@ -1130,7 +1110,7 @@ bool virNetServerClientNeedAuth(virNetServerClientPtr client) { bool need = false; virNetServerClientLock(client); - if (client->auth && !client->identity) + if (client->auth) need = true; virNetServerClientUnlock(client); return need; diff --git a/src/rpc/virnetserverclient.h b/src/rpc/virnetserverclient.h index 154a160..633e9e1 100644 --- a/src/rpc/virnetserverclient.h +++ b/src/rpc/virnetserverclient.h @@ -52,6 +52,7 @@ void virNetServerClientRemoveFilter(virNetServerClientPtr client, int filterID); int virNetServerClientGetAuth(virNetServerClientPtr client); +void virNetServerClientSetAuth(virNetServerClientPtr client, int auth); bool virNetServerClientGetReadonly(virNetServerClientPtr client); bool virNetServerClientHasTLSSession(virNetServerClientPtr client); @@ -66,10 +67,6 @@ int virNetServerClientGetFD(virNetServerClientPtr client); bool virNetServerClientIsSecure(virNetServerClientPtr client); -int virNetServerClientSetIdentity(virNetServerClientPtr client, - const char *identity); -const char *virNetServerClientGetIdentity(virNetServerClientPtr client); - int virNetServerClientGetUNIXIdentity(virNetServerClientPtr client, uid_t *uid, gid_t *gid, pid_t *pid); -- 1.7.7.5

From: "Daniel P. Berrange" <berrange@redhat.com> Add new APIs virNetServerClientGetTLSSession, virNetServerClientIsLocal, virNetServerClientGetSecurityContext virNetServerClientGetSASLSession, virNetSocketGetSecurityContext and virNetTLSSessionGetX509DName --- src/rpc/virnetserverclient.c | 48 ++++++++++++++++++++++++++++++++++++++++++ src/rpc/virnetserverclient.h | 7 ++++++ src/rpc/virnetsocket.c | 44 ++++++++++++++++++++++++++++++++++++++ src/rpc/virnetsocket.h | 2 + src/rpc/virnettlscontext.c | 18 +++++++++++++++ src/rpc/virnettlscontext.h | 2 + 6 files changed, 121 insertions(+), 0 deletions(-) diff --git a/src/rpc/virnetserverclient.c b/src/rpc/virnetserverclient.c index 81dbb32..1e9d3db 100644 --- a/src/rpc/virnetserverclient.c +++ b/src/rpc/virnetserverclient.c @@ -433,6 +433,16 @@ bool virNetServerClientHasTLSSession(virNetServerClientPtr client) return has; } + +virNetTLSSessionPtr virNetServerClientGetTLSSession(virNetServerClientPtr client) +{ + virNetTLSSessionPtr tls; + virNetServerClientLock(client); + tls = client->tls; + virNetServerClientUnlock(client); + return tls; +} + int virNetServerClientGetTLSKeySize(virNetServerClientPtr client) { int size = 0; @@ -453,6 +463,18 @@ int virNetServerClientGetFD(virNetServerClientPtr client) return fd; } + +bool virNetServerClientIsLocal(virNetServerClientPtr client) +{ + bool local = false; + virNetServerClientLock(client); + if (client->sock) + local = virNetSocketIsLocal(client->sock); + virNetServerClientUnlock(client); + return local; +} + + int virNetServerClientGetUNIXIdentity(virNetServerClientPtr client, uid_t *uid, gid_t *gid, pid_t *pid) { @@ -464,6 +486,22 @@ int virNetServerClientGetUNIXIdentity(virNetServerClientPtr client, return ret; } + +int virNetServerClientGetSecurityContext(virNetServerClientPtr client, + char **context) +{ + int ret; + *context = NULL; + virNetServerClientLock(client); + if (client->sock) + ret = virNetSocketGetSecurityContext(client->sock, context); + else + ret = 0; + virNetServerClientUnlock(client); + return ret; +} + + bool virNetServerClientIsSecure(virNetServerClientPtr client) { bool secure = false; @@ -495,6 +533,16 @@ void virNetServerClientSetSASLSession(virNetServerClientPtr client, virNetSASLSessionRef(sasl); virNetServerClientUnlock(client); } + + +virNetSASLSessionPtr virNetServerClientGetSASLSession(virNetServerClientPtr client) +{ + virNetSASLSessionPtr sasl; + virNetServerClientLock(client); + sasl = client->sasl; + virNetServerClientUnlock(client); + return sasl; +} #endif diff --git a/src/rpc/virnetserverclient.h b/src/rpc/virnetserverclient.h index 633e9e1..a3b37a3 100644 --- a/src/rpc/virnetserverclient.h +++ b/src/rpc/virnetserverclient.h @@ -56,20 +56,27 @@ void virNetServerClientSetAuth(virNetServerClientPtr client, int auth); bool virNetServerClientGetReadonly(virNetServerClientPtr client); bool virNetServerClientHasTLSSession(virNetServerClientPtr client); +virNetTLSSessionPtr virNetServerClientGetTLSSession(virNetServerClientPtr client); int virNetServerClientGetTLSKeySize(virNetServerClientPtr client); # ifdef HAVE_SASL void virNetServerClientSetSASLSession(virNetServerClientPtr client, virNetSASLSessionPtr sasl); +virNetSASLSessionPtr virNetServerClientGetSASLSession(virNetServerClientPtr client); # endif int virNetServerClientGetFD(virNetServerClientPtr client); bool virNetServerClientIsSecure(virNetServerClientPtr client); +bool virNetServerClientIsLocal(virNetServerClientPtr client); + int virNetServerClientGetUNIXIdentity(virNetServerClientPtr client, uid_t *uid, gid_t *gid, pid_t *pid); +int virNetServerClientGetSecurityContext(virNetServerClientPtr client, + char **context); + void virNetServerClientRef(virNetServerClientPtr client); typedef void (*virNetServerClientFreeFunc)(void *data); diff --git a/src/rpc/virnetsocket.c b/src/rpc/virnetsocket.c index 67d33b7..22f1683 100644 --- a/src/rpc/virnetsocket.c +++ b/src/rpc/virnetsocket.c @@ -35,6 +35,10 @@ # include <netinet/tcp.h> #endif +#ifdef HAVE_SELINUX +# include <selinux/selinux.h> +#endif + #include "virnetsocket.h" #include "util.h" #include "memory.h" @@ -860,6 +864,46 @@ int virNetSocketGetUNIXIdentity(virNetSocketPtr sock ATTRIBUTE_UNUSED, } #endif +#ifdef HAVE_SELINUX +int virNetSocketGetSecurityContext(virNetSocketPtr sock, + char **context) +{ + security_context_t seccon = NULL; + int ret = -1; + + *context = NULL; + + virMutexLock(&sock->lock); + if (getpeercon(sock->fd, &seccon) < 0) { + if (errno == ENOSYS) { + ret = 0; + goto cleanup; + } + virReportSystemError(errno, "%s", + _("Unable to query peer security context")); + goto cleanup; + } + + if (!(*context = strdup(seccon))) { + virReportOOMError(); + goto cleanup; + } + + ret = 0; +cleanup: + freecon(seccon); + virMutexUnlock(&sock->lock); + return ret; +} +#else +int virNetSocketGetSecurityContext(virNetSocketPtr sock, + char **context) +{ + *context = NULL; + return 0; +} +#endif + int virNetSocketSetBlocking(virNetSocketPtr sock, bool blocking) diff --git a/src/rpc/virnetsocket.h b/src/rpc/virnetsocket.h index 5ba7c8f..de42e5c 100644 --- a/src/rpc/virnetsocket.h +++ b/src/rpc/virnetsocket.h @@ -90,6 +90,8 @@ int virNetSocketGetUNIXIdentity(virNetSocketPtr sock, uid_t *uid, gid_t *gid, pid_t *pid); +int virNetSocketGetSecurityContext(virNetSocketPtr sock, + char **context); int virNetSocketSetBlocking(virNetSocketPtr sock, bool blocking); diff --git a/src/rpc/virnettlscontext.c b/src/rpc/virnettlscontext.c index 74a61e0..2aca7a9 100644 --- a/src/rpc/virnettlscontext.c +++ b/src/rpc/virnettlscontext.c @@ -77,6 +77,7 @@ struct _virNetTLSSession { virNetTLSSessionWriteFunc writeFunc; virNetTLSSessionReadFunc readFunc; void *opaque; + char *x509dname; }; @@ -1025,6 +1026,10 @@ static int virNetTLSContextValidCertificate(virNetTLSContextPtr ctxt, "[session]", gnutls_strerror(ret)); goto authfail; } + if (!(sess->x509dname = strdup(dname))) { + virReportOOMError(); + goto authfail; + } VIR_DEBUG("Peer DN is %s", dname); if (virNetTLSContextCheckCertDN(cert, "[session]", sess->hostname, dname, @@ -1395,6 +1400,18 @@ cleanup: return ssf; } +const char *virNetTLSSessionGetX509DName(virNetTLSSessionPtr sess) +{ + const char *ret = NULL; + + virMutexLock(&sess->lock); + + ret = sess->x509dname; + + virMutexUnlock(&sess->lock); + + return ret; +} void virNetTLSSessionFree(virNetTLSSessionPtr sess) { @@ -1411,6 +1428,7 @@ void virNetTLSSessionFree(virNetTLSSessionPtr sess) return; } + VIR_FREE(sess->x509dname); VIR_FREE(sess->hostname); gnutls_deinit(sess->session); virMutexUnlock(&sess->lock); diff --git a/src/rpc/virnettlscontext.h b/src/rpc/virnettlscontext.h index fdfce6d..0c45cb0 100644 --- a/src/rpc/virnettlscontext.h +++ b/src/rpc/virnettlscontext.h @@ -99,6 +99,8 @@ virNetTLSSessionGetHandshakeStatus(virNetTLSSessionPtr sess); int virNetTLSSessionGetKeySize(virNetTLSSessionPtr sess); +const char *virNetTLSSessionGetX509DName(virNetTLSSessionPtr sess); + void virNetTLSSessionFree(virNetTLSSessionPtr sess); -- 1.7.7.5

From: "Daniel P. Berrange" <berrange@redhat.com> --- include/libvirt/libvirt.h.in | 30 +++++++ include/libvirt/virterror.h | 1 + src/datatypes.h | 22 +++++- src/libvirt.c | 176 ++++++++++++++++++++++++++++++++++++++++++ src/util/virterror.c | 6 ++ 5 files changed, 234 insertions(+), 1 deletions(-) diff --git a/include/libvirt/libvirt.h.in b/include/libvirt/libvirt.h.in index e436f3c..967a925 100644 --- a/include/libvirt/libvirt.h.in +++ b/include/libvirt/libvirt.h.in @@ -1020,6 +1020,36 @@ typedef virConnectAuth *virConnectAuthPtr; VIR_EXPORT_VAR virConnectAuthPtr virConnectAuthPtrDefault; +typedef struct _virIdentity virIdentity; +typedef virIdentity *virIdentityPtr; + +typedef enum { + VIR_IDENTITY_ATTR_UNIX_USER_NAME, + VIR_IDENTITY_ATTR_UNIX_GROUP_NAME, + VIR_IDENTITY_ATTR_UNIX_PROCESS_ID, + VIR_IDENTITY_ATTR_SASL_USER_NAME, + VIR_IDENTITY_ATTR_X509_DISTINGUISHED_NAME, + VIR_IDENTITY_ATTR_SECURITY_CONTEXT, + + VIR_IDENTITY_ATTR_LAST, +} virIdentityAttrType; + + +virIdentityPtr virIdentityNew(void); +int virIdentityRef(virIdentityPtr ident); +int virIdentitySetAttr(virIdentityPtr ident, + unsigned int attr, + const char *value); + +int virIdentityGetAttr(virIdentityPtr ident, + unsigned int attr, + const char **value); + +int virIdentityIsEqual(virIdentityPtr identA, + virIdentityPtr identB); + +int virIdentityFree(virIdentityPtr ident); + /** * VIR_UUID_BUFLEN: * diff --git a/include/libvirt/virterror.h b/include/libvirt/virterror.h index e896d67..6db31d8 100644 --- a/include/libvirt/virterror.h +++ b/include/libvirt/virterror.h @@ -243,6 +243,7 @@ typedef enum { risky domain snapshot revert */ VIR_ERR_OPERATION_ABORTED = 78, /* operation on a domain was canceled/aborted by user */ + VIR_ERR_INVALID_IDENTITY = 79, /* Invalid identity pointer */ } virErrorNumber; /** diff --git a/src/datatypes.h b/src/datatypes.h index 91b1bfd..3cf3097 100644 --- a/src/datatypes.h +++ b/src/datatypes.h @@ -31,13 +31,23 @@ * VIR_CONNECT_MAGIC: * * magic value used to protect the API when pointers to connection structures - * are passed down by the uers. + * are passed down by the users. */ # define VIR_CONNECT_MAGIC 0x4F23DEAD # define VIR_IS_CONNECT(obj) ((obj) && (obj)->magic==VIR_CONNECT_MAGIC) /** + * VIR_IDENTITY_MAGIC: + * + * magic value used to protect the API when pointers to identity structures + * are passed down by the users. + */ +# define VIR_IDENTITY_MAGIC 0xB33FCAF3 +# define VIR_IS_IDENTITY(obj) ((obj) && (obj)->magic==VIR_IDENTITY_MAGIC) + + +/** * VIR_DOMAIN_MAGIC: * * magic value used to protect the API when pointers to domain structures @@ -190,6 +200,16 @@ struct _virConnect { int refs; /* reference count */ }; + +struct _virIdentity { + unsigned int magic; + virMutex lock; + int refs; + + char *attrs[VIR_IDENTITY_ATTR_LAST]; +}; + + /** * _virDomain: * diff --git a/src/libvirt.c b/src/libvirt.c index 7b8adf7..4ced8b9 100644 --- a/src/libvirt.c +++ b/src/libvirt.c @@ -1510,6 +1510,182 @@ virConnectRef(virConnectPtr conn) return 0; } + +virIdentityPtr virIdentityNew(void) +{ + virIdentityPtr ident; + + if (VIR_ALLOC(ident) < 0) { + virReportOOMError(); + return NULL; + } + + if (virMutexInit(&ident->lock) < 0) { + virReportSystemError(errno, "%s", + _("Unable to initialize mutex")); + VIR_FREE(ident); + return NULL; + } + ident->magic = VIR_IDENTITY_MAGIC; + ident->refs = 1; + + return ident; +} + +int virIdentityRef(virIdentityPtr ident) +{ + if ((!VIR_IS_IDENTITY(ident))) { + virLibConnError(VIR_ERR_INVALID_CONN, __FUNCTION__); + virDispatchError(NULL); + return -1; + } + virMutexLock(&ident->lock); + VIR_DEBUG("ident=%p refs=%d", ident, ident->refs); + ident->refs++; + virMutexUnlock(&ident->lock); + return 0; +} + +int virIdentitySetAttr(virIdentityPtr ident, + unsigned int attr, + const char *value) +{ + VIR_DEBUG("ident=%p attribute=%u value=%s", ident, attr, NULLSTR(value)); + + if ((!VIR_IS_IDENTITY(ident))) { + virLibConnError(VIR_ERR_INVALID_CONN, __FUNCTION__); + virDispatchError(NULL); + return -1; + } + + if (attr >= VIR_IDENTITY_ATTR_LAST) { + virLibConnError(VIR_ERR_INVALID_ARG, __FUNCTION__); + virDispatchError(NULL); + return -1; + } + + if (!value) { + virLibConnError(VIR_ERR_INVALID_ARG, __FUNCTION__); + virDispatchError(NULL); + return -1; + } + + virMutexLock(&ident->lock); + + if (ident->attrs[attr]) { + virLibConnError(VIR_ERR_OPERATION_DENIED, "%s", + _("Identity attribute is already set")); + goto error; + } + + if (!(ident->attrs[attr] = strdup(value))) { + virReportOOMError(); + goto error; + } + + virMutexUnlock(&ident->lock); + return 0; + +error: + virDispatchError(NULL); + virMutexUnlock(&ident->lock); + return -1; +} + + +int virIdentityGetAttr(virIdentityPtr ident, + unsigned int attr, + const char **value) +{ + VIR_DEBUG("ident=%p attribute=%d value=%p", ident, attr, value); + + if ((!VIR_IS_IDENTITY(ident))) { + virLibConnError(VIR_ERR_INVALID_CONN, __FUNCTION__); + virDispatchError(NULL); + return -1; + } + + if (attr >= VIR_IDENTITY_ATTR_LAST) { + virLibConnError(VIR_ERR_INVALID_ARG, __FUNCTION__); + virDispatchError(NULL); + return -1; + } + + virMutexLock(&ident->lock); + *value = ident->attrs[attr]; + virMutexUnlock(&ident->lock); + return 0; +} + + +int virIdentityIsEqual(virIdentityPtr identA, + virIdentityPtr identB) +{ + VIR_DEBUG("identA=%p identB=%p", identA, identB); + int ret = 0; + size_t i; + + if ((!VIR_IS_IDENTITY(identA))) { + virLibConnError(VIR_ERR_INVALID_CONN, __FUNCTION__); + virDispatchError(NULL); + return -1; + } + if ((!VIR_IS_IDENTITY(identB))) { + virLibConnError(VIR_ERR_INVALID_CONN, __FUNCTION__); + virDispatchError(NULL); + return -1; + } + virMutexLock(&identA->lock); + virMutexLock(&identB->lock); + + for (i = 0 ; i < VIR_IDENTITY_ATTR_LAST ; i++) { + if (identA->attrs[i] == NULL && + identB->attrs[i] != NULL) + goto cleanup; + if (identA->attrs[i] != NULL && + identB->attrs[i] == NULL) + goto cleanup; + if (STRNEQ(identA->attrs[i], + identB->attrs[i])) + goto cleanup; + } + + ret = 1; +cleanup: + virMutexUnlock(&identA->lock); + virMutexUnlock(&identB->lock); + return ret; +} + + +int virIdentityFree(virIdentityPtr ident) +{ + size_t i; + + if ((!VIR_IS_IDENTITY(ident))) { + virLibConnError(VIR_ERR_INVALID_CONN, __FUNCTION__); + virDispatchError(NULL); + return -1; + } + virMutexLock(&ident->lock); + VIR_DEBUG("ident=%p refs=%d", ident, ident->refs); + ident->refs--; + if (ident->refs > 0) { + int ret = ident->refs; + virMutexUnlock(&ident->lock); + return ret; + } + for (i = 0 ; i < VIR_IDENTITY_ATTR_LAST ; i++) + VIR_FREE(ident->attrs[i]); + + virMutexUnlock(&ident->lock); + virMutexDestroy(&ident->lock); + VIR_FREE(ident); + return 0; +} + + + /* * Not for public use. This function is part of the internal * implementation of driver features in the remote case. diff --git a/src/util/virterror.c b/src/util/virterror.c index 380dc56..701aa2f 100644 --- a/src/util/virterror.c +++ b/src/util/virterror.c @@ -1219,6 +1219,12 @@ virErrorMsg(virErrorNumber error, const char *info) else errmsg = _("operation aborted: %s"); break; + case VIR_ERR_INVALID_IDENTITY: + if (info == NULL) + errmsg = _("invalid identity pointer in"); + else + errmsg = _("invalid identity pointer in %s"); + break; } return (errmsg); } -- 1.7.7.5

From: "Daniel P. Berrange" <berrange@redhat.com> --- include/libvirt/virterror.h | 3 + po/POTFILES.in | 1 + src/Makefile.am | 16 + src/access/apis.txt | 577 +++++++++++++++++++++++++++++++++++++ src/access/viraccessdriver.h | 51 ++++ src/access/viraccessdrivernop.c | 44 +++ src/access/viraccessdrivernop.h | 28 ++ src/access/viraccessdriverstack.c | 105 +++++++ src/access/viraccessdriverstack.h | 32 ++ src/access/viraccessmanager.c | 328 +++++++++++++++++++++ src/access/viraccessmanager.h | 56 ++++ src/access/viraccessvector.c | 37 +++ src/access/viraccessvector.h | 73 +++++ src/libvirt_private.syms | 22 ++ src/util/virterror.c | 9 + 15 files changed, 1382 insertions(+), 0 deletions(-) create mode 100644 src/access/apis.txt create mode 100644 src/access/viraccessdriver.h create mode 100644 src/access/viraccessdrivernop.c create mode 100644 src/access/viraccessdrivernop.h create mode 100644 src/access/viraccessdriverstack.c create mode 100644 src/access/viraccessdriverstack.h create mode 100644 src/access/viraccessmanager.c create mode 100644 src/access/viraccessmanager.h create mode 100644 src/access/viraccessvector.c create mode 100644 src/access/viraccessvector.h diff --git a/include/libvirt/virterror.h b/include/libvirt/virterror.h index 6db31d8..74c2dec 100644 --- a/include/libvirt/virterror.h +++ b/include/libvirt/virterror.h @@ -85,6 +85,7 @@ typedef enum { VIR_FROM_LOCKING = 42, /* Error from lock manager */ VIR_FROM_HYPERV = 43, /* Error from Hyper-V driver */ VIR_FROM_CAPABILITIES = 44, /* Error from capabilities */ + VIR_FROM_ACCESS = 45, /* Error from access control manager */ } virErrorDomain; @@ -244,6 +245,8 @@ typedef enum { VIR_ERR_OPERATION_ABORTED = 78, /* operation on a domain was canceled/aborted by user */ VIR_ERR_INVALID_IDENTITY = 79, /* Invalid identity pointer */ + VIR_ERR_ACCESS_DENIED = 80, /* operation on the object/resource + was denied */ } virErrorNumber; /** diff --git a/po/POTFILES.in b/po/POTFILES.in index 3e8359a..0a5443f 100644 --- a/po/POTFILES.in +++ b/po/POTFILES.in @@ -4,6 +4,7 @@ daemon/remote.c daemon/remote_dispatch.h daemon/stream.c gnulib/lib/gai_strerror.c +src/access/viraccessmanager.c src/conf/cpu_conf.c src/conf/domain_conf.c src/conf/domain_event.c diff --git a/src/Makefile.am b/src/Makefile.am index 0a1221a..0baa17d 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -518,6 +518,13 @@ SECURITY_DRIVER_APPARMOR_SOURCES = \ security/security_apparmor.h security/security_apparmor.c +ACCESS_DRIVER_SOURCES = \ + access/viraccessvector.h access/viraccessvector.c \ + access/viraccessmanager.h access/viraccessmanager.c \ + access/viraccessdriver.h \ + access/viraccessdrivernop.h access/viraccessdrivernop.c \ + access/viraccessdriverstack.h access/viraccessdriverstack.c + NODE_DEVICE_DRIVER_SOURCES = \ node_device/node_device_driver.c \ node_device/node_device_driver.h \ @@ -1101,6 +1108,15 @@ libvirt_driver_security_la_CFLAGS += $(APPARMOR_CFLAGS) libvirt_driver_security_la_LIBADD += $(APPARMOR_LIBS) endif +libvirt_driver_access_la_SOURCES = $(ACCESS_DRIVER_SOURCES) +noinst_LTLIBRARIES += libvirt_driver_access.la +libvirt_la_BUILT_LIBADD += libvirt_driver_access.la +libvirt_driver_access_la_CFLAGS = \ + -I@top_srcdir@/src/conf $(AM_CFLAGS) +libvirt_driver_access_la_LDFLAGS = $(AM_LDFLAGS) +libvirt_driver_access_la_LIBADD = + + # Add all conditional sources just in case... EXTRA_DIST += \ $(TEST_DRIVER_SOURCES) \ diff --git a/src/access/apis.txt b/src/access/apis.txt new file mode 100644 index 0000000..16cc49c --- /dev/null +++ b/src/access/apis.txt @@ -0,0 +1,577 @@ + +Non-driver based APIs + + +virConnCopyLastError: +virResetError: +virResetLastError: +virSaveLastError: +virSetErrorFunc: +virConnGetLastError: +virConnResetLastError: +virConnSetErrorFunc: +virCopyLastError: +virDefaultErrorFunc: +virFreeError: +virGetLastError: + +virInitialize: +virConnectClose: +virConnectGetLibVersion: +virGetVersion: +virConnectGetVersion: +virConnectGetType: +virConnectGetURI: + + +virConnectRef: +virDomainRef: +virInterfaceRef: +virNetworkRef: +virNodeDeviceRef: +virNWFilterRef: +virSecretRef: +virStoragePoolRef: +virStorageVolRef: +virStreamRef: + +virIdentityFree: +virDomainFree: +virDomainSnapshotFree: +virInterfaceFree: +virNetworkFree: +virNodeDeviceFree: +virNWFilterFree: +virSecretFree: +virStoragePoolFree: +virStorageVolFree: +virStreamFree: + +virDomainGetConnect: +virInterfaceGetConnect: +virDomainSnapshotGetConnect: +virNetworkGetConnect: +virSecretGetConnect: +virStoragePoolGetConnect: +virStorageVolGetConnect: +virDomainSnapshotGetDomain: + +virEventAddHandle: +virEventAddTimeout: +virEventRegisterDefaultImpl: +virEventRegisterImpl: +virEventRemoveHandle: +virEventRemoveTimeout: +virEventRunDefaultImpl: +virEventUpdateHandle: +virEventUpdateTimeout: + + +virConnectBaselineCPU: + + - No state access + +virConnectCompareCPU: + + - Access host CPU + +virConnectGetCapabilities: + + - Access host CPU, emulators, NUMA + +virConnectGetHostname: + + - hostname resolve + +virConnectGetIdentity: + + - No state + +virConnectGetMaxVcpus: + + - Hypercall + +virConnectGetSysinfo: + + - Sysfs / dmidecode ? (cached from capabilities) + +virConnectIsAlive: + + - Driver check + +virConnectIsEncrypted: +virConnectIsSecure: + + - Property lookup + +virConnectOpen: +virConnectOpenAuth: +virConnectOpenReadOnly: + + - RPC layer + +virConnectSetIdentity: + + - RPC layer + +virConnectSetKeepAlive: + + - RPC layer + +virNodeGetCellsFreeMemory: + + - NUMA props + +virNodeGetCPUStats: + + - Cgroups + +virNodeGetFreeMemory: + + - NUMA props + +virNodeGetInfo: + + - NUMA / sysfs + +virNodeGetMemoryStats: + + - CGroups + +virNodeGetSecurityModel: + + - Capabilities + +virNodeSuspendForDuration: + + - PM utils invoke + + + +virConnectNumOfDefinedDomains: +virConnectNumOfDomains: +virConnectListDefinedDomains: +virConnectListDomains: + + - 'search_domains' on libvirtd + - 'getattr' on each domain + + +virConnectDomainEventDeregister: +virConnectDomainEventDeregisterAny: +virConnectDomainEventRegister: +virConnectDomainEventRegisterAny: + + - 'monitor' on domain + +virConnectDomainXMLFromNative: +virConnectDomainXMLToNative: + + - 'domain_xml' on libvirtd + +virDomainAbortJob: + + - 'abort_job' + +virDomainBlockJobAbort: + + - 'abort_block_job' + +virDomainBlockJobSetSpeed: + + - 'setattr_block_job' + +virDomainBlockPeek: + + - 'block_peek' + +virDomainBlockPull: + + - 'block_pull' + - 'create_block_job' + +virDomainBlockResize: + + - 'block_resize' + +virDomainBlockStats: +virDomainBlockStatsFlags: + + - 'read' + +virDomainCoreDump: + + - 'coredump' + +virDomainCreate: +virDomainCreateLinux: +virDomainCreateWithFlags: + + - 'start' + +virDomainCreateXML: + + - 'start' + 'write' + +virDomainDefineXML: + + - 'save' + 'write' + +virDomainDestroy: +virDomainDestroyFlags: + + - 'stop' + + +virDomainGetID: +virDomainGetName: +virDomainGetUUID: +virDomainGetUUIDString: +virDomainLookupByUUIDString: + + - Outside driver + +virDomainLookupByID: +virDomainLookupByName: +virDomainLookupByUUID: + + - getattr + +virDomainGetAutostart: +virDomainGetBlkioParameters: +virDomainGetBlockInfo: +virDomainGetBlockIoTune: +virDomainGetBlockJobInfo: +virDomainGetControlInfo: +virDomainGetInfo: +virDomainGetInterfaceParameters: +virDomainGetJobInfo: +virDomainGetMaxMemory: +virDomainGetMaxVcpus: +virDomainGetMemoryParameters: +virDomainGetNumaParameters: +virDomainGetOSType: +virDomainGetSchedulerParameters: +virDomainGetSchedulerParametersFlags: +virDomainGetSchedulerType: +virDomainGetSecurityLabel: +virDomainGetState: +virDomainGetVcpuPinInfo: +virDomainGetVcpus: +virDomainGetVcpusFlags: +virDomainGetXMLDesc: +virDomainHasCurrentSnapshot: +virDomainHasManagedSaveImage: +virDomainInterfaceStats: +virDomainIsActive: +virDomainIsPersistent: +virDomainIsUpdated: +virDomainMemoryStats: + + - 'read' + +virDomainInjectNMI: + + - inject_nmi + + +virDomainManagedSave: + + - save_create + +virDomainManagedSaveRemove: + + - save_delete + +virDomainMemoryPeek: + + - memory_peek + + +virDomainMigrate: +virDomainMigrate2: +virDomainMigrateGetMaxSpeed: +virDomainMigrateSetMaxDowntime: +virDomainMigrateSetMaxSpeed: +virDomainMigrateToURI: +virDomainMigrateToURI2: + + - migrate + +virDomainOpenConsole: + + - open_console + +virDomainOpenGraphics: + + - open_graphics + +virDomainPinVcpu: +virDomainPinVcpuFlags: + + - write ? + +virDomainReboot: + + - reboot + +virDomainReset: + + - reset + +virDomainRestore: +virDomainRestoreFlags: + + - restore + - start + +virDomainResume: + + - resume + +virDomainRevertToSnapshot: + + - snapshot_revert + +virDomainSave: +virDomainSaveFlags: + + - stop + - save + +virDomainSaveImageDefineXML: + + - save_write (setattr ?) + +virDomainSaveImageGetXMLDesc: + + - save_getattr + + +virDomainScreenshot: + + - screenshot + +virDomainSendKey: + + - sendkey + +virDomainSetAutostart: +virDomainSetBlkioParameters: +virDomainSetBlockIoTune: +virDomainSetInterfaceParameters: +virDomainSetMaxMemory: +virDomainSetMemory: +virDomainSetMemoryFlags: +virDomainSetMemoryParameters: +virDomainSetNumaParameters: +virDomainSetSchedulerParameters: +virDomainSetSchedulerParametersFlags: +virDomainSetVcpus: +virDomainSetVcpusFlags: +virDomainAttachDevice: +virDomainAttachDeviceFlags: +virDomainUpdateDeviceFlags: +virDomainDetachDevice: +virDomainDetachDeviceFlags: + + + - write (+ possible save) + +virDomainShutdown: + + - shutdown + +virDomainSnapshotCreateXML: + + - snapshot_create + +virDomainSnapshotCurrent: + + - snapshot_getattr (or getattr ?) + +virDomainSnapshotDelete: + + - snapshot_delete + +virDomainSnapshotGetName: +virDomainSnapshotGetParent: +virDomainSnapshotGetXMLDesc: + + - snapshot_getattr + +virDomainSnapshotListChildrenNames: +virDomainSnapshotListNames: + + - snapshot_search + - Filter on snapshot_getattr + +virDomainSnapshotLookupByName: + + - snapshot_getattr + +virDomainSnapshotNum: +virDomainSnapshotNumChildren: + + - snapshot_search + - Filter on snapshot_getattr + +virDomainSuspend: + + - suspend + +virDomainUndefine: +virDomainUndefineFlags: + + - delete + + + +virConnectNumOfDefinedInterfaces: +virConnectNumOfInterfaces: +virConnectListDefinedInterfaces: +virConnectListInterfaces: +virInterfaceChangeBegin: +virInterfaceChangeCommit: +virInterfaceChangeRollback: +virInterfaceCreate: +virInterfaceDefineXML: +virInterfaceDestroy: +virInterfaceGetMACString: +virInterfaceGetName: +virInterfaceGetXMLDesc: +virInterfaceIsActive: +virInterfaceLookupByMACString: +virInterfaceLookupByName: +virInterfaceUndefine: + + +virConnectNumOfDefinedNetworks: +virConnectNumOfNetworks: +virConnectListDefinedNetworks: +virConnectListNetworks: +virNetworkCreate: +virNetworkCreateXML: +virNetworkDefineXML: +virNetworkDestroy: +virNetworkGetAutostart: +virNetworkGetBridgeName: +virNetworkGetName: +virNetworkGetUUID: +virNetworkGetUUIDString: +virNetworkGetXMLDesc: +virNetworkIsActive: +virNetworkIsPersistent: +virNetworkLookupByName: +virNetworkLookupByUUID: +virNetworkLookupByUUIDString: +virNetworkSetAutostart: +virNetworkUndefine: + + + +virNodeDeviceCreateXML: +virNodeDeviceDestroy: +virNodeDeviceDettach: +virNodeDeviceGetName: +virNodeDeviceGetParent: +virNodeDeviceGetXMLDesc: +virNodeDeviceListCaps: +virNodeDeviceLookupByName: +virNodeDeviceNumOfCaps: +virNodeDeviceReAttach: +virNodeDeviceReset: +virNodeListDevices: +virNodeNumOfDevices: + + + +virConnectNumOfNWFilters: +virConnectListNWFilters: +virNWFilterDefineXML: +virNWFilterGetName: +virNWFilterGetUUID: +virNWFilterGetUUIDString: +virNWFilterGetXMLDesc: +virNWFilterLookupByName: +virNWFilterLookupByUUID: +virNWFilterLookupByUUIDString: +virNWFilterUndefine: + + + +virConnectNumOfSecrets: +virConnectListSecrets: +virSecretDefineXML: +virSecretGetUsageID: +virSecretGetUsageType: +virSecretGetUUID: +virSecretGetUUIDString: +virSecretGetValue: +virSecretGetXMLDesc: +virSecretLookupByUsage: +virSecretLookupByUUID: +virSecretLookupByUUIDString: +virSecretSetValue: +virSecretUndefine: + + + +virConnectNumOfDefinedStoragePools: +virConnectNumOfStoragePools: +virConnectListDefinedStoragePools: +virConnectListStoragePools: +virConnectFindStoragePoolSources: +virStoragePoolBuild: +virStoragePoolCreate: +virStoragePoolCreateXML: +virStoragePoolDefineXML: +virStoragePoolDelete: +virStoragePoolDestroy: +virStoragePoolGetAutostart: +virStoragePoolGetInfo: +virStoragePoolGetName: +virStoragePoolGetUUID: +virStoragePoolGetUUIDString: +virStoragePoolGetXMLDesc: +virStoragePoolIsActive: +virStoragePoolIsPersistent: +virStoragePoolListVolumes: +virStoragePoolLookupByName: +virStoragePoolLookupByUUID: +virStoragePoolLookupByUUIDString: +virStoragePoolLookupByVolume: +virStoragePoolNumOfVolumes: +virStoragePoolRefresh: +virStoragePoolSetAutostart: +virStoragePoolUndefine: + + + +virStorageVolCreateXML: +virStorageVolCreateXMLFrom: +virStorageVolDelete: +virStorageVolDownload: +virStorageVolGetInfo: +virStorageVolGetKey: +virStorageVolGetName: +virStorageVolGetPath: +virStorageVolGetXMLDesc: +virStorageVolLookupByKey: +virStorageVolLookupByName: +virStorageVolLookupByPath: +virStorageVolUpload: +virStorageVolWipe: + + + +virStreamAbort: +virStreamEventAddCallback: +virStreamEventRemoveCallback: +virStreamEventUpdateCallback: +virStreamFinish: +virStreamNew: +virStreamRecv: +virStreamRecvAll: +virStreamSend: +virStreamSendAll: diff --git a/src/access/viraccessdriver.h b/src/access/viraccessdriver.h new file mode 100644 index 0000000..21ae303 --- /dev/null +++ b/src/access/viraccessdriver.h @@ -0,0 +1,51 @@ +/* + * viraccessdriver.h: 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, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef __VIR_ACCESS_DRIVER_H__ +# define __VIR_ACCESS_DRIVER_H__ + +# include "conf/domain_conf.h" +# include "access/viraccessmanager.h" + +typedef bool (*virAccessDriverCheckConnectDrv)(virAccessManagerPtr manager, + virAccessVectorConnect av); +typedef bool (*virAccessDriverCheckDomainDrv)(virAccessManagerPtr manager, + virDomainDefPtr def, + virAccessVectorDomain av); + +typedef int (*virAccessDriverSetupDrv)(virAccessManagerPtr manager); +typedef void (*virAccessDriverCleanupDrv)(virAccessManagerPtr manager); + +typedef struct _virAccessDriver virAccessDriver; +typedef virAccessDriver *virAccessDriverPtr; + +struct _virAccessDriver { + size_t privateDataLen; + const char *name; + + virAccessDriverSetupDrv setup; + virAccessDriverCleanupDrv cleanup; + + virAccessDriverCheckConnectDrv checkConnect; + virAccessDriverCheckDomainDrv checkDomain; +}; + + +#endif /* __VIR_ACCESS_DRIVER_H__ */ diff --git a/src/access/viraccessdrivernop.c b/src/access/viraccessdrivernop.c new file mode 100644 index 0000000..26a14e3 --- /dev/null +++ b/src/access/viraccessdrivernop.c @@ -0,0 +1,44 @@ +/* + * viraccessdrivernop.c: no-op access control driver + * + * Copyright (C) 2011 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, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include <config.h> + +#include "access/viraccessdrivernop.h" + +static bool +virAccessDriverNopCheckConnect(virAccessManagerPtr manager ATTRIBUTE_UNUSED, + virAccessVectorConnect av ATTRIBUTE_UNUSED) +{ + return true; +} + +static bool +virAccessDriverNopCheckDomain(virAccessManagerPtr manager ATTRIBUTE_UNUSED, + virDomainDefPtr def ATTRIBUTE_UNUSED, + virAccessVectorDomain av ATTRIBUTE_UNUSED) +{ + return true; +} + +virAccessDriver accessDriverNop = { + .name = "none", + .checkConnect = virAccessDriverNopCheckConnect, + .checkDomain = virAccessDriverNopCheckDomain, +}; diff --git a/src/access/viraccessdrivernop.h b/src/access/viraccessdrivernop.h new file mode 100644 index 0000000..a3d9be3 --- /dev/null +++ b/src/access/viraccessdrivernop.h @@ -0,0 +1,28 @@ +/* + * viraccessdrivernop.h: no-op access control driver + * + * Copyright (C) 2011 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, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef __VIR_ACCESS_DRIVER_NOP_H__ +# define __VIR_ACCESS_DRIVER_NOP_H__ + +# include "access/viraccessdriver.h" + +extern virAccessDriver accessDriverNop; + +#endif /* __VIR_ACCESS_DRIVER_NOP_H__ */ diff --git a/src/access/viraccessdriverstack.c b/src/access/viraccessdriverstack.c new file mode 100644 index 0000000..783e156 --- /dev/null +++ b/src/access/viraccessdriverstack.c @@ -0,0 +1,105 @@ +/* + * viraccessdriverstack.c: stacked 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, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include <config.h> + +#include "access/viraccessdriverstack.h" +#include "memory.h" +#include "virterror_internal.h" + +#define VIR_FROM_THIS VIR_FROM_ACCESS + +typedef struct _virAccessDriverStackPrivate virAccessDriverStackPrivate; +typedef virAccessDriverStackPrivate *virAccessDriverStackPrivatePtr; + +struct _virAccessDriverStackPrivate { + virAccessManagerPtr *managers; + size_t managersLen; +}; + + +int virAccessDriverStackAppend(virAccessManagerPtr manager, + virAccessManagerPtr child) +{ + virAccessDriverStackPrivatePtr priv = virAccessManagerGetPrivateData(manager); + + if (VIR_EXPAND_N(priv->managers, priv->managersLen, 1) < 0) { + virReportOOMError(); + return -1; + } + + priv->managers[priv->managersLen-1] = child; + + return 0; +} + + +static void virAccessDriverStackCleanup(virAccessManagerPtr manager) +{ + virAccessDriverStackPrivatePtr priv = virAccessManagerGetPrivateData(manager); + size_t i; + + for (i = 0 ; i < priv->managersLen ; i++) { + virAccessManagerFree(priv->managers[i]); + } + VIR_FREE(priv->managers); +} + + +static bool +virAccessDriverStackCheckConnect(virAccessManagerPtr manager, + virAccessVectorConnect av) +{ + virAccessDriverStackPrivatePtr priv = virAccessManagerGetPrivateData(manager); + bool ret = true; + size_t i; + + for (i = 0 ; i < priv->managersLen ; i++) { + /* We do not short-circuit on first denial - always check all drivers */ + if (!virAccessManagerCheckConnect(priv->managers[i], av)) + ret = false; + } + + return ret; +} + +static bool +virAccessDriverStackCheckDomain(virAccessManagerPtr manager, + virDomainDefPtr def, + virAccessVectorDomain av) +{ + virAccessDriverStackPrivatePtr priv = virAccessManagerGetPrivateData(manager); + bool ret = true; + size_t i; + + for (i = 0 ; i < priv->managersLen ; i++) { + /* We do not short-circuit on first denial - always check all drivers */ + if (!virAccessManagerCheckDomain(priv->managers[i], def, av)) + ret = false; + } + + return ret; +} + +virAccessDriver accessDriverStack = { + .cleanup = virAccessDriverStackCleanup, + .checkConnect = virAccessDriverStackCheckConnect, + .checkDomain = virAccessDriverStackCheckDomain, +}; diff --git a/src/access/viraccessdriverstack.h b/src/access/viraccessdriverstack.h new file mode 100644 index 0000000..8ab3a0d --- /dev/null +++ b/src/access/viraccessdriverstack.h @@ -0,0 +1,32 @@ +/* + * viraccessdriverstack.h: stacked 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, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef __VIR_ACCESS_DRIVER_STACK_H__ +# define __VIR_ACCESS_DRIVER_STACK_H__ + +# include "access/viraccessdriver.h" + + +int virAccessDriverStackAppend(virAccessManagerPtr manager, + virAccessManagerPtr child); + +extern virAccessDriver accessDriverStack; + +#endif /* __VIR_ACCESS_DRIVER_STACK_H__ */ diff --git a/src/access/viraccessmanager.c b/src/access/viraccessmanager.c new file mode 100644 index 0000000..5bb4404 --- /dev/null +++ b/src/access/viraccessmanager.c @@ -0,0 +1,328 @@ +/* + * viraccessmanager.c: access control manager + * + * 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, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include <config.h> + +#include "viraccessmanager.h" +#include "memory.h" +#include "virterror_internal.h" +#include "threads.h" +#if HAVE_SELINUX +# include <selinux/selinux.h> +#endif +#include "access/viraccessdrivernop.h" +#include "access/viraccessdriverstack.h" +#include "logging.h" + +#define VIR_FROM_THIS VIR_FROM_ACCESS +#define virAccessError(code, ...) \ + virReportErrorHelper(VIR_FROM_THIS, code, __FILE__, \ + __FUNCTION__, __LINE__, __VA_ARGS__) + +static volatile bool onceInitErr = false; +static virOnceControl onceInit = VIR_ONCE_CONTROL_INITIALIZER; +static virThreadLocal realIdentity; +static virThreadLocal effectiveIdentity; + +struct _virAccessManager { + virAccessDriverPtr drv; +}; + + +static void virAccessManagerOnceInit(void) +{ + if (virThreadLocalInit(&realIdentity, + (virThreadLocalCleanup)virIdentityFree) < 0) + onceInitErr = true; + if (virThreadLocalInit(&effectiveIdentity, + (virThreadLocalCleanup)virIdentityFree) < 0) + onceInitErr = true; +} + +static bool virAccessManagerInit(void) +{ + if (virOnce(&onceInit, virAccessManagerOnceInit) < 0 || + onceInitErr) { + virReportSystemError(errno, "%s", + _("Failed to initialize access manager")); + return false; + } + + return true; +} + +virIdentityPtr virAccessManagerGetSystemIdentity(void) +{ + char *username = NULL; + char *groupname = NULL; + char *seccontext = NULL; + virIdentityPtr ret = NULL; + gid_t gid = getgid(); + uid_t uid = getuid(); +#if HAVE_SELINUX + security_context_t con; +#endif + + if (!(username = virGetUserName(uid))) + goto cleanup; + if (!(groupname = virGetGroupName(gid))) + goto cleanup; + +#if HAVE_SELINUX + if (getcon(&con) < 0) { + virReportSystemError(errno, "%s", + _("Unable to lookup SELinux process context")); + goto cleanup; + } + seccontext = strdup(con); + freecon(con); + if (!seccontext) { + virReportOOMError(); + goto cleanup; + } +#endif + + if (!(ret = virIdentityNew())) + goto cleanup; + + if (username && + virIdentitySetAttr(ret, VIR_IDENTITY_ATTR_UNIX_USER_NAME, username) < 0) + goto error; + if (groupname && + virIdentitySetAttr(ret, VIR_IDENTITY_ATTR_UNIX_GROUP_NAME, groupname) < 0) + goto error; + if (seccontext && + virIdentitySetAttr(ret, VIR_IDENTITY_ATTR_SECURITY_CONTEXT, seccontext) < 0) + goto error; + +cleanup: + VIR_FREE(username); + VIR_FREE(groupname); + VIR_FREE(seccontext); + return ret; + +error: + virIdentityFree(ret); + ret = NULL; + goto cleanup; +} + +virIdentityPtr virAccessManagerGetEffectiveIdentity(void) +{ + virIdentityPtr ret; + + if (!virAccessManagerInit()) + return NULL; + + ret = virThreadLocalGet(&effectiveIdentity); + virIdentityRef(ret); + return ret; +} + +int virAccessManagerSetEffectiveIdentity(virIdentityPtr identity) +{ + virIdentityPtr old; + + if (!virAccessManagerInit()) + return -1; + + old = virThreadLocalGet(&effectiveIdentity); + if (old) + virIdentityFree(old); + + if (identity) + virIdentityRef(identity); + virThreadLocalSet(&effectiveIdentity, identity); + return 0; +} + +virIdentityPtr virAccessManagerGetRealIdentity(void) +{ + virIdentityPtr ret; + + if (!virAccessManagerInit()) + return NULL; + + ret = virThreadLocalGet(&realIdentity); + virIdentityRef(ret); + return ret; +} + +int virAccessManagerSetRealIdentity(virIdentityPtr identity) +{ + virIdentityPtr old; + + if (!virAccessManagerInit()) + return -1; + + old = virThreadLocalGet(&realIdentity); + if (old) + virIdentityFree(old); + + if (identity) + virIdentityRef(identity); + virThreadLocalSet(&realIdentity, identity); + return 0; +} + + +static virAccessManagerPtr virAccessManagerNewDriver(virAccessDriverPtr drv) +{ + virAccessManagerPtr mgr; + + if (VIR_ALLOC_VAR(mgr, char, drv->privateDataLen) < 0) { + virReportOOMError(); + return NULL; + } + + mgr->drv = drv; + + if (mgr->drv->setup && + mgr->drv->setup(mgr) < 0) { + VIR_FREE(mgr); + return NULL; + } + + return mgr; +} + + +static virAccessDriverPtr accessDrivers[] = { + &accessDriverNop, +}; + + +static virAccessDriverPtr virAccessManagerFindDriver(const char *name) +{ + size_t i; + for (i = 0 ; i < ARRAY_CARDINALITY(accessDrivers) ; i++) { + if (STREQ(name, accessDrivers[i]->name)) + return accessDrivers[i]; + } + + return NULL; +} + + +virAccessManagerPtr virAccessManagerNew(const char *name) +{ + virAccessDriverPtr drv = virAccessManagerFindDriver(name); + if (!drv) + return NULL; + + return virAccessManagerNewDriver(drv); +} + + +virAccessManagerPtr virAccessManagerNewStack(const char **names, + size_t namesLen) +{ + virAccessManagerPtr manager = virAccessManagerNewDriver(&accessDriverStack); + size_t i; + + if (!manager) + return NULL; + + for (i = 0 ; i < namesLen ; i++) { + virAccessManagerPtr child = virAccessManagerNew(names[i]); + + if (!child) + goto error; + + if (virAccessDriverStackAppend(manager, child) < 0) { + virAccessManagerFree(child); + goto error; + } + } + + return manager; + +error: + virAccessManagerFree(manager); + return NULL; +} + + +void *virAccessManagerGetPrivateData(virAccessManagerPtr mgr) +{ + /* This accesses the memory just beyond mgr, which was allocated + * via VIR_ALLOC_VAR earlier. */ + return mgr + 1; +} + + +void virAccessManagerFree(virAccessManagerPtr mgr) +{ + if (!mgr) + return; + + if (mgr->drv->cleanup) + mgr->drv->cleanup(mgr); + + VIR_FREE(mgr); +} + + +/* Standard security practice is to not tell the caller *why* + * they were denied access. So this method takes the real + * libvirt errors & replaces it with a generic error. Fortunately + * the daemon logs will still contain the original error message + * should the admin need to debug things + */ +static bool +virAccessManagerSanitizeError(bool ret) +{ + if (!ret) { + virResetLastError(); + virAccessError(VIR_ERR_ACCESS_DENIED, NULL); + } + + return ret; +} + +bool virAccessManagerCheckConnect(virAccessManagerPtr manager, + virAccessVectorConnect av) +{ + bool ret = true; + VIR_DEBUG("manager=%p driver=%s av=%d", + manager, manager->drv->name, av); + + if (manager->drv->checkConnect && + !manager->drv->checkConnect(manager, av)) + ret = false; + + return virAccessManagerSanitizeError(ret); +} + + +bool virAccessManagerCheckDomain(virAccessManagerPtr manager, + virDomainDefPtr def, + virAccessVectorDomain av) +{ + bool ret = true; + VIR_DEBUG("manager=%p driver=%s def=%p av=%d", + manager, manager->drv->name, def, av); + + if (manager->drv->checkDomain && + !manager->drv->checkDomain(manager, def, av)) + ret = false; + + return virAccessManagerSanitizeError(ret); +} diff --git a/src/access/viraccessmanager.h b/src/access/viraccessmanager.h new file mode 100644 index 0000000..ccbae12 --- /dev/null +++ b/src/access/viraccessmanager.h @@ -0,0 +1,56 @@ +/* + * viraccessmanager.h: access control manager + * + * 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, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef __VIR_ACCESS_MANAGER_H__ +# define __VIR_ACCESS_MANAGER_H__ + +# include "rpc/virnetserverclient.h" +# include "conf/domain_conf.h" +# include "access/viraccessvector.h" + +virIdentityPtr virAccessManagerGetClientIdentity(virNetServerClientPtr client); +virIdentityPtr virAccessManagerGetSystemIdentity(void); + +virIdentityPtr virAccessManagerGetEffectiveIdentity(void); +int virAccessManagerSetEffectiveIdentity(virIdentityPtr identity); + +virIdentityPtr virAccessManagerGetRealIdentity(void); +int virAccessManagerSetRealIdentity(virIdentityPtr identity); + +typedef struct _virAccessManager virAccessManager; +typedef virAccessManager *virAccessManagerPtr; + +virAccessManagerPtr virAccessManagerNew(const char *name); +virAccessManagerPtr virAccessManagerNewStack(const char **names, + size_t namesLen); + + +void *virAccessManagerGetPrivateData(virAccessManagerPtr manager); +void virAccessManagerFree(virAccessManagerPtr manager); + + +bool virAccessManagerCheckConnect(virAccessManagerPtr manager, + virAccessVectorConnect av); +bool virAccessManagerCheckDomain(virAccessManagerPtr manager, + virDomainDefPtr def, + virAccessVectorDomain av); + + +#endif /* __VIR_ACCESS_MANAGER_H__ */ diff --git a/src/access/viraccessvector.c b/src/access/viraccessvector.c new file mode 100644 index 0000000..5f40164 --- /dev/null +++ b/src/access/viraccessvector.c @@ -0,0 +1,37 @@ +/* + * viraccessvector.c: access control vectors + * + * 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, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include <config.h> + +#include "viraccessvector.h" + + +VIR_ENUM_IMPL(virAccessVectorConnect, + VIR_ACCESS_VECTOR_CONNECT_LAST, + "getattr", "search_domains"); + +VIR_ENUM_IMPL(virAccessVectorDomain, + VIR_ACCESS_VECTOR_DOMAIN_LAST, + "getattr", "read", "write", "read_secure", + "start", "stop", "save", "delete", + "shutdown", "reboot", "reset", + "migrate", "snapshot", "suspend", "hibernate", "core_dump", + "inject_nmi", "send_key", "read_block", "read_mem", + "open_graphics", "open_console", "screenshot"); diff --git a/src/access/viraccessvector.h b/src/access/viraccessvector.h new file mode 100644 index 0000000..82f71cc --- /dev/null +++ b/src/access/viraccessvector.h @@ -0,0 +1,73 @@ +/* + * viraccessvector.h: access control vectors + * + * 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, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef __VIR_ACCESS_VECTOR_H__ +# define __VIR_ACCESS_VECTOR_H__ + +# include "internal.h" +# include "util.h" + +typedef enum { + VIR_ACCESS_VECTOR_CONNECT_GETATTR, + VIR_ACCESS_VECTOR_CONNECT_SEARCH_DOMAINS, + + VIR_ACCESS_VECTOR_CONNECT_LAST, +} virAccessVectorConnect; + +typedef enum { + VIR_ACCESS_VECTOR_DOMAIN_GETATTR, /* Name/ID/UUID access */ + VIR_ACCESS_VECTOR_DOMAIN_READ, /* Config access */ + VIR_ACCESS_VECTOR_DOMAIN_WRITE, /* Config change */ + VIR_ACCESS_VECTOR_DOMAIN_READ_SECURE, + + VIR_ACCESS_VECTOR_DOMAIN_START, + VIR_ACCESS_VECTOR_DOMAIN_STOP, + + VIR_ACCESS_VECTOR_DOMAIN_SAVE, + VIR_ACCESS_VECTOR_DOMAIN_DELETE, + + /* Merge these 3 into 1 ? */ + VIR_ACCESS_VECTOR_DOMAIN_SHUTDOWN, + VIR_ACCESS_VECTOR_DOMAIN_REBOOT, + VIR_ACCESS_VECTOR_DOMAIN_RESET, + + VIR_ACCESS_VECTOR_DOMAIN_MIGRATE, + VIR_ACCESS_VECTOR_DOMAIN_SNAPSHOT, + VIR_ACCESS_VECTOR_DOMAIN_SUSPEND, + VIR_ACCESS_VECTOR_DOMAIN_HIBERNATE, + VIR_ACCESS_VECTOR_DOMAIN_CORE_DUMP, + + VIR_ACCESS_VECTOR_DOMAIN_INJECT_NMI, + VIR_ACCESS_VECTOR_DOMAIN_SEND_KEY, + + VIR_ACCESS_VECTOR_DOMAIN_READ_BLOCK, + VIR_ACCESS_VECTOR_DOMAIN_READ_MEM, + + VIR_ACCESS_VECTOR_DOMAIN_OPEN_GRAPHICS, + VIR_ACCESS_VECTOR_DOMAIN_OPEN_CONSOLE, + VIR_ACCESS_VECTOR_DOMAIN_SCREENSHOT, + + VIR_ACCESS_VECTOR_DOMAIN_LAST, +} virAccessVectorDomain; + +VIR_ENUM_DECL(virAccessVectorConnect); +VIR_ENUM_DECL(virAccessVectorDomain); + +#endif /* __VIR_ACCESS_VECTOR_H__ */ diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms index 363f316..829efb1 100644 --- a/src/libvirt_private.syms +++ b/src/libvirt_private.syms @@ -5,6 +5,21 @@ # Keep this file sorted by header name, then by symbols with each header. # + +# access/viraccessmanager.h +virAccessManagerInit; +virAccessManagerGetSystemIdentity; +virAccessManagerGetRealIdentity; +virAccessManagerGetEffectiveIdentity; +virAccessManagerSetRealIdentity; +virAccessManagerSetEffectiveIdentity; +virAccessManagerNew; +virAccessManagerNewStack; +virAccessManagerFree; +virAccessManagerCheckConnect; +virAccessManagerCheckDomain; + + # authhelper.h virRequestPassword; virRequestUsername; @@ -1158,6 +1173,13 @@ virUUIDGenerate; virUUIDParse; +# viraccessvector.h +virAccessVectorConnectTypeFromString; +virAccessVectorConnectTypeToString; +virAccessVectorDomainTypeFromString; +virAccessVectorDomainTypeToString; + + # viraudit.h virAuditClose; virAuditEncode; diff --git a/src/util/virterror.c b/src/util/virterror.c index 701aa2f..050f370 100644 --- a/src/util/virterror.c +++ b/src/util/virterror.c @@ -178,6 +178,9 @@ static const char *virErrorDomainName(virErrorDomain domain) { case VIR_FROM_CAPABILITIES: dom = "Capabilities "; break; + case VIR_FROM_ACCESS: + dom = "Access Manager "; + break; } return(dom); } @@ -1225,6 +1228,12 @@ virErrorMsg(virErrorNumber error, const char *info) else errmsg = _("invalid identity pointer in %s"); break; + case VIR_ERR_ACCESS_DENIED: + if (info == NULL) + errmsg = _("access denied"); + else + errmsg = _("access denied: %s"); + break; } return (errmsg); } -- 1.7.7.5

From: "Daniel P. Berrange" <berrange@redhat.com> Allow the logging APIs to be called with a va_list for format args, instead of requiring var-args usage. * src/util/logging.h, src/util/logging.c: Add virLogVMessage --- src/util/logging.c | 29 ++++++++++++++++++++++++----- src/util/logging.h | 5 +++++ 2 files changed, 29 insertions(+), 5 deletions(-) diff --git a/src/util/logging.c b/src/util/logging.c index 8d60280..6e89d10 100644 --- a/src/util/logging.c +++ b/src/util/logging.c @@ -683,6 +683,29 @@ virLogVersionString(char **msg) void virLogMessage(const char *category, int priority, const char *funcname, long long linenr, unsigned int flags, const char *fmt, ...) { + va_list ap; + va_start(ap, fmt); + virLogVMessage(category, priority, funcname, linenr, flags, fmt, ap); + va_end(ap); +} + +/** + * virLogMessage: + * @category: where is that message coming from + * @priority: the priority level + * @funcname: the function emitting the (debug) message + * @linenr: line where the message was emitted + * @flags: extra flags, 1 if coming from the error handler + * @fmt: the string format + * @vargs: format args + * + * Call the libvirt logger with some information. Based on the configuration + * the message may be stored, sent to output or just discarded + */ +void virLogVMessage(const char *category, int priority, const char *funcname, + long long linenr, unsigned int flags, const char *fmt, + va_list vargs) +{ static bool logVersionStderr = true; char *str = NULL; char *msg = NULL; @@ -690,7 +713,6 @@ void virLogMessage(const char *category, int priority, const char *funcname, int fprio, i, ret; int saved_errno = errno; int emit = 1; - va_list ap; if (!virLogInitialized) virLogStartup(); @@ -715,12 +737,9 @@ void virLogMessage(const char *category, int priority, const char *funcname, /* * serialize the error message, add level and timestamp */ - va_start(ap, fmt); - if (virVasprintf(&str, fmt, ap) < 0) { - va_end(ap); + if (virVasprintf(&str, fmt, vargs) < 0) { goto cleanup; } - va_end(ap); ret = virLogFormatString(&msg, funcname, linenr, priority, str); VIR_FREE(str); diff --git a/src/util/logging.h b/src/util/logging.h index 2343de0..e1a8116 100644 --- a/src/util/logging.h +++ b/src/util/logging.h @@ -128,6 +128,11 @@ extern void virLogMessage(const char *category, int priority, const char *funcname, long long linenr, unsigned int flags, const char *fmt, ...) ATTRIBUTE_FMT_PRINTF(6, 7); +extern void virLogVMessage(const char *category, int priority, + const char *funcname, long long linenr, + unsigned int flags, + const char *fmt, + va_list vargs) ATTRIBUTE_FMT_PRINTF(6, 0); extern int virLogSetBufferSize(int size); extern void virLogEmergencyDumpAll(int signum); #endif -- 1.7.7.5

From: "Daniel P. Berrange" <berrange@redhat.com> * src/util/threads-pthread.c, src/util/threads.h: Add virThreadCancel --- src/util/threads-pthread.c | 5 +++++ src/util/threads.h | 1 + 2 files changed, 6 insertions(+), 0 deletions(-) diff --git a/src/util/threads-pthread.c b/src/util/threads-pthread.c index 5b8fd5b..34eceb6 100644 --- a/src/util/threads-pthread.c +++ b/src/util/threads-pthread.c @@ -233,6 +233,11 @@ void virThreadJoin(virThreadPtr thread) pthread_join(thread->thread, NULL); } +void virThreadCancel(virThreadPtr thread) +{ + pthread_cancel(thread->thread); +} + int virThreadLocalInit(virThreadLocalPtr l, virThreadLocalCleanup c) { diff --git a/src/util/threads.h b/src/util/threads.h index e52f3a9..329288e 100644 --- a/src/util/threads.h +++ b/src/util/threads.h @@ -53,6 +53,7 @@ int virThreadCreate(virThreadPtr thread, void virThreadSelf(virThreadPtr thread); bool virThreadIsSelf(virThreadPtr thread); void virThreadJoin(virThreadPtr thread); +void virThreadCancel(virThreadPtr thread); /* These next two functions are for debugging only, since they are not * guaranteed to give unique values for distinct threads on all -- 1.7.7.5

From: "Daniel P. Berrange" <berrange@redhat.com> Add APIs which allow storage of a real & effective identity on all server clients. Also add an API which allows creation of an initial identity based on the results of client authentication processes like TLS, x509, SASL, SO_PEERCRED --- src/rpc/virnetserverclient.c | 152 ++++++++++++++++++++++++++++++++++++++++++ src/rpc/virnetserverclient.h | 11 +++ 2 files changed, 163 insertions(+), 0 deletions(-) diff --git a/src/rpc/virnetserverclient.c b/src/rpc/virnetserverclient.c index 1e9d3db..9647ac3 100644 --- a/src/rpc/virnetserverclient.c +++ b/src/rpc/virnetserverclient.c @@ -75,6 +75,10 @@ struct _virNetServerClient int sockTimer; /* Timer to be fired upon cached data, * so we jump out from poll() immediately */ + + virIdentityPtr realIdentity; + virIdentityPtr effectiveIdentity; + /* Count of messages in the 'tx' queue, * and the server worker pool queue * ie RPC calls in progress. Does not count @@ -487,6 +491,149 @@ int virNetServerClientGetUNIXIdentity(virNetServerClientPtr client, } +virIdentityPtr virNetServerClientGetIdentity(virNetServerClientPtr client) +{ + char *processid = NULL; + char *username = NULL; + char *groupname = NULL; +#if HAVE_SASL + char *saslname = NULL; +#endif + char *x509dname = NULL; + char *seccontext = NULL; + virIdentityPtr ret = NULL; + virNetSASLSessionPtr sasl; + virNetTLSSessionPtr tls; + + if (virNetServerClientIsLocal(client)) { + gid_t gid; + uid_t uid; + pid_t pid; + if (virNetServerClientGetUNIXIdentity(client, &uid, &gid, &pid) < 0) + goto cleanup; + + if (!(username = virGetUserName(uid))) + goto cleanup; + if (!(groupname = virGetGroupName(gid))) + goto cleanup; + if (virAsprintf(&processid, "%d", (int)pid) < 0) + goto cleanup; + } + +#if HAVE_SASL + if ((sasl = virNetServerClientGetSASLSession(client))) { + const char *identity = virNetSASLSessionGetIdentity(sasl); + if (identity && + !(saslname = strdup(identity))) { + virReportOOMError(); + goto cleanup; + } + } +#endif + + if ((tls = virNetServerClientGetTLSSession(client))) { + const char *identity = virNetTLSSessionGetX509DName(tls); + if (identity && + !(x509dname = strdup(identity))) { + virReportOOMError(); + goto cleanup; + } + } + + if (virNetServerClientGetSecurityContext(client, &seccontext) < 0) + goto cleanup; + + if (!(ret = virIdentityNew())) + goto cleanup; + + if (username && + virIdentitySetAttr(ret, VIR_IDENTITY_ATTR_UNIX_USER_NAME, username) < 0) + goto error; + if (groupname && + virIdentitySetAttr(ret, VIR_IDENTITY_ATTR_UNIX_GROUP_NAME, groupname) < 0) + goto error; + if (processid && + virIdentitySetAttr(ret, VIR_IDENTITY_ATTR_UNIX_PROCESS_ID, processid) < 0) + goto error; +#if HAVE_SASL + if (saslname && + virIdentitySetAttr(ret, VIR_IDENTITY_ATTR_SASL_USER_NAME, saslname) < 0) + goto error; +#endif + if (x509dname && + virIdentitySetAttr(ret, VIR_IDENTITY_ATTR_X509_DISTINGUISHED_NAME, x509dname) < 0) + goto error; + if (seccontext && + virIdentitySetAttr(ret, VIR_IDENTITY_ATTR_SECURITY_CONTEXT, seccontext) < 0) + goto error; + +cleanup: + VIR_FREE(username); + VIR_FREE(groupname); + VIR_FREE(processid); + VIR_FREE(seccontext); +#if HAVE_SASL + VIR_FREE(saslname); +#endif + VIR_FREE(x509dname); + return ret; + +error: + virIdentityFree(ret); + ret = NULL; + goto cleanup; +} + + +virIdentityPtr virNetServerClientGetRealIdentity(virNetServerClientPtr client) +{ + virIdentityPtr ret; + virNetServerClientLock(client); + ret = client->realIdentity; + if (ret) + virIdentityRef(ret); + virNetServerClientUnlock(client); + return ret; +} + + +virIdentityPtr virNetServerClientGetEffectiveIdentity(virNetServerClientPtr client) +{ + virIdentityPtr ret; + virNetServerClientLock(client); + ret = client->effectiveIdentity; + if (ret) + virIdentityRef(ret); + virNetServerClientUnlock(client); + return ret; +} + + +void virNetServerClientSetRealIdentity(virNetServerClientPtr client, + virIdentityPtr ident) +{ + virNetServerClientLock(client); + if (client->realIdentity) + virIdentityFree(client->realIdentity); + if (ident) + virIdentityRef(ident); + client->realIdentity = ident; + virNetServerClientUnlock(client); +} + +void virNetServerClientSetEffectiveIdentity(virNetServerClientPtr client, + virIdentityPtr ident) +{ + virNetServerClientLock(client); + if (client->effectiveIdentity) + virIdentityFree(client->effectiveIdentity); + if (ident) + virIdentityRef(ident); + client->effectiveIdentity = ident; + virNetServerClientUnlock(client); +} + + int virNetServerClientGetSecurityContext(virNetServerClientPtr client, char **context) { @@ -625,6 +772,11 @@ void virNetServerClientFree(virNetServerClientPtr client) return; } + if (client->realIdentity) + virIdentityFree(client->realIdentity); + if (client->effectiveIdentity) + virIdentityFree(client->effectiveIdentity); + if (client->privateData && client->privateDataFreeFunc) client->privateDataFreeFunc(client->privateData); diff --git a/src/rpc/virnetserverclient.h b/src/rpc/virnetserverclient.h index a3b37a3..7435eee 100644 --- a/src/rpc/virnetserverclient.h +++ b/src/rpc/virnetserverclient.h @@ -77,6 +77,17 @@ int virNetServerClientGetUNIXIdentity(virNetServerClientPtr client, int virNetServerClientGetSecurityContext(virNetServerClientPtr client, char **context); +virIdentityPtr virNetServerClientGetIdentity(virNetServerClientPtr client); + +virIdentityPtr virNetServerClientGetRealIdentity(virNetServerClientPtr client); +virIdentityPtr virNetServerClientGetEffectiveIdentity(virNetServerClientPtr client); + +void virNetServerClientSetRealIdentity(virNetServerClientPtr client, + virIdentityPtr ident); +void virNetServerClientSetEffectiveIdentity(virNetServerClientPtr client, + virIdentityPtr iden); + + void virNetServerClientRef(virNetServerClientPtr client); typedef void (*virNetServerClientFreeFunc)(void *data); -- 1.7.7.5

From: "Daniel P. Berrange" <berrange@redhat.com> When dispatching an RPC API call, setup the access manager to hold the real & effective identities of the current server client whose RPC is being dispatched. The setting is thread-local, so only affects the API call in this thread --- src/rpc/virnetserverclient.c | 41 +++++++++++++++++++++++++++++++++++++++++ src/rpc/virnetserverclient.h | 3 +++ src/rpc/virnetserverprogram.c | 9 +++++++++ 3 files changed, 53 insertions(+), 0 deletions(-) diff --git a/src/rpc/virnetserverclient.c b/src/rpc/virnetserverclient.c index 9647ac3..043938e 100644 --- a/src/rpc/virnetserverclient.c +++ b/src/rpc/virnetserverclient.c @@ -34,6 +34,7 @@ #include "memory.h" #include "threads.h" #include "virkeepalive.h" +#include "access/viraccessmanager.h" #define VIR_FROM_THIS VIR_FROM_RPC #define virNetError(code, ...) \ @@ -634,6 +635,46 @@ void virNetServerClientSetEffectiveIdentity(virNetServerClientPtr client, } +int virNetServerClientActivateIdentity(virNetServerClientPtr client) +{ + virIdentityPtr real; + virIdentityPtr effective; + virIdentityPtr sys = NULL; + int ret = -1; + + real = virNetServerClientGetRealIdentity(client); + effective = virNetServerClientGetEffectiveIdentity(client); + if ((!real || !effective) && + !(sys = virAccessManagerGetSystemIdentity())) + goto cleanup; + + if (virAccessManagerSetRealIdentity(real ? real : sys) < 0) + goto cleanup; + if (virAccessManagerSetEffectiveIdentity(effective ? effective : sys) < 0) + goto cleanup; + + ret = 0; +cleanup: + if (real) + virIdentityFree(real); + if (effective) + virIdentityFree(effective); + if (sys) + virIdentityFree(sys); + return ret; +} + + +int virNetServerClientDeactivateIdentity(virNetServerClientPtr client ATTRIBUTE_UNUSED) +{ + if (virAccessManagerSetRealIdentity(NULL) < 0) + return -1; + if (virAccessManagerSetEffectiveIdentity(NULL) < 0) + return -1; + return 0; +} + + int virNetServerClientGetSecurityContext(virNetServerClientPtr client, char **context) { diff --git a/src/rpc/virnetserverclient.h b/src/rpc/virnetserverclient.h index 7435eee..f3c95cc 100644 --- a/src/rpc/virnetserverclient.h +++ b/src/rpc/virnetserverclient.h @@ -87,6 +87,9 @@ void virNetServerClientSetRealIdentity(virNetServerClientPtr client, void virNetServerClientSetEffectiveIdentity(virNetServerClientPtr client, virIdentityPtr iden); +int virNetServerClientActivateIdentity(virNetServerClientPtr client); +int virNetServerClientDeactivateIdentity(virNetServerClientPtr client); + void virNetServerClientRef(virNetServerClientPtr client); diff --git a/src/rpc/virnetserverprogram.c b/src/rpc/virnetserverprogram.c index d2ede6b..d4d56ff 100644 --- a/src/rpc/virnetserverprogram.c +++ b/src/rpc/virnetserverprogram.c @@ -403,6 +403,9 @@ virNetServerProgramDispatchCall(virNetServerProgramPtr prog, if (virNetMessageDecodePayload(msg, dispatcher->arg_filter, arg) < 0) goto error; + if (virNetServerClientActivateIdentity(client) < 0) + goto error; + /* * When the RPC handler is called: * @@ -415,6 +418,12 @@ virNetServerProgramDispatchCall(virNetServerProgramPtr prog, */ rv = (dispatcher->func)(server, client, msg, &rerr, arg, ret); + if (virNetServerClientDeactivateIdentity(client) < 0) { + virErrorPtr err = virGetLastError(); + VIR_WARN("Failed to deactive identity %s", err ? err->message : "null"); + virResetLastError(); + } + /* * Clear out the FDs we got from the client, we don't * want to send them back ! -- 1.7.7.5

From: "Daniel P. Berrange" <berrange@redhat.com> Introduce a new 'access_driver' configuration parameter which specifies the name of the access control manager driver to activate. By default the 'no op' driver is active --- src/qemu/qemu.conf | 5 +++++ src/qemu/qemu_conf.c | 10 ++++++++++ src/qemu/qemu_conf.h | 3 +++ src/qemu/qemu_driver.c | 24 ++++++++++++++++++++++++ 4 files changed, 42 insertions(+), 0 deletions(-) diff --git a/src/qemu/qemu.conf b/src/qemu/qemu.conf index 4ec5e6c..866905f 100644 --- a/src/qemu/qemu.conf +++ b/src/qemu/qemu.conf @@ -139,6 +139,11 @@ # security_driver = "selinux" +# There is no default access control driver +# +# access_driver = "polkit" + + # The user ID for QEMU processes run by the system instance. #user = "root" diff --git a/src/qemu/qemu_conf.c b/src/qemu/qemu_conf.c index bc0a646..cb10f7a 100644 --- a/src/qemu/qemu_conf.c +++ b/src/qemu/qemu_conf.c @@ -195,6 +195,16 @@ int qemudLoadDriverConfig(struct qemud_driver *driver, } } + p = virConfGetValue (conf, "access_driver"); + CHECK_TYPE ("access_driver", VIR_CONF_STRING); + if (p && p->str) { + if (!(driver->accessDriverName = strdup(p->str))) { + virReportOOMError(); + virConfFree(conf); + return -1; + } + } + p = virConfGetValue (conf, "vnc_sasl"); CHECK_TYPE ("vnc_sasl", VIR_CONF_LONG); if (p) driver->vncSASL = p->l; diff --git a/src/qemu/qemu_conf.h b/src/qemu/qemu_conf.h index 7d79823..19a2589 100644 --- a/src/qemu/qemu_conf.h +++ b/src/qemu/qemu_conf.h @@ -34,6 +34,7 @@ # include "domain_event.h" # include "threads.h" # include "security/security_manager.h" +# include "access/viraccessmanager.h" # include "cgroup.h" # include "pci.h" # include "hostusb.h" @@ -116,6 +117,8 @@ struct qemud_driver { char *securityDriverName; virSecurityManagerPtr securityManager; + char *accessDriverName; + virAccessManagerPtr accessManager; char *saveImageFormat; char *dumpImageFormat; diff --git a/src/qemu/qemu_driver.c b/src/qemu/qemu_driver.c index c920bfd..0507b43 100644 --- a/src/qemu/qemu_driver.c +++ b/src/qemu/qemu_driver.c @@ -239,6 +239,26 @@ error: } +static int +qemuAccessInit(struct qemud_driver *driver) +{ + virAccessManagerPtr mgr = virAccessManagerNew(driver->accessDriverName ? + driver->accessDriverName : + "none"); + if (!mgr) + goto error; + + driver->accessManager = mgr; + + return 0; + +error: + VIR_ERROR(_("Failed to initialize access drivers")); + virAccessManagerFree(mgr); + return -1; +} + + static virCapsPtr qemuCreateCapabilities(virCapsPtr oldcaps, struct qemud_driver *driver) @@ -578,6 +598,9 @@ qemudStartup(int privileged) { if (qemuSecurityInit(qemu_driver) < 0) goto error; + if (qemuAccessInit(qemu_driver) < 0) + goto error; + if ((qemu_driver->caps = qemuCreateCapabilities(NULL, qemu_driver)) == NULL) goto error; @@ -815,6 +838,7 @@ qemudShutdown(void) { VIR_FREE(qemu_driver->dumpImageFormat); virSecurityManagerFree(qemu_driver->securityManager); + virAccessManagerFree(qemu_driver->accessManager); ebtablesContextFree(qemu_driver->ebtables); -- 1.7.7.5

From: "Daniel P. Berrange" <berrange@redhat.com> Inserts the minimal access control checks to the QEMU driver to protect usage of virDomainObjPtr objects. --- src/qemu/qemu_driver.c | 631 ++++++++++++++++++++++++++++++++++++++++++-- src/qemu/qemu_migration.c | 5 + 2 files changed, 607 insertions(+), 29 deletions(-) diff --git a/src/qemu/qemu_driver.c b/src/qemu/qemu_driver.c index 0507b43..ce0b68e 100644 --- a/src/qemu/qemu_driver.c +++ b/src/qemu/qemu_driver.c @@ -1151,6 +1151,11 @@ static virDomainPtr qemudDomainLookupByID(virConnectPtr conn, goto cleanup; } + if (!virAccessManagerCheckDomain(driver->accessManager, + vm->def, + VIR_ACCESS_VECTOR_DOMAIN_GETATTR)) + goto cleanup; + dom = virGetDomain(conn, vm->def->name, vm->def->uuid); if (dom) dom->id = vm->def->id; @@ -1178,6 +1183,11 @@ static virDomainPtr qemudDomainLookupByUUID(virConnectPtr conn, goto cleanup; } + if (!virAccessManagerCheckDomain(driver->accessManager, + vm->def, + VIR_ACCESS_VECTOR_DOMAIN_GETATTR)) + goto cleanup; + dom = virGetDomain(conn, vm->def->name, vm->def->uuid); if (dom) dom->id = vm->def->id; @@ -1203,6 +1213,11 @@ static virDomainPtr qemudDomainLookupByName(virConnectPtr conn, goto cleanup; } + if (!virAccessManagerCheckDomain(driver->accessManager, + vm->def, + VIR_ACCESS_VECTOR_DOMAIN_GETATTR)) + goto cleanup; + dom = virGetDomain(conn, vm->def->name, vm->def->uuid); if (dom) dom->id = vm->def->id; @@ -1216,72 +1231,90 @@ cleanup: static int qemuDomainIsActive(virDomainPtr dom) { struct qemud_driver *driver = dom->conn->privateData; - virDomainObjPtr obj; + virDomainObjPtr vm; int ret = -1; qemuDriverLock(driver); - obj = virDomainFindByUUID(&driver->domains, dom->uuid); + vm = virDomainFindByUUID(&driver->domains, dom->uuid); qemuDriverUnlock(driver); - if (!obj) { + if (!vm) { char uuidstr[VIR_UUID_STRING_BUFLEN]; virUUIDFormat(dom->uuid, uuidstr); qemuReportError(VIR_ERR_NO_DOMAIN, _("no domain with matching uuid '%s'"), uuidstr); goto cleanup; } - ret = virDomainObjIsActive(obj); + + if (!virAccessManagerCheckDomain(driver->accessManager, + vm->def, + VIR_ACCESS_VECTOR_DOMAIN_READ)) + goto cleanup; + + ret = virDomainObjIsActive(vm); cleanup: - if (obj) - virDomainObjUnlock(obj); + if (vm) + virDomainObjUnlock(vm); return ret; } static int qemuDomainIsPersistent(virDomainPtr dom) { struct qemud_driver *driver = dom->conn->privateData; - virDomainObjPtr obj; + virDomainObjPtr vm; int ret = -1; qemuDriverLock(driver); - obj = virDomainFindByUUID(&driver->domains, dom->uuid); + vm = virDomainFindByUUID(&driver->domains, dom->uuid); qemuDriverUnlock(driver); - if (!obj) { + if (!vm) { char uuidstr[VIR_UUID_STRING_BUFLEN]; virUUIDFormat(dom->uuid, uuidstr); qemuReportError(VIR_ERR_NO_DOMAIN, _("no domain with matching uuid '%s'"), uuidstr); goto cleanup; } - ret = obj->persistent; + + if (!virAccessManagerCheckDomain(driver->accessManager, + vm->def, + VIR_ACCESS_VECTOR_DOMAIN_READ)) + goto cleanup; + + ret = vm->persistent; cleanup: - if (obj) - virDomainObjUnlock(obj); + if (vm) + virDomainObjUnlock(vm); return ret; } static int qemuDomainIsUpdated(virDomainPtr dom) { struct qemud_driver *driver = dom->conn->privateData; - virDomainObjPtr obj; + virDomainObjPtr vm; int ret = -1; qemuDriverLock(driver); - obj = virDomainFindByUUID(&driver->domains, dom->uuid); + vm = virDomainFindByUUID(&driver->domains, dom->uuid); qemuDriverUnlock(driver); - if (!obj) { + if (!vm) { char uuidstr[VIR_UUID_STRING_BUFLEN]; virUUIDFormat(dom->uuid, uuidstr); qemuReportError(VIR_ERR_NO_DOMAIN, _("no domain with matching uuid '%s'"), uuidstr); goto cleanup; } - ret = obj->updated; + + if (!virAccessManagerCheckDomain(driver->accessManager, + vm->def, + VIR_ACCESS_VECTOR_DOMAIN_READ)) + goto cleanup; + + ret = vm->updated; cleanup: - if (obj) - virDomainObjUnlock(obj); + if (vm) + virDomainObjUnlock(vm); return ret; } @@ -1303,10 +1336,12 @@ cleanup: static int qemudListDomains(virConnectPtr conn, int *ids, int nids) { struct qemud_driver *driver = conn->privateData; - int n; + int n = -1; qemuDriverLock(driver); - n = virDomainObjListGetActiveIDs(&driver->domains, ids, nids); + if (virAccessManagerCheckConnect(driver->accessManager, + VIR_ACCESS_VECTOR_CONNECT_SEARCH_DOMAINS)) + n = virDomainObjListGetActiveIDs(&driver->domains, ids, nids); qemuDriverUnlock(driver); return n; @@ -1314,10 +1349,12 @@ static int qemudListDomains(virConnectPtr conn, int *ids, int nids) { static int qemudNumDomains(virConnectPtr conn) { struct qemud_driver *driver = conn->privateData; - int n; + int n = -1; qemuDriverLock(driver); - n = virDomainObjListNumOfDomains(&driver->domains, 1); + if (virAccessManagerCheckConnect(driver->accessManager, + VIR_ACCESS_VECTOR_CONNECT_SEARCH_DOMAINS)) + n = virDomainObjListNumOfDomains(&driver->domains, 1); qemuDriverUnlock(driver); return n; @@ -1344,6 +1381,15 @@ static virDomainPtr qemudDomainCreate(virConnectPtr conn, const char *xml, if (virSecurityManagerVerify(driver->securityManager, def) < 0) goto cleanup; + if (!virAccessManagerCheckDomain(driver->accessManager, + def, + VIR_ACCESS_VECTOR_DOMAIN_WRITE)) + goto cleanup; + if (!virAccessManagerCheckDomain(driver->accessManager, + def, + VIR_ACCESS_VECTOR_DOMAIN_START)) + goto cleanup; + if (virDomainObjIsDuplicate(&driver->domains, def, 1) < 0) goto cleanup; @@ -1435,6 +1481,11 @@ static int qemudDomainSuspend(virDomainPtr dom) { goto cleanup; } + if (!virAccessManagerCheckDomain(driver->accessManager, + vm->def, + VIR_ACCESS_VECTOR_DOMAIN_SUSPEND)) + goto cleanup; + priv = vm->privateData; if (priv->job.asyncJob == QEMU_ASYNC_JOB_MIGRATION_OUT) { @@ -1497,6 +1548,11 @@ static int qemudDomainResume(virDomainPtr dom) { goto cleanup; } + if (!virAccessManagerCheckDomain(driver->accessManager, + vm->def, + VIR_ACCESS_VECTOR_DOMAIN_SUSPEND)) + goto cleanup; + if (qemuDomainObjBeginJobWithDriver(driver, vm, QEMU_JOB_MODIFY) < 0) goto cleanup; @@ -1554,6 +1610,11 @@ static int qemuDomainShutdown(virDomainPtr dom) { goto cleanup; } + if (!virAccessManagerCheckDomain(driver->accessManager, + vm->def, + VIR_ACCESS_VECTOR_DOMAIN_SHUTDOWN)) + goto cleanup; + if (qemuDomainObjBeginJob(driver, vm, QEMU_JOB_MODIFY) < 0) goto cleanup; @@ -1601,6 +1662,11 @@ static int qemuDomainReboot(virDomainPtr dom, unsigned int flags) { goto cleanup; } + if (!virAccessManagerCheckDomain(driver->accessManager, + vm->def, + VIR_ACCESS_VECTOR_DOMAIN_REBOOT)) + goto cleanup; + priv = vm->privateData; if (qemuCapsGet(priv->qemuCaps, QEMU_CAPS_MONITOR_JSON)) { @@ -1663,6 +1729,11 @@ qemuDomainReset(virDomainPtr dom, unsigned int flags) goto cleanup; } + if (!virAccessManagerCheckDomain(driver->accessManager, + vm->def, + VIR_ACCESS_VECTOR_DOMAIN_RESET)) + goto cleanup; + if (qemuDomainObjBeginJob(driver, vm, QEMU_JOB_MODIFY) < 0) goto cleanup; @@ -1725,6 +1796,11 @@ qemuDomainDestroyFlags(virDomainPtr dom, goto cleanup; } + if (!virAccessManagerCheckDomain(driver->accessManager, + vm->def, + VIR_ACCESS_VECTOR_DOMAIN_STOP)) + goto cleanup; + priv = vm->privateData; qemuDomainSetFakeReboot(driver, vm, false); @@ -1801,6 +1877,11 @@ static char *qemudDomainGetOSType(virDomainPtr dom) { goto cleanup; } + if (!virAccessManagerCheckDomain(driver->accessManager, + vm->def, + VIR_ACCESS_VECTOR_DOMAIN_READ)) + goto cleanup; + if (!(type = strdup(vm->def->os.type))) virReportOOMError(); @@ -1828,6 +1909,11 @@ static unsigned long qemudDomainGetMaxMemory(virDomainPtr dom) { goto cleanup; } + if (!virAccessManagerCheckDomain(driver->accessManager, + vm->def, + VIR_ACCESS_VECTOR_DOMAIN_READ)) + goto cleanup; + ret = vm->def->mem.max_balloon; cleanup: @@ -1859,6 +1945,17 @@ static int qemudDomainSetMemoryFlags(virDomainPtr dom, unsigned long newmem, goto cleanup; } + if (!virAccessManagerCheckDomain(driver->accessManager, + vm->def, + VIR_ACCESS_VECTOR_DOMAIN_WRITE)) + goto cleanup; + if ((flags & VIR_DOMAIN_AFFECT_CONFIG) && + !virAccessManagerCheckDomain(driver->accessManager, + vm->def, + VIR_ACCESS_VECTOR_DOMAIN_READ)) + goto cleanup; + + if (qemuDomainObjBeginJob(driver, vm, QEMU_JOB_MODIFY) < 0) goto cleanup; @@ -1961,6 +2058,11 @@ static int qemuDomainInjectNMI(virDomainPtr domain, unsigned int flags) goto cleanup; } + if (!virAccessManagerCheckDomain(driver->accessManager, + vm->def, + VIR_ACCESS_VECTOR_DOMAIN_INJECT_NMI)) + goto cleanup; + if (!virDomainObjIsActive(vm)) { qemuReportError(VIR_ERR_OPERATION_INVALID, "%s", _("domain is not running")); @@ -2038,6 +2140,11 @@ static int qemuDomainSendKey(virDomainPtr domain, goto cleanup; } + if (!virAccessManagerCheckDomain(driver->accessManager, + vm->def, + VIR_ACCESS_VECTOR_DOMAIN_SEND_KEY)) + goto cleanup; + priv = vm->privateData; if (qemuDomainObjBeginJobWithDriver(driver, vm, QEMU_JOB_MODIFY) < 0) @@ -2084,6 +2191,11 @@ static int qemudDomainGetInfo(virDomainPtr dom, goto cleanup; } + if (!virAccessManagerCheckDomain(driver->accessManager, + vm->def, + VIR_ACCESS_VECTOR_DOMAIN_READ)) + goto cleanup; + info->state = virDomainObjGetState(vm, NULL); if (!virDomainObjIsActive(vm)) { @@ -2171,6 +2283,11 @@ qemuDomainGetState(virDomainPtr dom, goto cleanup; } + if (!virAccessManagerCheckDomain(driver->accessManager, + vm->def, + VIR_ACCESS_VECTOR_DOMAIN_READ)) + goto cleanup; + *state = virDomainObjGetState(vm, reason); ret = 0; @@ -2204,6 +2321,11 @@ qemuDomainGetControlInfo(virDomainPtr dom, goto cleanup; } + if (!virAccessManagerCheckDomain(driver->accessManager, + vm->def, + VIR_ACCESS_VECTOR_DOMAIN_READ)) + goto cleanup; + if (!virDomainObjIsActive(vm)) { qemuReportError(VIR_ERR_OPERATION_INVALID, "%s", _("domain is not running")); @@ -2706,6 +2828,15 @@ qemuDomainSaveFlags(virDomainPtr dom, const char *path, const char *dxml, goto cleanup; } + if (!virAccessManagerCheckDomain(driver->accessManager, + vm->def, + VIR_ACCESS_VECTOR_DOMAIN_STOP)) + goto cleanup; + if (!virAccessManagerCheckDomain(driver->accessManager, + vm->def, + VIR_ACCESS_VECTOR_DOMAIN_HIBERNATE)) + goto cleanup; + if (!virDomainObjIsActive(vm)) { qemuReportError(VIR_ERR_OPERATION_INVALID, "%s", _("domain is not running")); @@ -2765,6 +2896,15 @@ qemuDomainManagedSave(virDomainPtr dom, unsigned int flags) goto cleanup; } + if (!virAccessManagerCheckDomain(driver->accessManager, + vm->def, + VIR_ACCESS_VECTOR_DOMAIN_STOP)) + goto cleanup; + if (!virAccessManagerCheckDomain(driver->accessManager, + vm->def, + VIR_ACCESS_VECTOR_DOMAIN_HIBERNATE)) + goto cleanup; + if (!virDomainObjIsActive(vm)) { qemuReportError(VIR_ERR_OPERATION_INVALID, "%s", _("domain is not running")); @@ -2816,6 +2956,11 @@ qemuDomainHasManagedSaveImage(virDomainPtr dom, unsigned int flags) goto cleanup; } + if (!virAccessManagerCheckDomain(driver->accessManager, + vm->def, + VIR_ACCESS_VECTOR_DOMAIN_READ)) + goto cleanup; + name = qemuDomainManagedSavePath(driver, vm); if (name == NULL) goto cleanup; @@ -2850,6 +2995,11 @@ qemuDomainManagedSaveRemove(virDomainPtr dom, unsigned int flags) goto cleanup; } + if (!virAccessManagerCheckDomain(driver->accessManager, + vm->def, + VIR_ACCESS_VECTOR_DOMAIN_WRITE)) + goto cleanup; + name = qemuDomainManagedSavePath(driver, vm); if (name == NULL) goto cleanup; @@ -2974,6 +3124,21 @@ static int qemudDomainCoreDump(virDomainPtr dom, goto cleanup; } + if (!virAccessManagerCheckDomain(driver->accessManager, + vm->def, + VIR_ACCESS_VECTOR_DOMAIN_CORE_DUMP)) + goto cleanup; + if (!(flags & VIR_DUMP_LIVE) && + !virAccessManagerCheckDomain(driver->accessManager, + vm->def, + VIR_ACCESS_VECTOR_DOMAIN_SUSPEND)) + goto cleanup; + if ((flags & VIR_DUMP_CRASH) && + !virAccessManagerCheckDomain(driver->accessManager, + vm->def, + VIR_ACCESS_VECTOR_DOMAIN_STOP)) + goto cleanup; + if (qemuDomainObjBeginAsyncJobWithDriver(driver, vm, QEMU_ASYNC_JOB_DUMP) < 0) goto cleanup; @@ -3084,6 +3249,11 @@ qemuDomainScreenshot(virDomainPtr dom, goto cleanup; } + if (!virAccessManagerCheckDomain(driver->accessManager, + vm->def, + VIR_ACCESS_VECTOR_DOMAIN_SCREENSHOT)) + goto cleanup; + priv = vm->privateData; if (qemuDomainObjBeginJob(driver, vm, QEMU_JOB_QUERY) < 0) @@ -3310,6 +3480,16 @@ qemuDomainSetVcpusFlags(virDomainPtr dom, unsigned int nvcpus, goto cleanup; } + if (!virAccessManagerCheckDomain(driver->accessManager, + vm->def, + VIR_ACCESS_VECTOR_DOMAIN_WRITE)) + goto cleanup; + if ((flags & VIR_DOMAIN_AFFECT_CONFIG) && + !virAccessManagerCheckDomain(driver->accessManager, + vm->def, + VIR_ACCESS_VECTOR_DOMAIN_SAVE)) + goto cleanup; + if (qemuDomainObjBeginJob(driver, vm, QEMU_JOB_MODIFY) < 0) goto cleanup; @@ -3428,6 +3608,16 @@ qemudDomainPinVcpuFlags(virDomainPtr dom, goto cleanup; } + if (!virAccessManagerCheckDomain(driver->accessManager, + vm->def, + VIR_ACCESS_VECTOR_DOMAIN_READ)) + goto cleanup; + if ((flags & VIR_DOMAIN_AFFECT_CONFIG) && + !virAccessManagerCheckDomain(driver->accessManager, + vm->def, + VIR_ACCESS_VECTOR_DOMAIN_SAVE)) + goto cleanup; + if (virDomainLiveConfigHelperMethod(driver->caps, vm, &flags, &persistentDef) < 0) goto cleanup; @@ -3561,6 +3751,11 @@ qemudDomainGetVcpuPinInfo(virDomainPtr dom, goto cleanup; } + if (!virAccessManagerCheckDomain(driver->accessManager, + vm->def, + VIR_ACCESS_VECTOR_DOMAIN_READ)) + goto cleanup; + if (virDomainLiveConfigHelperMethod(driver->caps, vm, &flags, &targetDef) < 0) goto cleanup; @@ -3640,6 +3835,11 @@ qemudDomainGetVcpus(virDomainPtr dom, goto cleanup; } + if (!virAccessManagerCheckDomain(driver->accessManager, + vm->def, + VIR_ACCESS_VECTOR_DOMAIN_READ)) + goto cleanup; + if (!virDomainObjIsActive(vm)) { qemuReportError(VIR_ERR_OPERATION_INVALID, "%s", @@ -3730,6 +3930,11 @@ qemudDomainGetVcpusFlags(virDomainPtr dom, unsigned int flags) goto cleanup; } + if (!virAccessManagerCheckDomain(driver->accessManager, + vm->def, + VIR_ACCESS_VECTOR_DOMAIN_READ)) + goto cleanup; + if (virDomainLiveConfigHelperMethod(driver->caps, vm, &flags, &def) < 0) goto cleanup; @@ -3771,6 +3976,11 @@ static int qemudDomainGetSecurityLabel(virDomainPtr dom, virSecurityLabelPtr sec goto cleanup; } + if (!virAccessManagerCheckDomain(driver->accessManager, + vm->def, + VIR_ACCESS_VECTOR_DOMAIN_READ)) + goto cleanup; + if (!virDomainVirtTypeToString(vm->def->virtType)) { qemuReportError(VIR_ERR_INTERNAL_ERROR, _("unknown virt type in domain definition '%d'"), @@ -4140,6 +4350,11 @@ qemuDomainRestoreFlags(virConnectPtr conn, if (fd < 0) goto cleanup; + if (!virAccessManagerCheckDomain(driver->accessManager, + def, + VIR_ACCESS_VECTOR_DOMAIN_START)) + goto cleanup; + if (virDomainObjIsDuplicate(&driver->domains, def, 1) < 0) goto cleanup; @@ -4204,6 +4419,11 @@ qemuDomainSaveImageGetXMLDesc(virConnectPtr conn, const char *path, if (fd < 0) goto cleanup; + if (!virAccessManagerCheckDomain(driver->accessManager, + def, + VIR_ACCESS_VECTOR_DOMAIN_READ)) + goto cleanup; + ret = qemuDomainDefFormatXML(driver, def, flags); cleanup: @@ -4246,6 +4466,15 @@ qemuDomainSaveImageDefineXML(virConnectPtr conn, const char *path, goto cleanup; } + if (!virAccessManagerCheckDomain(driver->accessManager, + def, + VIR_ACCESS_VECTOR_DOMAIN_WRITE)) + goto cleanup; + if (!virAccessManagerCheckDomain(driver->accessManager, + def, + VIR_ACCESS_VECTOR_DOMAIN_SAVE)) + goto cleanup; + xml = qemuDomainDefFormatXML(driver, def, (VIR_DOMAIN_XML_INACTIVE | VIR_DOMAIN_XML_SECURE)); if (!xml) @@ -4360,6 +4589,16 @@ static char *qemuDomainGetXMLDesc(virDomainPtr dom, goto cleanup; } + if (!virAccessManagerCheckDomain(driver->accessManager, + vm->def, + VIR_ACCESS_VECTOR_DOMAIN_READ)) + goto cleanup; + if ((flags & VIR_DOMAIN_XML_SECURE) && + !virAccessManagerCheckDomain(driver->accessManager, + vm->def, + VIR_ACCESS_VECTOR_DOMAIN_READ_SECURE)) + goto cleanup; + /* Refresh current memory based on balloon info if supported */ if ((vm->def->memballoon != NULL) && (vm->def->memballoon->model != VIR_DOMAIN_MEMBALLOON_MODEL_NONE) && @@ -4576,20 +4815,24 @@ cleanup: static int qemudListDefinedDomains(virConnectPtr conn, char **const names, int nnames) { struct qemud_driver *driver = conn->privateData; - int n; + int n = -1; qemuDriverLock(driver); - n = virDomainObjListGetInactiveNames(&driver->domains, names, nnames); + if (virAccessManagerCheckConnect(driver->accessManager, + VIR_ACCESS_VECTOR_CONNECT_SEARCH_DOMAINS)) + n = virDomainObjListGetInactiveNames(&driver->domains, names, nnames); qemuDriverUnlock(driver); return n; } static int qemudNumDefinedDomains(virConnectPtr conn) { struct qemud_driver *driver = conn->privateData; - int n; + int n = -1; qemuDriverLock(driver); - n = virDomainObjListNumOfDomains(&driver->domains, 0); + if (virAccessManagerCheckConnect(driver->accessManager, + VIR_ACCESS_VECTOR_CONNECT_SEARCH_DOMAINS)) + n = virDomainObjListNumOfDomains(&driver->domains, 0); qemuDriverUnlock(driver); return n; @@ -4687,6 +4930,11 @@ qemuDomainStartWithFlags(virDomainPtr dom, unsigned int flags) goto cleanup; } + if (!virAccessManagerCheckDomain(driver->accessManager, + vm->def, + VIR_ACCESS_VECTOR_DOMAIN_START)) + goto cleanup; + if (qemuDomainObjBeginJobWithDriver(driver, vm, QEMU_JOB_MODIFY) < 0) goto cleanup; @@ -4835,6 +5083,15 @@ static virDomainPtr qemudDomainDefine(virConnectPtr conn, const char *xml) { if (virSecurityManagerVerify(driver->securityManager, def) < 0) goto cleanup; + if (!virAccessManagerCheckDomain(driver->accessManager, + vm->def, + VIR_ACCESS_VECTOR_DOMAIN_WRITE)) + goto cleanup; + if (!virAccessManagerCheckDomain(driver->accessManager, + vm->def, + VIR_ACCESS_VECTOR_DOMAIN_SAVE)) + goto cleanup; + if ((dupVM = virDomainObjIsDuplicate(&driver->domains, def, 0)) < 0) goto cleanup; @@ -4905,6 +5162,11 @@ qemuDomainUndefineFlags(virDomainPtr dom, goto cleanup; } + if (!virAccessManagerCheckDomain(driver->accessManager, + vm->def, + VIR_ACCESS_VECTOR_DOMAIN_DELETE)) + goto cleanup; + if (!vm->persistent) { qemuReportError(VIR_ERR_OPERATION_INVALID, "%s", _("cannot undefine transient domain")); @@ -5515,6 +5777,17 @@ qemuDomainModifyDeviceFlags(virDomainPtr dom, const char *xml, goto cleanup; } + if (!virAccessManagerCheckDomain(driver->accessManager, + vm->def, + VIR_ACCESS_VECTOR_DOMAIN_WRITE)) + goto cleanup; + + if ((flags & VIR_DOMAIN_AFFECT_CONFIG) && + !virAccessManagerCheckDomain(driver->accessManager, + vm->def, + VIR_ACCESS_VECTOR_DOMAIN_SAVE)) + goto cleanup; + if (qemuDomainObjBeginJobWithDriver(driver, vm, QEMU_JOB_MODIFY) < 0) goto cleanup; @@ -5682,6 +5955,11 @@ static int qemudDomainGetAutostart(virDomainPtr dom, goto cleanup; } + if (!virAccessManagerCheckDomain(driver->accessManager, + vm->def, + VIR_ACCESS_VECTOR_DOMAIN_READ)) + goto cleanup; + *autostart = vm->autostart; ret = 0; @@ -5709,6 +5987,15 @@ static int qemudDomainSetAutostart(virDomainPtr dom, goto cleanup; } + if (!virAccessManagerCheckDomain(driver->accessManager, + vm->def, + VIR_ACCESS_VECTOR_DOMAIN_WRITE)) + goto cleanup; + if (!virAccessManagerCheckDomain(driver->accessManager, + vm->def, + VIR_ACCESS_VECTOR_DOMAIN_SAVE)) + goto cleanup; + if (!vm->persistent) { qemuReportError(VIR_ERR_OPERATION_INVALID, "%s", _("cannot set autostart for transient domain")); @@ -5799,8 +6086,22 @@ static char *qemuGetSchedulerType(virDomainPtr dom, struct qemud_driver *driver = dom->conn->privateData; char *ret = NULL; int rc; + virDomainObjPtr vm = NULL; qemuDriverLock(driver); + vm = virDomainFindByUUID(&driver->domains, dom->uuid); + + if (vm == NULL) { + qemuReportError(VIR_ERR_INTERNAL_ERROR, + _("No such domain %s"), dom->uuid); + goto cleanup; + } + + if (!virAccessManagerCheckDomain(driver->accessManager, + vm->def, + VIR_ACCESS_VECTOR_DOMAIN_READ)) + goto cleanup; + if (!qemuCgroupControllerActive(driver, VIR_CGROUP_CONTROLLER_CPU)) { qemuReportError(VIR_ERR_OPERATION_INVALID, "%s", _("cgroup CPU controller is not mounted")); @@ -5822,6 +6123,8 @@ static char *qemuGetSchedulerType(virDomainPtr dom, virReportOOMError(); cleanup: + if (vm) + virDomainObjUnlock(vm); qemuDriverUnlock(driver); return ret; } @@ -5944,9 +6247,9 @@ qemuDomainMergeDeviceWeights(virBlkioDeviceWeightPtr *def, size_t *def_size, } static int qemuDomainSetBlkioParameters(virDomainPtr dom, - virTypedParameterPtr params, - int nparams, - unsigned int flags) + virTypedParameterPtr params, + int nparams, + unsigned int flags) { struct qemud_driver *driver = dom->conn->privateData; int i; @@ -5967,6 +6270,16 @@ static int qemuDomainSetBlkioParameters(virDomainPtr dom, goto cleanup; } + if (!virAccessManagerCheckDomain(driver->accessManager, + vm->def, + VIR_ACCESS_VECTOR_DOMAIN_WRITE)) + goto cleanup; + if ((flags & VIR_DOMAIN_AFFECT_CONFIG) && + !virAccessManagerCheckDomain(driver->accessManager, + vm->def, + VIR_ACCESS_VECTOR_DOMAIN_SAVE)) + goto cleanup; + if (virDomainLiveConfigHelperMethod(driver->caps, vm, &flags, &persistentDef) < 0) goto cleanup; @@ -6156,6 +6469,11 @@ static int qemuDomainGetBlkioParameters(virDomainPtr dom, goto cleanup; } + if (!virAccessManagerCheckDomain(driver->accessManager, + vm->def, + VIR_ACCESS_VECTOR_DOMAIN_READ)) + goto cleanup; + if ((*nparams) == 0) { /* Current number of blkio parameters supported by cgroups */ *nparams = QEMU_NB_BLKIO_PARAM; @@ -6349,6 +6667,16 @@ static int qemuDomainSetMemoryParameters(virDomainPtr dom, goto cleanup; } + if (!virAccessManagerCheckDomain(driver->accessManager, + vm->def, + VIR_ACCESS_VECTOR_DOMAIN_WRITE)) + goto cleanup; + if ((flags & VIR_DOMAIN_AFFECT_CONFIG) && + !virAccessManagerCheckDomain(driver->accessManager, + vm->def, + VIR_ACCESS_VECTOR_DOMAIN_SAVE)) + goto cleanup; + if (virDomainLiveConfigHelperMethod(driver->caps, vm, &flags, &persistentDef) < 0) goto cleanup; @@ -6489,6 +6817,11 @@ static int qemuDomainGetMemoryParameters(virDomainPtr dom, goto cleanup; } + if (!virAccessManagerCheckDomain(driver->accessManager, + vm->def, + VIR_ACCESS_VECTOR_DOMAIN_READ)) + goto cleanup; + if (virDomainLiveConfigHelperMethod(driver->caps, vm, &flags, &persistentDef) < 0) goto cleanup; @@ -6664,6 +6997,16 @@ qemuDomainSetNumaParameters(virDomainPtr dom, goto cleanup; } + if (!virAccessManagerCheckDomain(driver->accessManager, + vm->def, + VIR_ACCESS_VECTOR_DOMAIN_WRITE)) + goto cleanup; + if ((flags & VIR_DOMAIN_AFFECT_CONFIG) && + !virAccessManagerCheckDomain(driver->accessManager, + vm->def, + VIR_ACCESS_VECTOR_DOMAIN_SAVE)) + goto cleanup; + if (virDomainLiveConfigHelperMethod(driver->caps, vm, &flags, &persistentDef) < 0) goto cleanup; @@ -6846,6 +7189,11 @@ qemuDomainGetNumaParameters(virDomainPtr dom, goto cleanup; } + if (!virAccessManagerCheckDomain(driver->accessManager, + vm->def, + VIR_ACCESS_VECTOR_DOMAIN_READ)) + goto cleanup; + if (virDomainLiveConfigHelperMethod(driver->caps, vm, &flags, &persistentDef) < 0) goto cleanup; @@ -7070,6 +7418,16 @@ static int qemuSetSchedulerParametersFlags(virDomainPtr dom, goto cleanup; } + if (!virAccessManagerCheckDomain(driver->accessManager, + vm->def, + VIR_ACCESS_VECTOR_DOMAIN_WRITE)) + goto cleanup; + if ((flags & VIR_DOMAIN_AFFECT_CONFIG) && + !virAccessManagerCheckDomain(driver->accessManager, + vm->def, + VIR_ACCESS_VECTOR_DOMAIN_SAVE)) + goto cleanup; + if (virDomainLiveConfigHelperMethod(driver->caps, vm, &flags, &vmdef) < 0) goto cleanup; @@ -7307,6 +7665,11 @@ qemuGetSchedulerParametersFlags(virDomainPtr dom, goto cleanup; } + if (!virAccessManagerCheckDomain(driver->accessManager, + vm->def, + VIR_ACCESS_VECTOR_DOMAIN_READ)) + goto cleanup; + if (virDomainLiveConfigHelperMethod(driver->caps, vm, &flags, &persistentDef) < 0) goto cleanup; @@ -7452,6 +7815,11 @@ qemuDomainBlockResize (virDomainPtr dom, goto cleanup; } + if (!virAccessManagerCheckDomain(driver->accessManager, + vm->def, + VIR_ACCESS_VECTOR_DOMAIN_WRITE)) + goto cleanup; + priv = vm->privateData; if (qemuDomainObjBeginJob(driver, vm, QEMU_JOB_MODIFY) < 0) @@ -7522,6 +7890,11 @@ qemuDomainBlockStats(virDomainPtr dom, goto cleanup; } + if (!virAccessManagerCheckDomain(driver->accessManager, + vm->def, + VIR_ACCESS_VECTOR_DOMAIN_READ)) + goto cleanup; + if (!virDomainObjIsActive(vm)) { qemuReportError(VIR_ERR_OPERATION_INVALID, "%s", _("domain is not running")); @@ -7607,6 +7980,11 @@ qemuDomainBlockStatsFlags(virDomainPtr dom, goto cleanup; } + if (!virAccessManagerCheckDomain(driver->accessManager, + vm->def, + VIR_ACCESS_VECTOR_DOMAIN_READ)) + goto cleanup; + if (!virDomainObjIsActive(vm)) { qemuReportError(VIR_ERR_OPERATION_INVALID, "%s", _("domain is not running")); @@ -7820,6 +8198,11 @@ qemudDomainInterfaceStats (virDomainPtr dom, goto cleanup; } + if (!virAccessManagerCheckDomain(driver->accessManager, + vm->def, + VIR_ACCESS_VECTOR_DOMAIN_READ)) + goto cleanup; + if (!virDomainObjIsActive(vm)) { qemuReportError(VIR_ERR_OPERATION_INVALID, "%s", _("domain is not running")); @@ -7886,6 +8269,16 @@ qemuDomainSetInterfaceParameters(virDomainPtr dom, goto cleanup; } + if (!virAccessManagerCheckDomain(driver->accessManager, + vm->def, + VIR_ACCESS_VECTOR_DOMAIN_WRITE)) + goto cleanup; + if ((flags & VIR_DOMAIN_AFFECT_CONFIG) && + !virAccessManagerCheckDomain(driver->accessManager, + vm->def, + VIR_ACCESS_VECTOR_DOMAIN_SAVE)) + goto cleanup; + if (virDomainLiveConfigHelperMethod(driver->caps, vm, &flags, &persistentDef) < 0) goto cleanup; @@ -8108,6 +8501,11 @@ qemuDomainGetInterfaceParameters(virDomainPtr dom, goto cleanup; } + if (!virAccessManagerCheckDomain(driver->accessManager, + vm->def, + VIR_ACCESS_VECTOR_DOMAIN_READ)) + goto cleanup; + if (virDomainLiveConfigHelperMethod(driver->caps, vm, &flags, &persistentDef) < 0) goto cleanup; @@ -8237,6 +8635,11 @@ qemudDomainMemoryStats (virDomainPtr dom, goto cleanup; } + if (!virAccessManagerCheckDomain(driver->accessManager, + vm->def, + VIR_ACCESS_VECTOR_DOMAIN_READ)) + goto cleanup; + if (qemuDomainObjBeginJob(driver, vm, QEMU_JOB_QUERY) < 0) goto cleanup; @@ -8285,6 +8688,11 @@ qemudDomainBlockPeek (virDomainPtr dom, goto cleanup; } + if (!virAccessManagerCheckDomain(driver->accessManager, + vm->def, + VIR_ACCESS_VECTOR_DOMAIN_READ_BLOCK)) + goto cleanup; + if (!path || path[0] == '\0') { qemuReportError(VIR_ERR_INVALID_ARG, "%s", _("NULL or empty path")); @@ -8353,6 +8761,11 @@ qemudDomainMemoryPeek (virDomainPtr dom, goto cleanup; } + if (!virAccessManagerCheckDomain(driver->accessManager, + vm->def, + VIR_ACCESS_VECTOR_DOMAIN_READ_MEM)) + goto cleanup; + if (flags != VIR_MEMORY_VIRTUAL && flags != VIR_MEMORY_PHYSICAL) { qemuReportError(VIR_ERR_INVALID_ARG, "%s", _("flags parameter must be VIR_MEMORY_VIRTUAL or VIR_MEMORY_PHYSICAL")); @@ -8450,6 +8863,11 @@ static int qemuDomainGetBlockInfo(virDomainPtr dom, goto cleanup; } + if (!virAccessManagerCheckDomain(driver->accessManager, + vm->def, + VIR_ACCESS_VECTOR_DOMAIN_READ)) + goto cleanup; + if (!path || path[0] == '\0') { qemuReportError(VIR_ERR_INVALID_ARG, "%s", _("NULL or empty path")); @@ -8813,6 +9231,11 @@ qemudDomainMigratePerform (virDomainPtr dom, goto cleanup; } + if (!virAccessManagerCheckDomain(driver->accessManager, + vm->def, + VIR_ACCESS_VECTOR_DOMAIN_MIGRATE)) + goto cleanup; + if (flags & VIR_MIGRATE_PEER2PEER) { dconnuri = uri; uri = NULL; @@ -8859,6 +9282,11 @@ qemudDomainMigrateFinish2 (virConnectPtr dconn, goto cleanup; } + if (!virAccessManagerCheckDomain(driver->accessManager, + vm->def, + VIR_ACCESS_VECTOR_DOMAIN_MIGRATE)) + goto cleanup; + /* Do not use cookies in v2 protocol, since the cookie * length was not sufficiently large, causing failures * migrating between old & new libvirtd @@ -8902,6 +9330,11 @@ qemuDomainMigrateBegin3(virDomainPtr domain, goto cleanup; } + if (!virAccessManagerCheckDomain(driver->accessManager, + vm->def, + VIR_ACCESS_VECTOR_DOMAIN_MIGRATE)) + goto cleanup; + if ((flags & VIR_MIGRATE_CHANGE_PROTECTION)) { if (qemuMigrationJobStart(driver, vm, QEMU_ASYNC_JOB_MIGRATION_OUT) < 0) goto cleanup; @@ -9083,6 +9516,11 @@ qemuDomainMigratePerform3(virDomainPtr dom, goto cleanup; } + if (!virAccessManagerCheckDomain(driver->accessManager, + vm->def, + VIR_ACCESS_VECTOR_DOMAIN_MIGRATE)) + goto cleanup; + ret = qemuMigrationPerform(driver, dom->conn, vm, xmlin, dconnuri, uri, cookiein, cookieinlen, cookieout, cookieoutlen, @@ -9120,6 +9558,11 @@ qemuDomainMigrateFinish3(virConnectPtr dconn, goto cleanup; } + if (!virAccessManagerCheckDomain(driver->accessManager, + vm->def, + VIR_ACCESS_VECTOR_DOMAIN_MIGRATE)) + goto cleanup; + dom = qemuMigrationFinish(driver, dconn, vm, cookiein, cookieinlen, cookieout, cookieoutlen, @@ -9154,6 +9597,11 @@ qemuDomainMigrateConfirm3(virDomainPtr domain, goto cleanup; } + if (!virAccessManagerCheckDomain(driver->accessManager, + vm->def, + VIR_ACCESS_VECTOR_DOMAIN_MIGRATE)) + goto cleanup; + if (!qemuMigrationJobIsActive(vm, QEMU_ASYNC_JOB_MIGRATION_OUT)) goto cleanup; @@ -9394,6 +9842,11 @@ static int qemuDomainGetJobInfo(virDomainPtr dom, goto cleanup; } + if (!virAccessManagerCheckDomain(driver->accessManager, + vm->def, + VIR_ACCESS_VECTOR_DOMAIN_READ)) + goto cleanup; + priv = vm->privateData; if (virDomainObjIsActive(vm)) { @@ -9444,6 +9897,11 @@ static int qemuDomainAbortJob(virDomainPtr dom) { goto cleanup; } + if (!virAccessManagerCheckDomain(driver->accessManager, + vm->def, + VIR_ACCESS_VECTOR_DOMAIN_WRITE)) + goto cleanup; + if (qemuDomainObjBeginJob(driver, vm, QEMU_JOB_ABORT) < 0) goto cleanup; @@ -9506,6 +9964,11 @@ qemuDomainMigrateSetMaxDowntime(virDomainPtr dom, return -1; } + if (!virAccessManagerCheckDomain(driver->accessManager, + vm->def, + VIR_ACCESS_VECTOR_DOMAIN_MIGRATE)) + goto cleanup; + if (qemuDomainObjBeginJob(driver, vm, QEMU_JOB_MIGRATION_OP) < 0) goto cleanup; @@ -9562,6 +10025,11 @@ qemuDomainMigrateSetMaxSpeed(virDomainPtr dom, return -1; } + if (!virAccessManagerCheckDomain(driver->accessManager, + vm->def, + VIR_ACCESS_VECTOR_DOMAIN_MIGRATE)) + goto cleanup; + priv = vm->privateData; if (virDomainObjIsActive(vm)) { if (qemuDomainObjBeginJob(driver, vm, QEMU_JOB_MIGRATION_OP) < 0) @@ -9619,6 +10087,11 @@ qemuDomainMigrateGetMaxSpeed(virDomainPtr dom, goto cleanup; } + if (!virAccessManagerCheckDomain(driver->accessManager, + vm->def, + VIR_ACCESS_VECTOR_DOMAIN_MIGRATE)) + goto cleanup; + priv = vm->privateData; *bandwidth = priv->migMaxBandwidth; ret = 0; @@ -10098,6 +10571,11 @@ qemuDomainSnapshotCreateXML(virDomainPtr domain, goto cleanup; } + if (!virAccessManagerCheckDomain(driver->accessManager, + vm->def, + VIR_ACCESS_VECTOR_DOMAIN_SNAPSHOT)) + goto cleanup; + if (qemuProcessAutoDestroyActive(driver, vm)) { qemuReportError(VIR_ERR_OPERATION_INVALID, "%s", _("domain is marked for auto destroy")); @@ -10351,6 +10829,11 @@ static int qemuDomainSnapshotListNames(virDomainPtr domain, char **names, goto cleanup; } + if (!virAccessManagerCheckDomain(driver->accessManager, + vm->def, + VIR_ACCESS_VECTOR_DOMAIN_READ)) + goto cleanup; + n = virDomainSnapshotObjListGetNames(&vm->snapshots, names, nameslen, flags); @@ -10382,6 +10865,11 @@ static int qemuDomainSnapshotNum(virDomainPtr domain, goto cleanup; } + if (!virAccessManagerCheckDomain(driver->accessManager, + vm->def, + VIR_ACCESS_VECTOR_DOMAIN_READ)) + goto cleanup; + /* All qemu snapshots have libvirt metadata, so * VIR_DOMAIN_SNAPSHOT_LIST_METADATA makes no difference to our * answer. */ @@ -10420,6 +10908,11 @@ qemuDomainSnapshotListChildrenNames(virDomainSnapshotPtr snapshot, goto cleanup; } + if (!virAccessManagerCheckDomain(driver->accessManager, + vm->def, + VIR_ACCESS_VECTOR_DOMAIN_READ)) + goto cleanup; + snap = virDomainSnapshotFindByName(&vm->snapshots, snapshot->name); if (!snap) { qemuReportError(VIR_ERR_NO_DOMAIN_SNAPSHOT, @@ -10460,6 +10953,11 @@ qemuDomainSnapshotNumChildren(virDomainSnapshotPtr snapshot, goto cleanup; } + if (!virAccessManagerCheckDomain(driver->accessManager, + vm->def, + VIR_ACCESS_VECTOR_DOMAIN_READ)) + goto cleanup; + snap = virDomainSnapshotFindByName(&vm->snapshots, snapshot->name); if (!snap) { qemuReportError(VIR_ERR_NO_DOMAIN_SNAPSHOT, @@ -10502,6 +11000,11 @@ static virDomainSnapshotPtr qemuDomainSnapshotLookupByName(virDomainPtr domain, goto cleanup; } + if (!virAccessManagerCheckDomain(driver->accessManager, + vm->def, + VIR_ACCESS_VECTOR_DOMAIN_READ)) + goto cleanup; + snap = virDomainSnapshotFindByName(&vm->snapshots, name); if (!snap) { qemuReportError(VIR_ERR_NO_DOMAIN_SNAPSHOT, @@ -10537,6 +11040,11 @@ static int qemuDomainHasCurrentSnapshot(virDomainPtr domain, goto cleanup; } + if (!virAccessManagerCheckDomain(driver->accessManager, + vm->def, + VIR_ACCESS_VECTOR_DOMAIN_READ)) + goto cleanup; + ret = (vm->current_snapshot != NULL); cleanup: @@ -10567,6 +11075,11 @@ qemuDomainSnapshotGetParent(virDomainSnapshotPtr snapshot, goto cleanup; } + if (!virAccessManagerCheckDomain(driver->accessManager, + vm->def, + VIR_ACCESS_VECTOR_DOMAIN_READ)) + goto cleanup; + snap = virDomainSnapshotFindByName(&vm->snapshots, snapshot->name); if (!snap) { qemuReportError(VIR_ERR_NO_DOMAIN_SNAPSHOT, @@ -10610,6 +11123,11 @@ static virDomainSnapshotPtr qemuDomainSnapshotCurrent(virDomainPtr domain, goto cleanup; } + if (!virAccessManagerCheckDomain(driver->accessManager, + vm->def, + VIR_ACCESS_VECTOR_DOMAIN_READ)) + goto cleanup; + if (!vm->current_snapshot) { qemuReportError(VIR_ERR_NO_DOMAIN_SNAPSHOT, "%s", _("the domain does not have a current snapshot")); @@ -10645,6 +11163,11 @@ static char *qemuDomainSnapshotGetXMLDesc(virDomainSnapshotPtr snapshot, goto cleanup; } + if (!virAccessManagerCheckDomain(driver->accessManager, + vm->def, + VIR_ACCESS_VECTOR_DOMAIN_READ)) + goto cleanup; + snap = virDomainSnapshotFindByName(&vm->snapshots, snapshot->name); if (!snap) { qemuReportError(VIR_ERR_NO_DOMAIN_SNAPSHOT, @@ -10715,6 +11238,11 @@ static int qemuDomainRevertToSnapshot(virDomainSnapshotPtr snapshot, goto cleanup; } + if (!virAccessManagerCheckDomain(driver->accessManager, + vm->def, + VIR_ACCESS_VECTOR_DOMAIN_SNAPSHOT)) + goto cleanup; + snap = virDomainSnapshotFindByName(&vm->snapshots, snapshot->name); if (!snap) { qemuReportError(VIR_ERR_NO_DOMAIN_SNAPSHOT, @@ -11076,6 +11604,11 @@ static int qemuDomainSnapshotDelete(virDomainSnapshotPtr snapshot, goto cleanup; } + if (!virAccessManagerCheckDomain(driver->accessManager, + vm->def, + VIR_ACCESS_VECTOR_DOMAIN_SNAPSHOT)) + goto cleanup; + snap = virDomainSnapshotFindByName(&vm->snapshots, snapshot->name); if (!snap) { qemuReportError(VIR_ERR_NO_DOMAIN_SNAPSHOT, @@ -11193,6 +11726,11 @@ static int qemuDomainMonitorCommand(virDomainPtr domain, const char *cmd, goto cleanup; } + if (!virAccessManagerCheckDomain(driver->accessManager, + vm->def, + VIR_ACCESS_VECTOR_DOMAIN_WRITE)) + goto cleanup; + if (!virDomainObjIsActive(vm)) { qemuReportError(VIR_ERR_OPERATION_INVALID, "%s", _("domain is not running")); @@ -11251,6 +11789,11 @@ static virDomainPtr qemuDomainAttach(virConnectPtr conn, &pidfile, &monConfig, &monJSON))) goto cleanup; + if (!virAccessManagerCheckDomain(driver->accessManager, + vm->def, + VIR_ACCESS_VECTOR_DOMAIN_START)) + goto cleanup; + if (!monConfig) { qemuReportError(VIR_ERR_CONFIG_UNSUPPORTED, _("No monitor connection for pid %u"), @@ -11341,6 +11884,11 @@ qemuDomainOpenConsole(virDomainPtr dom, goto cleanup; } + if (!virAccessManagerCheckDomain(driver->accessManager, + vm->def, + VIR_ACCESS_VECTOR_DOMAIN_OPEN_CONSOLE)) + goto cleanup; + if (!virDomainObjIsActive(vm)) { qemuReportError(VIR_ERR_OPERATION_INVALID, "%s", _("domain is not running")); @@ -11447,6 +11995,11 @@ qemuDomainBlockJobImpl(virDomainPtr dom, const char *path, goto cleanup; } + if (!virAccessManagerCheckDomain(driver->accessManager, + vm->def, + VIR_ACCESS_VECTOR_DOMAIN_WRITE)) + goto cleanup; + if (!virDomainObjIsActive(vm)) { qemuReportError(VIR_ERR_OPERATION_INVALID, "%s", _("domain is not running")); @@ -11547,6 +12100,11 @@ qemuDomainOpenGraphics(virDomainPtr dom, goto cleanup; } + if (!virAccessManagerCheckDomain(driver->accessManager, + vm->def, + VIR_ACCESS_VECTOR_DOMAIN_OPEN_GRAPHICS)) + goto cleanup; + if (!virDomainObjIsActive(vm)) { qemuReportError(VIR_ERR_OPERATION_INVALID, "%s", _("domain is not running")); @@ -11624,6 +12182,16 @@ qemuDomainSetBlockIoTune(virDomainPtr dom, goto cleanup; } + if (!virAccessManagerCheckDomain(driver->accessManager, + vm->def, + VIR_ACCESS_VECTOR_DOMAIN_WRITE)) + goto cleanup; + if ((flags & VIR_DOMAIN_AFFECT_CONFIG) && + !virAccessManagerCheckDomain(driver->accessManager, + vm->def, + VIR_ACCESS_VECTOR_DOMAIN_SAVE)) + goto cleanup; + device = qemuDiskPathToAlias(vm, disk); if (!device) { goto cleanup; @@ -11753,6 +12321,11 @@ qemuDomainGetBlockIoTune(virDomainPtr dom, goto cleanup; } + if (!virAccessManagerCheckDomain(driver->accessManager, + vm->def, + VIR_ACCESS_VECTOR_DOMAIN_READ)) + goto cleanup; + if ((*nparams) == 0) { /* Current number of parameters supported by QEMU Block I/O Throttling */ *nparams = QEMU_NB_BLOCK_IO_TUNE_PARAM; diff --git a/src/qemu/qemu_migration.c b/src/qemu/qemu_migration.c index 8453a47..554e0e6 100644 --- a/src/qemu/qemu_migration.c +++ b/src/qemu/qemu_migration.c @@ -1105,6 +1105,11 @@ qemuMigrationPrepareAny(struct qemud_driver *driver, VIR_DOMAIN_XML_INACTIVE))) goto cleanup; + if (!virAccessManagerCheckDomain(driver->accessManager, + vm->def, + VIR_ACCESS_VECTOR_DOMAIN_MIGRATE)) + goto cleanup; + if (!qemuMigrationIsAllowed(driver, NULL, def)) goto cleanup; -- 1.7.7.5

From: "Daniel P. Berrange" <berrange@redhat.com> --- po/POTFILES.in | 1 + src/Makefile.am | 12 ++- src/access/org.libvirt.domain.policy | 37 ++++++++ src/access/viraccessdriverpolkit.c | 163 ++++++++++++++++++++++++++++++++++ src/access/viraccessdriverpolkit.h | 28 ++++++ src/access/viraccessmanager.c | 2 + 6 files changed, 241 insertions(+), 2 deletions(-) create mode 100644 src/access/org.libvirt.domain.policy create mode 100644 src/access/viraccessdriverpolkit.c create mode 100644 src/access/viraccessdriverpolkit.h diff --git a/po/POTFILES.in b/po/POTFILES.in index 0a5443f..0548479 100644 --- a/po/POTFILES.in +++ b/po/POTFILES.in @@ -4,6 +4,7 @@ daemon/remote.c daemon/remote_dispatch.h daemon/stream.c gnulib/lib/gai_strerror.c +src/access/viraccessdriverpolkit.c src/access/viraccessmanager.c src/conf/cpu_conf.c src/conf/domain_conf.c diff --git a/src/Makefile.am b/src/Makefile.am index 0baa17d..ebc8301 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -523,7 +523,12 @@ ACCESS_DRIVER_SOURCES = \ access/viraccessmanager.h access/viraccessmanager.c \ access/viraccessdriver.h \ access/viraccessdrivernop.h access/viraccessdrivernop.c \ - access/viraccessdriverstack.h access/viraccessdriverstack.c + access/viraccessdriverstack.h access/viraccessdriverstack.c \ + access/viraccessdriverpolkit.h access/viraccessdriverpolkit.c + +ACCESS_DRIVER_POLKIT_POLICY = \ + access/org.libvirt.domain.policy + NODE_DEVICE_DRIVER_SOURCES = \ node_device/node_device_driver.c \ @@ -1116,6 +1121,8 @@ libvirt_driver_access_la_CFLAGS = \ libvirt_driver_access_la_LDFLAGS = $(AM_LDFLAGS) libvirt_driver_access_la_LIBADD = +polkitactiondir = $(datadir)/polkit-1/actions +polkitaction_DATA = $(ACCESS_DRIVER_POLKIT_POLICY) # Add all conditional sources just in case... EXTRA_DIST += \ @@ -1152,7 +1159,8 @@ EXTRA_DIST += \ $(SECRET_DRIVER_SOURCES) \ $(VBOX_DRIVER_EXTRA_DIST) \ $(VMWARE_DRIVER_SOURCES) \ - $(XENXS_SOURCES) + $(XENXS_SOURCES) \ + $(ACCESS_DRIVER_POLKIT_POLICY) check-local: augeas-check diff --git a/src/access/org.libvirt.domain.policy b/src/access/org.libvirt.domain.policy new file mode 100644 index 0000000..a8ebd84 --- /dev/null +++ b/src/access/org.libvirt.domain.policy @@ -0,0 +1,37 @@ +<!DOCTYPE policyconfig PUBLIC + "-//freedesktop//DTD PolicyKit Policy Configuration 1.0//EN" + "http://www.freedesktop.org/standards/PolicyKit/1.0/policyconfig.dtd"> + +<!-- +Policy definitions for libvirt daemon + +Copyright (c) 2007 Daniel P. Berrange <berrange redhat com> + +libvirt is licensed to you under the GNU Lesser General Public License +version 2. See COPYING for details. + +NOTE: If you make changes to this file, make sure to validate the file +using the polkit-policy-file-validate(1) tool. Changes made to this +file are instantly applied. +--> + +<policyconfig> + <action id="org.libvirt.domain.getattr"> + <description>Get virtual domain attributes</description> + <message>System policy prevents getattr on guest domains</message> + <defaults> + <allow_any>yes</allow_any> + <allow_inactive>yes</allow_inactive> + <allow_active>yes</allow_active> + </defaults> + </action> + <action id="org.libvirt.domain.read"> + <description>Get virtual domain attributes</description> + <message>System policy prevents getattr on guest domains</message> + <defaults> + <allow_any>yes</allow_any> + <allow_inactive>yes</allow_inactive> + <allow_active>yes</allow_active> + </defaults> + </action> +</policyconfig> diff --git a/src/access/viraccessdriverpolkit.c b/src/access/viraccessdriverpolkit.c new file mode 100644 index 0000000..d330653 --- /dev/null +++ b/src/access/viraccessdriverpolkit.c @@ -0,0 +1,163 @@ +/* + * 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, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include <config.h> + +#include "access/viraccessdriverpolkit.h" +#include "memory.h" +#include "command.h" +#include "logging.h" +#include "virterror_internal.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" + +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 *avname) +{ + char *actionid = NULL; + + if (virAsprintf(&actionid, "%s.%s.%s", + VIR_ACCESS_DRIVER_POLKIT_ACTION_PREFIX, + typename, avname) < 0) { + virReportOOMError(); + return NULL; + } + + return actionid; +} + + +static char * +virAccessDriverPolkitFormatProcess(void) +{ + virIdentityPtr identity = virAccessManagerGetEffectiveIdentity(); + const char *process = NULL; + char *ret = NULL; + + if (!identity) { + virAccessError(VIR_ERR_INTERNAL_ERROR, "%s", + _("No identity available")); + 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 (!(ret = strdup(process))) { + virReportOOMError(); + goto cleanup; + } +cleanup: + virIdentityFree(identity); + return ret; +} + + +static bool +virAccessDriverPolkitCheck(virAccessManagerPtr manager ATTRIBUTE_UNUSED, + const char *typename, + const char *avname) +{ + char *actionid = virAccessDriverPolkitFormatAction(typename, avname); + char *process = virAccessDriverPolkitFormatProcess(); + virCommandPtr cmd; + int status; + int ret = false; + + if (!actionid || !process) + goto cleanup; + + cmd = virCommandNewArgList(PKCHECK_PATH, + "--action-id", actionid, + "--process", process, + NULL); + + if (virCommandRun(cmd, &status) < 0) + goto cleanup; + + if (status != 0) { + char *tmp = virCommandTranslateStatus(status); + virAccessError(VIR_ERR_ACCESS_DENIED, + _("Policy kit denied action %s from %s: %s"), + actionid, process, NULLSTR(tmp)); + VIR_FREE(tmp); + goto cleanup; + } + + ret = true; + +cleanup: + VIR_FREE(actionid); + VIR_FREE(process); + return ret; +} + + +static bool +virAccessDriverPolkitCheckConnect(virAccessManagerPtr manager, + virAccessVectorConnect av) +{ + return virAccessDriverPolkitCheck(manager, + "connect", + virAccessVectorConnectTypeToString(av)); +} + + +static bool +virAccessDriverPolkitCheckDomain(virAccessManagerPtr manager, + virDomainDefPtr def ATTRIBUTE_UNUSED, + virAccessVectorDomain av) +{ + return virAccessDriverPolkitCheck(manager, + "domain", + virAccessVectorDomainTypeToString(av)); +} + + +virAccessDriver accessDriverPolkit = { + .name = "polkit", + .cleanup = virAccessDriverPolkitCleanup, + .checkConnect = virAccessDriverPolkitCheckConnect, + .checkDomain = virAccessDriverPolkitCheckDomain, +}; diff --git a/src/access/viraccessdriverpolkit.h b/src/access/viraccessdriverpolkit.h new file mode 100644 index 0000000..ac71fa5 --- /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, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef __VIR_ACCESS_DRIVER_POLKIT_H__ +# define __VIR_ACCESS_DRIVER_POLKIT_H__ + +# include "access/viraccessdriver.h" + +extern virAccessDriver accessDriverPolkit; + +#endif /* __VIR_ACCESS_DRIVER_POLKIT_H__ */ diff --git a/src/access/viraccessmanager.c b/src/access/viraccessmanager.c index 5bb4404..4698a4f 100644 --- a/src/access/viraccessmanager.c +++ b/src/access/viraccessmanager.c @@ -29,6 +29,7 @@ #endif #include "access/viraccessdrivernop.h" #include "access/viraccessdriverstack.h" +#include "access/viraccessdriverpolkit.h" #include "logging.h" #define VIR_FROM_THIS VIR_FROM_ACCESS @@ -206,6 +207,7 @@ static virAccessManagerPtr virAccessManagerNewDriver(virAccessDriverPtr drv) static virAccessDriverPtr accessDrivers[] = { &accessDriverNop, + &accessDriverPolkit, }; -- 1.7.7.5

From: "Daniel P. Berrange" <berrange@redhat.com> --- po/POTFILES.in | 1 + src/Makefile.am | 3 +- src/access/viraccessdriverselinux.c | 388 +++++++++++++++++++++++++++++++++++ src/access/viraccessdriverselinux.h | 28 +++ src/access/viraccessmanager.c | 2 + 5 files changed, 421 insertions(+), 1 deletions(-) create mode 100644 src/access/viraccessdriverselinux.c create mode 100644 src/access/viraccessdriverselinux.h diff --git a/po/POTFILES.in b/po/POTFILES.in index 0548479..24a6f93 100644 --- a/po/POTFILES.in +++ b/po/POTFILES.in @@ -5,6 +5,7 @@ daemon/remote_dispatch.h daemon/stream.c gnulib/lib/gai_strerror.c src/access/viraccessdriverpolkit.c +src/access/viraccessdriverselinux.c src/access/viraccessmanager.c src/conf/cpu_conf.c src/conf/domain_conf.c diff --git a/src/Makefile.am b/src/Makefile.am index ebc8301..636b424 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -524,7 +524,8 @@ ACCESS_DRIVER_SOURCES = \ access/viraccessdriver.h \ access/viraccessdrivernop.h access/viraccessdrivernop.c \ access/viraccessdriverstack.h access/viraccessdriverstack.c \ - access/viraccessdriverpolkit.h access/viraccessdriverpolkit.c + access/viraccessdriverpolkit.h access/viraccessdriverpolkit.c \ + access/viraccessdriverselinux.h access/viraccessdriverselinux.c ACCESS_DRIVER_POLKIT_POLICY = \ access/org.libvirt.domain.policy diff --git a/src/access/viraccessdriverselinux.c b/src/access/viraccessdriverselinux.c new file mode 100644 index 0000000..6084508 --- /dev/null +++ b/src/access/viraccessdriverselinux.c @@ -0,0 +1,388 @@ +/* + * viraccessdriverselinux.c: selinuxed 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, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include <config.h> + +#include "access/viraccessdriverselinux.h" +#include "memory.h" +#include "command.h" +#include "logging.h" +#include "threads.h" +#include "virterror_internal.h" + +#include <selinux/selinux.h> +#include <selinux/avc.h> +#include <selinux/av_permissions.h> +#include <selinux/flask.h> + + +#define VIR_FROM_THIS VIR_FROM_ACCESS +#define virAccessError(code, ...) \ + virReportErrorHelper(VIR_FROM_THIS, code, __FILE__, \ + __FUNCTION__, __LINE__, __VA_ARGS__) + +static void virAccessDriverSELinuxAVCLog(const char *fmt, ...) ATTRIBUTE_FMT_PRINTF(1, 2); +static void virAccessDriverSELinuxAVCLogAudit(void *data, security_class_t class, char *buf, size_t bufleft); +static void *virAccessDriverSELinuxAVCCreateThread(void (*run) (void)); +static void virAccessDriverSELinuxAVCStopThread(void *thread); +static void *virAccessDriverSELinuxAVCAllocLock(void); +static void virAccessDriverSELinuxAVCGetLock(void *lock); +static void virAccessDriverSELinuxAVCReleaseLock(void *lock); +static void virAccessDriverSELinuxAVCFreeLock(void *lock); + + +/* AVC callback structures for use in avc_init. */ +static const struct avc_memory_callback virAccessDriverSELinuxAVCMemCallbacks = +{ + .func_malloc = malloc, + .func_free = free, +}; +static const struct avc_log_callback virAccessDriverSELinuxAVCLogCallbacks = +{ + .func_log = virAccessDriverSELinuxAVCLog, + .func_audit = virAccessDriverSELinuxAVCLogAudit, +}; +static const struct avc_thread_callback virAccessDriverSELinuxAVCThreadCallbacks = +{ + .func_create_thread = virAccessDriverSELinuxAVCCreateThread, + .func_stop_thread = virAccessDriverSELinuxAVCStopThread, +}; +static const struct avc_lock_callback virAccessDriverSELinuxAVCLockCallbacks = +{ + .func_alloc_lock = virAccessDriverSELinuxAVCAllocLock, + .func_get_lock = virAccessDriverSELinuxAVCGetLock, + .func_release_lock = virAccessDriverSELinuxAVCReleaseLock, + .func_free_lock = virAccessDriverSELinuxAVCFreeLock, +}; + + +typedef struct _virAccessDriverSELinuxPrivate virAccessDriverSELinuxPrivate; +typedef virAccessDriverSELinuxPrivate *virAccessDriverSELinuxPrivatePtr; + +struct _virAccessDriverSELinuxPrivate { + bool enabled; + + /* Cache for AVCs */ + struct avc_entry_ref aeref; + + /* SID of the daemon */ + security_id_t localSid; +}; + + +static int virAccessDriverSELinuxSetup(virAccessManagerPtr manager) +{ + virAccessDriverSELinuxPrivatePtr priv = virAccessManagerGetPrivateData(manager); + int r; + security_context_t localCon; + int ret = -1; + + if ((r = is_selinux_enabled()) < 0) { + virReportSystemError(errno, "%s", + _("Unable to determine if SELinux is enabled")); + return -1; + } + priv->enabled = r != 0; + priv->localSid = SECSID_WILD; + + avc_entry_ref_init(&priv->aeref); + + if (avc_init("avc", + &virAccessDriverSELinuxAVCMemCallbacks, + &virAccessDriverSELinuxAVCLogCallbacks, + &virAccessDriverSELinuxAVCThreadCallbacks, + &virAccessDriverSELinuxAVCLockCallbacks) < 0) { + virReportSystemError(errno, "%s", + _("Unable to initialize AVC system")); + goto cleanup; + } + + if (getcon(&localCon) < 0) { + virReportSystemError(errno, "%s", + _("Unable to get context of daemon")); + goto cleanup; + } + + if (avc_context_to_sid(localCon, &priv->localSid) < 0) { + virReportSystemError(errno, + _("Unable to convert context %s to SID"), + (char*)localCon); + goto cleanup; + } + VIR_FREE(localCon); + + ret = 0; +cleanup: + if (ret < 0) + priv->localSid = SECSID_WILD; + freecon(localCon); + return ret; +} + + +static void virAccessDriverSELinuxCleanup(virAccessManagerPtr manager) +{ + virAccessDriverSELinuxPrivatePtr priv = virAccessManagerGetPrivateData(manager); + + priv->localSid = SECSID_WILD; +} + + +static security_id_t +virAccessDriverSELinuxGetClientSID(void) +{ + virIdentityPtr identity = virAccessManagerGetEffectiveIdentity(); + const char *seccon = NULL; + security_id_t sid = SECSID_WILD; + + if (!identity) { + virAccessError(VIR_ERR_INTERNAL_ERROR, "%s", + _("No identity available")); + return NULL; + } + if (virIdentityGetAttr(identity, VIR_IDENTITY_ATTR_SECURITY_CONTEXT, &seccon) < 0) + goto cleanup; + + if (!seccon) { + virAccessError(VIR_ERR_INTERNAL_ERROR, "%s", + _("No security context available")); + goto cleanup; + } + + if (avc_context_to_sid((security_context_t)seccon, &sid) < 0) { + virReportSystemError(errno, + _("Unable to convert context %s to SID"), + seccon); + sid = SECSID_WILD; + goto cleanup; + } + +cleanup: + virIdentityFree(identity); + return sid; +} + + +static security_class_t +virAccessDriverSELinuxGetObjectClass(const char *typename) +{ + security_class_t ret; + + if ((ret = string_to_security_class(typename)) == 0) { + virReportSystemError(errno, + _("Unable to find security class '%s'"), typename); + return 0; + } + + return ret; +} + + +static access_vector_t +virAccessDriverSELinuxGetObjectVector(const char *avname, const char *typename, security_class_t objectClass) +{ + access_vector_t ret; + + if (objectClass == 0) + return 0; + + if ((ret = string_to_av_perm(objectClass, avname)) == 0) { + virReportSystemError(errno, + _("Unable to find access vector '%s' for '%s'"), avname, typename); + return 0; + } + + return ret; +} + +static bool +virAccessDriverSELinuxCheck(virAccessManagerPtr manager, + security_id_t objectSid, + const char *typename, + const char *avname) +{ + virAccessDriverSELinuxPrivatePtr priv = virAccessManagerGetPrivateData(manager); + security_id_t clientSid = virAccessDriverSELinuxGetClientSID(); + security_class_t objectClass = virAccessDriverSELinuxGetObjectClass(typename); + access_vector_t objectVector = virAccessDriverSELinuxGetObjectVector(avname, typename, objectClass); + int ret = false; + + if (clientSid == SECSID_WILD || + objectClass == 0 || + objectVector == 0) { + if (security_deny_unknown() == 0) { + VIR_WARN("Allow access, because policy does not deny unknown objects"); + ret = true; + } + goto cleanup; + } + + if (avc_has_perm(clientSid, objectSid, + objectClass, objectVector, + &priv->aeref, NULL) < 0) { + int save_errno = errno; + if (security_getenforce() == 0) { + char ebuf[1024]; + VIR_WARN("Ignoring denial in non-enforcing mode: %s", + virStrerror(save_errno, ebuf, sizeof(ebuf))); + ret = true; + goto cleanup; + } + switch (save_errno) { + case EACCES: + virAccessError(VIR_ERR_ACCESS_DENIED, "%s", + _("SELinux denying due to security policy")); + break; + case EINVAL: + virAccessError(VIR_ERR_ACCESS_DENIED, "%s", + _("SELinux denying due to invalid security context")); + break; + default: + virReportSystemError(errno, "%s", + _("SELinux denying")); + break; + } + goto cleanup; + } + + ret = true; + +cleanup: + return ret; +} + + +static bool +virAccessDriverSELinuxCheckConnect(virAccessManagerPtr manager, + virAccessVectorConnect av) +{ + virAccessDriverSELinuxPrivatePtr priv = virAccessManagerGetPrivateData(manager); + + /* There's no object to use for targetSid here, so we + * instead use the daemon's context as the targetSid */ + return virAccessDriverSELinuxCheck(manager, + priv->localSid, + "connect", + virAccessVectorConnectTypeToString(av)); +} + + +static bool +virAccessDriverSELinuxCheckDomain(virAccessManagerPtr manager, + virDomainDefPtr def ATTRIBUTE_UNUSED, + virAccessVectorDomain av) +{ + security_id_t objectSid = 0; /* XXX get from 'def' */ + + return virAccessDriverSELinuxCheck(manager, + objectSid, + "domain", + virAccessVectorDomainTypeToString(av)); +} + + + +static void virAccessDriverSELinuxAVCLog(const char *fmt, ...) +{ + va_list ap; + va_start(ap, fmt); + virLogVMessage("avc", VIR_LOG_WARN,__func__, __LINE__, 0, fmt, ap); + va_end(ap); +} + + +static void virAccessDriverSELinuxAVCLogAudit(void *data ATTRIBUTE_UNUSED, + security_class_t class ATTRIBUTE_UNUSED, + char *buf ATTRIBUTE_UNUSED, + size_t bufleft ATTRIBUTE_UNUSED) +{ +} + + +static void *virAccessDriverSELinuxAVCCreateThread(void (*run) (void)) +{ + virThreadPtr thread; + + if (VIR_ALLOC(thread) < 0) { + virReportOOMError(); + return NULL; + } + + if (virThreadCreate(thread, false, (virThreadFunc)run, NULL) < 0) { + virReportSystemError(errno, "%s", + _("Unable to create thread")); + VIR_FREE(thread); + } + + return thread; +} + + +static void virAccessDriverSELinuxAVCStopThread(void *thread) +{ + virThreadCancel(thread); + VIR_FREE(thread); +} + + +static void *virAccessDriverSELinuxAVCAllocLock(void) +{ + virMutexPtr lock; + if (VIR_ALLOC(lock) < 0) { + virReportOOMError(); + return NULL; + } + if (virMutexInit(lock) < 0) { + virReportSystemError(errno, "%s", + _("Unable to initialize mutex")); + VIR_FREE(lock); + return NULL; + } + return lock; +} + + +static void virAccessDriverSELinuxAVCGetLock(void *lock) +{ + virMutexLock(lock); +} + + +static void virAccessDriverSELinuxAVCReleaseLock(void *lock) +{ + virMutexUnlock(lock); +} + + +static void virAccessDriverSELinuxAVCFreeLock(void *lock) +{ + virMutexDestroy(lock); + VIR_FREE(lock); +} + + +virAccessDriver accessDriverSELinux = { + .privateDataLen = sizeof(virAccessDriverSELinuxPrivate), + .name = "selinux", + .setup = virAccessDriverSELinuxSetup, + .cleanup = virAccessDriverSELinuxCleanup, + .checkConnect = virAccessDriverSELinuxCheckConnect, + .checkDomain = virAccessDriverSELinuxCheckDomain, +}; diff --git a/src/access/viraccessdriverselinux.h b/src/access/viraccessdriverselinux.h new file mode 100644 index 0000000..3a53686 --- /dev/null +++ b/src/access/viraccessdriverselinux.h @@ -0,0 +1,28 @@ +/* + * viraccessdriverselinux.h: selinuxed 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, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef __VIR_ACCESS_DRIVER_SELINUX_H__ +# define __VIR_ACCESS_DRIVER_SELINUX_H__ + +# include "access/viraccessdriver.h" + +extern virAccessDriver accessDriverSELinux; + +#endif /* __VIR_ACCESS_DRIVER_SELINUX_H__ */ diff --git a/src/access/viraccessmanager.c b/src/access/viraccessmanager.c index 4698a4f..5c29a34 100644 --- a/src/access/viraccessmanager.c +++ b/src/access/viraccessmanager.c @@ -30,6 +30,7 @@ #include "access/viraccessdrivernop.h" #include "access/viraccessdriverstack.h" #include "access/viraccessdriverpolkit.h" +#include "access/viraccessdriverselinux.h" #include "logging.h" #define VIR_FROM_THIS VIR_FROM_ACCESS @@ -208,6 +209,7 @@ static virAccessManagerPtr virAccessManagerNewDriver(virAccessDriverPtr drv) static virAccessDriverPtr accessDrivers[] = { &accessDriverNop, &accessDriverPolkit, + &accessDriverSELinux, }; -- 1.7.7.5
participants (1)
-
Daniel P. Berrange