summaryrefslogtreecommitdiff
path: root/lib
diff options
context:
space:
mode:
authorOndrej Valousek <ondrej.valousek.xm@renesas.com>2022-12-02 13:40:19 +0100
committerBruno Haible <bruno@clisp.org>2022-12-22 18:02:44 +0100
commitb0604a8e134dbcc307c0ffdd5ebd3693e9de7081 (patch)
tree1f454db2eb8dcd1fd5a07ba41513b9a27ccb4ffb /lib
parent4f6131a7865e3558184f5d1c8b308395640d802e (diff)
downloadgnulib-b0604a8e134dbcc307c0ffdd5ebd3693e9de7081.tar.gz
file-has-acl: Basic support for checking NFSv4 ACLs in Linux.
* lib/acl-internal.h (acl_nfs4_nontrivial): New declaration. * lib/acl-internal.c (acl_nfs4_nontrivial): New function. * lib/file-has-acl.c: Include <arpa/inet.h>. (XATTR_NAME_NFSV4_ACL, TRIVIAL_NFS4_ACL_MAX_LENGTH): New macros. (file_has_acl): Test for NFSv4 ACLs. * doc/acl-nfsv4.txt: New file.
Diffstat (limited to 'lib')
-rw-r--r--lib/acl-internal.c100
-rw-r--r--lib/acl-internal.h3
-rw-r--r--lib/file-has-acl.c21
3 files changed, 124 insertions, 0 deletions
diff --git a/lib/acl-internal.c b/lib/acl-internal.c
index be244c67a2..4c65dffcc6 100644
--- a/lib/acl-internal.c
+++ b/lib/acl-internal.c
@@ -25,6 +25,9 @@
#if USE_ACL && HAVE_ACL_GET_FILE /* Linux, FreeBSD, Mac OS X, IRIX, Tru64, Cygwin >= 2.5 */
+# include <string.h>
+# include <arpa/inet.h>
+
# if HAVE_ACL_TYPE_EXTENDED /* Mac OS X */
/* ACL is an ACL, from a file, stored as type ACL_TYPE_EXTENDED.
@@ -122,6 +125,103 @@ acl_default_nontrivial (acl_t acl)
return (acl_entries (acl) > 0);
}
+# define ACE4_WHO_OWNER "OWNER@"
+# define ACE4_WHO_GROUP "GROUP@"
+# define ACE4_WHO_EVERYONE "EVERYONE@"
+
+# define ACE4_ACCESS_ALLOWED_ACE_TYPE 0
+# define ACE4_ACCESS_DENIED_ACE_TYPE 1
+
+/* ACE flag values */
+# define ACE4_IDENTIFIER_GROUP 0x00000040
+# define ROUNDUP(x, y) (((x) + (y) - 1) & - (y))
+
+int
+acl_nfs4_nontrivial (char *xattr, int len)
+{
+ int bufs = len;
+ uint32_t num_aces = ntohl (*((uint32_t*)(xattr))), /* Grab the number of aces in the acl */
+ num_a_aces = 0,
+ num_d_aces = 0;
+ char *bufp = xattr;
+
+ bufp += 4; /* sizeof(uint32_t); */
+ bufs -= 4;
+
+ for (uint32_t ace_n = 0; num_aces > ace_n ; ace_n++)
+ {
+ int d_ptr;
+ uint32_t flag,
+ wholen,
+ type;
+
+ /* Get the acl type */
+ if (bufs <= 0)
+ return -1;
+
+ type = ntohl (*((uint32_t*)bufp));
+
+ bufp += 4;
+ bufs -= 4;
+ if (bufs <= 0)
+ return -1;
+
+ flag = ntohl (*((uint32_t*)bufp));
+ /* As per RFC 7530, the flag should be 0, but we are just generous to Netapp
+ * and also accept the Group flag
+ */
+ if (flag & ~ACE4_IDENTIFIER_GROUP)
+ return 1;
+
+ /* we skip mask -
+ * it's too risky to test it and it does not seem to be actually needed */
+ bufp += 2*4;
+ bufs -= 2*4;
+
+ if (bufs <= 0)
+ return -1;
+
+ wholen = ntohl (*((uint32_t*)bufp));
+
+ bufp += 4;
+ bufs -= 4;
+
+ /* Get the who string */
+ if (bufs <= 0)
+ return -1;
+
+ /* for trivial ACL, we expect max 5 (typically 3) ACES, 3 Allow, 2 deny */
+ if (((strncmp (bufp, ACE4_WHO_OWNER, wholen) == 0)
+ || (strncmp (bufp, ACE4_WHO_GROUP, wholen) == 0))
+ && wholen == 6)
+ {
+ if (type == ACE4_ACCESS_ALLOWED_ACE_TYPE)
+ num_a_aces++;
+ if (type == ACE4_ACCESS_DENIED_ACE_TYPE)
+ num_d_aces++;
+ }
+ else
+ if ((strncmp (bufp, ACE4_WHO_EVERYONE, wholen) == 0)
+ && (type == ACE4_ACCESS_ALLOWED_ACE_TYPE)
+ && (wholen == 9))
+ num_a_aces++;
+ else
+ return 1;
+
+ d_ptr = ROUNDUP (wholen, 4);
+ bufp += d_ptr;
+ bufs -= d_ptr;
+
+ /* Make sure we aren't outside our domain */
+ if (bufs < 0)
+ return -1;
+
+ }
+ return !((num_a_aces <= 3) && (num_d_aces <= 2)
+ && (num_a_aces + num_d_aces == num_aces));
+
+}
+
# endif
#elif USE_ACL && HAVE_FACL && defined GETACL /* Solaris, Cygwin < 2.5, not HP-UX */
diff --git a/lib/acl-internal.h b/lib/acl-internal.h
index 94553fab25..5da7c115c6 100644
--- a/lib/acl-internal.h
+++ b/lib/acl-internal.h
@@ -146,6 +146,9 @@ rpl_acl_set_fd (int fd, acl_t acl)
# define acl_entries rpl_acl_entries
extern int acl_entries (acl_t);
# endif
+/* Return 1 if given ACL in XDR format is non-trivial
+ * Return 0 if it is trivial */
+extern int acl_nfs4_nontrivial (char *, int);
# if HAVE_ACL_TYPE_EXTENDED /* Mac OS X */
/* ACL is an ACL, from a file, stored as type ACL_TYPE_EXTENDED.
diff --git a/lib/file-has-acl.c b/lib/file-has-acl.c
index e02f0626ad..171023488c 100644
--- a/lib/file-has-acl.c
+++ b/lib/file-has-acl.c
@@ -32,6 +32,11 @@
#if GETXATTR_WITH_POSIX_ACLS
# include <sys/xattr.h>
# include <linux/xattr.h>
+# include <arpa/inet.h>
+# ifndef XATTR_NAME_NFSV4_ACL
+# define XATTR_NAME_NFSV4_ACL "system.nfs4_acl"
+# endif
+# define TRIVIAL_NFS4_ACL_MAX_LENGTH 128
#endif
/* Return 1 if NAME has a nontrivial access control list,
@@ -68,6 +73,22 @@ file_has_acl (char const *name, struct stat const *sb)
}
if (ret < 0)
+ { /* we might be on NFS, so try to check NFSv4 ACLs too */
+ char xattr[TRIVIAL_NFS4_ACL_MAX_LENGTH];
+
+ errno = 0; /* we need to reset errno set by the previous getxattr() */
+ ret = getxattr (name, XATTR_NAME_NFSV4_ACL, xattr, TRIVIAL_NFS4_ACL_MAX_LENGTH);
+ if (ret < 0 && errno == ENODATA)
+ ret = 0;
+ else
+ if (ret < 0 && errno == ERANGE)
+ return 1; /* we won't fit into the buffer, so non-trivial ACL is presented */
+ else
+ if (ret > 0)
+ /* looks like trivial ACL, but we need to investigate further */
+ return acl_nfs4_nontrivial (xattr, ret);
+ }
+ if (ret < 0)
return - acl_errno_valid (errno);
return ret;