For now, only two APIs are implemented: virFileSetACL for setting requested permissions for a specific user, virFileRemoveACL to remove those permissions. Both these traverse the whole path from root directory and set or unset S_IXUSR flag on all directories met so user can really access the file. --- configure.ac | 1 + libvirt.spec.in | 3 + src/libvirt_private.syms | 2 + src/util/virfile.c | 202 +++++++++++++++++++++++++++++++++++++++++++++++ src/util/virfile.h | 7 ++ 5 files changed, 215 insertions(+) diff --git a/configure.ac b/configure.ac index cbb20c4..0df8a72 100644 --- a/configure.ac +++ b/configure.ac @@ -276,6 +276,7 @@ AM_CONDITIONAL([HAVE_LIBTASN1], [test "x$ac_cv_header_libtasn1_h" = "xyes"]) AC_CHECK_LIB([intl],[gettext],[]) AC_CHECK_LIB([attr],[getxattr]) +AC_CHECK_LIB([acl], [acl_init]) dnl Do we have rpcgen? AC_PATH_PROG([RPCGEN], [rpcgen], [no]) diff --git a/libvirt.spec.in b/libvirt.spec.in index 90629c8..67c5799 100644 --- a/libvirt.spec.in +++ b/libvirt.spec.in @@ -580,6 +580,9 @@ BuildRequires: scrub # For xattr BuildRequires: libattr-devel +# For ACL +BuildRequires: libacl-devel + %if %{with_numad} BuildRequires: numad %endif diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms index fc7568e..980e3ea 100644 --- a/src/libvirt_private.syms +++ b/src/libvirt_private.syms @@ -1275,8 +1275,10 @@ virFileFclose; virFileFdopen; virFileGetAttr; virFileLoopDeviceAssociate; +virFileRemoveACL; virFileRemoveAttr; virFileRewrite; +virFileSetACL; virFileSetAttr; virFileTouch; virFileUpdatePerm; diff --git a/src/util/virfile.c b/src/util/virfile.c index aa4579e..5d4254d 100644 --- a/src/util/virfile.c +++ b/src/util/virfile.c @@ -42,6 +42,11 @@ # include <sys/types.h> #endif +#ifdef HAVE_LIBACL +# include <sys/acl.h> +# include <sys/types.h> +#endif + #include "vircommand.h" #include "configmake.h" #include "viralloc.h" @@ -750,3 +755,200 @@ virFileRemoveAttr(const char *file ATTRIBUTE_UNUSED, return -1; } #endif /* HAVE_LIBATTR */ + +#ifdef HAVE_LIBACL /* HAVE_LIBACL */ +static acl_entry_t +findACLEntry(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; + + while (true) { + 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); + } + if (acl_get_entry(acl, ACL_NEXT_ENTRY, &ent) != 1) + return NULL; + } +} + +static void +setACLPerms(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 int +cloneACLEntry(acl_t fromAcl, acl_tag_t fromType, id_t user, + acl_t *toAcl, acl_tag_t toType) +{ + acl_entry_t fromEntry, toEntry; + if (!(fromEntry = findACLEntry(fromAcl, fromType, user))) + return 1; + + if (acl_create_entry(toAcl, &toEntry) != 0) { + virReportSystemError(errno, "%s", _("Unable to clone ACL entry")); + return -1; + } + + acl_copy_entry(toEntry, fromEntry); + acl_set_tag_type(toEntry, toType); + return 0; +} + +static int +virFileSetOrRemoveACLHelper(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 = findACLEntry(acl, ACL_USER, user); + if (!ent && set) { + if (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); + + setACLPerms(ent, perms); + + /* Recompute mask */ + if (!findACLEntry(acl, ACL_MASK, ACL_UNDEFINED_ID) && + cloneACLEntry(acl, ACL_USER, user, &acl, ACL_MASK) < 0) + goto cleanup; + } else if (ent && !set) { + if (acl_delete_entry(acl, ent) < 0) { + virReportSystemError(errno, "%s", _("Unable to delete ACL entity")); + goto cleanup; + } + + if ((ent = findACLEntry(acl, ACL_MASK, user)) && + acl_delete_entry(acl, ent) < 0) { + virReportSystemError(errno, "%s", _("Unable to delete ACL entity")); + 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; +} + +static int +virFileSetOrRemoveACL(const char *file, + uid_t user, + mode_t perms, + bool set) +{ + int ret = -1; + char *fileDup = NULL; + char *p; + + if (!(fileDup = strdup(file))) { + virReportOOMError(); + return ret; + } + + if (virFileSetOrRemoveACLHelper(file, user, perms, set) < 0) + goto cleanup; + + /* For parent directories we want executable flag */ + perms |= S_IXUSR; + + while ((p = strrchr(fileDup, '/')) != fileDup) { + if (!p) { + virReportSystemError(EINVAL, _("Invalid relative path '%s'"), file); + goto cleanup; + } + + *p = '\0'; + + if (virFileSetOrRemoveACLHelper(fileDup, user, perms, set) < 0) + goto cleanup; + } + + ret = 0; + +cleanup: + VIR_FREE(fileDup); + return ret; +} + +int +virFileSetACL(const char *file, + uid_t user, + mode_t perms) +{ + return virFileSetOrRemoveACL(file, user, perms, true); +} + +int +virFileRemoveACL(const char *file, + uid_t user) +{ + return virFileSetOrRemoveACL(file, user, 0, false); +} + +#else /* HAVE_LIBACL */ +int +virFileSetACL(const char *file ATTRIBUTE_UNUSED, + uid_t user ATTRIBUTE_UNUSED, + mode_t perms ATTRIBUTE_UNUSED) +{ + virReportSystemError(ENOSYS, "%s", _("Unable to set ACL")); + return -1; +} + +int +virFileRemoveACL(const char *file ATTRIBUTE_UNUSED, + uid_t user ATTRIBUTE_UNUSED) +{ + virReportSystemError(ENOSYS, "%s", _("Unable to remove ACL")); + return -1; +} +#endif /* HAVE_LIBACL */ diff --git a/src/util/virfile.h b/src/util/virfile.h index 3b4d672..b530aed 100644 --- a/src/util/virfile.h +++ b/src/util/virfile.h @@ -120,4 +120,11 @@ int virFileGetAttr(const char *file, int virFileRemoveAttr(const char *file, const char *name); +int virFileSetACL(const char *file, + uid_t user, + mode_t perms); + +int virFileRemoveACL(const char *file, + uid_t user); + #endif /* __VIR_FILES_H */ -- 1.8.1.5 -- libvir-list mailing list libvir-list@xxxxxxxxxx https://www.redhat.com/mailman/listinfo/libvir-list