[libvirt] [PATCH 06/12] Define basic internal API for access control

Eric Blake eblake at redhat.com
Wed May 2 22:06:47 UTC 2012


On 05/02/2012 05:44 AM, Daniel P. Berrange wrote:
> From: "Daniel P. Berrange" <berrange at redhat.com>
> 

A bit sparse on the commit message, maybe even mentioning that
src/access/apis.txt contains the details would help.

You are adding a new directory; do any of the cfg.mk syntax check rules
need to be updated to state which other drivers can include the headers
from this directory?

> +++ b/src/access/apis.txt
> @@ -0,0 +1,577 @@

Copyright header?  Also, can you add a short description about what the
rest of this file is describing?

> +
> +Non-driver based APIs
> +
> +
> +virConnCopyLastError:
> +virResetError:
> +virResetLastError:
> +virSaveLastError:
> +virSetErrorFunc:
> +virConnGetLastError:
> +virConnResetLastError:
> +virConnSetErrorFunc:
> +virCopyLastError:
> +virDefaultErrorFunc:
> +virFreeError:
> +virGetLastError:

You know, we really ought to divide libvirt.h into multiple headers, one
per object type, to make this a bit easier to track.


> +
> +virConnectBaselineCPU:
> +
> + - No state access
> +
> +virConnectCompareCPU:
> +
> + - Access host CPU

If I understand this file correctly, you were basically enumerating
which APIs need which access controls.  Does that mean that adding a new
API to libvirt.h will now also require updating this file?  Is there any
way to write a syntax-check rule that ensures that all public functions
also have an entry in this file, to make sure we don't forget?


> +++ b/src/access/viraccessdriver.h

> +++ b/src/access/viraccessdrivernop.c
> @@ -0,0 +1,44 @@
> +/*
> + * viraccessdrivernop.c: no-op access control driver
> + *
> + * Copyright (C) 2011 Red Hat, Inc.

2012 now (wow, you've been working on this for some time now...)


> +++ b/src/access/viraccessdrivernop.h
> @@ -0,0 +1,28 @@
> +/*
> + * viraccessdrivernop.h: no-op access control driver
> + *
> + * Copyright (C) 2011 Red Hat, Inc.

2012


> +++ b/src/access/viraccessdriverstack.c

> +static bool
> +virAccessDriverStackCheckConnect(virAccessManagerPtr manager,
> +                                 virAccessPermConnect 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;

So anyone can deny, but all are given a shot at it in case the access
manager has side effects such as auditing their own decision (but note
that an audited success may be countermanded by a different manager's
denial).  Seems reasonable.

> +++ b/src/access/viraccessmanager.c


> +
> +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;

Does this fail even if SELinux is not enabled?  And if so, what is the
impact of failing here?

> +    }
> +    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;

Especially since here, you seem to be catering to the case of a NULL
seccontext when SELinux was not compiled in.


> +int virAccessManagerSetEffectiveIdentity(virIdentityPtr identity)
> +{
> +    virIdentityPtr old;
> +
> +    if (!virAccessManagerInit())
> +        return -1;
> +
> +    old = virThreadLocalGet(&effectiveIdentity);
> +    if (old)
> +        virIdentityFree(old);

This loses the old identity,

> +
> +    if (virThreadLocalSet(&effectiveIdentity, identity) < 0) {
> +        virReportSystemError(errno, "%s",
> +                             _("Unable to set thread local identity"));
> +        return -1;

even if we fail to set the new one.  I think you need to swap these two
cases (and keep old in force on failure to set a new identity).


> +int virAccessManagerSetRealIdentity(virIdentityPtr identity)
> +{
> +    virIdentityPtr old;
> +
> +    if (!virAccessManagerInit())
> +        return -1;
> +
> +    old = virThreadLocalGet(&realIdentity);
> +    if (old)
> +        virIdentityFree(old);
> +
> +    if (virThreadLocalSet(&realIdentity, identity) < 0) {
> +        virReportSystemError(errno, "%s",
> +                             _("Unable to set thread local identity"));
> +        return -1;
> +    }

Same comment.

> +void *virAccessManagerGetPrivateData(virAccessManagerPtr mgr)
> +{
> +    /* This accesses the memory just beyond mgr, which was allocated
> +     * via VIR_ALLOC_VAR earlier.  */
> +    return mgr + 1;

Phew - we learned our lesson with the security manager.


> +
> +typedef enum {
> +    VIR_ACCESS_PERM_CONNECT_GETATTR,
> +    VIR_ACCESS_PERM_CONNECT_SEARCH_DOMAINS,
> +
> +    VIR_ACCESS_PERM_CONNECT_LAST,
> +} virAccessPermConnect;
> +
> +typedef enum {
> +    VIR_ACCESS_PERM_DOMAIN_GETATTR,  /* Name/ID/UUID access */
> +    VIR_ACCESS_PERM_DOMAIN_READ,     /* Config access */
> +    VIR_ACCESS_PERM_DOMAIN_WRITE,    /* Config change */
> +    VIR_ACCESS_PERM_DOMAIN_READ_SECURE,
> +
> +    VIR_ACCESS_PERM_DOMAIN_START,
> +    VIR_ACCESS_PERM_DOMAIN_STOP,
> +
> +    VIR_ACCESS_PERM_DOMAIN_SAVE,
> +    VIR_ACCESS_PERM_DOMAIN_DELETE,
> +
> +    /* Merge these 3 into 1 ? */
> +    VIR_ACCESS_PERM_DOMAIN_SHUTDOWN,
> +    VIR_ACCESS_PERM_DOMAIN_REBOOT,
> +    VIR_ACCESS_PERM_DOMAIN_RESET,

Maybe not.  While reboot can reuse an existing qemu process,
shutdown/start will create a new qemu process, and there may be
ramifications to that difference (such as whether a vnc session to qemu
has to reconnect).  Reboot and Reset might be mergeable, but I think
I've made a case for keeping shutdown separate.

> +++ b/src/libvirt_private.syms
> @@ -1181,6 +1181,27 @@ virAuthConfigNew;
>  virAuthConfigNewData;
>  
>  
> +# viraccessmanager.h

Sorting: viraccessmanager.h comes before virauth.h, and...

> +virAccessManagerInit;
> +virAccessManagerGetSystemIdentity;
> +virAccessManagerGetRealIdentity;
> +virAccessManagerGetEffectiveIdentity;
> +virAccessManagerSetRealIdentity;
> +virAccessManagerSetEffectiveIdentity;
> +virAccessManagerNew;
> +virAccessManagerNewStack;
> +virAccessManagerFree;
> +virAccessManagerCheckConnect;
> +virAccessManagerCheckDomain;

within the file, sort the function names.

-- 
Eric Blake   eblake at redhat.com    +1-919-301-3266
Libvirt virtualization library http://libvirt.org

-------------- next part --------------
A non-text attachment was scrubbed...
Name: signature.asc
Type: application/pgp-signature
Size: 620 bytes
Desc: OpenPGP digital signature
URL: <http://listman.redhat.com/archives/libvir-list/attachments/20120502/59a2980d/attachment-0001.sig>


More information about the libvir-list mailing list