From 72f27f2fbf620439935bd961ba2350d6d10654b6 Mon Sep 17 00:00:00 2001 From: Aneesh Kumar K.V Date: Fri, 13 Nov 2009 22:48:41 +0530 Subject: [PATCH 1/5] richacl: Rename the tools as richacl-tools. Rename the acl as richacl along with kernel changes. Linux kernel already have an exported nfsv4 acl interface via nfsv4 client. So having another nfs4acl-tools will create confusion. Signed-off-by: Aneesh Kumar K.V --- Makefile | 20 +- include/nfs4acl-internal.h | 40 -- include/nfs4acl.h | 155 ------ include/nfs4acl_xattr.h | 28 -- include/richacl-internal.h | 40 ++ include/richacl.h | 155 ++++++ include/richacl_xattr.h | 28 ++ lib/nfs4acl.c | 1127 -------------------------------------------- lib/nfs4acl_compat.c | 610 ------------------------ lib/richacl.c | 1127 ++++++++++++++++++++++++++++++++++++++++++++ lib/richacl_compat.c | 610 ++++++++++++++++++++++++ src/nfs4acl.c | 413 ---------------- src/richacl.c | 413 ++++++++++++++++ 13 files changed, 2383 insertions(+), 2383 deletions(-) delete mode 100644 include/nfs4acl-internal.h delete mode 100644 include/nfs4acl.h delete mode 100644 include/nfs4acl_xattr.h create mode 100644 include/richacl-internal.h create mode 100644 include/richacl.h create mode 100644 include/richacl_xattr.h delete mode 100644 lib/nfs4acl.c delete mode 100644 lib/nfs4acl_compat.c create mode 100644 lib/richacl.c create mode 100644 lib/richacl_compat.c delete mode 100644 src/nfs4acl.c create mode 100644 src/richacl.c diff --git a/Makefile b/Makefile index 84a5711..6eb8a67 100644 --- a/Makefile +++ b/Makefile @@ -1,9 +1,9 @@ -NAME := nfs4acl +NAME := richacl VERSION := 0.9 -INCLUDE_SOURCES := include/nfs4acl.h include/nfs4acl_xattr.h include/nfs4acl-internal.h include/string_buffer.h -SRC_SOURCES := src/nfs4acl.c -LIB_SOURCES := lib/nfs4acl.c lib/nfs4acl_compat.c lib/string_buffer.c +INCLUDE_SOURCES := include/richacl.h include/richacl_xattr.h include/richacl-internal.h include/string_buffer.h +SRC_SOURCES := src/richacl.c +LIB_SOURCES := lib/richacl.c lib/richacl_compat.c lib/string_buffer.c TESTS := test/run $(wildcard test/*.test) SOURCES := Makefile $(INCLUDE_SOURCES) $(SRC_SOURCES) $(LIB_SOURCES) @@ -11,12 +11,12 @@ CPPFLAGS := -Iinclude CFLAGS := -g -Wall -DVERSION=\"$(VERSION)\" LDFLAGS := -g -all: src/nfs4acl +all: src/richacl -src/nfs4acl : src/nfs4acl.o lib/libnfs4acl.a +src/richacl : src/richacl.o lib/librichacl.a $(CC) $(LDFLAGS) -o $@ $+ -lib/libnfs4acl.a : lib/nfs4acl.o lib/string_buffer.o lib/nfs4acl_compat.o +lib/librichacl.a : lib/richacl.o lib/string_buffer.o lib/richacl_compat.o $(RM) -f $@ $(AR) r $@ $+ @@ -32,9 +32,9 @@ install: all $(INSTALL) -d -m 755 $(DESTDIR)$(includedir)/$(NAME) for file in $(INCLUDE_SOURCES) ; do $(INSTALL) -m 644 $$file $(DESTDIR)$(includedir)/$(NAME) ; done $(INSTALL) -d -m 755 $(DESTDIR)$(libdir) - $(INSTALL) -m 644 lib/libnfs4acl.a $(DESTDIR)$(libdir) + $(INSTALL) -m 644 lib/librichacl.a $(DESTDIR)$(libdir) $(INSTALL) -d -m 755 $(DESTDIR)$(sbindir) - $(INSTALL) -m 755 src/nfs4acl $(DESTDIR)$(sbindir) + $(INSTALL) -m 755 src/richacl $(DESTDIR)$(sbindir) $(INSTALL) -d -m 755 $(DESTDIR)$(pkgdatadir)/test for file in $(TESTS) ; do $(INSTALL) -m 644 $$file $(DESTDIR)$(pkgdatadir)/test ; done chmod 755 $(DESTDIR)$(pkgdatadir)/test/run @@ -46,4 +46,4 @@ dist: rm -f $(NAME)-$(VERSION) clean: - rm -f src/nfs4acl.o lib/libnfs4acl.a lib/nfs4acl.o lib/string_buffer.o lib/nfs4acl_compat.o + rm -f src/richacl src/richacl.o lib/librichacl.a lib/richacl.o lib/string_buffer.o lib/richacl_compat.o diff --git a/include/nfs4acl-internal.h b/include/nfs4acl-internal.h deleted file mode 100644 index 3838d00..0000000 --- a/include/nfs4acl-internal.h +++ /dev/null @@ -1,40 +0,0 @@ -#ifndef __NFS4ACL_INTERNAL_H -#define __NFS4ACL_INTERNAL_H - -/* - * The ACE4_READ_ATTRIBUTES and ACE4_READ_ACL flags are always granted - * in POSIX. The ACE4_SYNCHRONIZE flag has no meaning under POSIX. - */ -#define ACE4_POSIX_ALWAYS_ALLOWED ( \ - ACE4_SYNCHRONIZE | \ - ACE4_READ_ATTRIBUTES | \ - ACE4_READ_ACL ) - -/* e_flags bitflags */ -#define ACE4_SPECIAL_WHO 0x4000 /* internal to the library */ - -static inline void -nfs4ace_clear_inheritance_flags(struct nfs4ace *ace) -{ - ace->e_flags &= ~(ACE4_FILE_INHERIT_ACE | - ACE4_DIRECTORY_INHERIT_ACE | - ACE4_NO_PROPAGATE_INHERIT_ACE | - ACE4_INHERIT_ONLY_ACE); -} - -static inline int nfs4ace_is_inheritable(const struct nfs4ace *ace) -{ - return ace->e_flags & (ACE4_FILE_INHERIT_ACE | - ACE4_DIRECTORY_INHERIT_ACE); -} - -static inline int nfs4ace_is_inherit_only(const struct nfs4ace *ace) -{ - return ace->e_flags & ACE4_INHERIT_ONLY_ACE; -} - -extern const char *nfs4ace_owner_who; -extern const char *nfs4ace_group_who; -extern const char *nfs4ace_everyone_who; - -#endif /* __NFS4ACL_INTERNAL_H */ diff --git a/include/nfs4acl.h b/include/nfs4acl.h deleted file mode 100644 index ab8fd9e..0000000 --- a/include/nfs4acl.h +++ /dev/null @@ -1,155 +0,0 @@ -#ifndef __NFS4ACL_H -#define __NFS4ACL_H - -#include -#include - -/* a_flags values */ -#define ACL4_AUTO_INHERIT 0x01 -#define ACL4_PROTECTED 0x02 -#define ACL4_DEFAULTED 0x04 -#define ACL4_WRITE_THROUGH 0x40 - -#define ACL4_VALID_FLAGS ( \ - ACL4_AUTO_INHERIT | \ - ACL4_PROTECTED | \ - ACL4_DEFAULTED | \ - ACL4_WRITE_THROUGH ) - -/* e_type values */ -#define ACE4_ACCESS_ALLOWED_ACE_TYPE 0x0000 -#define ACE4_ACCESS_DENIED_ACE_TYPE 0x0001 - -/* e_flags bitflags */ -#define ACE4_FILE_INHERIT_ACE 0x0001 -#define ACE4_DIRECTORY_INHERIT_ACE 0x0002 -#define ACE4_NO_PROPAGATE_INHERIT_ACE 0x0004 -#define ACE4_INHERIT_ONLY_ACE 0x0008 -#define ACE4_IDENTIFIER_GROUP 0x0040 -#define ACE4_INHERITED_ACE 0x0080 - -#define ACE4_VALID_FLAGS ( \ - ACE4_FILE_INHERIT_ACE | \ - ACE4_DIRECTORY_INHERIT_ACE | \ - ACE4_NO_PROPAGATE_INHERIT_ACE | \ - ACE4_INHERIT_ONLY_ACE | \ - ACE4_IDENTIFIER_GROUP | \ - ACE4_INHERITED_ACE ) - -/* e_mask bitflags */ -#define ACE4_READ_DATA 0x00000001 -#define ACE4_LIST_DIRECTORY 0x00000001 -#define ACE4_WRITE_DATA 0x00000002 -#define ACE4_ADD_FILE 0x00000002 -#define ACE4_APPEND_DATA 0x00000004 -#define ACE4_ADD_SUBDIRECTORY 0x00000004 -#define ACE4_READ_NAMED_ATTRS 0x00000008 -#define ACE4_WRITE_NAMED_ATTRS 0x00000010 -#define ACE4_EXECUTE 0x00000020 -#define ACE4_DELETE_CHILD 0x00000040 -#define ACE4_READ_ATTRIBUTES 0x00000080 -#define ACE4_WRITE_ATTRIBUTES 0x00000100 -#define ACE4_DELETE 0x00010000 -#define ACE4_READ_ACL 0x00020000 -#define ACE4_WRITE_ACL 0x00040000 -#define ACE4_WRITE_OWNER 0x00080000 -#define ACE4_SYNCHRONIZE 0x00100000 - -#define ACE4_VALID_MASK ( \ - ACE4_READ_DATA | ACE4_LIST_DIRECTORY | \ - ACE4_WRITE_DATA | ACE4_ADD_FILE | \ - ACE4_APPEND_DATA | ACE4_ADD_SUBDIRECTORY | \ - ACE4_READ_NAMED_ATTRS | \ - ACE4_WRITE_NAMED_ATTRS | \ - ACE4_EXECUTE | \ - ACE4_DELETE_CHILD | \ - ACE4_READ_ATTRIBUTES | \ - ACE4_WRITE_ATTRIBUTES | \ - ACE4_DELETE | \ - ACE4_READ_ACL | \ - ACE4_WRITE_ACL | \ - ACE4_WRITE_OWNER | \ - ACE4_SYNCHRONIZE ) - -struct nfs4ace { - unsigned short e_type; - unsigned short e_flags; - unsigned int e_mask; - union { - id_t e_id; - const char *e_who; - } u; -}; - -struct nfs4acl { - unsigned char a_flags; - unsigned short a_count; - unsigned int a_owner_mask; - unsigned int a_group_mask; - unsigned int a_other_mask; - struct nfs4ace a_entries[0]; -}; - -#define nfs4acl_for_each_entry(_ace, _acl) \ - for ((_ace) = (_acl)->a_entries; \ - (_ace) != (_acl)->a_entries + (_acl)->a_count; \ - (_ace)++) - -#define nfs4acl_for_each_entry_reverse(_ace, _acl) \ - for ((_ace) = (_acl)->a_entries + (_acl)->a_count - 1; \ - (_ace) != (_acl)->a_entries - 1; \ - (_ace)--) - -/* nfs4acl_to_text flags */ -#define NFS4ACL_TEXT_LONG 1 -#define NFS4ACL_TEXT_FILE_CONTEXT 2 -#define NFS4ACL_TEXT_DIRECTORY_CONTEXT 4 -#define NFS4ACL_TEXT_SHOW_MASKS 8 -#define NFS4ACL_TEXT_SIMPLIFY 16 - -/* nfs4acl_from_text flags */ -#define NFS4ACL_TEXT_OWNER_MASK 1 -#define NFS4ACL_TEXT_GROUP_MASK 2 -#define NFS4ACL_TEXT_OTHER_MASK 4 -#define NFS4ACL_TEXT_FLAGS 8 - -extern int nfs4ace_is_owner(const struct nfs4ace *); -extern int nfs4ace_is_group(const struct nfs4ace *); -extern int nfs4ace_is_everyone(const struct nfs4ace *); - -static inline int nfs4ace_is_allow(const struct nfs4ace *ace) -{ - return ace->e_type == ACE4_ACCESS_ALLOWED_ACE_TYPE; -} - -static inline int nfs4ace_is_deny(const struct nfs4ace *ace) -{ - return ace->e_type == ACE4_ACCESS_DENIED_ACE_TYPE; -} - -extern const char *nfs4ace_get_who(const struct nfs4ace *); - -extern int nfs4ace_set_who(struct nfs4ace *, const char *); -extern void nfs4ace_set_uid(struct nfs4ace *, uid_t); -extern void nfs4ace_set_gid(struct nfs4ace *, gid_t); -extern int nfs4ace_is_same_identifier(const struct nfs4ace *, const struct nfs4ace *); -extern void nfs4ace_copy(struct nfs4ace *, const struct nfs4ace *); - -extern struct nfs4acl *nfs4acl_get_file(const char *); -extern struct nfs4acl *nfs4acl_get_fd(int); -extern int nfs4acl_set_file(const char *, const struct nfs4acl *); -extern int nfs4acl_set_fd(int, const struct nfs4acl *); - -extern char *nfs4acl_to_text(const struct nfs4acl *, int); -extern struct nfs4acl *nfs4acl_from_text(const char *, int *, - void (*)(const char *, ...)); - -extern struct nfs4acl *nfs4acl_alloc(size_t); -extern struct nfs4acl *nfs4acl_clone(struct nfs4acl *); -extern void nfs4acl_free(struct nfs4acl *); - -extern int nfs4acl_apply_masks(struct nfs4acl **); -extern void nfs4acl_compute_max_masks(struct nfs4acl *); -extern struct nfs4acl *nfs4acl_from_mode(mode_t); - -#endif /* __NFS4ACL_H */ diff --git a/include/nfs4acl_xattr.h b/include/nfs4acl_xattr.h deleted file mode 100644 index ff89ff7..0000000 --- a/include/nfs4acl_xattr.h +++ /dev/null @@ -1,28 +0,0 @@ -#ifndef __NFS4ACL_XATTR_H -#define __NFS4ACL_XATTR_H - -#include -#include - -struct nfs4ace_xattr { - uint16_t e_type; - uint16_t e_flags; - uint32_t e_mask; - uint32_t e_id; - char e_who[0]; -}; - -struct nfs4acl_xattr { - unsigned char a_version; - unsigned char a_flags; - uint16_t a_count; - uint32_t a_owner_mask; - uint32_t a_group_mask; - uint32_t a_other_mask; -}; - -#define SYSTEM_NFS4ACL "system.nfs4acl" -#define ACL4_XATTR_VERSION 0 -#define ACL4_XATTR_MAX_COUNT 1024 - -#endif /* __NFS4ACL_XATTR_H */ diff --git a/include/richacl-internal.h b/include/richacl-internal.h new file mode 100644 index 0000000..0727724 --- /dev/null +++ b/include/richacl-internal.h @@ -0,0 +1,40 @@ +#ifndef __RICHACL_INTERNAL_H +#define __RICHACL_INTERNAL_H + +/* + * The ACE4_READ_ATTRIBUTES and ACE4_READ_ACL flags are always granted + * in POSIX. The ACE4_SYNCHRONIZE flag has no meaning under POSIX. + */ +#define ACE4_POSIX_ALWAYS_ALLOWED ( \ + ACE4_SYNCHRONIZE | \ + ACE4_READ_ATTRIBUTES | \ + ACE4_READ_ACL ) + +/* e_flags bitflags */ +#define ACE4_SPECIAL_WHO 0x4000 /* internal to the library */ + +static inline void +richace_clear_inheritance_flags(struct richace *ace) +{ + ace->e_flags &= ~(ACE4_FILE_INHERIT_ACE | + ACE4_DIRECTORY_INHERIT_ACE | + ACE4_NO_PROPAGATE_INHERIT_ACE | + ACE4_INHERIT_ONLY_ACE); +} + +static inline int richace_is_inheritable(const struct richace *ace) +{ + return ace->e_flags & (ACE4_FILE_INHERIT_ACE | + ACE4_DIRECTORY_INHERIT_ACE); +} + +static inline int richace_is_inherit_only(const struct richace *ace) +{ + return ace->e_flags & ACE4_INHERIT_ONLY_ACE; +} + +extern const char *richace_owner_who; +extern const char *richace_group_who; +extern const char *richace_everyone_who; + +#endif /* __RICHACL_INTERNAL_H */ diff --git a/include/richacl.h b/include/richacl.h new file mode 100644 index 0000000..bb708fa --- /dev/null +++ b/include/richacl.h @@ -0,0 +1,155 @@ +#ifndef __RICHACL_H +#define __RICHACL_H + +#include +#include + +/* a_flags values */ +#define ACL4_AUTO_INHERIT 0x01 +#define ACL4_PROTECTED 0x02 +#define ACL4_DEFAULTED 0x04 +#define ACL4_WRITE_THROUGH 0x40 + +#define ACL4_VALID_FLAGS ( \ + ACL4_AUTO_INHERIT | \ + ACL4_PROTECTED | \ + ACL4_DEFAULTED | \ + ACL4_WRITE_THROUGH ) + +/* e_type values */ +#define ACE4_ACCESS_ALLOWED_ACE_TYPE 0x0000 +#define ACE4_ACCESS_DENIED_ACE_TYPE 0x0001 + +/* e_flags bitflags */ +#define ACE4_FILE_INHERIT_ACE 0x0001 +#define ACE4_DIRECTORY_INHERIT_ACE 0x0002 +#define ACE4_NO_PROPAGATE_INHERIT_ACE 0x0004 +#define ACE4_INHERIT_ONLY_ACE 0x0008 +#define ACE4_IDENTIFIER_GROUP 0x0040 +#define ACE4_INHERITED_ACE 0x0080 + +#define ACE4_VALID_FLAGS ( \ + ACE4_FILE_INHERIT_ACE | \ + ACE4_DIRECTORY_INHERIT_ACE | \ + ACE4_NO_PROPAGATE_INHERIT_ACE | \ + ACE4_INHERIT_ONLY_ACE | \ + ACE4_IDENTIFIER_GROUP | \ + ACE4_INHERITED_ACE ) + +/* e_mask bitflags */ +#define ACE4_READ_DATA 0x00000001 +#define ACE4_LIST_DIRECTORY 0x00000001 +#define ACE4_WRITE_DATA 0x00000002 +#define ACE4_ADD_FILE 0x00000002 +#define ACE4_APPEND_DATA 0x00000004 +#define ACE4_ADD_SUBDIRECTORY 0x00000004 +#define ACE4_READ_NAMED_ATTRS 0x00000008 +#define ACE4_WRITE_NAMED_ATTRS 0x00000010 +#define ACE4_EXECUTE 0x00000020 +#define ACE4_DELETE_CHILD 0x00000040 +#define ACE4_READ_ATTRIBUTES 0x00000080 +#define ACE4_WRITE_ATTRIBUTES 0x00000100 +#define ACE4_DELETE 0x00010000 +#define ACE4_READ_ACL 0x00020000 +#define ACE4_WRITE_ACL 0x00040000 +#define ACE4_WRITE_OWNER 0x00080000 +#define ACE4_SYNCHRONIZE 0x00100000 + +#define ACE4_VALID_MASK ( \ + ACE4_READ_DATA | ACE4_LIST_DIRECTORY | \ + ACE4_WRITE_DATA | ACE4_ADD_FILE | \ + ACE4_APPEND_DATA | ACE4_ADD_SUBDIRECTORY | \ + ACE4_READ_NAMED_ATTRS | \ + ACE4_WRITE_NAMED_ATTRS | \ + ACE4_EXECUTE | \ + ACE4_DELETE_CHILD | \ + ACE4_READ_ATTRIBUTES | \ + ACE4_WRITE_ATTRIBUTES | \ + ACE4_DELETE | \ + ACE4_READ_ACL | \ + ACE4_WRITE_ACL | \ + ACE4_WRITE_OWNER | \ + ACE4_SYNCHRONIZE ) + +struct richace { + unsigned short e_type; + unsigned short e_flags; + unsigned int e_mask; + union { + id_t e_id; + const char *e_who; + } u; +}; + +struct richacl { + unsigned char a_flags; + unsigned short a_count; + unsigned int a_owner_mask; + unsigned int a_group_mask; + unsigned int a_other_mask; + struct richace a_entries[0]; +}; + +#define richacl_for_each_entry(_ace, _acl) \ + for ((_ace) = (_acl)->a_entries; \ + (_ace) != (_acl)->a_entries + (_acl)->a_count; \ + (_ace)++) + +#define richacl_for_each_entry_reverse(_ace, _acl) \ + for ((_ace) = (_acl)->a_entries + (_acl)->a_count - 1; \ + (_ace) != (_acl)->a_entries - 1; \ + (_ace)--) + +/* richacl_to_text flags */ +#define RICHACL_TEXT_LONG 1 +#define RICHACL_TEXT_FILE_CONTEXT 2 +#define RICHACL_TEXT_DIRECTORY_CONTEXT 4 +#define RICHACL_TEXT_SHOW_MASKS 8 +#define RICHACL_TEXT_SIMPLIFY 16 + +/* richacl_from_text flags */ +#define RICHACL_TEXT_OWNER_MASK 1 +#define RICHACL_TEXT_GROUP_MASK 2 +#define RICHACL_TEXT_OTHER_MASK 4 +#define RICHACL_TEXT_FLAGS 8 + +extern int richace_is_owner(const struct richace *); +extern int richace_is_group(const struct richace *); +extern int richace_is_everyone(const struct richace *); + +static inline int richace_is_allow(const struct richace *ace) +{ + return ace->e_type == ACE4_ACCESS_ALLOWED_ACE_TYPE; +} + +static inline int richace_is_deny(const struct richace *ace) +{ + return ace->e_type == ACE4_ACCESS_DENIED_ACE_TYPE; +} + +extern const char *richace_get_who(const struct richace *); + +extern int richace_set_who(struct richace *, const char *); +extern void richace_set_uid(struct richace *, uid_t); +extern void richace_set_gid(struct richace *, gid_t); +extern int richace_is_same_identifier(const struct richace *, const struct richace *); +extern void richace_copy(struct richace *, const struct richace *); + +extern struct richacl *richacl_get_file(const char *); +extern struct richacl *richacl_get_fd(int); +extern int richacl_set_file(const char *, const struct richacl *); +extern int richacl_set_fd(int, const struct richacl *); + +extern char *richacl_to_text(const struct richacl *, int); +extern struct richacl *richacl_from_text(const char *, int *, + void (*)(const char *, ...)); + +extern struct richacl *richacl_alloc(size_t); +extern struct richacl *richacl_clone(struct richacl *); +extern void richacl_free(struct richacl *); + +extern int richacl_apply_masks(struct richacl **); +extern void richacl_compute_max_masks(struct richacl *); +extern struct richacl *richacl_from_mode(mode_t); + +#endif /* __RICHACL_H */ diff --git a/include/richacl_xattr.h b/include/richacl_xattr.h new file mode 100644 index 0000000..517dd3f --- /dev/null +++ b/include/richacl_xattr.h @@ -0,0 +1,28 @@ +#ifndef __RICHACL_XATTR_H +#define __RICHACL_XATTR_H + +#include +#include + +struct richace_xattr { + uint16_t e_type; + uint16_t e_flags; + uint32_t e_mask; + uint32_t e_id; + char e_who[0]; +}; + +struct richacl_xattr { + unsigned char a_version; + unsigned char a_flags; + uint16_t a_count; + uint32_t a_owner_mask; + uint32_t a_group_mask; + uint32_t a_other_mask; +}; + +#define SYSTEM_RICHACL "system.richacl" +#define ACL4_XATTR_VERSION 0 +#define ACL4_XATTR_MAX_COUNT 1024 + +#endif /* __RICHACL_XATTR_H */ diff --git a/lib/nfs4acl.c b/lib/nfs4acl.c deleted file mode 100644 index 8b75a40..0000000 --- a/lib/nfs4acl.c +++ /dev/null @@ -1,1127 +0,0 @@ -#define _GNU_SOURCE -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include "string_buffer.h" -#include "nfs4acl.h" -#include "nfs4acl_xattr.h" -#include "nfs4acl-internal.h" - -#define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0])) -#define ALIGN(x,a) (((x)+(a)-1)&~((a)-1)) - -const char *nfs4ace_owner_who = "OWNER@"; -const char *nfs4ace_group_who = "GROUP@"; -const char *nfs4ace_everyone_who = "EVERYONE@"; - -/* - * The POSIX permissions are supersets of the following mask flags. - */ -#define ACE4_POSIX_MODE_READ ( \ - ACE4_READ_DATA | ACE4_LIST_DIRECTORY ) -#define ACE4_POSIX_MODE_WRITE ( \ - ACE4_WRITE_DATA | ACE4_ADD_FILE | \ - ACE4_APPEND_DATA | ACE4_ADD_SUBDIRECTORY | \ - ACE4_DELETE_CHILD ) -#define ACE4_POSIX_MODE_EXEC ( \ - ACE4_EXECUTE) - -static struct { - char a_char; - unsigned char a_flag; - const char *a_name; -} acl_flag_bits[] = { - { 'a', ACL4_AUTO_INHERIT, "auto_inherit" }, - { 'p', ACL4_PROTECTED, "protected" }, - { 'd', ACL4_DEFAULTED, "defaulted" }, - { 'w', ACL4_WRITE_THROUGH, "write_through" }, -}; - -static struct { - uint16_t e_type; - const char *e_name; -} type_values[] = { - { ACE4_ACCESS_ALLOWED_ACE_TYPE, "allow" }, - { ACE4_ACCESS_DENIED_ACE_TYPE, "deny" }, -}; - -#define FLAGS_BIT(c, name, str) \ - { ACE4_ ## name, c, #name } - -static struct { - uint16_t e_flag; - char e_char; - const char *e_name; -} ace_flag_bits[] = { - FLAGS_BIT('f', FILE_INHERIT_ACE, "file_inherit_ace"), - FLAGS_BIT('d', DIRECTORY_INHERIT_ACE, "directory_inherit_ace"), - FLAGS_BIT('n', NO_PROPAGATE_INHERIT_ACE, "no_propagate_inherit_ace"), - FLAGS_BIT('i', INHERIT_ONLY_ACE, "inherit_only_ace"), - FLAGS_BIT('g', IDENTIFIER_GROUP, "identifier_group"), - FLAGS_BIT('a', INHERITED_ACE, "inherited_ace"), -}; - -#undef FLAGS_BIT - -#define MASK_BIT(c, name, str) \ - { ACE4_ ## name, c, str, NFS4ACL_TEXT_FILE_CONTEXT | \ - NFS4ACL_TEXT_DIRECTORY_CONTEXT } -#define FILE_MASK_BIT(c, name, str) \ - { ACE4_ ## name, c, str, NFS4ACL_TEXT_FILE_CONTEXT } -#define DIRECTORY_MASK_BIT(c, name, str) \ - { ACE4_ ## name, c, str, NFS4ACL_TEXT_DIRECTORY_CONTEXT } - -struct { - uint32_t e_mask; - char e_char; - const char *e_name; - int e_context; -} mask_bits[] = { - MASK_BIT('*', VALID_MASK, "*"), - FILE_MASK_BIT('r', READ_DATA, "read_data"), - DIRECTORY_MASK_BIT('r', LIST_DIRECTORY, "list_directory"), - FILE_MASK_BIT('w', WRITE_DATA, "write_data"), - DIRECTORY_MASK_BIT('w', ADD_FILE, "add_file"), - FILE_MASK_BIT('a', APPEND_DATA, "append_data"), - DIRECTORY_MASK_BIT('a', ADD_SUBDIRECTORY, "add_subdirectory"), - MASK_BIT('N', READ_NAMED_ATTRS, "read_named_attrs"), - MASK_BIT('n', WRITE_NAMED_ATTRS, "write_named_attrs"), - MASK_BIT('x', EXECUTE, "execute"), - MASK_BIT('d', DELETE_CHILD, "delete_child"), - MASK_BIT('T', READ_ATTRIBUTES, "read_attributes"), - MASK_BIT('t', WRITE_ATTRIBUTES, "write_attributes"), - MASK_BIT('D', DELETE, "delete"), - MASK_BIT('M', READ_ACL, "read_acl"), - MASK_BIT('m', WRITE_ACL, "write_acl"), - MASK_BIT('o', WRITE_OWNER, "take_ownership"), - MASK_BIT('s', SYNCHRONIZE, "synchronize"), -}; - -#undef MASK_BIT -#undef FILE_MASK_BIT -#undef DIRECTORY_MASK_BIT - -const char *nfs4ace_get_who(const struct nfs4ace *ace) -{ - if (!(ace->e_flags & ACE4_SPECIAL_WHO)) - return NULL; - return ace->u.e_who; -} - -int nfs4ace_is_same_identifier(const struct nfs4ace *a, const struct nfs4ace *b) -{ -#define WHO_FLAGS (ACE4_SPECIAL_WHO | ACE4_IDENTIFIER_GROUP) - if ((a->e_flags & WHO_FLAGS) != (b->e_flags & WHO_FLAGS)) - return 0; - if (a->e_flags & ACE4_SPECIAL_WHO) - return a->u.e_who == b->u.e_who; - else - return a->u.e_id == b->u.e_id; -#undef WHO_FLAGS -} - -int nfs4ace_is_owner(const struct nfs4ace *ace) -{ - return (ace->e_flags & ACE4_SPECIAL_WHO) && - ace->u.e_who == nfs4ace_owner_who; -} - -int nfs4ace_is_group(const struct nfs4ace *ace) -{ - return (ace->e_flags & ACE4_SPECIAL_WHO) && - ace->u.e_who == nfs4ace_group_who; -} - -int nfs4ace_is_everyone(const struct nfs4ace *ace) -{ - return (ace->e_flags & ACE4_SPECIAL_WHO) && - ace->u.e_who == nfs4ace_everyone_who; -} - -struct nfs4acl *nfs4acl_alloc(size_t count) -{ - size_t size = sizeof(struct nfs4acl) + count * sizeof(struct nfs4ace); - struct nfs4acl *acl = malloc(size); - - if (acl) { - memset(acl, 0, size); - acl->a_count = count; - } - return acl; -} - -struct nfs4acl *nfs4acl_clone(struct nfs4acl *acl) -{ - size_t size; - struct nfs4acl *acl2; - - if (!acl) - return NULL; - size = sizeof(struct nfs4acl) + acl->a_count * sizeof(struct nfs4ace); - acl2 = malloc(size); - if (acl2) - memcpy(acl2, acl, size); - return acl2; -} - -void nfs4acl_free(struct nfs4acl *acl) -{ - free(acl); -} - -/** - * nfs4acl_allowed_to_who - mask flags allowed to a specific who value - * - * Computes the mask values allowed to a specific who value, taking - * EVERYONE@ entries into account. - */ -static unsigned int nfs4acl_allowed_to_who(struct nfs4acl *acl, - struct nfs4ace *who) -{ - struct nfs4ace *ace; - unsigned int allowed = 0; - - nfs4acl_for_each_entry_reverse(ace, acl) { - if (nfs4ace_is_inherit_only(ace)) - continue; - if (nfs4ace_is_same_identifier(ace, who) || - nfs4ace_is_everyone(ace)) { - if (nfs4ace_is_allow(ace)) - allowed |= ace->e_mask; - else if (nfs4ace_is_deny(ace)) - allowed &= ~ace->e_mask; - } - } - return allowed; -} - -/** - * nfs4acl_compute_max_masks - compute upper bound masks - * - * Computes upper bound owner, group, and other masks so that none of - * the mask flags allowed by the acl are disabled (for any choice of the - * file owner or group membership). - */ -void nfs4acl_compute_max_masks(struct nfs4acl *acl) -{ - struct nfs4ace *ace; - - acl->a_owner_mask = 0; - acl->a_group_mask = 0; - acl->a_other_mask = 0; - - nfs4acl_for_each_entry_reverse(ace, acl) { - if (nfs4ace_is_inherit_only(ace)) - continue; - - if (nfs4ace_is_owner(ace)) { - if (nfs4ace_is_allow(ace)) - acl->a_owner_mask |= ace->e_mask; - else if (nfs4ace_is_deny(ace)) - acl->a_owner_mask &= ~ace->e_mask; - } else if (nfs4ace_is_everyone(ace)) { - if (nfs4ace_is_allow(ace)) { - struct nfs4ace who = { - .e_flags = ACE4_SPECIAL_WHO, - .u.e_who = nfs4ace_group_who, - }; - - acl->a_other_mask |= ace->e_mask; - acl->a_group_mask |= - nfs4acl_allowed_to_who(acl, &who); - acl->a_owner_mask |= ace->e_mask; - } else if (nfs4ace_is_deny(ace)) { - acl->a_other_mask &= ~ace->e_mask; - acl->a_group_mask &= ~ace->e_mask; - acl->a_owner_mask &= ~ace->e_mask; - } - } else { - if (nfs4ace_is_allow(ace)) { - unsigned int mask = - nfs4acl_allowed_to_who(acl, ace); - - acl->a_group_mask |= mask; - acl->a_owner_mask |= mask; - } - } - } -} - -static struct nfs4acl *nfs4acl_from_xattr(const void *value, size_t size) -{ - const struct nfs4acl_xattr *xattr_acl = value; - const struct nfs4ace_xattr *xattr_ace = (void *)(xattr_acl + 1); - struct nfs4acl *acl = NULL; - struct nfs4ace *ace; - int count; - - if (size < sizeof(struct nfs4acl_xattr) || - xattr_acl->a_version != ACL4_XATTR_VERSION) - goto fail_einval; - - count = ntohs(xattr_acl->a_count); - if (count > ACL4_XATTR_MAX_COUNT) - goto fail_einval; - - acl = nfs4acl_alloc(count); - if (!acl) - return NULL; - - acl->a_flags = xattr_acl->a_flags; - acl->a_owner_mask = ntohl(xattr_acl->a_owner_mask); - acl->a_group_mask = ntohl(xattr_acl->a_group_mask); - acl->a_other_mask = ntohl(xattr_acl->a_other_mask); - - nfs4acl_for_each_entry(ace, acl) { - const char *who = (void *)(xattr_ace + 1), *end; - ssize_t used = (void *)who - value; - - if (used > size) - goto fail_einval; - end = memchr(who, 0, size - used); - if (!end) - goto fail_einval; - - ace->e_type = ntohs(xattr_ace->e_type); - ace->e_flags = ntohs(xattr_ace->e_flags); - ace->e_mask = ntohl(xattr_ace->e_mask); - ace->u.e_id = ntohl(xattr_ace->e_id); - - if (who == end) { - if (ace->u.e_id == -1) - goto fail_einval; /* uid/gid needed */ - } else if (nfs4ace_set_who(ace, who)) - goto fail_einval; - - xattr_ace = (void *)who + ALIGN(end - who + 1, 4); - } - - return acl; - -fail_einval: - nfs4acl_free(acl); - errno = EINVAL; - return NULL; -} - -static size_t nfs4acl_xattr_size(const struct nfs4acl *acl) -{ - size_t size = sizeof(struct nfs4acl_xattr); - const struct nfs4ace *ace; - - nfs4acl_for_each_entry(ace, acl) { - size += sizeof(struct nfs4ace_xattr) + - (nfs4ace_get_who(ace) ? - ALIGN(strlen(ace->u.e_who) + 1, 4) : 4); - } - return size; -} - -static void nfs4acl_to_xattr(const struct nfs4acl *acl, void *buffer) -{ - struct nfs4acl_xattr *xattr_acl = buffer; - struct nfs4ace_xattr *xattr_ace; - const struct nfs4ace *ace; - - xattr_acl->a_version = ACL4_XATTR_VERSION; - xattr_acl->a_flags = acl->a_flags; - xattr_acl->a_count = htons(acl->a_count); - - xattr_acl->a_owner_mask = htonl(acl->a_owner_mask); - xattr_acl->a_group_mask = htonl(acl->a_group_mask); - xattr_acl->a_other_mask = htonl(acl->a_other_mask); - - xattr_ace = (void *)(xattr_acl + 1); - nfs4acl_for_each_entry(ace, acl) { - xattr_ace->e_type = htons(ace->e_type); - xattr_ace->e_flags = htons(ace->e_flags & ACE4_VALID_FLAGS); - xattr_ace->e_mask = htonl(ace->e_mask); - if (nfs4ace_get_who(ace)) { - int sz = ALIGN(strlen(ace->u.e_who) + 1, 4); - - xattr_ace->e_id = htonl(-1); - memset(xattr_ace->e_who + sz - 4, 0, 4); - strcpy(xattr_ace->e_who, ace->u.e_who); - xattr_ace = (void *)xattr_ace->e_who + sz; - } else { - xattr_ace->e_id = htonl(ace->u.e_id); - memset(xattr_ace->e_who, 0, 4); - xattr_ace = (void *)xattr_ace->e_who + 4; - } - } -} - -struct nfs4acl *nfs4acl_get_file(const char *path) -{ - void *value; - ssize_t retval; - struct nfs4acl *acl; - - retval = getxattr(path, SYSTEM_NFS4ACL, NULL, 0); - if (retval <= 0) - return NULL; - - value = alloca(retval); - if (!value) - return NULL; - retval = getxattr(path, SYSTEM_NFS4ACL, value, retval); - acl = nfs4acl_from_xattr(value, retval); - - return acl; -} - -struct nfs4acl *nfs4acl_get_fd(int fd) -{ - void *value; - ssize_t retval; - struct nfs4acl *acl; - - retval = fgetxattr(fd, SYSTEM_NFS4ACL, NULL, 0); - if (retval <= 0) - return NULL; - - value = alloca(retval); - if (!value) - return NULL; - retval = fgetxattr(fd, SYSTEM_NFS4ACL, value, retval); - acl = nfs4acl_from_xattr(value, retval); - - return acl; -} - -int nfs4acl_set_file(const char *path, const struct nfs4acl *acl) -{ - size_t size = nfs4acl_xattr_size(acl); - void *value = alloca(size); - - nfs4acl_to_xattr(acl, value); - return setxattr(path, SYSTEM_NFS4ACL, value, size, 0); -} - -int nfs4acl_set_fd(int fd, const struct nfs4acl *acl) -{ - size_t size = nfs4acl_xattr_size(acl); - void *value = alloca(size); - - nfs4acl_to_xattr(acl, value); - return fsetxattr(fd, SYSTEM_NFS4ACL, value, size, 0); -} - -static void write_acl_flags(struct string_buffer *buffer, unsigned char flags, int fmt) -{ - int cont = 0, i; - - if (!flags) - return; - buffer_sprintf(buffer, "flags:"); - for (i = 0; i < ARRAY_SIZE(acl_flag_bits); i++) { - if (!(flags & acl_flag_bits[i].a_flag)) - continue; - - flags &= ~acl_flag_bits[i].a_flag; - if (fmt & NFS4ACL_TEXT_LONG) { - if (cont) - buffer_sprintf(buffer, "/"); - buffer_sprintf(buffer, "%s", acl_flag_bits[i].a_name); - } else - buffer_sprintf(buffer, "%c", acl_flag_bits[i].a_char); - cont = 1; - } - if (flags) { - if (cont) - buffer_sprintf(buffer, "/"); - buffer_sprintf(buffer, "0x%x", flags); - } - buffer_sprintf(buffer, "\n"); -} - -static void write_type(struct string_buffer *buffer, uint16_t type) -{ - int i; - - for (i = 0; i < ARRAY_SIZE(type_values); i++) { - if (type == type_values[i].e_type) { - buffer_sprintf(buffer, "%s", type_values[i].e_name); - break; - } - } - if (i == ARRAY_SIZE(type_values)) - buffer_sprintf(buffer, "%u", type); -} - -static void write_ace_flags(struct string_buffer *buffer, uint16_t flags, int fmt) -{ - int cont = 0, i; - - flags &= ~ACE4_SPECIAL_WHO; - - for (i = 0; i < ARRAY_SIZE(ace_flag_bits); i++) { - if (!(flags & ace_flag_bits[i].e_flag)) - continue; - - flags &= ~ace_flag_bits[i].e_flag; - if (fmt & NFS4ACL_TEXT_LONG) { - if (cont) - buffer_sprintf(buffer, "/"); - buffer_sprintf(buffer, "%s", ace_flag_bits[i].e_name); - } else - buffer_sprintf(buffer, "%c", ace_flag_bits[i].e_char); - cont = 1; - } - if (flags) { - if (cont) - buffer_sprintf(buffer, "/"); - buffer_sprintf(buffer, "0x%x", flags); - } -} - -static void write_mask(struct string_buffer *buffer, uint32_t mask, int fmt) -{ - int stuff_written = 0, i; - unsigned int nondir_mask, dir_mask; - - /* - * In long format, we write the non-directory and/or directory mask - * name depending on the context which applies. The short format - * does not distinguish between the two, so make sure that we won't - * repeat the same mask letters. - */ - if (!(fmt & (NFS4ACL_TEXT_FILE_CONTEXT | - NFS4ACL_TEXT_DIRECTORY_CONTEXT))) - fmt |= NFS4ACL_TEXT_FILE_CONTEXT | - NFS4ACL_TEXT_DIRECTORY_CONTEXT; - if (!(fmt & NFS4ACL_TEXT_LONG) && - (fmt & NFS4ACL_TEXT_FILE_CONTEXT)) - fmt &= ~NFS4ACL_TEXT_DIRECTORY_CONTEXT; - - nondir_mask = (fmt & NFS4ACL_TEXT_FILE_CONTEXT) ? mask : 0; - dir_mask = (fmt & NFS4ACL_TEXT_DIRECTORY_CONTEXT) ? mask : 0; - - for (i = 0; i < ARRAY_SIZE(mask_bits); i++) { - int found = 0; - - if ((nondir_mask & mask_bits[i].e_mask) == - mask_bits[i].e_mask && - (mask_bits[i].e_context & NFS4ACL_TEXT_FILE_CONTEXT)) { - nondir_mask &= ~mask_bits[i].e_mask; - found = 1; - } - if ((dir_mask & mask_bits[i].e_mask) == mask_bits[i].e_mask && - (mask_bits[i].e_context & NFS4ACL_TEXT_DIRECTORY_CONTEXT)) { - dir_mask &= ~mask_bits[i].e_mask; - found = 1; - } - if (found) { - if (fmt & NFS4ACL_TEXT_SIMPLIFY) { - /* Hide permissions that are always allowed. */ - if (mask_bits[i].e_mask == - (mask_bits[i].e_mask & - ACE4_POSIX_ALWAYS_ALLOWED)) - continue; - } - if (fmt & NFS4ACL_TEXT_LONG) { - if (stuff_written) - buffer_sprintf(buffer, "/"); - buffer_sprintf(buffer, "%s", - mask_bits[i].e_name); - } else - buffer_sprintf(buffer, "%c", - mask_bits[i].e_char); - stuff_written = 1; - } - } - if (nondir_mask | dir_mask) { - if (stuff_written) - buffer_sprintf(buffer, "/"); - buffer_sprintf(buffer, "0x%x", nondir_mask | dir_mask); - } -} - -static void write_identifier(struct string_buffer *buffer, - const struct nfs4ace *ace) -{ - /* FIXME: switch to getpwuid_r() and getgrgid_r() here. */ - - if (ace->e_flags & ACE4_SPECIAL_WHO) { - char *dup, *c; - - dup = alloca(strlen(ace->u.e_who) + 1); - strcpy(dup, ace->u.e_who); - for (c = dup; *c; c++) - *c = tolower(*c); - - buffer_sprintf(buffer, "%s", dup); - } else if (ace->e_flags & ACE4_IDENTIFIER_GROUP) { - struct group *group = getgrgid(ace->u.e_id); - - if (group) - buffer_sprintf(buffer, "%s", group->gr_name); - else - buffer_sprintf(buffer, "%d", ace->u.e_id); - } else { - struct passwd *passwd = getpwuid(ace->u.e_id); - - if (passwd) - buffer_sprintf(buffer, "%s", passwd->pw_name); - else - buffer_sprintf(buffer, "%d", ace->u.e_id); - } -} - -char *nfs4acl_to_text(const struct nfs4acl *acl, int fmt) -{ - struct string_buffer *buffer; - const struct nfs4ace *ace; - int fmt2; - - buffer = alloc_string_buffer(128); - if (!buffer) - return NULL; - - write_acl_flags(buffer, acl->a_flags, fmt); - if (fmt & NFS4ACL_TEXT_SHOW_MASKS) { - unsigned int allowed = 0; - - fmt2 = fmt; - nfs4acl_for_each_entry(ace, acl) { - if (nfs4ace_is_inherit_only(ace)) - continue; - - if (nfs4ace_is_allow(ace)) - allowed |= ace->e_mask; - - if (ace->e_flags & ACE4_FILE_INHERIT_ACE) - fmt2 |= NFS4ACL_TEXT_FILE_CONTEXT; - if (ace->e_flags & ACE4_DIRECTORY_INHERIT_ACE) - fmt2 |= NFS4ACL_TEXT_DIRECTORY_CONTEXT; - } - - if (!(fmt & NFS4ACL_TEXT_SIMPLIFY)) - allowed = ~0; - - buffer_sprintf(buffer, "owner:"); - write_mask(buffer, acl->a_owner_mask & allowed, fmt2); - buffer_sprintf(buffer, "::mask\n"); - buffer_sprintf(buffer, "group:"); - write_mask(buffer, acl->a_group_mask & allowed, fmt2); - buffer_sprintf(buffer, "::mask\n"); - buffer_sprintf(buffer, "other:"); - write_mask(buffer, acl->a_other_mask & allowed, fmt2); - buffer_sprintf(buffer, "::mask\n"); - } - - nfs4acl_for_each_entry(ace, acl) { - write_identifier(buffer, ace); - buffer_sprintf(buffer, ":"); - - fmt2 = fmt; - if (ace->e_flags & ACE4_INHERIT_ONLY_ACE) - fmt2 &= ~(NFS4ACL_TEXT_FILE_CONTEXT | - NFS4ACL_TEXT_DIRECTORY_CONTEXT); - if (ace->e_flags & ACE4_FILE_INHERIT_ACE) - fmt2 |= NFS4ACL_TEXT_FILE_CONTEXT; - if (ace->e_flags & ACE4_DIRECTORY_INHERIT_ACE) - fmt2 |= NFS4ACL_TEXT_DIRECTORY_CONTEXT; - - write_mask(buffer, ace->e_mask, fmt2); - buffer_sprintf(buffer, ":"); - write_ace_flags(buffer, ace->e_flags, fmt2); - buffer_sprintf(buffer, ":"); - write_type(buffer, ace->e_type); - buffer_sprintf(buffer, "\n"); - } - - if (string_buffer_okay(buffer)) { - char *str = realloc(buffer->buffer, buffer->offset + 1); - if (str) - return str; - } - - free_string_buffer(buffer); - errno = ENOMEM; - return NULL; -} - -static int acl_flags_from_text(const char *str, struct nfs4acl *acl, - void (*error)(const char *, ...)) -{ - char *dup, *end; - - end = alloca(strlen(str) + 1); - strcpy(end, str); - - acl->a_flags = 0; - while ((dup = end)) { - char *c; - unsigned long l; - int i; - - while (*dup == '/') - dup++; - end = strchr(dup, '/'); - if (end) - *end++ = 0; - if (!*dup) - break; - - l = strtoul(str, &c, 0); - if (*c == 0) { - acl->a_flags |= l; - continue; - } - - /* Recognize flag mnemonics */ - for (i = 0; i < ARRAY_SIZE(acl_flag_bits); i++) { - if (!strcasecmp(dup, acl_flag_bits[i].a_name)) { - acl->a_flags |= acl_flag_bits[i].a_flag; - break; - } - } - if (i != ARRAY_SIZE(acl_flag_bits)) - continue; - - /* Recognize single-character flags */ - for (c = dup; *c; c++) { - for (i = 0; i < ARRAY_SIZE(acl_flag_bits); i++) { - if (*c == acl_flag_bits[i].a_char) { - acl->a_flags |= acl_flag_bits[i].a_flag; - break; - } - } - if (i != ARRAY_SIZE(acl_flag_bits)) - continue; - - error("Invalid acl flag `%s'\n", c); - return -1; - } - } - - return 0; -} - -static int identifier_from_text(const char *str, struct nfs4ace *ace, - void (*error)(const char *, ...)) -{ - char *c; - unsigned long l; - - c = strchr(str, '@'); - if (c) { - char *dup; - - if (c[1]) { - error("Domain name not supported in `%s'\n", str); - goto fail; - } - - /* Ignore case in special identifiers. */ - dup = alloca(strlen(str) + 1); - strcpy(dup, str); - for (c = dup; *c; c++) - *c = toupper(*c); - - if (nfs4ace_set_who(ace, dup)) { - error("Special user `%s' not supported\n", str); - goto fail; - } - return 0; - } - l = strtoul(str, &c, 0); - if (*c == 0) { - ace->u.e_id = l; - return 0; - } - if (ace->e_flags & ACE4_IDENTIFIER_GROUP) { - struct group *group = getgrnam(str); - - if (!group) { - error("Group `%s' does not exist\n", str); - goto fail; - } - ace->u.e_id = group->gr_gid; - return 0; - } else { - struct passwd *passwd = getpwnam(str); - - if (!passwd) { - error("User `%s' does not exist\n", str); - goto fail; - } - ace->u.e_id = passwd->pw_uid; - return 0; - } -fail: - return -1; -} - -static int type_from_text(const char *str, struct nfs4ace *ace, - void (*error)(const char *, ...)) -{ - char *c; - int i; - unsigned long l; - - l = strtoul(str, &c, 0); - if (*c == 0) { - ace->e_type = l; - return 0; - } - - /* Recognize type mnemonic */ - for (i = 0; i < ARRAY_SIZE(type_values); i++) { - if (!strcasecmp(str, type_values[i].e_name)) { - ace->e_type = type_values[i].e_type; - return 0; - } - } - error("Invalid entry type `%s'\n", str); - return -1; -} - -static int ace_flags_from_text(const char *str, struct nfs4ace *ace, - void (*error)(const char *, ...)) -{ - char *dup, *end; - - end = alloca(strlen(str) + 1); - strcpy(end, str); - - ace->e_flags = 0; - while ((dup = end)) { - char *c; - unsigned long l; - int i; - - while (*dup == '/') - dup++; - end = strchr(dup, '/'); - if (end) - *end++ = 0; - if (!*dup) - break; - - l = strtoul(str, &c, 0); - if (*c == 0) { - ace->e_flags |= l; - continue; - } - - /* Recognize flag mnemonics */ - for (i = 0; i < ARRAY_SIZE(ace_flag_bits); i++) { - if (!strcasecmp(dup, ace_flag_bits[i].e_name)) { - ace->e_flags |= ace_flag_bits[i].e_flag; - break; - } - } - if (i != ARRAY_SIZE(ace_flag_bits)) - continue; - - /* Recognize single-character flags */ - for (c = dup; *c; c++) { - for (i = 0; i < ARRAY_SIZE(ace_flag_bits); i++) { - if (*c == ace_flag_bits[i].e_char) { - ace->e_flags |= ace_flag_bits[i].e_flag; - break; - } - } - if (i != ARRAY_SIZE(ace_flag_bits)) - continue; - - error("Invalid entry flag `%s'\n", c); - return -1; - } - } - - return 0; -} - -static int mask_from_text(const char *str, unsigned int *mask, - void (*error)(const char *, ...)) -{ - char *dup, *end; - - end = alloca(strlen(str) + 1); - strcpy(end, str); - - *mask = 0; - while ((dup = end)) { - char *c; - unsigned long l; - int i; - - while (*dup == '/') - dup++; - end = strchr(dup, '/'); - if (end) - *end++ = 0; - if (!*dup) - break; - - l = strtoul(dup, &c, 0); - if (*c == 0) { - *mask |= l; - continue; - } - - /* Recognize mask mnemonics */ - for (i = 0; i < ARRAY_SIZE(mask_bits); i++) { - if (!strcasecmp(dup, mask_bits[i].e_name)) { - *mask |= mask_bits[i].e_mask; - break; - } - } - if (i != ARRAY_SIZE(mask_bits)) - continue; - - /* Recognize single-character masks */ - for (c = dup; *c; c++) { - for (i = 0; i < ARRAY_SIZE(mask_bits); i++) { - if (*c == mask_bits[i].e_char) { - *mask |= mask_bits[i].e_mask; - break; - } - } - if (i != ARRAY_SIZE(mask_bits)) - continue; - - error("Invalid access mask `%s'\n", dup); - return -1; - } - } - - return 0; -} - -struct nfs4acl *nfs4acl_from_text(const char *str, int *pflags, - void (*error)(const char *, ...)) -{ - char *who_str = NULL, *mask_str = NULL, *flags_str = NULL, - *type_str = NULL; - struct nfs4acl *acl; - int flags = 0; - - acl = nfs4acl_alloc(0); - if (!acl) - return NULL; - - while (*str) { - struct nfs4acl *acl2; - struct nfs4ace *ace; - unsigned int mask; - const char *entry, *c; - - while (isspace(*str) || *str == ',') - str++; - if (!*str) - break; - - entry = str; - c = strchr(str, ':'); - if (!c) - goto fail_syntax; - who_str = strndup(str, c - str); - if (!who_str) - goto fail; - str = c + 1; - - if (!strcasecmp(who_str, "FLAGS")) { - for (c = str; *c; c++) { - if (*c == ':' || *c == ',' || isspace(*c)) - break; - } - mask_str = strndup(str, c - str); - if (!mask_str) - goto fail; - if (*c != ':') { - if (acl_flags_from_text(mask_str, acl, error)) - goto fail_einval; - flags |= NFS4ACL_TEXT_FLAGS; - str = c; - goto free_mask_str; - } else { - free(mask_str); - mask_str = NULL; - } - } - - c = strchr(str, ':'); - if (!c) - goto fail_syntax; - mask_str = strndup(str, c - str); - if (!mask_str) - goto fail; - str = c + 1; - - c = strchr(str, ':'); - if (!c) - goto fail_syntax; - flags_str = strndup(str, c - str); - if (!flags_str) - goto fail; - str = c + 1; - - for (c = str; *c; c++) { - if (*c == ',' || isspace(*c)) - break; - } - type_str = strndup(str, c - str); - if (!type_str) - goto fail; - str = c; - - if (mask_from_text(mask_str, &mask, error)) - goto fail_einval; - if (!strcasecmp(type_str, "MASK")) { - if (!strcasecmp(who_str, "OWNER")) { - acl->a_owner_mask = mask; - flags |= NFS4ACL_TEXT_OWNER_MASK; - } else if (!strcasecmp(who_str, "GROUP")) { - acl->a_group_mask = mask; - flags |= NFS4ACL_TEXT_GROUP_MASK; - } else if (!strcasecmp(who_str, "OTHER")) { - acl->a_other_mask = mask; - flags |= NFS4ACL_TEXT_OTHER_MASK; - } else { - error("Invalid file mask `%s'\n", - who_str); - goto fail_einval; - } - } else { - size_t size = sizeof(struct nfs4acl) + (acl->a_count - + 1) * sizeof(struct nfs4ace); - acl2 = realloc(acl, size); - if (!acl2) - goto fail; - acl = acl2; - memset(acl->a_entries + acl->a_count, 0, - sizeof(struct nfs4ace)); - acl->a_count++; - - ace = acl->a_entries + acl->a_count - 1; - ace->e_mask = mask; - if (ace_flags_from_text(flags_str, ace, error)) - goto fail_einval; - if (identifier_from_text(who_str, ace, error)) - goto fail_einval; - if (type_from_text(type_str, ace, error)) - goto fail_einval; - } - - free(type_str); - type_str = NULL; - free(flags_str); - flags_str = NULL; - free_mask_str: - free(mask_str); - mask_str = NULL; - free(who_str); - who_str = NULL; - continue; - - fail_syntax: - for (c = entry; *c && !(isspace(*c) || *c == ','); c++) - ; - error("Invalid entry `%.*s'\n", c - entry, entry); - goto fail_einval; - } - - if (pflags) - *pflags = flags; - return acl; - -fail_einval: - errno = EINVAL; - -fail: - free(type_str); - free(flags_str); - free(mask_str); - free(who_str); - nfs4acl_free(acl); - return NULL; -} - -int nfs4ace_set_who(struct nfs4ace *ace, const char *who) -{ - if (!strcmp(who, nfs4ace_owner_who)) - who = nfs4ace_owner_who; - else if (!strcmp(who, nfs4ace_group_who)) - who = nfs4ace_group_who; - else if (!strcmp(who, nfs4ace_everyone_who)) - who = nfs4ace_everyone_who; - else - return -1; - - ace->u.e_who = who; - ace->e_flags |= ACE4_SPECIAL_WHO; - /* - * Also clear the ACE4_IDENTIFIER_GROUP flag for ACEs with a special - * who value: nfs4ace_is_same_identifier() relies on that. - */ - ace->e_flags &= ~ACE4_IDENTIFIER_GROUP; - return 0; -} - -void nfs4ace_set_uid(struct nfs4ace *ace, uid_t uid) -{ - ace->u.e_id = uid; - ace->e_flags &= ~(ACE4_SPECIAL_WHO | ACE4_IDENTIFIER_GROUP); -} - -void nfs4ace_set_gid(struct nfs4ace *ace, gid_t gid) -{ - ace->u.e_id = gid; - ace->e_flags |= ACE4_IDENTIFIER_GROUP; - ace->e_flags &= ~ACE4_SPECIAL_WHO; -} - -void nfs4ace_copy(struct nfs4ace *dst, const struct nfs4ace *src) -{ - memcpy(dst, src, sizeof(struct nfs4ace)); -} - -static unsigned int nfs4acl_mode_to_mask(mode_t mode) -{ - unsigned int mask = ACE4_POSIX_ALWAYS_ALLOWED; - - if (mode & S_IROTH) - mask |= ACE4_POSIX_MODE_READ; - if (mode & S_IWOTH) - mask |= ACE4_POSIX_MODE_WRITE; - if (mode & S_IXOTH) - mask |= ACE4_POSIX_MODE_EXEC; - - return mask; -} - -struct nfs4acl *nfs4acl_from_mode(mode_t mode) -{ - struct nfs4acl *acl; - struct nfs4ace *ace; - - acl = nfs4acl_alloc(1); - if (!acl) - return NULL; - ace = acl->a_entries; - - acl->a_owner_mask = nfs4acl_mode_to_mask(mode >> 6); - acl->a_group_mask = nfs4acl_mode_to_mask(mode >> 3); - acl->a_other_mask = nfs4acl_mode_to_mask(mode); - - ace->e_type = ACE4_ACCESS_ALLOWED_ACE_TYPE; - ace->e_flags = ACE4_SPECIAL_WHO; - ace->e_mask = ACE4_VALID_MASK; - ace->u.e_who = nfs4ace_everyone_who; - - if (nfs4acl_apply_masks(&acl)) { - free(acl); - acl = NULL; - } - return acl; -} diff --git a/lib/nfs4acl_compat.c b/lib/nfs4acl_compat.c deleted file mode 100644 index a7837ae..0000000 --- a/lib/nfs4acl_compat.c +++ /dev/null @@ -1,610 +0,0 @@ -/* - * Copyright (C) 2006 Andreas Gruenbacher - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License as published by the - * Free Software Foundation; either version 2, or (at your option) any - * later version. - * - * This program 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 - * General Public License for more details. - */ - -#include -#include "nfs4acl.h" -#include "nfs4acl-internal.h" - -/** - * struct nfs4acl_alloc - remember how many entries are actually allocated - * @acl: acl with a_count <= @count - * @count: the actual number of entries allocated in @acl - * - * We pass around this structure while modifying an acl, so that we do - * not have to reallocate when we remove existing entries followed by - * adding new entries. - */ -struct nfs4acl_alloc { - struct nfs4acl *acl; - unsigned int count; -}; - -/** - * nfs4acl_delete_entry - delete an entry in an acl - * @x: acl and number of allocated entries - * @ace: an entry in @x->acl - * - * Updates @ace so that it points to the entry before the deleted entry - * on return. (When deleting the first entry, @ace will point to the - * (non-existant) entry before the first entry). This behavior is the - * expected behavior when deleting entries while forward iterating over - * an acl. - */ -static void -nfs4acl_delete_entry(struct nfs4acl_alloc *x, struct nfs4ace **ace) -{ - void *end = x->acl->a_entries + x->acl->a_count; - - memmove(*ace, *ace + 1, end - (void *)(*ace + 1)); - (*ace)--; - x->acl->a_count--; -} - -/** - * nfs4acl_insert_entry - insert an entry in an acl - * @x: acl and number of allocated entries - * @ace: entry before which the new entry shall be inserted - * - * Insert a new entry in @x->acl at position @ace, and zero-initialize - * it. This may require reallocating @x->acl. - */ -static int -nfs4acl_insert_entry(struct nfs4acl_alloc *x, struct nfs4ace **ace) -{ - int n = *ace - x->acl->a_entries; - - if (x->count == x->acl->a_count) { - size_t size = sizeof(struct nfs4acl) + - (x->count + 1) * sizeof(struct nfs4ace); - struct nfs4acl *acl2; - - acl2 = realloc(x->acl, size); - if (!acl2) - return -1; - x->count++; - x->acl = acl2; - *ace = acl2->a_entries + n; - } - memmove(*ace + 1, *ace, sizeof(struct nfs4ace) * (x->acl->a_count - n)); - memset(*ace, 0, sizeof(struct nfs4ace)); - x->acl->a_count++; - return 0; -} - -/** - * nfs4ace_change_mask - change the mask in @ace to @mask - * @x: acl and number of allocated entries - * @ace: entry to modify - * @mask: new mask for @ace - * - * Set the effective mask of @ace to @mask. This will require splitting - * off a separate acl entry if @ace is inheritable. In that case, the - * effective- only acl entry is inserted after the inheritable acl - * entry, end the inheritable acl entry is set to inheritable-only. If - * @mode is 0, either set the original acl entry to inheritable-only if - * it was inheritable, or remove it otherwise. The returned @ace points - * to the modified or inserted effective-only acl entry if that entry - * exists, to the entry that has become inheritable-only, or else to the - * previous entry in the acl. This is the expected behavior when - * modifying masks while forward iterating over an acl. - */ -static int -nfs4ace_change_mask(struct nfs4acl_alloc *x, struct nfs4ace **ace, - unsigned int mask) -{ - if (mask && (*ace)->e_mask == mask) - return 0; - if (mask & ~ACE4_POSIX_ALWAYS_ALLOWED) { - if (nfs4ace_is_inheritable(*ace)) { - if (nfs4acl_insert_entry(x, ace)) - return -1; - memcpy(*ace, *ace + 1, sizeof(struct nfs4ace)); - (*ace)->e_flags |= ACE4_INHERIT_ONLY_ACE; - (*ace)++; - nfs4ace_clear_inheritance_flags(*ace); - } - (*ace)->e_mask = mask; - } else { - if (nfs4ace_is_inheritable(*ace)) - (*ace)->e_flags |= ACE4_INHERIT_ONLY_ACE; - else - nfs4acl_delete_entry(x, ace); - } - return 0; -} - -/** - * nfs4acl_move_everyone_aces_down - move everyone@ acl entries to the end - * @x: acl and number of allocated entries - * - * Move all everyone acl entries to the bottom of the acl so that only a - * single everyone@ allow acl entry remains at the end, and update the - * mask fields of all acl entries on the way. If everyone@ is not - * granted any permissions, no empty everyone@ acl entry is inserted. - * - * This transformation does not modify the permissions that the acl - * grants, but we need it to simplify successive transformations. - */ -static int -nfs4acl_move_everyone_aces_down(struct nfs4acl_alloc *x) -{ - struct nfs4ace *ace; - unsigned int allowed = 0, denied = 0; - - nfs4acl_for_each_entry(ace, x->acl) { - if (nfs4ace_is_inherit_only(ace)) - continue; - if (nfs4ace_is_everyone(ace)) { - if (nfs4ace_is_allow(ace)) - allowed |= (ace->e_mask & ~denied); - else if (nfs4ace_is_deny(ace)) - denied |= (ace->e_mask & ~allowed); - else - continue; - if (nfs4ace_change_mask(x, &ace, 0)) - return -1; - } else { - if (nfs4ace_is_allow(ace)) { - if (nfs4ace_change_mask(x, &ace, allowed | - (ace->e_mask & ~denied))) - return -1; - } else if (nfs4ace_is_deny(ace)) { - if (nfs4ace_change_mask(x, &ace, denied | - (ace->e_mask & ~allowed))) - return -1; - } - } - } - if (allowed & ~ACE4_POSIX_ALWAYS_ALLOWED) { - struct nfs4ace *last_ace = ace - 1; - - if (nfs4ace_is_everyone(last_ace) && - nfs4ace_is_allow(last_ace) && - nfs4ace_is_inherit_only(last_ace) && - last_ace->e_mask == allowed) - last_ace->e_flags &= ~ACE4_INHERIT_ONLY_ACE; - else { - if (nfs4acl_insert_entry(x, &ace)) - return -1; - ace->e_type = ACE4_ACCESS_ALLOWED_ACE_TYPE; - ace->e_flags = ACE4_SPECIAL_WHO; - ace->e_mask = allowed; - ace->u.e_who = nfs4ace_everyone_who; - } - } - return 0; -} - -/** - * __nfs4acl_propagate_everyone - propagate everyone@ mask flags up for @who - * @x: acl and number of allocated entries - * @who: identifier to propagate mask flags for - * @allow: mask flags to propagate up - * - * Propagate mask flags from the trailing everyone@ allow acl entry up - * for the specified @who. - * - * The idea here is to precede the trailing EVERYONE@ ALLOW entry by an - * additional @who ALLOW entry, but with the following optimizations: - * (1) we don't bother setting any flags in the new @who ALLOW entry - * that has already been allowed or denied by a previous @who entry, (2) - * we merge the new @who entry with a previous @who entry if there is - * such a previous @who entry and there are no intervening DENY entries - * with mask flags that overlap the flags we care about. - */ -static int -__nfs4acl_propagate_everyone(struct nfs4acl_alloc *x, struct nfs4ace *who, - unsigned int allow) -{ - struct nfs4ace *allow_last = NULL, *ace; - - /* Remove the mask flags from allow that are already determined for - this who value, and figure out if there is an ALLOW entry for - this who value that is "reachable" from the trailing EVERYONE@ - ALLOW ACE. */ - nfs4acl_for_each_entry(ace, x->acl) { - if (nfs4ace_is_inherit_only(ace)) - continue; - if (nfs4ace_is_allow(ace)) { - if (nfs4ace_is_same_identifier(ace, who)) { - allow &= ~ace->e_mask; - allow_last = ace; - } - } else if (nfs4ace_is_deny(ace)) { - if (nfs4ace_is_same_identifier(ace, who)) - allow &= ~ace->e_mask; - if (allow & ace->e_mask) - allow_last = NULL; - } - } - - if (allow) { - if (allow_last) - return nfs4ace_change_mask(x, &allow_last, - allow_last->e_mask | allow); - else { - struct nfs4ace who_copy; - - ace = x->acl->a_entries + x->acl->a_count - 1; - memcpy(&who_copy, who, sizeof(struct nfs4ace)); - if (nfs4acl_insert_entry(x, &ace)) - return -1; - memcpy(ace, &who_copy, sizeof(struct nfs4ace)); - ace->e_type = ACE4_ACCESS_ALLOWED_ACE_TYPE; - nfs4ace_clear_inheritance_flags(ace); - ace->e_mask = allow; - } - } - return 0; -} - -/** - * nfs4acl_propagate_everyone - propagate everyone@ mask flags up the acl - * @x: acl and number of allocated entries - * - * Make sure for owner@, group@, and all other users, groups, and - * special identifiers that they are allowed or denied all permissions - * that are granted be the trailing everyone@ acl entry. If they are - * not, try to add the missing permissions to existing allow acl entries - * for those users, or introduce additional acl entries if that is not - * possible. - * - * We do this so that no mask flags will get lost when finally applying - * the file masks to the acl entries: otherwise, with an other file mask - * that is more restrictive than the owner and/or group file mask, mask - * flags that were allowed to processes in the owner and group classes - * and that the other mask denies would be lost. For example, the - * following two acls show the problem when mode 0664 is applied to - * them: - * - * masking without propagation (wrong) - * =========================================================== - * joe:r::allow => joe:r::allow - * everyone@:rwx::allow => everyone@:r::allow - * ----------------------------------------------------------- - * joe:w::deny => joe:w::deny - * everyone@:rwx::allow everyone@:r::allow - * - * Note that the permissions of joe end up being more restrictive than - * what the acl would allow when first computing the allowed flags and - * then applying the respective mask. With propagation of permissions, - * we get: - * - * masking after propagation (correct) - * =========================================================== - * joe:r::allow => joe:rw::allow - * owner@:rw::allow - * group@:rw::allow - * everyone@:rwx::allow everyone@:r::allow - * ----------------------------------------------------------- - * joe:w::deny => owner@:x::deny - * joe:w::deny - * owner@:rw::allow - * owner@:rw::allow - * joe:r::allow - * everyone@:rwx::allow everyone@:r::allow - * - * The examples show the acls that would result from propagation with no - * masking performed. In fact, we do apply the respective mask to the - * acl entries before computing the propagation because this will save - * us from adding acl entries that would end up with empty mask fields - * after applying the masks. - * - * It is ensured that no more than one entry will be inserted for each - * who value, no matter how many entries each who value has already. - */ -static int -nfs4acl_propagate_everyone(struct nfs4acl_alloc *x) -{ - int write_through = x->acl->a_flags & ACL4_WRITE_THROUGH; - struct nfs4ace who = { .e_flags = ACE4_SPECIAL_WHO }; - struct nfs4ace *ace; - unsigned int owner_allow, group_allow; - - if (!((x->acl->a_owner_mask | x->acl->a_group_mask) & - ~x->acl->a_other_mask)) - return 0; - if (!x->acl->a_count) - return 0; - ace = x->acl->a_entries + x->acl->a_count - 1; - if (nfs4ace_is_inherit_only(ace) || !nfs4ace_is_everyone(ace)) - return 0; - if (!(ace->e_mask & ~x->acl->a_other_mask)) { - /* None of the allowed permissions will get masked. */ - return 0; - } - owner_allow = ace->e_mask & x->acl->a_owner_mask; - group_allow = ace->e_mask & x->acl->a_group_mask; - - /* Propagate everyone@ permissions through to owner@. */ - if (owner_allow && !write_through && - (x->acl->a_owner_mask & ~x->acl->a_other_mask)) { - who.u.e_who = nfs4ace_owner_who; - if (__nfs4acl_propagate_everyone(x, &who, owner_allow)) - return -1; - } - - if (group_allow && (x->acl->a_group_mask & ~x->acl->a_other_mask)) { - int n; - - if (!write_through) { - /* Propagate everyone@ permissions through to group@. */ - who.u.e_who = nfs4ace_group_who; - if (__nfs4acl_propagate_everyone(x, &who, group_allow)) - return -1; - } - - /* Start from the entry before the trailing EVERYONE@ ALLOW - entry. We will not hit EVERYONE@ entries in the loop. */ - for (n = x->acl->a_count - 2; n != -1; n--) { - ace = x->acl->a_entries + n; - - if (nfs4ace_is_inherit_only(ace) || - nfs4ace_is_owner(ace) || - nfs4ace_is_group(ace)) - continue; - if (nfs4ace_is_allow(ace) || nfs4ace_is_deny(ace)) { - /* Any inserted entry will end up below the - current entry. */ - if (__nfs4acl_propagate_everyone(x, ace, group_allow)) - return -1; - } - } - } - return 0; -} - -/** - * __nfs4acl_apply_masks - apply the masks to the acl entries - * @x: acl and number of allocated entries - * - * Apply the owner file mask to owner@ entries, the intersection of the - * group and other file masks to everyone@ entries, and the group file - * mask to all other entries. - */ -static int -__nfs4acl_apply_masks(struct nfs4acl_alloc *x) -{ - struct nfs4ace *ace; - - nfs4acl_for_each_entry(ace, x->acl) { - unsigned int mask; - - if (nfs4ace_is_inherit_only(ace) || !nfs4ace_is_allow(ace)) - continue; - if (nfs4ace_is_owner(ace)) - mask = x->acl->a_owner_mask; - else if (nfs4ace_is_everyone(ace)) - mask = x->acl->a_other_mask; - else - mask = x->acl->a_group_mask; - if (nfs4ace_change_mask(x, &ace, ace->e_mask & mask)) - return -1; - } - return 0; -} - -/** - * nfs4acl_max_allowed - maximum mask flags that anybody is allowed - */ -static unsigned int -nfs4acl_max_allowed(struct nfs4acl *acl) -{ - struct nfs4ace *ace; - unsigned int allowed = 0; - - nfs4acl_for_each_entry_reverse(ace, acl) { - if (nfs4ace_is_inherit_only(ace)) - continue; - if (nfs4ace_is_allow(ace)) - allowed |= ace->e_mask; - else if (nfs4ace_is_deny(ace)) { - if (nfs4ace_is_everyone(ace)) - allowed &= ~ace->e_mask; - } - } - return allowed; -} - -/** - * nfs4acl_isolate_owner_class - limit the owner class to the owner file mask - * @x: acl and number of allocated entries - * - * Make sure the owner class (owner@) is granted no more than the owner - * mask by first checking which permissions anyone is granted, and then - * denying owner@ all permissions beyond that. - */ -static int -nfs4acl_isolate_owner_class(struct nfs4acl_alloc *x) -{ - struct nfs4ace *ace; - unsigned int allowed = 0; - - allowed = nfs4acl_max_allowed(x->acl); - if (allowed & ~x->acl->a_owner_mask) { - /* Figure out if we can update an existig OWNER@ DENY entry. */ - nfs4acl_for_each_entry(ace, x->acl) { - if (nfs4ace_is_inherit_only(ace)) - continue; - if (nfs4ace_is_deny(ace)) { - if (nfs4ace_is_owner(ace)) - break; - } else if (nfs4ace_is_allow(ace)) { - ace = x->acl->a_entries + x->acl->a_count; - break; - } - } - if (ace != x->acl->a_entries + x->acl->a_count) { - if (nfs4ace_change_mask(x, &ace, ace->e_mask | - (allowed & ~x->acl->a_owner_mask))) - return -1; - } else { - /* Insert an owner@ deny entry at the front. */ - ace = x->acl->a_entries; - if (nfs4acl_insert_entry(x, &ace)) - return -1; - ace->e_type = ACE4_ACCESS_DENIED_ACE_TYPE; - ace->e_flags = ACE4_SPECIAL_WHO; - ace->e_mask = allowed & ~x->acl->a_owner_mask; - ace->u.e_who = nfs4ace_owner_who; - } - } - return 0; -} - -/** - * __nfs4acl_isolate_who - isolate entry from EVERYONE@ ALLOW entry - * @x: acl and number of allocated entries - * @who: identifier to isolate - * @deny: mask flags this identifier should not be allowed - * - * Make sure that @who is not allowed any mask flags in @deny by checking - * which mask flags this identifier is allowed, and adding excess allowed - * mask flags to an existing DENY entry before the trailing EVERYONE@ ALLOW - * entry, or inserting such an entry. - */ -static int -__nfs4acl_isolate_who(struct nfs4acl_alloc *x, struct nfs4ace *who, - unsigned int deny) -{ - struct nfs4ace *ace; - unsigned int allowed = 0, n; - - /* Compute the mask flags granted to this who value. */ - nfs4acl_for_each_entry_reverse(ace, x->acl) { - if (nfs4ace_is_inherit_only(ace)) - continue; - if (nfs4ace_is_same_identifier(ace, who)) { - if (nfs4ace_is_allow(ace)) - allowed |= ace->e_mask; - else if (nfs4ace_is_deny(ace)) - allowed &= ~ace->e_mask; - deny &= ~ace->e_mask; - } - } - if (!deny) - return 0; - - /* Figure out if we can update an existig DENY entry. Start - from the entry before the trailing EVERYONE@ ALLOW entry. We - will not hit EVERYONE@ entries in the loop. */ - for (n = x->acl->a_count - 2; n != -1; n--) { - ace = x->acl->a_entries + n; - if (nfs4ace_is_inherit_only(ace)) - continue; - if (nfs4ace_is_deny(ace)) { - if (nfs4ace_is_same_identifier(ace, who)) - break; - } else if (nfs4ace_is_allow(ace) && - (ace->e_mask & deny)) { - n = -1; - break; - } - } - if (n != -1) { - if (nfs4ace_change_mask(x, &ace, ace->e_mask | deny)) - return -1; - } else { - /* Insert a eny entry before the trailing EVERYONE@ DENY - entry. */ - struct nfs4ace who_copy; - - ace = x->acl->a_entries + x->acl->a_count - 1; - memcpy(&who_copy, who, sizeof(struct nfs4ace)); - if (nfs4acl_insert_entry(x, &ace)) - return -1; - memcpy(ace, &who_copy, sizeof(struct nfs4ace)); - ace->e_type = ACE4_ACCESS_DENIED_ACE_TYPE; - nfs4ace_clear_inheritance_flags(ace); - ace->e_mask = deny; - } - return 0; -} - -/** - * nfs4acl_isolate_group_class - limit the group class to the group file mask - * @x: acl and number of allocated entries - * - * Make sure the group class (all entries except owner@ and everyone@) is - * granted no more than the group mask by inserting DENY entries for group - * class entries where necessary. - */ -static int -nfs4acl_isolate_group_class(struct nfs4acl_alloc *x) -{ - struct nfs4ace who = { - .e_flags = ACE4_SPECIAL_WHO, - .u.e_who = nfs4ace_group_who, - }; - struct nfs4ace *ace; - unsigned int deny; - - if (!x->acl->a_count) - return 0; - ace = x->acl->a_entries + x->acl->a_count - 1; - if (nfs4ace_is_inherit_only(ace) || !nfs4ace_is_everyone(ace)) - return 0; - deny = ace->e_mask & ~x->acl->a_group_mask; - - if (deny) { - unsigned int n; - - if (__nfs4acl_isolate_who(x, &who, deny)) - return -1; - - /* Start from the entry before the trailing EVERYONE@ ALLOW - entry. We will not hit EVERYONE@ entries in the loop. */ - for (n = x->acl->a_count - 2; n != -1; n--) { - ace = x->acl->a_entries + n; - - if (nfs4ace_is_inherit_only(ace) || - nfs4ace_is_owner(ace) || - nfs4ace_is_group(ace)) - continue; - if (__nfs4acl_isolate_who(x, ace, deny)) - return -1; - } - } - return 0; -} - -/** - * nfs4acl_apply_masks - apply the masks to the acl - * - * Apply the masks so that the acl allows no more flags than the - * intersection between the flags that the original acl allows and the - * mask matching the process. - * - * Note: this algorithm may push the number of entries in the acl above - * ACL4_XATTR_MAX_COUNT, so a read-modify-write cycle would fail. - */ -int -nfs4acl_apply_masks(struct nfs4acl **acl) -{ - struct nfs4acl_alloc x = { - .acl = *acl, - .count = (*acl)->a_count, - }; - int retval = 0; - - if (nfs4acl_move_everyone_aces_down(&x) || - nfs4acl_propagate_everyone(&x) || - __nfs4acl_apply_masks(&x) || - nfs4acl_isolate_owner_class(&x) || - nfs4acl_isolate_group_class(&x)) - retval = -1; - - *acl = x.acl; - return retval; -} diff --git a/lib/richacl.c b/lib/richacl.c new file mode 100644 index 0000000..f8bbe92 --- /dev/null +++ b/lib/richacl.c @@ -0,0 +1,1127 @@ +#define _GNU_SOURCE +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "string_buffer.h" +#include "richacl.h" +#include "richacl_xattr.h" +#include "richacl-internal.h" + +#define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0])) +#define ALIGN(x,a) (((x)+(a)-1)&~((a)-1)) + +const char *richace_owner_who = "OWNER@"; +const char *richace_group_who = "GROUP@"; +const char *richace_everyone_who = "EVERYONE@"; + +/* + * The POSIX permissions are supersets of the following mask flags. + */ +#define ACE4_POSIX_MODE_READ ( \ + ACE4_READ_DATA | ACE4_LIST_DIRECTORY ) +#define ACE4_POSIX_MODE_WRITE ( \ + ACE4_WRITE_DATA | ACE4_ADD_FILE | \ + ACE4_APPEND_DATA | ACE4_ADD_SUBDIRECTORY | \ + ACE4_DELETE_CHILD ) +#define ACE4_POSIX_MODE_EXEC ( \ + ACE4_EXECUTE) + +static struct { + char a_char; + unsigned char a_flag; + const char *a_name; +} acl_flag_bits[] = { + { 'a', ACL4_AUTO_INHERIT, "auto_inherit" }, + { 'p', ACL4_PROTECTED, "protected" }, + { 'd', ACL4_DEFAULTED, "defaulted" }, + { 'w', ACL4_WRITE_THROUGH, "write_through" }, +}; + +static struct { + uint16_t e_type; + const char *e_name; +} type_values[] = { + { ACE4_ACCESS_ALLOWED_ACE_TYPE, "allow" }, + { ACE4_ACCESS_DENIED_ACE_TYPE, "deny" }, +}; + +#define FLAGS_BIT(c, name, str) \ + { ACE4_ ## name, c, #name } + +static struct { + uint16_t e_flag; + char e_char; + const char *e_name; +} ace_flag_bits[] = { + FLAGS_BIT('f', FILE_INHERIT_ACE, "file_inherit_ace"), + FLAGS_BIT('d', DIRECTORY_INHERIT_ACE, "directory_inherit_ace"), + FLAGS_BIT('n', NO_PROPAGATE_INHERIT_ACE, "no_propagate_inherit_ace"), + FLAGS_BIT('i', INHERIT_ONLY_ACE, "inherit_only_ace"), + FLAGS_BIT('g', IDENTIFIER_GROUP, "identifier_group"), + FLAGS_BIT('a', INHERITED_ACE, "inherited_ace"), +}; + +#undef FLAGS_BIT + +#define MASK_BIT(c, name, str) \ + { ACE4_ ## name, c, str, RICHACL_TEXT_FILE_CONTEXT | \ + RICHACL_TEXT_DIRECTORY_CONTEXT } +#define FILE_MASK_BIT(c, name, str) \ + { ACE4_ ## name, c, str, RICHACL_TEXT_FILE_CONTEXT } +#define DIRECTORY_MASK_BIT(c, name, str) \ + { ACE4_ ## name, c, str, RICHACL_TEXT_DIRECTORY_CONTEXT } + +struct { + uint32_t e_mask; + char e_char; + const char *e_name; + int e_context; +} mask_bits[] = { + MASK_BIT('*', VALID_MASK, "*"), + FILE_MASK_BIT('r', READ_DATA, "read_data"), + DIRECTORY_MASK_BIT('r', LIST_DIRECTORY, "list_directory"), + FILE_MASK_BIT('w', WRITE_DATA, "write_data"), + DIRECTORY_MASK_BIT('w', ADD_FILE, "add_file"), + FILE_MASK_BIT('a', APPEND_DATA, "append_data"), + DIRECTORY_MASK_BIT('a', ADD_SUBDIRECTORY, "add_subdirectory"), + MASK_BIT('N', READ_NAMED_ATTRS, "read_named_attrs"), + MASK_BIT('n', WRITE_NAMED_ATTRS, "write_named_attrs"), + MASK_BIT('x', EXECUTE, "execute"), + MASK_BIT('d', DELETE_CHILD, "delete_child"), + MASK_BIT('T', READ_ATTRIBUTES, "read_attributes"), + MASK_BIT('t', WRITE_ATTRIBUTES, "write_attributes"), + MASK_BIT('D', DELETE, "delete"), + MASK_BIT('M', READ_ACL, "read_acl"), + MASK_BIT('m', WRITE_ACL, "write_acl"), + MASK_BIT('o', WRITE_OWNER, "take_ownership"), + MASK_BIT('s', SYNCHRONIZE, "synchronize"), +}; + +#undef MASK_BIT +#undef FILE_MASK_BIT +#undef DIRECTORY_MASK_BIT + +const char *richace_get_who(const struct richace *ace) +{ + if (!(ace->e_flags & ACE4_SPECIAL_WHO)) + return NULL; + return ace->u.e_who; +} + +int richace_is_same_identifier(const struct richace *a, const struct richace *b) +{ +#define WHO_FLAGS (ACE4_SPECIAL_WHO | ACE4_IDENTIFIER_GROUP) + if ((a->e_flags & WHO_FLAGS) != (b->e_flags & WHO_FLAGS)) + return 0; + if (a->e_flags & ACE4_SPECIAL_WHO) + return a->u.e_who == b->u.e_who; + else + return a->u.e_id == b->u.e_id; +#undef WHO_FLAGS +} + +int richace_is_owner(const struct richace *ace) +{ + return (ace->e_flags & ACE4_SPECIAL_WHO) && + ace->u.e_who == richace_owner_who; +} + +int richace_is_group(const struct richace *ace) +{ + return (ace->e_flags & ACE4_SPECIAL_WHO) && + ace->u.e_who == richace_group_who; +} + +int richace_is_everyone(const struct richace *ace) +{ + return (ace->e_flags & ACE4_SPECIAL_WHO) && + ace->u.e_who == richace_everyone_who; +} + +struct richacl *richacl_alloc(size_t count) +{ + size_t size = sizeof(struct richacl) + count * sizeof(struct richace); + struct richacl *acl = malloc(size); + + if (acl) { + memset(acl, 0, size); + acl->a_count = count; + } + return acl; +} + +struct richacl *richacl_clone(struct richacl *acl) +{ + size_t size; + struct richacl *acl2; + + if (!acl) + return NULL; + size = sizeof(struct richacl) + acl->a_count * sizeof(struct richace); + acl2 = malloc(size); + if (acl2) + memcpy(acl2, acl, size); + return acl2; +} + +void richacl_free(struct richacl *acl) +{ + free(acl); +} + +/** + * richacl_allowed_to_who - mask flags allowed to a specific who value + * + * Computes the mask values allowed to a specific who value, taking + * EVERYONE@ entries into account. + */ +static unsigned int richacl_allowed_to_who(struct richacl *acl, + struct richace *who) +{ + struct richace *ace; + unsigned int allowed = 0; + + richacl_for_each_entry_reverse(ace, acl) { + if (richace_is_inherit_only(ace)) + continue; + if (richace_is_same_identifier(ace, who) || + richace_is_everyone(ace)) { + if (richace_is_allow(ace)) + allowed |= ace->e_mask; + else if (richace_is_deny(ace)) + allowed &= ~ace->e_mask; + } + } + return allowed; +} + +/** + * richacl_compute_max_masks - compute upper bound masks + * + * Computes upper bound owner, group, and other masks so that none of + * the mask flags allowed by the acl are disabled (for any choice of the + * file owner or group membership). + */ +void richacl_compute_max_masks(struct richacl *acl) +{ + struct richace *ace; + + acl->a_owner_mask = 0; + acl->a_group_mask = 0; + acl->a_other_mask = 0; + + richacl_for_each_entry_reverse(ace, acl) { + if (richace_is_inherit_only(ace)) + continue; + + if (richace_is_owner(ace)) { + if (richace_is_allow(ace)) + acl->a_owner_mask |= ace->e_mask; + else if (richace_is_deny(ace)) + acl->a_owner_mask &= ~ace->e_mask; + } else if (richace_is_everyone(ace)) { + if (richace_is_allow(ace)) { + struct richace who = { + .e_flags = ACE4_SPECIAL_WHO, + .u.e_who = richace_group_who, + }; + + acl->a_other_mask |= ace->e_mask; + acl->a_group_mask |= + richacl_allowed_to_who(acl, &who); + acl->a_owner_mask |= ace->e_mask; + } else if (richace_is_deny(ace)) { + acl->a_other_mask &= ~ace->e_mask; + acl->a_group_mask &= ~ace->e_mask; + acl->a_owner_mask &= ~ace->e_mask; + } + } else { + if (richace_is_allow(ace)) { + unsigned int mask = + richacl_allowed_to_who(acl, ace); + + acl->a_group_mask |= mask; + acl->a_owner_mask |= mask; + } + } + } +} + +static struct richacl *richacl_from_xattr(const void *value, size_t size) +{ + const struct richacl_xattr *xattr_acl = value; + const struct richace_xattr *xattr_ace = (void *)(xattr_acl + 1); + struct richacl *acl = NULL; + struct richace *ace; + int count; + + if (size < sizeof(struct richacl_xattr) || + xattr_acl->a_version != ACL4_XATTR_VERSION) + goto fail_einval; + + count = ntohs(xattr_acl->a_count); + if (count > ACL4_XATTR_MAX_COUNT) + goto fail_einval; + + acl = richacl_alloc(count); + if (!acl) + return NULL; + + acl->a_flags = xattr_acl->a_flags; + acl->a_owner_mask = ntohl(xattr_acl->a_owner_mask); + acl->a_group_mask = ntohl(xattr_acl->a_group_mask); + acl->a_other_mask = ntohl(xattr_acl->a_other_mask); + + richacl_for_each_entry(ace, acl) { + const char *who = (void *)(xattr_ace + 1), *end; + ssize_t used = (void *)who - value; + + if (used > size) + goto fail_einval; + end = memchr(who, 0, size - used); + if (!end) + goto fail_einval; + + ace->e_type = ntohs(xattr_ace->e_type); + ace->e_flags = ntohs(xattr_ace->e_flags); + ace->e_mask = ntohl(xattr_ace->e_mask); + ace->u.e_id = ntohl(xattr_ace->e_id); + + if (who == end) { + if (ace->u.e_id == -1) + goto fail_einval; /* uid/gid needed */ + } else if (richace_set_who(ace, who)) + goto fail_einval; + + xattr_ace = (void *)who + ALIGN(end - who + 1, 4); + } + + return acl; + +fail_einval: + richacl_free(acl); + errno = EINVAL; + return NULL; +} + +static size_t richacl_xattr_size(const struct richacl *acl) +{ + size_t size = sizeof(struct richacl_xattr); + const struct richace *ace; + + richacl_for_each_entry(ace, acl) { + size += sizeof(struct richace_xattr) + + (richace_get_who(ace) ? + ALIGN(strlen(ace->u.e_who) + 1, 4) : 4); + } + return size; +} + +static void richacl_to_xattr(const struct richacl *acl, void *buffer) +{ + struct richacl_xattr *xattr_acl = buffer; + struct richace_xattr *xattr_ace; + const struct richace *ace; + + xattr_acl->a_version = ACL4_XATTR_VERSION; + xattr_acl->a_flags = acl->a_flags; + xattr_acl->a_count = htons(acl->a_count); + + xattr_acl->a_owner_mask = htonl(acl->a_owner_mask); + xattr_acl->a_group_mask = htonl(acl->a_group_mask); + xattr_acl->a_other_mask = htonl(acl->a_other_mask); + + xattr_ace = (void *)(xattr_acl + 1); + richacl_for_each_entry(ace, acl) { + xattr_ace->e_type = htons(ace->e_type); + xattr_ace->e_flags = htons(ace->e_flags & ACE4_VALID_FLAGS); + xattr_ace->e_mask = htonl(ace->e_mask); + if (richace_get_who(ace)) { + int sz = ALIGN(strlen(ace->u.e_who) + 1, 4); + + xattr_ace->e_id = htonl(-1); + memset(xattr_ace->e_who + sz - 4, 0, 4); + strcpy(xattr_ace->e_who, ace->u.e_who); + xattr_ace = (void *)xattr_ace->e_who + sz; + } else { + xattr_ace->e_id = htonl(ace->u.e_id); + memset(xattr_ace->e_who, 0, 4); + xattr_ace = (void *)xattr_ace->e_who + 4; + } + } +} + +struct richacl *richacl_get_file(const char *path) +{ + void *value; + ssize_t retval; + struct richacl *acl; + + retval = getxattr(path, SYSTEM_RICHACL, NULL, 0); + if (retval <= 0) + return NULL; + + value = alloca(retval); + if (!value) + return NULL; + retval = getxattr(path, SYSTEM_RICHACL, value, retval); + acl = richacl_from_xattr(value, retval); + + return acl; +} + +struct richacl *richacl_get_fd(int fd) +{ + void *value; + ssize_t retval; + struct richacl *acl; + + retval = fgetxattr(fd, SYSTEM_RICHACL, NULL, 0); + if (retval <= 0) + return NULL; + + value = alloca(retval); + if (!value) + return NULL; + retval = fgetxattr(fd, SYSTEM_RICHACL, value, retval); + acl = richacl_from_xattr(value, retval); + + return acl; +} + +int richacl_set_file(const char *path, const struct richacl *acl) +{ + size_t size = richacl_xattr_size(acl); + void *value = alloca(size); + + richacl_to_xattr(acl, value); + return setxattr(path, SYSTEM_RICHACL, value, size, 0); +} + +int richacl_set_fd(int fd, const struct richacl *acl) +{ + size_t size = richacl_xattr_size(acl); + void *value = alloca(size); + + richacl_to_xattr(acl, value); + return fsetxattr(fd, SYSTEM_RICHACL, value, size, 0); +} + +static void write_acl_flags(struct string_buffer *buffer, unsigned char flags, int fmt) +{ + int cont = 0, i; + + if (!flags) + return; + buffer_sprintf(buffer, "flags:"); + for (i = 0; i < ARRAY_SIZE(acl_flag_bits); i++) { + if (!(flags & acl_flag_bits[i].a_flag)) + continue; + + flags &= ~acl_flag_bits[i].a_flag; + if (fmt & RICHACL_TEXT_LONG) { + if (cont) + buffer_sprintf(buffer, "/"); + buffer_sprintf(buffer, "%s", acl_flag_bits[i].a_name); + } else + buffer_sprintf(buffer, "%c", acl_flag_bits[i].a_char); + cont = 1; + } + if (flags) { + if (cont) + buffer_sprintf(buffer, "/"); + buffer_sprintf(buffer, "0x%x", flags); + } + buffer_sprintf(buffer, "\n"); +} + +static void write_type(struct string_buffer *buffer, uint16_t type) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(type_values); i++) { + if (type == type_values[i].e_type) { + buffer_sprintf(buffer, "%s", type_values[i].e_name); + break; + } + } + if (i == ARRAY_SIZE(type_values)) + buffer_sprintf(buffer, "%u", type); +} + +static void write_ace_flags(struct string_buffer *buffer, uint16_t flags, int fmt) +{ + int cont = 0, i; + + flags &= ~ACE4_SPECIAL_WHO; + + for (i = 0; i < ARRAY_SIZE(ace_flag_bits); i++) { + if (!(flags & ace_flag_bits[i].e_flag)) + continue; + + flags &= ~ace_flag_bits[i].e_flag; + if (fmt & RICHACL_TEXT_LONG) { + if (cont) + buffer_sprintf(buffer, "/"); + buffer_sprintf(buffer, "%s", ace_flag_bits[i].e_name); + } else + buffer_sprintf(buffer, "%c", ace_flag_bits[i].e_char); + cont = 1; + } + if (flags) { + if (cont) + buffer_sprintf(buffer, "/"); + buffer_sprintf(buffer, "0x%x", flags); + } +} + +static void write_mask(struct string_buffer *buffer, uint32_t mask, int fmt) +{ + int stuff_written = 0, i; + unsigned int nondir_mask, dir_mask; + + /* + * In long format, we write the non-directory and/or directory mask + * name depending on the context which applies. The short format + * does not distinguish between the two, so make sure that we won't + * repeat the same mask letters. + */ + if (!(fmt & (RICHACL_TEXT_FILE_CONTEXT | + RICHACL_TEXT_DIRECTORY_CONTEXT))) + fmt |= RICHACL_TEXT_FILE_CONTEXT | + RICHACL_TEXT_DIRECTORY_CONTEXT; + if (!(fmt & RICHACL_TEXT_LONG) && + (fmt & RICHACL_TEXT_FILE_CONTEXT)) + fmt &= ~RICHACL_TEXT_DIRECTORY_CONTEXT; + + nondir_mask = (fmt & RICHACL_TEXT_FILE_CONTEXT) ? mask : 0; + dir_mask = (fmt & RICHACL_TEXT_DIRECTORY_CONTEXT) ? mask : 0; + + for (i = 0; i < ARRAY_SIZE(mask_bits); i++) { + int found = 0; + + if ((nondir_mask & mask_bits[i].e_mask) == + mask_bits[i].e_mask && + (mask_bits[i].e_context & RICHACL_TEXT_FILE_CONTEXT)) { + nondir_mask &= ~mask_bits[i].e_mask; + found = 1; + } + if ((dir_mask & mask_bits[i].e_mask) == mask_bits[i].e_mask && + (mask_bits[i].e_context & RICHACL_TEXT_DIRECTORY_CONTEXT)) { + dir_mask &= ~mask_bits[i].e_mask; + found = 1; + } + if (found) { + if (fmt & RICHACL_TEXT_SIMPLIFY) { + /* Hide permissions that are always allowed. */ + if (mask_bits[i].e_mask == + (mask_bits[i].e_mask & + ACE4_POSIX_ALWAYS_ALLOWED)) + continue; + } + if (fmt & RICHACL_TEXT_LONG) { + if (stuff_written) + buffer_sprintf(buffer, "/"); + buffer_sprintf(buffer, "%s", + mask_bits[i].e_name); + } else + buffer_sprintf(buffer, "%c", + mask_bits[i].e_char); + stuff_written = 1; + } + } + if (nondir_mask | dir_mask) { + if (stuff_written) + buffer_sprintf(buffer, "/"); + buffer_sprintf(buffer, "0x%x", nondir_mask | dir_mask); + } +} + +static void write_identifier(struct string_buffer *buffer, + const struct richace *ace) +{ + /* FIXME: switch to getpwuid_r() and getgrgid_r() here. */ + + if (ace->e_flags & ACE4_SPECIAL_WHO) { + char *dup, *c; + + dup = alloca(strlen(ace->u.e_who) + 1); + strcpy(dup, ace->u.e_who); + for (c = dup; *c; c++) + *c = tolower(*c); + + buffer_sprintf(buffer, "%s", dup); + } else if (ace->e_flags & ACE4_IDENTIFIER_GROUP) { + struct group *group = getgrgid(ace->u.e_id); + + if (group) + buffer_sprintf(buffer, "%s", group->gr_name); + else + buffer_sprintf(buffer, "%d", ace->u.e_id); + } else { + struct passwd *passwd = getpwuid(ace->u.e_id); + + if (passwd) + buffer_sprintf(buffer, "%s", passwd->pw_name); + else + buffer_sprintf(buffer, "%d", ace->u.e_id); + } +} + +char *richacl_to_text(const struct richacl *acl, int fmt) +{ + struct string_buffer *buffer; + const struct richace *ace; + int fmt2; + + buffer = alloc_string_buffer(128); + if (!buffer) + return NULL; + + write_acl_flags(buffer, acl->a_flags, fmt); + if (fmt & RICHACL_TEXT_SHOW_MASKS) { + unsigned int allowed = 0; + + fmt2 = fmt; + richacl_for_each_entry(ace, acl) { + if (richace_is_inherit_only(ace)) + continue; + + if (richace_is_allow(ace)) + allowed |= ace->e_mask; + + if (ace->e_flags & ACE4_FILE_INHERIT_ACE) + fmt2 |= RICHACL_TEXT_FILE_CONTEXT; + if (ace->e_flags & ACE4_DIRECTORY_INHERIT_ACE) + fmt2 |= RICHACL_TEXT_DIRECTORY_CONTEXT; + } + + if (!(fmt & RICHACL_TEXT_SIMPLIFY)) + allowed = ~0; + + buffer_sprintf(buffer, "owner:"); + write_mask(buffer, acl->a_owner_mask & allowed, fmt2); + buffer_sprintf(buffer, "::mask\n"); + buffer_sprintf(buffer, "group:"); + write_mask(buffer, acl->a_group_mask & allowed, fmt2); + buffer_sprintf(buffer, "::mask\n"); + buffer_sprintf(buffer, "other:"); + write_mask(buffer, acl->a_other_mask & allowed, fmt2); + buffer_sprintf(buffer, "::mask\n"); + } + + richacl_for_each_entry(ace, acl) { + write_identifier(buffer, ace); + buffer_sprintf(buffer, ":"); + + fmt2 = fmt; + if (ace->e_flags & ACE4_INHERIT_ONLY_ACE) + fmt2 &= ~(RICHACL_TEXT_FILE_CONTEXT | + RICHACL_TEXT_DIRECTORY_CONTEXT); + if (ace->e_flags & ACE4_FILE_INHERIT_ACE) + fmt2 |= RICHACL_TEXT_FILE_CONTEXT; + if (ace->e_flags & ACE4_DIRECTORY_INHERIT_ACE) + fmt2 |= RICHACL_TEXT_DIRECTORY_CONTEXT; + + write_mask(buffer, ace->e_mask, fmt2); + buffer_sprintf(buffer, ":"); + write_ace_flags(buffer, ace->e_flags, fmt2); + buffer_sprintf(buffer, ":"); + write_type(buffer, ace->e_type); + buffer_sprintf(buffer, "\n"); + } + + if (string_buffer_okay(buffer)) { + char *str = realloc(buffer->buffer, buffer->offset + 1); + if (str) + return str; + } + + free_string_buffer(buffer); + errno = ENOMEM; + return NULL; +} + +static int acl_flags_from_text(const char *str, struct richacl *acl, + void (*error)(const char *, ...)) +{ + char *dup, *end; + + end = alloca(strlen(str) + 1); + strcpy(end, str); + + acl->a_flags = 0; + while ((dup = end)) { + char *c; + unsigned long l; + int i; + + while (*dup == '/') + dup++; + end = strchr(dup, '/'); + if (end) + *end++ = 0; + if (!*dup) + break; + + l = strtoul(str, &c, 0); + if (*c == 0) { + acl->a_flags |= l; + continue; + } + + /* Recognize flag mnemonics */ + for (i = 0; i < ARRAY_SIZE(acl_flag_bits); i++) { + if (!strcasecmp(dup, acl_flag_bits[i].a_name)) { + acl->a_flags |= acl_flag_bits[i].a_flag; + break; + } + } + if (i != ARRAY_SIZE(acl_flag_bits)) + continue; + + /* Recognize single-character flags */ + for (c = dup; *c; c++) { + for (i = 0; i < ARRAY_SIZE(acl_flag_bits); i++) { + if (*c == acl_flag_bits[i].a_char) { + acl->a_flags |= acl_flag_bits[i].a_flag; + break; + } + } + if (i != ARRAY_SIZE(acl_flag_bits)) + continue; + + error("Invalid acl flag `%s'\n", c); + return -1; + } + } + + return 0; +} + +static int identifier_from_text(const char *str, struct richace *ace, + void (*error)(const char *, ...)) +{ + char *c; + unsigned long l; + + c = strchr(str, '@'); + if (c) { + char *dup; + + if (c[1]) { + error("Domain name not supported in `%s'\n", str); + goto fail; + } + + /* Ignore case in special identifiers. */ + dup = alloca(strlen(str) + 1); + strcpy(dup, str); + for (c = dup; *c; c++) + *c = toupper(*c); + + if (richace_set_who(ace, dup)) { + error("Special user `%s' not supported\n", str); + goto fail; + } + return 0; + } + l = strtoul(str, &c, 0); + if (*c == 0) { + ace->u.e_id = l; + return 0; + } + if (ace->e_flags & ACE4_IDENTIFIER_GROUP) { + struct group *group = getgrnam(str); + + if (!group) { + error("Group `%s' does not exist\n", str); + goto fail; + } + ace->u.e_id = group->gr_gid; + return 0; + } else { + struct passwd *passwd = getpwnam(str); + + if (!passwd) { + error("User `%s' does not exist\n", str); + goto fail; + } + ace->u.e_id = passwd->pw_uid; + return 0; + } +fail: + return -1; +} + +static int type_from_text(const char *str, struct richace *ace, + void (*error)(const char *, ...)) +{ + char *c; + int i; + unsigned long l; + + l = strtoul(str, &c, 0); + if (*c == 0) { + ace->e_type = l; + return 0; + } + + /* Recognize type mnemonic */ + for (i = 0; i < ARRAY_SIZE(type_values); i++) { + if (!strcasecmp(str, type_values[i].e_name)) { + ace->e_type = type_values[i].e_type; + return 0; + } + } + error("Invalid entry type `%s'\n", str); + return -1; +} + +static int ace_flags_from_text(const char *str, struct richace *ace, + void (*error)(const char *, ...)) +{ + char *dup, *end; + + end = alloca(strlen(str) + 1); + strcpy(end, str); + + ace->e_flags = 0; + while ((dup = end)) { + char *c; + unsigned long l; + int i; + + while (*dup == '/') + dup++; + end = strchr(dup, '/'); + if (end) + *end++ = 0; + if (!*dup) + break; + + l = strtoul(str, &c, 0); + if (*c == 0) { + ace->e_flags |= l; + continue; + } + + /* Recognize flag mnemonics */ + for (i = 0; i < ARRAY_SIZE(ace_flag_bits); i++) { + if (!strcasecmp(dup, ace_flag_bits[i].e_name)) { + ace->e_flags |= ace_flag_bits[i].e_flag; + break; + } + } + if (i != ARRAY_SIZE(ace_flag_bits)) + continue; + + /* Recognize single-character flags */ + for (c = dup; *c; c++) { + for (i = 0; i < ARRAY_SIZE(ace_flag_bits); i++) { + if (*c == ace_flag_bits[i].e_char) { + ace->e_flags |= ace_flag_bits[i].e_flag; + break; + } + } + if (i != ARRAY_SIZE(ace_flag_bits)) + continue; + + error("Invalid entry flag `%s'\n", c); + return -1; + } + } + + return 0; +} + +static int mask_from_text(const char *str, unsigned int *mask, + void (*error)(const char *, ...)) +{ + char *dup, *end; + + end = alloca(strlen(str) + 1); + strcpy(end, str); + + *mask = 0; + while ((dup = end)) { + char *c; + unsigned long l; + int i; + + while (*dup == '/') + dup++; + end = strchr(dup, '/'); + if (end) + *end++ = 0; + if (!*dup) + break; + + l = strtoul(dup, &c, 0); + if (*c == 0) { + *mask |= l; + continue; + } + + /* Recognize mask mnemonics */ + for (i = 0; i < ARRAY_SIZE(mask_bits); i++) { + if (!strcasecmp(dup, mask_bits[i].e_name)) { + *mask |= mask_bits[i].e_mask; + break; + } + } + if (i != ARRAY_SIZE(mask_bits)) + continue; + + /* Recognize single-character masks */ + for (c = dup; *c; c++) { + for (i = 0; i < ARRAY_SIZE(mask_bits); i++) { + if (*c == mask_bits[i].e_char) { + *mask |= mask_bits[i].e_mask; + break; + } + } + if (i != ARRAY_SIZE(mask_bits)) + continue; + + error("Invalid access mask `%s'\n", dup); + return -1; + } + } + + return 0; +} + +struct richacl *richacl_from_text(const char *str, int *pflags, + void (*error)(const char *, ...)) +{ + char *who_str = NULL, *mask_str = NULL, *flags_str = NULL, + *type_str = NULL; + struct richacl *acl; + int flags = 0; + + acl = richacl_alloc(0); + if (!acl) + return NULL; + + while (*str) { + struct richacl *acl2; + struct richace *ace; + unsigned int mask; + const char *entry, *c; + + while (isspace(*str) || *str == ',') + str++; + if (!*str) + break; + + entry = str; + c = strchr(str, ':'); + if (!c) + goto fail_syntax; + who_str = strndup(str, c - str); + if (!who_str) + goto fail; + str = c + 1; + + if (!strcasecmp(who_str, "FLAGS")) { + for (c = str; *c; c++) { + if (*c == ':' || *c == ',' || isspace(*c)) + break; + } + mask_str = strndup(str, c - str); + if (!mask_str) + goto fail; + if (*c != ':') { + if (acl_flags_from_text(mask_str, acl, error)) + goto fail_einval; + flags |= RICHACL_TEXT_FLAGS; + str = c; + goto free_mask_str; + } else { + free(mask_str); + mask_str = NULL; + } + } + + c = strchr(str, ':'); + if (!c) + goto fail_syntax; + mask_str = strndup(str, c - str); + if (!mask_str) + goto fail; + str = c + 1; + + c = strchr(str, ':'); + if (!c) + goto fail_syntax; + flags_str = strndup(str, c - str); + if (!flags_str) + goto fail; + str = c + 1; + + for (c = str; *c; c++) { + if (*c == ',' || isspace(*c)) + break; + } + type_str = strndup(str, c - str); + if (!type_str) + goto fail; + str = c; + + if (mask_from_text(mask_str, &mask, error)) + goto fail_einval; + if (!strcasecmp(type_str, "MASK")) { + if (!strcasecmp(who_str, "OWNER")) { + acl->a_owner_mask = mask; + flags |= RICHACL_TEXT_OWNER_MASK; + } else if (!strcasecmp(who_str, "GROUP")) { + acl->a_group_mask = mask; + flags |= RICHACL_TEXT_GROUP_MASK; + } else if (!strcasecmp(who_str, "OTHER")) { + acl->a_other_mask = mask; + flags |= RICHACL_TEXT_OTHER_MASK; + } else { + error("Invalid file mask `%s'\n", + who_str); + goto fail_einval; + } + } else { + size_t size = sizeof(struct richacl) + (acl->a_count + + 1) * sizeof(struct richace); + acl2 = realloc(acl, size); + if (!acl2) + goto fail; + acl = acl2; + memset(acl->a_entries + acl->a_count, 0, + sizeof(struct richace)); + acl->a_count++; + + ace = acl->a_entries + acl->a_count - 1; + ace->e_mask = mask; + if (ace_flags_from_text(flags_str, ace, error)) + goto fail_einval; + if (identifier_from_text(who_str, ace, error)) + goto fail_einval; + if (type_from_text(type_str, ace, error)) + goto fail_einval; + } + + free(type_str); + type_str = NULL; + free(flags_str); + flags_str = NULL; + free_mask_str: + free(mask_str); + mask_str = NULL; + free(who_str); + who_str = NULL; + continue; + + fail_syntax: + for (c = entry; *c && !(isspace(*c) || *c == ','); c++) + ; + error("Invalid entry `%.*s'\n", c - entry, entry); + goto fail_einval; + } + + if (pflags) + *pflags = flags; + return acl; + +fail_einval: + errno = EINVAL; + +fail: + free(type_str); + free(flags_str); + free(mask_str); + free(who_str); + richacl_free(acl); + return NULL; +} + +int richace_set_who(struct richace *ace, const char *who) +{ + if (!strcmp(who, richace_owner_who)) + who = richace_owner_who; + else if (!strcmp(who, richace_group_who)) + who = richace_group_who; + else if (!strcmp(who, richace_everyone_who)) + who = richace_everyone_who; + else + return -1; + + ace->u.e_who = who; + ace->e_flags |= ACE4_SPECIAL_WHO; + /* + * Also clear the ACE4_IDENTIFIER_GROUP flag for ACEs with a special + * who value: richace_is_same_identifier() relies on that. + */ + ace->e_flags &= ~ACE4_IDENTIFIER_GROUP; + return 0; +} + +void richace_set_uid(struct richace *ace, uid_t uid) +{ + ace->u.e_id = uid; + ace->e_flags &= ~(ACE4_SPECIAL_WHO | ACE4_IDENTIFIER_GROUP); +} + +void richace_set_gid(struct richace *ace, gid_t gid) +{ + ace->u.e_id = gid; + ace->e_flags |= ACE4_IDENTIFIER_GROUP; + ace->e_flags &= ~ACE4_SPECIAL_WHO; +} + +void richace_copy(struct richace *dst, const struct richace *src) +{ + memcpy(dst, src, sizeof(struct richace)); +} + +static unsigned int richacl_mode_to_mask(mode_t mode) +{ + unsigned int mask = ACE4_POSIX_ALWAYS_ALLOWED; + + if (mode & S_IROTH) + mask |= ACE4_POSIX_MODE_READ; + if (mode & S_IWOTH) + mask |= ACE4_POSIX_MODE_WRITE; + if (mode & S_IXOTH) + mask |= ACE4_POSIX_MODE_EXEC; + + return mask; +} + +struct richacl *richacl_from_mode(mode_t mode) +{ + struct richacl *acl; + struct richace *ace; + + acl = richacl_alloc(1); + if (!acl) + return NULL; + ace = acl->a_entries; + + acl->a_owner_mask = richacl_mode_to_mask(mode >> 6); + acl->a_group_mask = richacl_mode_to_mask(mode >> 3); + acl->a_other_mask = richacl_mode_to_mask(mode); + + ace->e_type = ACE4_ACCESS_ALLOWED_ACE_TYPE; + ace->e_flags = ACE4_SPECIAL_WHO; + ace->e_mask = ACE4_VALID_MASK; + ace->u.e_who = richace_everyone_who; + + if (richacl_apply_masks(&acl)) { + free(acl); + acl = NULL; + } + return acl; +} diff --git a/lib/richacl_compat.c b/lib/richacl_compat.c new file mode 100644 index 0000000..1890ae1 --- /dev/null +++ b/lib/richacl_compat.c @@ -0,0 +1,610 @@ +/* + * Copyright (C) 2006 Andreas Gruenbacher + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2, or (at your option) any + * later version. + * + * This program 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 + * General Public License for more details. + */ + +#include +#include "richacl.h" +#include "richacl-internal.h" + +/** + * struct richacl_alloc - remember how many entries are actually allocated + * @acl: acl with a_count <= @count + * @count: the actual number of entries allocated in @acl + * + * We pass around this structure while modifying an acl, so that we do + * not have to reallocate when we remove existing entries followed by + * adding new entries. + */ +struct richacl_alloc { + struct richacl *acl; + unsigned int count; +}; + +/** + * richacl_delete_entry - delete an entry in an acl + * @x: acl and number of allocated entries + * @ace: an entry in @x->acl + * + * Updates @ace so that it points to the entry before the deleted entry + * on return. (When deleting the first entry, @ace will point to the + * (non-existant) entry before the first entry). This behavior is the + * expected behavior when deleting entries while forward iterating over + * an acl. + */ +static void +richacl_delete_entry(struct richacl_alloc *x, struct richace **ace) +{ + void *end = x->acl->a_entries + x->acl->a_count; + + memmove(*ace, *ace + 1, end - (void *)(*ace + 1)); + (*ace)--; + x->acl->a_count--; +} + +/** + * richacl_insert_entry - insert an entry in an acl + * @x: acl and number of allocated entries + * @ace: entry before which the new entry shall be inserted + * + * Insert a new entry in @x->acl at position @ace, and zero-initialize + * it. This may require reallocating @x->acl. + */ +static int +richacl_insert_entry(struct richacl_alloc *x, struct richace **ace) +{ + int n = *ace - x->acl->a_entries; + + if (x->count == x->acl->a_count) { + size_t size = sizeof(struct richacl) + + (x->count + 1) * sizeof(struct richace); + struct richacl *acl2; + + acl2 = realloc(x->acl, size); + if (!acl2) + return -1; + x->count++; + x->acl = acl2; + *ace = acl2->a_entries + n; + } + memmove(*ace + 1, *ace, sizeof(struct richace) * (x->acl->a_count - n)); + memset(*ace, 0, sizeof(struct richace)); + x->acl->a_count++; + return 0; +} + +/** + * richace_change_mask - change the mask in @ace to @mask + * @x: acl and number of allocated entries + * @ace: entry to modify + * @mask: new mask for @ace + * + * Set the effective mask of @ace to @mask. This will require splitting + * off a separate acl entry if @ace is inheritable. In that case, the + * effective- only acl entry is inserted after the inheritable acl + * entry, end the inheritable acl entry is set to inheritable-only. If + * @mode is 0, either set the original acl entry to inheritable-only if + * it was inheritable, or remove it otherwise. The returned @ace points + * to the modified or inserted effective-only acl entry if that entry + * exists, to the entry that has become inheritable-only, or else to the + * previous entry in the acl. This is the expected behavior when + * modifying masks while forward iterating over an acl. + */ +static int +richace_change_mask(struct richacl_alloc *x, struct richace **ace, + unsigned int mask) +{ + if (mask && (*ace)->e_mask == mask) + return 0; + if (mask & ~ACE4_POSIX_ALWAYS_ALLOWED) { + if (richace_is_inheritable(*ace)) { + if (richacl_insert_entry(x, ace)) + return -1; + memcpy(*ace, *ace + 1, sizeof(struct richace)); + (*ace)->e_flags |= ACE4_INHERIT_ONLY_ACE; + (*ace)++; + richace_clear_inheritance_flags(*ace); + } + (*ace)->e_mask = mask; + } else { + if (richace_is_inheritable(*ace)) + (*ace)->e_flags |= ACE4_INHERIT_ONLY_ACE; + else + richacl_delete_entry(x, ace); + } + return 0; +} + +/** + * richacl_move_everyone_aces_down - move everyone@ acl entries to the end + * @x: acl and number of allocated entries + * + * Move all everyone acl entries to the bottom of the acl so that only a + * single everyone@ allow acl entry remains at the end, and update the + * mask fields of all acl entries on the way. If everyone@ is not + * granted any permissions, no empty everyone@ acl entry is inserted. + * + * This transformation does not modify the permissions that the acl + * grants, but we need it to simplify successive transformations. + */ +static int +richacl_move_everyone_aces_down(struct richacl_alloc *x) +{ + struct richace *ace; + unsigned int allowed = 0, denied = 0; + + richacl_for_each_entry(ace, x->acl) { + if (richace_is_inherit_only(ace)) + continue; + if (richace_is_everyone(ace)) { + if (richace_is_allow(ace)) + allowed |= (ace->e_mask & ~denied); + else if (richace_is_deny(ace)) + denied |= (ace->e_mask & ~allowed); + else + continue; + if (richace_change_mask(x, &ace, 0)) + return -1; + } else { + if (richace_is_allow(ace)) { + if (richace_change_mask(x, &ace, allowed | + (ace->e_mask & ~denied))) + return -1; + } else if (richace_is_deny(ace)) { + if (richace_change_mask(x, &ace, denied | + (ace->e_mask & ~allowed))) + return -1; + } + } + } + if (allowed & ~ACE4_POSIX_ALWAYS_ALLOWED) { + struct richace *last_ace = ace - 1; + + if (richace_is_everyone(last_ace) && + richace_is_allow(last_ace) && + richace_is_inherit_only(last_ace) && + last_ace->e_mask == allowed) + last_ace->e_flags &= ~ACE4_INHERIT_ONLY_ACE; + else { + if (richacl_insert_entry(x, &ace)) + return -1; + ace->e_type = ACE4_ACCESS_ALLOWED_ACE_TYPE; + ace->e_flags = ACE4_SPECIAL_WHO; + ace->e_mask = allowed; + ace->u.e_who = richace_everyone_who; + } + } + return 0; +} + +/** + * __richacl_propagate_everyone - propagate everyone@ mask flags up for @who + * @x: acl and number of allocated entries + * @who: identifier to propagate mask flags for + * @allow: mask flags to propagate up + * + * Propagate mask flags from the trailing everyone@ allow acl entry up + * for the specified @who. + * + * The idea here is to precede the trailing EVERYONE@ ALLOW entry by an + * additional @who ALLOW entry, but with the following optimizations: + * (1) we don't bother setting any flags in the new @who ALLOW entry + * that has already been allowed or denied by a previous @who entry, (2) + * we merge the new @who entry with a previous @who entry if there is + * such a previous @who entry and there are no intervening DENY entries + * with mask flags that overlap the flags we care about. + */ +static int +__richacl_propagate_everyone(struct richacl_alloc *x, struct richace *who, + unsigned int allow) +{ + struct richace *allow_last = NULL, *ace; + + /* Remove the mask flags from allow that are already determined for + this who value, and figure out if there is an ALLOW entry for + this who value that is "reachable" from the trailing EVERYONE@ + ALLOW ACE. */ + richacl_for_each_entry(ace, x->acl) { + if (richace_is_inherit_only(ace)) + continue; + if (richace_is_allow(ace)) { + if (richace_is_same_identifier(ace, who)) { + allow &= ~ace->e_mask; + allow_last = ace; + } + } else if (richace_is_deny(ace)) { + if (richace_is_same_identifier(ace, who)) + allow &= ~ace->e_mask; + if (allow & ace->e_mask) + allow_last = NULL; + } + } + + if (allow) { + if (allow_last) + return richace_change_mask(x, &allow_last, + allow_last->e_mask | allow); + else { + struct richace who_copy; + + ace = x->acl->a_entries + x->acl->a_count - 1; + memcpy(&who_copy, who, sizeof(struct richace)); + if (richacl_insert_entry(x, &ace)) + return -1; + memcpy(ace, &who_copy, sizeof(struct richace)); + ace->e_type = ACE4_ACCESS_ALLOWED_ACE_TYPE; + richace_clear_inheritance_flags(ace); + ace->e_mask = allow; + } + } + return 0; +} + +/** + * richacl_propagate_everyone - propagate everyone@ mask flags up the acl + * @x: acl and number of allocated entries + * + * Make sure for owner@, group@, and all other users, groups, and + * special identifiers that they are allowed or denied all permissions + * that are granted be the trailing everyone@ acl entry. If they are + * not, try to add the missing permissions to existing allow acl entries + * for those users, or introduce additional acl entries if that is not + * possible. + * + * We do this so that no mask flags will get lost when finally applying + * the file masks to the acl entries: otherwise, with an other file mask + * that is more restrictive than the owner and/or group file mask, mask + * flags that were allowed to processes in the owner and group classes + * and that the other mask denies would be lost. For example, the + * following two acls show the problem when mode 0664 is applied to + * them: + * + * masking without propagation (wrong) + * =========================================================== + * joe:r::allow => joe:r::allow + * everyone@:rwx::allow => everyone@:r::allow + * ----------------------------------------------------------- + * joe:w::deny => joe:w::deny + * everyone@:rwx::allow everyone@:r::allow + * + * Note that the permissions of joe end up being more restrictive than + * what the acl would allow when first computing the allowed flags and + * then applying the respective mask. With propagation of permissions, + * we get: + * + * masking after propagation (correct) + * =========================================================== + * joe:r::allow => joe:rw::allow + * owner@:rw::allow + * group@:rw::allow + * everyone@:rwx::allow everyone@:r::allow + * ----------------------------------------------------------- + * joe:w::deny => owner@:x::deny + * joe:w::deny + * owner@:rw::allow + * owner@:rw::allow + * joe:r::allow + * everyone@:rwx::allow everyone@:r::allow + * + * The examples show the acls that would result from propagation with no + * masking performed. In fact, we do apply the respective mask to the + * acl entries before computing the propagation because this will save + * us from adding acl entries that would end up with empty mask fields + * after applying the masks. + * + * It is ensured that no more than one entry will be inserted for each + * who value, no matter how many entries each who value has already. + */ +static int +richacl_propagate_everyone(struct richacl_alloc *x) +{ + int write_through = x->acl->a_flags & ACL4_WRITE_THROUGH; + struct richace who = { .e_flags = ACE4_SPECIAL_WHO }; + struct richace *ace; + unsigned int owner_allow, group_allow; + + if (!((x->acl->a_owner_mask | x->acl->a_group_mask) & + ~x->acl->a_other_mask)) + return 0; + if (!x->acl->a_count) + return 0; + ace = x->acl->a_entries + x->acl->a_count - 1; + if (richace_is_inherit_only(ace) || !richace_is_everyone(ace)) + return 0; + if (!(ace->e_mask & ~x->acl->a_other_mask)) { + /* None of the allowed permissions will get masked. */ + return 0; + } + owner_allow = ace->e_mask & x->acl->a_owner_mask; + group_allow = ace->e_mask & x->acl->a_group_mask; + + /* Propagate everyone@ permissions through to owner@. */ + if (owner_allow && !write_through && + (x->acl->a_owner_mask & ~x->acl->a_other_mask)) { + who.u.e_who = richace_owner_who; + if (__richacl_propagate_everyone(x, &who, owner_allow)) + return -1; + } + + if (group_allow && (x->acl->a_group_mask & ~x->acl->a_other_mask)) { + int n; + + if (!write_through) { + /* Propagate everyone@ permissions through to group@. */ + who.u.e_who = richace_group_who; + if (__richacl_propagate_everyone(x, &who, group_allow)) + return -1; + } + + /* Start from the entry before the trailing EVERYONE@ ALLOW + entry. We will not hit EVERYONE@ entries in the loop. */ + for (n = x->acl->a_count - 2; n != -1; n--) { + ace = x->acl->a_entries + n; + + if (richace_is_inherit_only(ace) || + richace_is_owner(ace) || + richace_is_group(ace)) + continue; + if (richace_is_allow(ace) || richace_is_deny(ace)) { + /* Any inserted entry will end up below the + current entry. */ + if (__richacl_propagate_everyone(x, ace, group_allow)) + return -1; + } + } + } + return 0; +} + +/** + * __richacl_apply_masks - apply the masks to the acl entries + * @x: acl and number of allocated entries + * + * Apply the owner file mask to owner@ entries, the intersection of the + * group and other file masks to everyone@ entries, and the group file + * mask to all other entries. + */ +static int +__richacl_apply_masks(struct richacl_alloc *x) +{ + struct richace *ace; + + richacl_for_each_entry(ace, x->acl) { + unsigned int mask; + + if (richace_is_inherit_only(ace) || !richace_is_allow(ace)) + continue; + if (richace_is_owner(ace)) + mask = x->acl->a_owner_mask; + else if (richace_is_everyone(ace)) + mask = x->acl->a_other_mask; + else + mask = x->acl->a_group_mask; + if (richace_change_mask(x, &ace, ace->e_mask & mask)) + return -1; + } + return 0; +} + +/** + * richacl_max_allowed - maximum mask flags that anybody is allowed + */ +static unsigned int +richacl_max_allowed(struct richacl *acl) +{ + struct richace *ace; + unsigned int allowed = 0; + + richacl_for_each_entry_reverse(ace, acl) { + if (richace_is_inherit_only(ace)) + continue; + if (richace_is_allow(ace)) + allowed |= ace->e_mask; + else if (richace_is_deny(ace)) { + if (richace_is_everyone(ace)) + allowed &= ~ace->e_mask; + } + } + return allowed; +} + +/** + * richacl_isolate_owner_class - limit the owner class to the owner file mask + * @x: acl and number of allocated entries + * + * Make sure the owner class (owner@) is granted no more than the owner + * mask by first checking which permissions anyone is granted, and then + * denying owner@ all permissions beyond that. + */ +static int +richacl_isolate_owner_class(struct richacl_alloc *x) +{ + struct richace *ace; + unsigned int allowed = 0; + + allowed = richacl_max_allowed(x->acl); + if (allowed & ~x->acl->a_owner_mask) { + /* Figure out if we can update an existig OWNER@ DENY entry. */ + richacl_for_each_entry(ace, x->acl) { + if (richace_is_inherit_only(ace)) + continue; + if (richace_is_deny(ace)) { + if (richace_is_owner(ace)) + break; + } else if (richace_is_allow(ace)) { + ace = x->acl->a_entries + x->acl->a_count; + break; + } + } + if (ace != x->acl->a_entries + x->acl->a_count) { + if (richace_change_mask(x, &ace, ace->e_mask | + (allowed & ~x->acl->a_owner_mask))) + return -1; + } else { + /* Insert an owner@ deny entry at the front. */ + ace = x->acl->a_entries; + if (richacl_insert_entry(x, &ace)) + return -1; + ace->e_type = ACE4_ACCESS_DENIED_ACE_TYPE; + ace->e_flags = ACE4_SPECIAL_WHO; + ace->e_mask = allowed & ~x->acl->a_owner_mask; + ace->u.e_who = richace_owner_who; + } + } + return 0; +} + +/** + * __richacl_isolate_who - isolate entry from EVERYONE@ ALLOW entry + * @x: acl and number of allocated entries + * @who: identifier to isolate + * @deny: mask flags this identifier should not be allowed + * + * Make sure that @who is not allowed any mask flags in @deny by checking + * which mask flags this identifier is allowed, and adding excess allowed + * mask flags to an existing DENY entry before the trailing EVERYONE@ ALLOW + * entry, or inserting such an entry. + */ +static int +__richacl_isolate_who(struct richacl_alloc *x, struct richace *who, + unsigned int deny) +{ + struct richace *ace; + unsigned int allowed = 0, n; + + /* Compute the mask flags granted to this who value. */ + richacl_for_each_entry_reverse(ace, x->acl) { + if (richace_is_inherit_only(ace)) + continue; + if (richace_is_same_identifier(ace, who)) { + if (richace_is_allow(ace)) + allowed |= ace->e_mask; + else if (richace_is_deny(ace)) + allowed &= ~ace->e_mask; + deny &= ~ace->e_mask; + } + } + if (!deny) + return 0; + + /* Figure out if we can update an existig DENY entry. Start + from the entry before the trailing EVERYONE@ ALLOW entry. We + will not hit EVERYONE@ entries in the loop. */ + for (n = x->acl->a_count - 2; n != -1; n--) { + ace = x->acl->a_entries + n; + if (richace_is_inherit_only(ace)) + continue; + if (richace_is_deny(ace)) { + if (richace_is_same_identifier(ace, who)) + break; + } else if (richace_is_allow(ace) && + (ace->e_mask & deny)) { + n = -1; + break; + } + } + if (n != -1) { + if (richace_change_mask(x, &ace, ace->e_mask | deny)) + return -1; + } else { + /* Insert a eny entry before the trailing EVERYONE@ DENY + entry. */ + struct richace who_copy; + + ace = x->acl->a_entries + x->acl->a_count - 1; + memcpy(&who_copy, who, sizeof(struct richace)); + if (richacl_insert_entry(x, &ace)) + return -1; + memcpy(ace, &who_copy, sizeof(struct richace)); + ace->e_type = ACE4_ACCESS_DENIED_ACE_TYPE; + richace_clear_inheritance_flags(ace); + ace->e_mask = deny; + } + return 0; +} + +/** + * richacl_isolate_group_class - limit the group class to the group file mask + * @x: acl and number of allocated entries + * + * Make sure the group class (all entries except owner@ and everyone@) is + * granted no more than the group mask by inserting DENY entries for group + * class entries where necessary. + */ +static int +richacl_isolate_group_class(struct richacl_alloc *x) +{ + struct richace who = { + .e_flags = ACE4_SPECIAL_WHO, + .u.e_who = richace_group_who, + }; + struct richace *ace; + unsigned int deny; + + if (!x->acl->a_count) + return 0; + ace = x->acl->a_entries + x->acl->a_count - 1; + if (richace_is_inherit_only(ace) || !richace_is_everyone(ace)) + return 0; + deny = ace->e_mask & ~x->acl->a_group_mask; + + if (deny) { + unsigned int n; + + if (__richacl_isolate_who(x, &who, deny)) + return -1; + + /* Start from the entry before the trailing EVERYONE@ ALLOW + entry. We will not hit EVERYONE@ entries in the loop. */ + for (n = x->acl->a_count - 2; n != -1; n--) { + ace = x->acl->a_entries + n; + + if (richace_is_inherit_only(ace) || + richace_is_owner(ace) || + richace_is_group(ace)) + continue; + if (__richacl_isolate_who(x, ace, deny)) + return -1; + } + } + return 0; +} + +/** + * richacl_apply_masks - apply the masks to the acl + * + * Apply the masks so that the acl allows no more flags than the + * intersection between the flags that the original acl allows and the + * mask matching the process. + * + * Note: this algorithm may push the number of entries in the acl above + * ACL4_XATTR_MAX_COUNT, so a read-modify-write cycle would fail. + */ +int +richacl_apply_masks(struct richacl **acl) +{ + struct richacl_alloc x = { + .acl = *acl, + .count = (*acl)->a_count, + }; + int retval = 0; + + if (richacl_move_everyone_aces_down(&x) || + richacl_propagate_everyone(&x) || + __richacl_apply_masks(&x) || + richacl_isolate_owner_class(&x) || + richacl_isolate_group_class(&x)) + retval = -1; + + *acl = x.acl; + return retval; +} diff --git a/src/nfs4acl.c b/src/nfs4acl.c deleted file mode 100644 index 7e0ab58..0000000 --- a/src/nfs4acl.c +++ /dev/null @@ -1,413 +0,0 @@ -/* - * Copyright (C) 2006-2008 Andreas Gruenbacher - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License as published by the - * Free Software Foundation; either version 2, or (at your option) any - * later version. - * - * This program 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 - * General Public License for more details. - */ - -/* - * FIXME: Make ls show a `+' for nfs4acls (in coreutils). - */ - -#define _GNU_SOURCE -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "nfs4acl.h" -#include "string_buffer.h" - -static const char *progname; - -void printf_stderr(const char *fmt, ...) -{ - va_list ap; - - va_start(ap, fmt); - vfprintf(stderr, fmt, ap); - va_end(ap); -} - -int modify_nfs4acl(struct nfs4acl **acl2, struct nfs4acl *acl, int acl_has) -{ - struct nfs4ace *ace2, *ace; - - nfs4acl_for_each_entry(ace, acl) { - struct nfs4acl *acl3; - struct nfs4ace *ace3; - - nfs4acl_for_each_entry(ace2, *acl2) { - if (ace2->e_type == ace->e_type && - nfs4ace_is_same_identifier(ace, ace2)) { - ace2->e_mask = ace->e_mask; - ace2->e_flags = ace->e_flags; - break; - } - } - if (ace2 != (*acl2)->a_entries + (*acl2)->a_count) - continue; - - acl3 = nfs4acl_alloc((*acl2)->a_count + 1); - if (!acl3) - return -1; - ace3 = acl3->a_entries; - if (nfs4ace_is_deny(ace)) { - nfs4acl_for_each_entry(ace2, *acl2) { - if (!nfs4ace_is_deny(ace2)) - break; - nfs4ace_copy(ace3++, ace2); - } - nfs4ace_copy(ace3++, ace); - while (ace2 != (*acl2)->a_entries + (*acl2)->a_count) - nfs4ace_copy(ace3++, ace2++); - } else { - nfs4acl_for_each_entry(ace2, *acl2) - nfs4ace_copy(ace3++, ace2); - nfs4ace_copy(ace3++, ace); - } - - nfs4acl_free(*acl2); - *acl2 = acl3; - } - - if (acl_has & NFS4ACL_TEXT_FLAGS) - (*acl2)->a_flags = acl->a_flags; - - if (!((acl_has & NFS4ACL_TEXT_OWNER_MASK) && - (acl_has & NFS4ACL_TEXT_GROUP_MASK) && - (acl_has & NFS4ACL_TEXT_OTHER_MASK))) - nfs4acl_compute_max_masks(*acl2); - if (acl_has & NFS4ACL_TEXT_OWNER_MASK) - (*acl2)->a_owner_mask = acl->a_owner_mask; - if (acl_has & NFS4ACL_TEXT_GROUP_MASK) - (*acl2)->a_group_mask = acl->a_group_mask; - if (acl_has & NFS4ACL_TEXT_OTHER_MASK) - (*acl2)->a_other_mask = acl->a_other_mask; - - return 0; -} - -static struct nfs4acl *get_nfs4acl(const char *file, mode_t mode) -{ - struct nfs4acl *acl; - - acl = nfs4acl_get_file(file); - if (!acl && (errno == ENODATA || errno == ENOTSUP || errno == ENOSYS)) { - acl = nfs4acl_from_mode(mode); - } - return acl; -} - -static int print_nfs4acl(const char *file, struct nfs4acl **acl, int fmt) -{ - char *text; - - if (!(fmt & NFS4ACL_TEXT_SHOW_MASKS)) { - if (nfs4acl_apply_masks(acl)) - goto fail; - } - text = nfs4acl_to_text(*acl, fmt); - if (!text) - goto fail; - printf("%s:\n", file); - puts(text); - free(text); - return 0; - -fail: - return -1; -} - -int format_for_mode(mode_t mode) -{ - if (S_ISDIR(mode)) - return NFS4ACL_TEXT_DIRECTORY_CONTEXT; - else - return NFS4ACL_TEXT_FILE_CONTEXT; -} - -static struct option long_options[] = { - {"get", 0, 0, 'g'}, - {"modify", 1, 0, 'm'}, - {"modify-file", 1, 0, 'M'}, - {"set", 1, 0, 's'}, - {"set-file", 1, 0, 'S'}, - {"remove", 0, 0, 'r'}, - {"long", 0, 0, 'l'}, - {"raw", 0, 0, 1 }, - {"dry-run", 0, 0, 2 }, - {"version", 0, 0, 'v'}, - {"help", 0, 0, 'h'}, - { NULL, 0, 0, 0 } -}; - -static void synopsis(int help) -{ - FILE *file = help ? stdout : stderr; - - fprintf(file, "SYNOPSIS: %s options] {command} file ...\n", - basename(progname)); - if (!help) - exit(1); - fprintf(file, -"\n" -"Commands:\n" -" --get Display the ACL of file(s). Multiple ACL entries are separated\n" -" by newline.\n" -" --modify acl_entries\n" -" Modify the ACL of file(s) by replacing existing entries with\n" -" the entries in acl_entries, and adding all new entries.\n" -" --set acl Set the ACL of file(s) to acl. Multiple ACL entries are\n" -" separated by whitespace or commas.\n" -" --modify-file acl_entries_file, --set-file acl_file\n" -" Identical to --modify / --set, but read the ACL from a file\n" -" instead. If the file is `-', read from standard input.\n" -" --set-file acl_file\n" -" Identical to --set, but read the ACL from a file\n" -" instead. If the file is `-', read from standard input.\n" -" --delete-acl\n" -" Delete the ACL of file(s).\n" -" --version Display the version of %s and exit.\n" -" --help This help text.\n" -"\n" -"Options:\n" -" --long Display access masks and flags in their long form.\n" -"\n" -"ACL entries are represented by colon separated :::\n" -"fields. The field may be \"owner@\", \"group@\", \"everyone@\", a user\n" -"name or ID, or a group name or ID. Groups must have the identifier_group(g)\n" -"flag set in the field. The field may be \"allow\" or \"deny\".\n" -"The and fields are lists of single-letter abbreviations or\n" -"slash-separated names, or a combination of both.\n" -"\n" -"The supported values are:\n" -"\tread_data (r), list_directory (r), write_data (w), add_file (w),\n" -"\tappend_data (a), add_subdirectory (a), read_named_attrs (N),\n" -"\twrite_named_attrs (n), execute (x), delete_child (d),\n" -"\tread_attributes (T), write_attributes (t), delete (D),\n" -"\tread_acl (M), write_acl (m), take_ownership (o), synchronize (s)\n" -"\n" -"The supported values are:\n" -"\tfile_inherit_ace (f), directory_inherit_ace (d),\n" -"\tno_propagate_inherit_ace (n), inherit_only_ace (i),\n" -"\tidentifier_group (g), inherited_ace (a)\n" -"\n" -"Per-ACL flag values are:\n" -"\tauto_inherit (a), protected (p), defaulted (d), write_through (w)\n", - basename(progname)); - exit(0); -} - -int main(int argc, char *argv[]) -{ - int opt_get = 0, opt_remove = 0, opt_dry_run = 0; - int opt_modify = 0, opt_set = 0; - char *acl_text = NULL, *acl_file = NULL; - int format = NFS4ACL_TEXT_SIMPLIFY; - int status = 0; - int c; - - struct nfs4acl *acl = NULL; - int acl_has; - - progname = argv[0]; - - while ((c = getopt_long(argc, argv, "gm:M:s:S:nrlvh", - long_options, NULL)) != -1) { - switch(c) { - case 'g': - opt_get = 1; - break; - case 'm': - opt_modify = 1; - acl_text = optarg; - break; - - case 'M': - opt_modify = 1; - acl_file = optarg; - break; - case 's': - opt_set = 1; - acl_text = optarg; - break; - - case 'S': - opt_set = 1; - acl_file = optarg; - break; - - case 'r': - opt_remove = 1; - break; - - case 'l': - format |= NFS4ACL_TEXT_LONG; - break; - - case 'v': - printf("%s %s\n", basename(progname), VERSION); - exit(0); - - case 'h': - synopsis(1); - break; - - case 1: /* --raw */ - format |= NFS4ACL_TEXT_SHOW_MASKS; - format &= ~NFS4ACL_TEXT_SIMPLIFY; - break; - - case 2: /* --dry-run */ - opt_dry_run = 1; - break; - - default: - synopsis(0); - break; - } - } - if (opt_get + opt_remove + opt_modify + opt_set != 1 || - (acl_text ? 1 : 0) + (acl_file ? 1 : 0) > 1 || - optind == argc) - synopsis(argc == 1); - - if (acl_text) { - acl = nfs4acl_from_text(acl_text, &acl_has, printf_stderr); - if (!acl) - return 1; - } - - if (acl_file) { - struct string_buffer *buffer; - char buf[4097]; - FILE *file = stdin; - ssize_t sz; - - buffer = alloc_string_buffer(1024); - if (!buffer) { - perror(""); - return 1; - } - - if (strcmp(acl_file, "-")) { - file = fopen(acl_file, "r"); - if (!file) { - perror(acl_file); - return 1; - } - } - do { - sz = fread(buf, 1, sizeof(buf) - 1, file); - buf[sz] = 0; - buffer_sprintf(buffer, "%s", buf); - } while (!(feof(file) || ferror(file))); - fclose(file); - if (ferror(file)) { - perror(acl_file); - return 1; - } - - acl = nfs4acl_from_text(buffer->buffer, &acl_has, printf_stderr); - if (!acl) - return 1; - free_string_buffer(buffer); - } - - /* Compute all masks which haven't been set explicitly. */ - if (opt_set && acl && !((acl_has & NFS4ACL_TEXT_OWNER_MASK) && - (acl_has & NFS4ACL_TEXT_GROUP_MASK) && - (acl_has & NFS4ACL_TEXT_OTHER_MASK))) { - unsigned int owner_mask = acl->a_owner_mask; - unsigned int group_mask = acl->a_group_mask; - unsigned int other_mask = acl->a_other_mask; - - nfs4acl_compute_max_masks(acl); - if (acl_has & NFS4ACL_TEXT_OWNER_MASK) - acl->a_owner_mask = owner_mask; - if (acl_has & NFS4ACL_TEXT_GROUP_MASK) - acl->a_group_mask = group_mask; - if (acl_has & NFS4ACL_TEXT_OTHER_MASK) - acl->a_other_mask = other_mask; - } - - if (opt_dry_run && opt_set) { - const char *file = ""; - - if (print_nfs4acl(file, &acl, format | - NFS4ACL_TEXT_FILE_CONTEXT | - NFS4ACL_TEXT_DIRECTORY_CONTEXT)) { - perror(file); - return 1; - } - return 0; - } - - for (; optind < argc; optind++) { - const char *file = argv[optind]; - struct nfs4acl *acl2 = NULL; - - if (opt_set) { - if (nfs4acl_set_file(file, acl)) - goto fail; - } else if (opt_modify) { - struct stat st; - - if (stat(file, &st)) - goto fail; - acl2 = get_nfs4acl(file, st.st_mode); - if (!acl2) - goto fail; - if (modify_nfs4acl(&acl2, acl, acl_has)) - goto fail; - if (opt_dry_run) { - if (print_nfs4acl(file, &acl2, format | - format_for_mode(st.st_mode))) - goto fail; - } else { - if (nfs4acl_set_file(file, acl2)) - goto fail; - } - } else if (opt_remove) { - if (removexattr(file, "system.nfs4acl")) { - if (errno != ENODATA) - goto fail; - } - } else { - struct stat st; - - if (stat(file, &st)) - goto fail; - acl2 = get_nfs4acl(file, st.st_mode); - if (!acl2) - goto fail; - if (print_nfs4acl(file, &acl2, format | - format_for_mode(st.st_mode))) - goto fail; - } - nfs4acl_free(acl2); - continue; - - fail: - nfs4acl_free(acl2); - perror(file); - status = 1; - } - - nfs4acl_free(acl); - return status; -} diff --git a/src/richacl.c b/src/richacl.c new file mode 100644 index 0000000..ab447ef --- /dev/null +++ b/src/richacl.c @@ -0,0 +1,413 @@ +/* + * Copyright (C) 2006-2008 Andreas Gruenbacher + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2, or (at your option) any + * later version. + * + * This program 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 + * General Public License for more details. + */ + +/* + * FIXME: Make ls show a `+' for richacls (in coreutils). + */ + +#define _GNU_SOURCE +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "richacl.h" +#include "string_buffer.h" + +static const char *progname; + +void printf_stderr(const char *fmt, ...) +{ + va_list ap; + + va_start(ap, fmt); + vfprintf(stderr, fmt, ap); + va_end(ap); +} + +int modify_richacl(struct richacl **acl2, struct richacl *acl, int acl_has) +{ + struct richace *ace2, *ace; + + richacl_for_each_entry(ace, acl) { + struct richacl *acl3; + struct richace *ace3; + + richacl_for_each_entry(ace2, *acl2) { + if (ace2->e_type == ace->e_type && + richace_is_same_identifier(ace, ace2)) { + ace2->e_mask = ace->e_mask; + ace2->e_flags = ace->e_flags; + break; + } + } + if (ace2 != (*acl2)->a_entries + (*acl2)->a_count) + continue; + + acl3 = richacl_alloc((*acl2)->a_count + 1); + if (!acl3) + return -1; + ace3 = acl3->a_entries; + if (richace_is_deny(ace)) { + richacl_for_each_entry(ace2, *acl2) { + if (!richace_is_deny(ace2)) + break; + richace_copy(ace3++, ace2); + } + richace_copy(ace3++, ace); + while (ace2 != (*acl2)->a_entries + (*acl2)->a_count) + richace_copy(ace3++, ace2++); + } else { + richacl_for_each_entry(ace2, *acl2) + richace_copy(ace3++, ace2); + richace_copy(ace3++, ace); + } + + richacl_free(*acl2); + *acl2 = acl3; + } + + if (acl_has & RICHACL_TEXT_FLAGS) + (*acl2)->a_flags = acl->a_flags; + + if (!((acl_has & RICHACL_TEXT_OWNER_MASK) && + (acl_has & RICHACL_TEXT_GROUP_MASK) && + (acl_has & RICHACL_TEXT_OTHER_MASK))) + richacl_compute_max_masks(*acl2); + if (acl_has & RICHACL_TEXT_OWNER_MASK) + (*acl2)->a_owner_mask = acl->a_owner_mask; + if (acl_has & RICHACL_TEXT_GROUP_MASK) + (*acl2)->a_group_mask = acl->a_group_mask; + if (acl_has & RICHACL_TEXT_OTHER_MASK) + (*acl2)->a_other_mask = acl->a_other_mask; + + return 0; +} + +static struct richacl *get_richacl(const char *file, mode_t mode) +{ + struct richacl *acl; + + acl = richacl_get_file(file); + if (!acl && (errno == ENODATA || errno == ENOTSUP || errno == ENOSYS)) { + acl = richacl_from_mode(mode); + } + return acl; +} + +static int print_richacl(const char *file, struct richacl **acl, int fmt) +{ + char *text; + + if (!(fmt & RICHACL_TEXT_SHOW_MASKS)) { + if (richacl_apply_masks(acl)) + goto fail; + } + text = richacl_to_text(*acl, fmt); + if (!text) + goto fail; + printf("%s:\n", file); + puts(text); + free(text); + return 0; + +fail: + return -1; +} + +int format_for_mode(mode_t mode) +{ + if (S_ISDIR(mode)) + return RICHACL_TEXT_DIRECTORY_CONTEXT; + else + return RICHACL_TEXT_FILE_CONTEXT; +} + +static struct option long_options[] = { + {"get", 0, 0, 'g'}, + {"modify", 1, 0, 'm'}, + {"modify-file", 1, 0, 'M'}, + {"set", 1, 0, 's'}, + {"set-file", 1, 0, 'S'}, + {"remove", 0, 0, 'r'}, + {"long", 0, 0, 'l'}, + {"raw", 0, 0, 1 }, + {"dry-run", 0, 0, 2 }, + {"version", 0, 0, 'v'}, + {"help", 0, 0, 'h'}, + { NULL, 0, 0, 0 } +}; + +static void synopsis(int help) +{ + FILE *file = help ? stdout : stderr; + + fprintf(file, "SYNOPSIS: %s options] {command} file ...\n", + basename(progname)); + if (!help) + exit(1); + fprintf(file, +"\n" +"Commands:\n" +" --get Display the ACL of file(s). Multiple ACL entries are separated\n" +" by newline.\n" +" --modify acl_entries\n" +" Modify the ACL of file(s) by replacing existing entries with\n" +" the entries in acl_entries, and adding all new entries.\n" +" --set acl Set the ACL of file(s) to acl. Multiple ACL entries are\n" +" separated by whitespace or commas.\n" +" --modify-file acl_entries_file, --set-file acl_file\n" +" Identical to --modify / --set, but read the ACL from a file\n" +" instead. If the file is `-', read from standard input.\n" +" --set-file acl_file\n" +" Identical to --set, but read the ACL from a file\n" +" instead. If the file is `-', read from standard input.\n" +" --delete-acl\n" +" Delete the ACL of file(s).\n" +" --version Display the version of %s and exit.\n" +" --help This help text.\n" +"\n" +"Options:\n" +" --long Display access masks and flags in their long form.\n" +"\n" +"ACL entries are represented by colon separated :::\n" +"fields. The field may be \"owner@\", \"group@\", \"everyone@\", a user\n" +"name or ID, or a group name or ID. Groups must have the identifier_group(g)\n" +"flag set in the field. The field may be \"allow\" or \"deny\".\n" +"The and fields are lists of single-letter abbreviations or\n" +"slash-separated names, or a combination of both.\n" +"\n" +"The supported values are:\n" +"\tread_data (r), list_directory (r), write_data (w), add_file (w),\n" +"\tappend_data (a), add_subdirectory (a), read_named_attrs (N),\n" +"\twrite_named_attrs (n), execute (x), delete_child (d),\n" +"\tread_attributes (T), write_attributes (t), delete (D),\n" +"\tread_acl (M), write_acl (m), take_ownership (o), synchronize (s)\n" +"\n" +"The supported values are:\n" +"\tfile_inherit_ace (f), directory_inherit_ace (d),\n" +"\tno_propagate_inherit_ace (n), inherit_only_ace (i),\n" +"\tidentifier_group (g), inherited_ace (a)\n" +"\n" +"Per-ACL flag values are:\n" +"\tauto_inherit (a), protected (p), defaulted (d), write_through (w)\n", + basename(progname)); + exit(0); +} + +int main(int argc, char *argv[]) +{ + int opt_get = 0, opt_remove = 0, opt_dry_run = 0; + int opt_modify = 0, opt_set = 0; + char *acl_text = NULL, *acl_file = NULL; + int format = RICHACL_TEXT_SIMPLIFY; + int status = 0; + int c; + + struct richacl *acl = NULL; + int acl_has; + + progname = argv[0]; + + while ((c = getopt_long(argc, argv, "gm:M:s:S:nrlvh", + long_options, NULL)) != -1) { + switch(c) { + case 'g': + opt_get = 1; + break; + case 'm': + opt_modify = 1; + acl_text = optarg; + break; + + case 'M': + opt_modify = 1; + acl_file = optarg; + break; + case 's': + opt_set = 1; + acl_text = optarg; + break; + + case 'S': + opt_set = 1; + acl_file = optarg; + break; + + case 'r': + opt_remove = 1; + break; + + case 'l': + format |= RICHACL_TEXT_LONG; + break; + + case 'v': + printf("%s %s\n", basename(progname), VERSION); + exit(0); + + case 'h': + synopsis(1); + break; + + case 1: /* --raw */ + format |= RICHACL_TEXT_SHOW_MASKS; + format &= ~RICHACL_TEXT_SIMPLIFY; + break; + + case 2: /* --dry-run */ + opt_dry_run = 1; + break; + + default: + synopsis(0); + break; + } + } + if (opt_get + opt_remove + opt_modify + opt_set != 1 || + (acl_text ? 1 : 0) + (acl_file ? 1 : 0) > 1 || + optind == argc) + synopsis(argc == 1); + + if (acl_text) { + acl = richacl_from_text(acl_text, &acl_has, printf_stderr); + if (!acl) + return 1; + } + + if (acl_file) { + struct string_buffer *buffer; + char buf[4097]; + FILE *file = stdin; + ssize_t sz; + + buffer = alloc_string_buffer(1024); + if (!buffer) { + perror(""); + return 1; + } + + if (strcmp(acl_file, "-")) { + file = fopen(acl_file, "r"); + if (!file) { + perror(acl_file); + return 1; + } + } + do { + sz = fread(buf, 1, sizeof(buf) - 1, file); + buf[sz] = 0; + buffer_sprintf(buffer, "%s", buf); + } while (!(feof(file) || ferror(file))); + fclose(file); + if (ferror(file)) { + perror(acl_file); + return 1; + } + + acl = richacl_from_text(buffer->buffer, &acl_has, printf_stderr); + if (!acl) + return 1; + free_string_buffer(buffer); + } + + /* Compute all masks which haven't been set explicitly. */ + if (opt_set && acl && !((acl_has & RICHACL_TEXT_OWNER_MASK) && + (acl_has & RICHACL_TEXT_GROUP_MASK) && + (acl_has & RICHACL_TEXT_OTHER_MASK))) { + unsigned int owner_mask = acl->a_owner_mask; + unsigned int group_mask = acl->a_group_mask; + unsigned int other_mask = acl->a_other_mask; + + richacl_compute_max_masks(acl); + if (acl_has & RICHACL_TEXT_OWNER_MASK) + acl->a_owner_mask = owner_mask; + if (acl_has & RICHACL_TEXT_GROUP_MASK) + acl->a_group_mask = group_mask; + if (acl_has & RICHACL_TEXT_OTHER_MASK) + acl->a_other_mask = other_mask; + } + + if (opt_dry_run && opt_set) { + const char *file = ""; + + if (print_richacl(file, &acl, format | + RICHACL_TEXT_FILE_CONTEXT | + RICHACL_TEXT_DIRECTORY_CONTEXT)) { + perror(file); + return 1; + } + return 0; + } + + for (; optind < argc; optind++) { + const char *file = argv[optind]; + struct richacl *acl2 = NULL; + + if (opt_set) { + if (richacl_set_file(file, acl)) + goto fail; + } else if (opt_modify) { + struct stat st; + + if (stat(file, &st)) + goto fail; + acl2 = get_richacl(file, st.st_mode); + if (!acl2) + goto fail; + if (modify_richacl(&acl2, acl, acl_has)) + goto fail; + if (opt_dry_run) { + if (print_richacl(file, &acl2, format | + format_for_mode(st.st_mode))) + goto fail; + } else { + if (richacl_set_file(file, acl2)) + goto fail; + } + } else if (opt_remove) { + if (removexattr(file, "system.richacl")) { + if (errno != ENODATA) + goto fail; + } + } else { + struct stat st; + + if (stat(file, &st)) + goto fail; + acl2 = get_richacl(file, st.st_mode); + if (!acl2) + goto fail; + if (print_richacl(file, &acl2, format | + format_for_mode(st.st_mode))) + goto fail; + } + richacl_free(acl2); + continue; + + fail: + richacl_free(acl2); + perror(file); + status = 1; + } + + richacl_free(acl); + return status; +} -- 1.6.5.2.74.g610f9