[libvirt] [PATCH v4 2/3] virfile: Introduce internal API for managing ACL

Daniel P. Berrange berrange at redhat.com
Fri Mar 15 14:18:34 UTC 2013


On Fri, Mar 15, 2013 at 03:12:02PM +0100, Michal Privoznik wrote:
> For now, only three APIs are implemented:
> virFileGetACL to retrieve permission for a specific user
> virFileSetACL for setting requested permissions for a specific user,
> virFileRemoveACL to remove those permissions.
> ---
> 
> diff to v3:
> -set errno=ENOSYS when building without WITH_ATTR for easier check within callee.
> -ACL mask is deleted prior recalc as after removing our entry, mask may be not
>  required anymore.
> 
> diff to v2:
> -Introduced m4 macro to check for libacl
> -new virFileGetACL API
> -ACL mask recalc offloaded to libacl 
> 
>  configure.ac             |   2 +
>  libvirt.spec.in          |   1 +
>  m4/virt-acl.m4           |   9 +++
>  src/Makefile.am          |   4 +-
>  src/libvirt_private.syms |   3 +
>  src/util/virfile.c       | 193 +++++++++++++++++++++++++++++++++++++++++++++++
>  src/util/virfile.h       |  14 ++++
>  7 files changed, 224 insertions(+), 2 deletions(-)
>  create mode 100644 m4/virt-acl.m4
> 
> diff --git a/configure.ac b/configure.ac
> index 9d366e9..4e6627c 100644
> --- a/configure.ac
> +++ b/configure.ac
> @@ -142,6 +142,7 @@ AC_MSG_RESULT([$VERSION_SCRIPT_FLAGS])
>  
>  LIBVIRT_COMPILE_WARNINGS
>  
> +LIBVIRT_CHECK_ACL
>  LIBVIRT_CHECK_APPARMOR
>  LIBVIRT_CHECK_ATTR
>  LIBVIRT_CHECK_AUDIT
> @@ -2462,6 +2463,7 @@ fi
>  AC_MSG_NOTICE([])
>  AC_MSG_NOTICE([Libraries])
>  AC_MSG_NOTICE([])
> +LIBVIRT_RESULT_ACL
>  LIBVIRT_RESULT_APPARMOR
>  LIBVIRT_RESULT_ATTR
>  LIBVIRT_RESULT_AUDIT
> diff --git a/libvirt.spec.in b/libvirt.spec.in
> index 9fb753a..222674d 100644
> --- a/libvirt.spec.in
> +++ b/libvirt.spec.in
> @@ -411,6 +411,7 @@ BuildRequires: gettext
>  BuildRequires: libtasn1-devel
>  BuildRequires: gnutls-devel
>  BuildRequires: libattr-devel
> +BuildRequires: libacl-devel
>  %if 0%{?fedora} >= 12 || 0%{?rhel} >= 6
>  # for augparse, optionally used in testing
>  BuildRequires: augeas
> diff --git a/m4/virt-acl.m4 b/m4/virt-acl.m4
> new file mode 100644
> index 0000000..7f16dca
> --- /dev/null
> +++ b/m4/virt-acl.m4
> @@ -0,0 +1,9 @@
> +dnl The libacl.so library
> +
> +AC_DEFUN([LIBVIRT_CHECK_ACL],[
> +  LIBVIRT_CHECK_LIB([ACL], [acl], [acl_init], [sys/acl.h])
> +])
> +
> +AC_DEFUN([LIBVIRT_RESULT_ACL],[
> +  LIBVIRT_RESULT_LIB([ACL])
> +])
> diff --git a/src/Makefile.am b/src/Makefile.am
> index 0c0dfb3..0ddc128 100644
> --- a/src/Makefile.am
> +++ b/src/Makefile.am
> @@ -764,11 +764,11 @@ libvirt_util_la_SOURCES =					\
>  		$(UTIL_SOURCES)
>  libvirt_util_la_CFLAGS = $(CAPNG_CFLAGS) $(YAJL_CFLAGS) $(LIBNL_CFLAGS) \
>  		$(AM_CFLAGS) $(AUDIT_CFLAGS) $(DEVMAPPER_CFLAGS) \
> -		$(DBUS_CFLAGS) $(LDEXP_LIBM)
> +		$(DBUS_CFLAGS) $(LDEXP_LIBM) $(ACL_CFLAGS)
>  libvirt_util_la_LIBADD = $(CAPNG_LIBS) $(YAJL_LIBS) $(LIBNL_LIBS) \
>  		$(THREAD_LIBS) $(AUDIT_LIBS) $(DEVMAPPER_LIBS) \
>  		$(LIB_CLOCK_GETTIME) $(DBUS_LIBS) $(MSCOM_LIBS) $(LIBXML_LIBS) \
> -		$(SECDRIVER_LIBS)
> +		$(SECDRIVER_LIBS) $(ACL_LIBS)
>  
>  
>  noinst_LTLIBRARIES += libvirt_conf.la
> diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms
> index 5a2cbe8..e1ec774 100644
> --- a/src/libvirt_private.syms
> +++ b/src/libvirt_private.syms
> @@ -1252,10 +1252,13 @@ virFileClose;
>  virFileDirectFdFlag;
>  virFileFclose;
>  virFileFdopen;
> +virFileGetACL;
>  virFileGetAttr;
>  virFileLoopDeviceAssociate;
> +virFileRemoveACL;
>  virFileRemoveAttr;
>  virFileRewrite;
> +virFileSetACL;
>  virFileSetAttr;
>  virFileTouch;
>  virFileUpdatePerm;
> diff --git a/src/util/virfile.c b/src/util/virfile.c
> index be50e83..7f50328 100644
> --- a/src/util/virfile.c
> +++ b/src/util/virfile.c
> @@ -41,6 +41,10 @@
>  # include <attr/xattr.h>
>  #endif
>  
> +#ifdef WITH_ACL
> +# include <acl/libacl.h>
> +#endif
> +
>  #include "vircommand.h"
>  #include "configmake.h"
>  #include "viralloc.h"
> @@ -752,3 +756,192 @@ virFileRemoveAttr(const char *file ATTRIBUTE_UNUSED,
>      return -1;
>  }
>  #endif /* WITH_ATTR */
> +
> +#ifdef WITH_ACL
> +static acl_entry_t
> +virFileACLFindEntry(acl_t acl, acl_tag_t type, id_t id)
> +{
> +    acl_entry_t ent;
> +    acl_tag_t e_type;
> +    id_t *e_id_p;
> +
> +    /* acl_get_entry returns 1 if there's an entry in @acl */
> +    if (acl_get_entry(acl, ACL_FIRST_ENTRY, &ent) != 1)
> +        return NULL;
> +
> +    do {
> +        acl_get_tag_type(ent, &e_type);
> +        if (e_type == type) {
> +            if (id == ACL_UNDEFINED_ID)
> +                return ent;
> +
> +            if (!(e_id_p = acl_get_qualifier(ent)))
> +                return NULL;
> +            if (*e_id_p == id) {
> +                acl_free(e_id_p);
> +                return ent;
> +            }
> +            acl_free(e_id_p);
> +        }
> +    } while (acl_get_entry(acl, ACL_NEXT_ENTRY, &ent) == 1);
> +
> +    return NULL;
> +}
> +
> +static void
> +virFileACLSetPerms(acl_entry_t ent, mode_t perms)
> +{
> +    acl_permset_t set;
> +
> +    acl_get_permset(ent, &set);
> +    if (perms & S_IRUSR)
> +        acl_add_perm(set, ACL_READ);
> +    else
> +        acl_delete_perm(set, ACL_READ);
> +    if (perms & S_IWUSR)
> +        acl_add_perm(set, ACL_WRITE);
> +    else
> +        acl_delete_perm(set, ACL_WRITE);
> +    if (perms & S_IXUSR)
> +        acl_add_perm(set, ACL_EXECUTE);
> +    else
> +        acl_delete_perm(set, ACL_EXECUTE);
> +}
> +
> +static void
> +virFileACLGetPerms(acl_entry_t ent, mode_t *perms)
> +{
> +    acl_permset_t set;
> +
> +    *perms = 0;
> +    acl_get_permset(ent, &set);
> +    if (acl_get_perm(set, ACL_READ))
> +        *perms |= S_IRUSR;
> +    if (acl_get_perm(set, ACL_WRITE))
> +        *perms |= S_IWUSR;
> +    if (acl_get_perm(set, ACL_EXECUTE))
> +        *perms |= S_IXUSR;
> +}
> +
> +static int
> +virFileACLSetOrRemove(const char *path,
> +                      uid_t user,
> +                      mode_t perms,
> +                      bool set)
> +{
> +    int ret = -1;
> +    acl_t acl;
> +    acl_entry_t ent;
> +
> +    if (!(acl = acl_get_file(path, ACL_TYPE_ACCESS))) {
> +        virReportSystemError(errno, _("Unable to get ACL on %s"), path);
> +        return ret;
> +    }
> +
> +    ent = virFileACLFindEntry(acl, ACL_USER, user);
> +    if (set) {
> +        if (!ent && acl_create_entry(&acl, &ent) < 0) {
> +            virReportSystemError(errno, "%s", _("Unable to create ACL entity"));
> +            goto cleanup;
> +        }
> +        acl_set_tag_type(ent, ACL_USER);
> +        acl_set_qualifier(ent, &user);
> +
> +        virFileACLSetPerms(ent, perms);
> +
> +    } else if (ent) {
> +        if (acl_delete_entry(acl, ent) < 0) {
> +            virReportSystemError(errno, "%s", _("Unable to delete ACL entity"));
> +            goto cleanup;
> +        }
> +    }
> +
> +    if ((ent = virFileACLFindEntry(acl, ACL_MASK, ACL_UNDEFINED_ID)) &&
> +        acl_delete_entry(acl, ent) < 0) {
> +        virReportSystemError(errno, "%s", _("Unable to delete ACL mask"));
> +        goto cleanup;
> +    }
> +
> +    if (acl_equiv_mode(acl, NULL) && acl_calc_mask(&acl) < 0) {
> +        virReportSystemError(errno, "%s", _("Unable to calculate ACL mask"));
> +        goto cleanup;
> +    }
> +
> +    if (acl_set_file(path, ACL_TYPE_ACCESS, acl) < 0) {
> +        virReportSystemError(errno, _("Unable to set ACL on %s"), path);
> +        goto cleanup;
> +    }
> +
> +    ret = 0;
> +cleanup:
> +    acl_free(acl);
> +    return ret;
> +}
> +
> +int
> +virFileSetACL(const char *file,
> +              uid_t user,
> +              mode_t perms)
> +{
> +    return virFileACLSetOrRemove(file, user, perms, true);
> +}
> +
> +int
> +virFileRemoveACL(const char *file,
> +                 uid_t user)
> +{
> +    return virFileACLSetOrRemove(file, user, 0, false);
> +}
> +
> +int
> +virFileGetACL(const char *file,
> +              uid_t user,
> +              mode_t *perms)
> +{
> +    acl_t acl;
> +    acl_entry_t ent;
> +
> +    if (!(acl = acl_get_file(file, ACL_TYPE_ACCESS))) {
> +        virReportSystemError(errno, _("Unable to get ACL on %s"), file);
> +        return -1;
> +    }
> +
> +    if ((ent = virFileACLFindEntry(acl, ACL_USER, user)))
> +        virFileACLGetPerms(ent, perms);
> +    else
> +        *perms = 0;
> +
> +    acl_free(acl);
> +    return 0;
> +}
> +
> +#else /* WITH_ACL */
> +
> +int
> +virFileSetACL(const char *file ATTRIBUTE_UNUSED,
> +              uid_t user ATTRIBUTE_UNUSED,
> +              mode_t perms ATTRIBUTE_UNUSED)
> +{
> +    errno = ENOSYS;

As with previous patch, NACK to setting 'errno' here.


Daniel
-- 
|: http://berrange.com      -o-    http://www.flickr.com/photos/dberrange/ :|
|: http://libvirt.org              -o-             http://virt-manager.org :|
|: http://autobuild.org       -o-         http://search.cpan.org/~danberr/ :|
|: http://entangle-photo.org       -o-       http://live.gnome.org/gtk-vnc :|




More information about the libvir-list mailing list