Kamil Dudka ad57d2
From 5a6af47c3db45b6303bac4dcd6da186fd5cd178c Mon Sep 17 00:00:00 2001
Kamil Dudka ad57d2
From: Ondrej Valousek <ondrej.valousek.xm@renesas.com>
Kamil Dudka ad57d2
Date: Fri, 2 Dec 2022 13:40:19 +0100
Kamil Dudka ad57d2
Subject: [PATCH 1/3] file-has-acl: Basic support for checking NFSv4 ACLs in
Kamil Dudka ad57d2
 Linux.
Kamil Dudka ad57d2
Kamil Dudka ad57d2
* lib/acl-internal.h (acl_nfs4_nontrivial): New declaration.
Kamil Dudka ad57d2
* lib/acl-internal.c (acl_nfs4_nontrivial): New function.
Kamil Dudka ad57d2
* lib/file-has-acl.c: Include <arpa/inet.h>.
Kamil Dudka ad57d2
(XATTR_NAME_NFSV4_ACL, TRIVIAL_NFS4_ACL_MAX_LENGTH): New macros.
Kamil Dudka ad57d2
(file_has_acl): Test for NFSv4 ACLs.
Kamil Dudka ad57d2
* doc/acl-nfsv4.txt: New file.
Kamil Dudka ad57d2
Kamil Dudka ad57d2
Upstream-commit: b0604a8e134dbcc307c0ffdd5ebd3693e9de7081
Kamil Dudka ad57d2
Signed-off-by: Kamil Dudka <kdudka@redhat.com>
Kamil Dudka ad57d2
---
Kamil Dudka ad57d2
 doc/acl-nfsv4.txt  |  17 ++++++++
Kamil Dudka ad57d2
 lib/acl-internal.c | 100 +++++++++++++++++++++++++++++++++++++++++++++
Kamil Dudka ad57d2
 lib/acl-internal.h |   3 ++
Kamil Dudka ad57d2
 lib/file-has-acl.c |  21 ++++++++++
Kamil Dudka ad57d2
 4 files changed, 141 insertions(+)
Kamil Dudka ad57d2
 create mode 100644 doc/acl-nfsv4.txt
Kamil Dudka ad57d2
Kamil Dudka ad57d2
diff --git a/doc/acl-nfsv4.txt b/doc/acl-nfsv4.txt
Kamil Dudka ad57d2
new file mode 100644
Kamil Dudka ad57d2
index 0000000..71352f5
Kamil Dudka ad57d2
--- /dev/null
Kamil Dudka ad57d2
+++ b/doc/acl-nfsv4.txt
Kamil Dudka ad57d2
@@ -0,0 +1,17 @@
Kamil Dudka ad57d2
+General introduction:
Kamil Dudka ad57d2
+   https://linux.die.net/man/5/nfs4_acl
Kamil Dudka ad57d2
+
Kamil Dudka ad57d2
+The NFSv4 acls are defined in RFC7530 and as such, every NFSv4 server supporting ACLs
Kamil Dudka ad57d2
+will support this kind of ACLs (note the difference from POSIX draft ACLs)
Kamil Dudka ad57d2
+
Kamil Dudka ad57d2
+The ACLs can be obtained via the nfsv4-acl-tools, i.e.
Kamil Dudka ad57d2
+
Kamil Dudka ad57d2
+$ nfs4_getfacl <file>
Kamil Dudka ad57d2
+
Kamil Dudka ad57d2
+# file: <file>
Kamil Dudka ad57d2
+A::OWNER@:rwaDxtTnNcCy
Kamil Dudka ad57d2
+A::GROUP@:rwaDxtTnNcy
Kamil Dudka ad57d2
+A::EVERYONE@:rwaDxtTnNcy
Kamil Dudka ad57d2
+
Kamil Dudka ad57d2
+Gnulib is aiming to only provide a basic support of these, i.e. recognize trivial
Kamil Dudka ad57d2
+and non-trivial ACLs
Kamil Dudka ad57d2
diff --git a/lib/acl-internal.c b/lib/acl-internal.c
Kamil Dudka ad57d2
index be244c6..4c65dff 100644
Kamil Dudka ad57d2
--- a/lib/acl-internal.c
Kamil Dudka ad57d2
+++ b/lib/acl-internal.c
Kamil Dudka ad57d2
@@ -25,6 +25,9 @@
Kamil Dudka ad57d2
 
Kamil Dudka ad57d2
 #if USE_ACL && HAVE_ACL_GET_FILE /* Linux, FreeBSD, Mac OS X, IRIX, Tru64, Cygwin >= 2.5 */
Kamil Dudka ad57d2
 
Kamil Dudka ad57d2
+# include <string.h>
Kamil Dudka ad57d2
+# include <arpa/inet.h>
Kamil Dudka ad57d2
+
Kamil Dudka ad57d2
 # if HAVE_ACL_TYPE_EXTENDED /* Mac OS X */
Kamil Dudka ad57d2
 
Kamil Dudka ad57d2
 /* ACL is an ACL, from a file, stored as type ACL_TYPE_EXTENDED.
Kamil Dudka ad57d2
@@ -122,6 +125,103 @@ acl_default_nontrivial (acl_t acl)
Kamil Dudka ad57d2
   return (acl_entries (acl) > 0);
Kamil Dudka ad57d2
 }
Kamil Dudka ad57d2
 
Kamil Dudka ad57d2
+#  define ACE4_WHO_OWNER    "OWNER@"
Kamil Dudka ad57d2
+#  define ACE4_WHO_GROUP    "GROUP@"
Kamil Dudka ad57d2
+#  define ACE4_WHO_EVERYONE "EVERYONE@"
Kamil Dudka ad57d2
+
Kamil Dudka ad57d2
+#  define ACE4_ACCESS_ALLOWED_ACE_TYPE   0
Kamil Dudka ad57d2
+#  define ACE4_ACCESS_DENIED_ACE_TYPE    1
Kamil Dudka ad57d2
+
Kamil Dudka ad57d2
+/* ACE flag values */
Kamil Dudka ad57d2
+#  define ACE4_IDENTIFIER_GROUP          0x00000040
Kamil Dudka ad57d2
+#  define ROUNDUP(x, y)                  (((x) + (y) - 1) & - (y))
Kamil Dudka ad57d2
+
Kamil Dudka ad57d2
+int
Kamil Dudka ad57d2
+acl_nfs4_nontrivial (char *xattr, int len)
Kamil Dudka ad57d2
+{
Kamil Dudka ad57d2
+  int      bufs = len;
Kamil Dudka ad57d2
+  uint32_t num_aces = ntohl (*((uint32_t*)(xattr))), /* Grab the number of aces in the acl */
Kamil Dudka ad57d2
+           num_a_aces = 0,
Kamil Dudka ad57d2
+           num_d_aces = 0;
Kamil Dudka ad57d2
+  char *bufp = xattr;
Kamil Dudka ad57d2
+
Kamil Dudka ad57d2
+  bufp += 4;  /* sizeof(uint32_t); */
Kamil Dudka ad57d2
+  bufs -= 4;
Kamil Dudka ad57d2
+
Kamil Dudka ad57d2
+  for (uint32_t ace_n = 0; num_aces > ace_n ; ace_n++)
Kamil Dudka ad57d2
+    {
Kamil Dudka ad57d2
+      int      d_ptr;
Kamil Dudka ad57d2
+      uint32_t flag,
Kamil Dudka ad57d2
+               wholen,
Kamil Dudka ad57d2
+               type;
Kamil Dudka ad57d2
+
Kamil Dudka ad57d2
+      /* Get the acl type */
Kamil Dudka ad57d2
+      if (bufs <= 0)
Kamil Dudka ad57d2
+        return -1;
Kamil Dudka ad57d2
+
Kamil Dudka ad57d2
+      type = ntohl (*((uint32_t*)bufp));
Kamil Dudka ad57d2
+
Kamil Dudka ad57d2
+      bufp += 4;
Kamil Dudka ad57d2
+      bufs -= 4;
Kamil Dudka ad57d2
+      if (bufs <= 0)
Kamil Dudka ad57d2
+        return -1;
Kamil Dudka ad57d2
+
Kamil Dudka ad57d2
+      flag = ntohl (*((uint32_t*)bufp));
Kamil Dudka ad57d2
+      /* As per RFC 7530, the flag should be 0, but we are just generous to Netapp
Kamil Dudka ad57d2
+       * and also accept the Group flag
Kamil Dudka ad57d2
+       */
Kamil Dudka ad57d2
+      if (flag & ~ACE4_IDENTIFIER_GROUP)
Kamil Dudka ad57d2
+        return 1;
Kamil Dudka ad57d2
+
Kamil Dudka ad57d2
+      /* we skip mask -
Kamil Dudka ad57d2
+       * it's too risky to test it and it does not seem to be actually needed */
Kamil Dudka ad57d2
+      bufp += 2*4;
Kamil Dudka ad57d2
+      bufs -= 2*4;
Kamil Dudka ad57d2
+
Kamil Dudka ad57d2
+      if (bufs <= 0)
Kamil Dudka ad57d2
+        return -1;
Kamil Dudka ad57d2
+
Kamil Dudka ad57d2
+      wholen = ntohl (*((uint32_t*)bufp));
Kamil Dudka ad57d2
+
Kamil Dudka ad57d2
+      bufp += 4;
Kamil Dudka ad57d2
+      bufs -= 4;
Kamil Dudka ad57d2
+
Kamil Dudka ad57d2
+      /* Get the who string */
Kamil Dudka ad57d2
+      if (bufs <= 0)
Kamil Dudka ad57d2
+        return -1;
Kamil Dudka ad57d2
+
Kamil Dudka ad57d2
+      /* for trivial ACL, we expect max 5 (typically 3) ACES, 3 Allow, 2 deny */
Kamil Dudka ad57d2
+      if (((strncmp (bufp, ACE4_WHO_OWNER, wholen) == 0)
Kamil Dudka ad57d2
+          || (strncmp (bufp, ACE4_WHO_GROUP, wholen) == 0))
Kamil Dudka ad57d2
+          &&  wholen == 6)
Kamil Dudka ad57d2
+        {
Kamil Dudka ad57d2
+          if (type == ACE4_ACCESS_ALLOWED_ACE_TYPE)
Kamil Dudka ad57d2
+            num_a_aces++;
Kamil Dudka ad57d2
+          if (type == ACE4_ACCESS_DENIED_ACE_TYPE)
Kamil Dudka ad57d2
+            num_d_aces++;
Kamil Dudka ad57d2
+        }
Kamil Dudka ad57d2
+      else
Kamil Dudka ad57d2
+        if ((strncmp (bufp, ACE4_WHO_EVERYONE, wholen) == 0)
Kamil Dudka ad57d2
+            && (type == ACE4_ACCESS_ALLOWED_ACE_TYPE)
Kamil Dudka ad57d2
+            && (wholen == 9))
Kamil Dudka ad57d2
+          num_a_aces++;
Kamil Dudka ad57d2
+        else
Kamil Dudka ad57d2
+          return 1;
Kamil Dudka ad57d2
+
Kamil Dudka ad57d2
+      d_ptr = ROUNDUP (wholen, 4);
Kamil Dudka ad57d2
+      bufp += d_ptr;
Kamil Dudka ad57d2
+      bufs -= d_ptr;
Kamil Dudka ad57d2
+
Kamil Dudka ad57d2
+      /* Make sure we aren't outside our domain */
Kamil Dudka ad57d2
+      if (bufs < 0)
Kamil Dudka ad57d2
+        return -1;
Kamil Dudka ad57d2
+
Kamil Dudka ad57d2
+    }
Kamil Dudka ad57d2
+  return !((num_a_aces <= 3) && (num_d_aces <= 2)
Kamil Dudka ad57d2
+         && (num_a_aces + num_d_aces == num_aces));
Kamil Dudka ad57d2
+
Kamil Dudka ad57d2
+}
Kamil Dudka ad57d2
+
Kamil Dudka ad57d2
 # endif
Kamil Dudka ad57d2
 
Kamil Dudka ad57d2
 #elif USE_ACL && HAVE_FACL && defined GETACL /* Solaris, Cygwin < 2.5, not HP-UX */
Kamil Dudka ad57d2
diff --git a/lib/acl-internal.h b/lib/acl-internal.h
Kamil Dudka ad57d2
index 9353376..2a249ff 100644
Kamil Dudka ad57d2
--- a/lib/acl-internal.h
Kamil Dudka ad57d2
+++ b/lib/acl-internal.h
Kamil Dudka ad57d2
@@ -147,6 +147,9 @@ rpl_acl_set_fd (int fd, acl_t acl)
Kamil Dudka ad57d2
 #   define acl_entries rpl_acl_entries
Kamil Dudka ad57d2
 extern int acl_entries (acl_t);
Kamil Dudka ad57d2
 #  endif
Kamil Dudka ad57d2
+/* Return 1 if given ACL in XDR format is non-trivial
Kamil Dudka ad57d2
+ * Return 0 if it is trivial */
Kamil Dudka ad57d2
+extern int acl_nfs4_nontrivial (char *, int);
Kamil Dudka ad57d2
 
Kamil Dudka ad57d2
 #  if HAVE_ACL_TYPE_EXTENDED /* Mac OS X */
Kamil Dudka ad57d2
 /* ACL is an ACL, from a file, stored as type ACL_TYPE_EXTENDED.
Kamil Dudka ad57d2
diff --git a/lib/file-has-acl.c b/lib/file-has-acl.c
Kamil Dudka ad57d2
index e02f062..1710234 100644
Kamil Dudka ad57d2
--- a/lib/file-has-acl.c
Kamil Dudka ad57d2
+++ b/lib/file-has-acl.c
Kamil Dudka ad57d2
@@ -32,6 +32,11 @@
Kamil Dudka ad57d2
 #if GETXATTR_WITH_POSIX_ACLS
Kamil Dudka ad57d2
 # include <sys/xattr.h>
Kamil Dudka ad57d2
 # include <linux/xattr.h>
Kamil Dudka ad57d2
+# include <arpa/inet.h>
Kamil Dudka ad57d2
+# ifndef XATTR_NAME_NFSV4_ACL
Kamil Dudka ad57d2
+#  define XATTR_NAME_NFSV4_ACL "system.nfs4_acl"
Kamil Dudka ad57d2
+# endif
Kamil Dudka ad57d2
+# define TRIVIAL_NFS4_ACL_MAX_LENGTH 128
Kamil Dudka ad57d2
 #endif
Kamil Dudka ad57d2
 
Kamil Dudka ad57d2
 /* Return 1 if NAME has a nontrivial access control list,
Kamil Dudka ad57d2
@@ -67,6 +72,22 @@ file_has_acl (char const *name, struct stat const *sb)
Kamil Dudka ad57d2
             return 1;
Kamil Dudka ad57d2
         }
Kamil Dudka ad57d2
 
Kamil Dudka ad57d2
+      if (ret < 0)
Kamil Dudka ad57d2
+        { /* we might be on NFS, so try to check NFSv4 ACLs too */
Kamil Dudka ad57d2
+          char xattr[TRIVIAL_NFS4_ACL_MAX_LENGTH];
Kamil Dudka ad57d2
+
Kamil Dudka ad57d2
+          errno = 0; /* we need to reset errno set by the previous getxattr() */
Kamil Dudka ad57d2
+          ret = getxattr (name, XATTR_NAME_NFSV4_ACL, xattr, TRIVIAL_NFS4_ACL_MAX_LENGTH);
Kamil Dudka ad57d2
+          if (ret < 0 && errno == ENODATA)
Kamil Dudka ad57d2
+            ret = 0;
Kamil Dudka ad57d2
+          else
Kamil Dudka ad57d2
+            if (ret < 0 && errno == ERANGE)
Kamil Dudka ad57d2
+              return 1;  /* we won't fit into the buffer, so non-trivial ACL is presented */
Kamil Dudka ad57d2
+            else
Kamil Dudka ad57d2
+              if (ret > 0)
Kamil Dudka ad57d2
+                /* looks like trivial ACL, but we need to investigate further */
Kamil Dudka ad57d2
+                return acl_nfs4_nontrivial (xattr, ret);
Kamil Dudka ad57d2
+        }
Kamil Dudka ad57d2
       if (ret < 0)
Kamil Dudka ad57d2
         return - acl_errno_valid (errno);
Kamil Dudka ad57d2
       return ret;
Kamil Dudka ad57d2
-- 
Kamil Dudka ad57d2
2.38.1
Kamil Dudka ad57d2
Kamil Dudka ad57d2
Kamil Dudka ad57d2
From c5266d204a446bea619fa18da8520dceb0a54192 Mon Sep 17 00:00:00 2001
Kamil Dudka ad57d2
From: Paul Eggert <eggert@cs.ucla.edu>
Kamil Dudka ad57d2
Date: Fri, 23 Dec 2022 15:18:29 -0800
Kamil Dudka ad57d2
Subject: [PATCH 2/3] file-has-acl: improve recent NFSv4 support
Kamil Dudka ad57d2
MIME-Version: 1.0
Kamil Dudka ad57d2
Content-Type: text/plain; charset=UTF-8
Kamil Dudka ad57d2
Content-Transfer-Encoding: 8bit
Kamil Dudka ad57d2
Kamil Dudka ad57d2
This fixes a link failure with emacsclient on GNU/Linux.  This
Kamil Dudka ad57d2
program wants file_has_acl but none of the other ACL primitives,
Kamil Dudka ad57d2
so it doesn’t link acl-internal.o; this way it doesn’t need to
Kamil Dudka ad57d2
link with -lacl.  While I was at it I reviewed the recent changes,
Kamil Dudka ad57d2
fixed some unlikely overflow bugs, and adjusted to GNU style.
Kamil Dudka ad57d2
* doc/acl-nfsv4.txt: Remove.  Its contents are now in a
Kamil Dudka ad57d2
comment in lib/file-has-acl.c.
Kamil Dudka ad57d2
* lib/acl-internal.c, lib/acl-internal.h: Move recent changes
Kamil Dudka ad57d2
relating to acl_nfs4_nontrivial to lib/file-has-acl.c, so that
Kamil Dudka ad57d2
there is no trouble linking programs that need only file_has_acl.
Kamil Dudka ad57d2
* lib/file-has-acl.c (acl_nfs4_nontrivial): Move here from
Kamil Dudka ad57d2
lib/acl-internal.c, so that we needn't link -lacl in
Kamil Dudka ad57d2
programs that want only file_has_acl, such as emacsclient.
Kamil Dudka ad57d2
Do not assume a char buffer is aligned for uint32_t.
Kamil Dudka ad57d2
Check more carefully for buffer read overrun.
Kamil Dudka ad57d2
Allow up to 6 ACEs, since other code does; but check
Kamil Dudka ad57d2
that they’re distinct.  Avoid integer overflow.
Kamil Dudka ad57d2
Use memcmp rather than strncmp to compare memory blocks.
Kamil Dudka ad57d2
(file_has_acl): Preserve initial errno instead of setting to 0.
Kamil Dudka ad57d2
Allocate a bit more room for trivial ACL buffer.
Kamil Dudka ad57d2
Use EINVAL for botchedk NFSv4 ACLs (which shouldn’t happen).
Kamil Dudka ad57d2
Kamil Dudka ad57d2
Upstream-commit: 35bd46f0c816948dc1a0430c8ba8b10a01167320
Kamil Dudka ad57d2
Signed-off-by: Kamil Dudka <kdudka@redhat.com>
Kamil Dudka ad57d2
---
Kamil Dudka ad57d2
 doc/acl-nfsv4.txt  |  17 ------
Kamil Dudka ad57d2
 lib/acl-internal.c | 100 -----------------------------------
Kamil Dudka ad57d2
 lib/acl-internal.h |   3 --
Kamil Dudka ad57d2
 lib/file-has-acl.c | 129 +++++++++++++++++++++++++++++++++++++++------
Kamil Dudka ad57d2
 4 files changed, 113 insertions(+), 136 deletions(-)
Kamil Dudka ad57d2
 delete mode 100644 doc/acl-nfsv4.txt
Kamil Dudka ad57d2
Kamil Dudka ad57d2
diff --git a/doc/acl-nfsv4.txt b/doc/acl-nfsv4.txt
Kamil Dudka ad57d2
deleted file mode 100644
Kamil Dudka ad57d2
index 71352f5..0000000
Kamil Dudka ad57d2
--- a/doc/acl-nfsv4.txt
Kamil Dudka ad57d2
+++ /dev/null
Kamil Dudka ad57d2
@@ -1,17 +0,0 @@
Kamil Dudka ad57d2
-General introduction:
Kamil Dudka ad57d2
-   https://linux.die.net/man/5/nfs4_acl
Kamil Dudka ad57d2
-
Kamil Dudka ad57d2
-The NFSv4 acls are defined in RFC7530 and as such, every NFSv4 server supporting ACLs
Kamil Dudka ad57d2
-will support this kind of ACLs (note the difference from POSIX draft ACLs)
Kamil Dudka ad57d2
-
Kamil Dudka ad57d2
-The ACLs can be obtained via the nfsv4-acl-tools, i.e.
Kamil Dudka ad57d2
-
Kamil Dudka ad57d2
-$ nfs4_getfacl <file>
Kamil Dudka ad57d2
-
Kamil Dudka ad57d2
-# file: <file>
Kamil Dudka ad57d2
-A::OWNER@:rwaDxtTnNcCy
Kamil Dudka ad57d2
-A::GROUP@:rwaDxtTnNcy
Kamil Dudka ad57d2
-A::EVERYONE@:rwaDxtTnNcy
Kamil Dudka ad57d2
-
Kamil Dudka ad57d2
-Gnulib is aiming to only provide a basic support of these, i.e. recognize trivial
Kamil Dudka ad57d2
-and non-trivial ACLs
Kamil Dudka ad57d2
diff --git a/lib/acl-internal.c b/lib/acl-internal.c
Kamil Dudka ad57d2
index 4c65dff..be244c6 100644
Kamil Dudka ad57d2
--- a/lib/acl-internal.c
Kamil Dudka ad57d2
+++ b/lib/acl-internal.c
Kamil Dudka ad57d2
@@ -25,9 +25,6 @@
Kamil Dudka ad57d2
 
Kamil Dudka ad57d2
 #if USE_ACL && HAVE_ACL_GET_FILE /* Linux, FreeBSD, Mac OS X, IRIX, Tru64, Cygwin >= 2.5 */
Kamil Dudka ad57d2
 
Kamil Dudka ad57d2
-# include <string.h>
Kamil Dudka ad57d2
-# include <arpa/inet.h>
Kamil Dudka ad57d2
-
Kamil Dudka ad57d2
 # if HAVE_ACL_TYPE_EXTENDED /* Mac OS X */
Kamil Dudka ad57d2
 
Kamil Dudka ad57d2
 /* ACL is an ACL, from a file, stored as type ACL_TYPE_EXTENDED.
Kamil Dudka ad57d2
@@ -125,103 +122,6 @@ acl_default_nontrivial (acl_t acl)
Kamil Dudka ad57d2
   return (acl_entries (acl) > 0);
Kamil Dudka ad57d2
 }
Kamil Dudka ad57d2
 
Kamil Dudka ad57d2
-#  define ACE4_WHO_OWNER    "OWNER@"
Kamil Dudka ad57d2
-#  define ACE4_WHO_GROUP    "GROUP@"
Kamil Dudka ad57d2
-#  define ACE4_WHO_EVERYONE "EVERYONE@"
Kamil Dudka ad57d2
-
Kamil Dudka ad57d2
-#  define ACE4_ACCESS_ALLOWED_ACE_TYPE   0
Kamil Dudka ad57d2
-#  define ACE4_ACCESS_DENIED_ACE_TYPE    1
Kamil Dudka ad57d2
-
Kamil Dudka ad57d2
-/* ACE flag values */
Kamil Dudka ad57d2
-#  define ACE4_IDENTIFIER_GROUP          0x00000040
Kamil Dudka ad57d2
-#  define ROUNDUP(x, y)                  (((x) + (y) - 1) & - (y))
Kamil Dudka ad57d2
-
Kamil Dudka ad57d2
-int
Kamil Dudka ad57d2
-acl_nfs4_nontrivial (char *xattr, int len)
Kamil Dudka ad57d2
-{
Kamil Dudka ad57d2
-  int      bufs = len;
Kamil Dudka ad57d2
-  uint32_t num_aces = ntohl (*((uint32_t*)(xattr))), /* Grab the number of aces in the acl */
Kamil Dudka ad57d2
-           num_a_aces = 0,
Kamil Dudka ad57d2
-           num_d_aces = 0;
Kamil Dudka ad57d2
-  char *bufp = xattr;
Kamil Dudka ad57d2
-
Kamil Dudka ad57d2
-  bufp += 4;  /* sizeof(uint32_t); */
Kamil Dudka ad57d2
-  bufs -= 4;
Kamil Dudka ad57d2
-
Kamil Dudka ad57d2
-  for (uint32_t ace_n = 0; num_aces > ace_n ; ace_n++)
Kamil Dudka ad57d2
-    {
Kamil Dudka ad57d2
-      int      d_ptr;
Kamil Dudka ad57d2
-      uint32_t flag,
Kamil Dudka ad57d2
-               wholen,
Kamil Dudka ad57d2
-               type;
Kamil Dudka ad57d2
-
Kamil Dudka ad57d2
-      /* Get the acl type */
Kamil Dudka ad57d2
-      if (bufs <= 0)
Kamil Dudka ad57d2
-        return -1;
Kamil Dudka ad57d2
-
Kamil Dudka ad57d2
-      type = ntohl (*((uint32_t*)bufp));
Kamil Dudka ad57d2
-
Kamil Dudka ad57d2
-      bufp += 4;
Kamil Dudka ad57d2
-      bufs -= 4;
Kamil Dudka ad57d2
-      if (bufs <= 0)
Kamil Dudka ad57d2
-        return -1;
Kamil Dudka ad57d2
-
Kamil Dudka ad57d2
-      flag = ntohl (*((uint32_t*)bufp));
Kamil Dudka ad57d2
-      /* As per RFC 7530, the flag should be 0, but we are just generous to Netapp
Kamil Dudka ad57d2
-       * and also accept the Group flag
Kamil Dudka ad57d2
-       */
Kamil Dudka ad57d2
-      if (flag & ~ACE4_IDENTIFIER_GROUP)
Kamil Dudka ad57d2
-        return 1;
Kamil Dudka ad57d2
-
Kamil Dudka ad57d2
-      /* we skip mask -
Kamil Dudka ad57d2
-       * it's too risky to test it and it does not seem to be actually needed */
Kamil Dudka ad57d2
-      bufp += 2*4;
Kamil Dudka ad57d2
-      bufs -= 2*4;
Kamil Dudka ad57d2
-
Kamil Dudka ad57d2
-      if (bufs <= 0)
Kamil Dudka ad57d2
-        return -1;
Kamil Dudka ad57d2
-
Kamil Dudka ad57d2
-      wholen = ntohl (*((uint32_t*)bufp));
Kamil Dudka ad57d2
-
Kamil Dudka ad57d2
-      bufp += 4;
Kamil Dudka ad57d2
-      bufs -= 4;
Kamil Dudka ad57d2
-
Kamil Dudka ad57d2
-      /* Get the who string */
Kamil Dudka ad57d2
-      if (bufs <= 0)
Kamil Dudka ad57d2
-        return -1;
Kamil Dudka ad57d2
-
Kamil Dudka ad57d2
-      /* for trivial ACL, we expect max 5 (typically 3) ACES, 3 Allow, 2 deny */
Kamil Dudka ad57d2
-      if (((strncmp (bufp, ACE4_WHO_OWNER, wholen) == 0)
Kamil Dudka ad57d2
-          || (strncmp (bufp, ACE4_WHO_GROUP, wholen) == 0))
Kamil Dudka ad57d2
-          &&  wholen == 6)
Kamil Dudka ad57d2
-        {
Kamil Dudka ad57d2
-          if (type == ACE4_ACCESS_ALLOWED_ACE_TYPE)
Kamil Dudka ad57d2
-            num_a_aces++;
Kamil Dudka ad57d2
-          if (type == ACE4_ACCESS_DENIED_ACE_TYPE)
Kamil Dudka ad57d2
-            num_d_aces++;
Kamil Dudka ad57d2
-        }
Kamil Dudka ad57d2
-      else
Kamil Dudka ad57d2
-        if ((strncmp (bufp, ACE4_WHO_EVERYONE, wholen) == 0)
Kamil Dudka ad57d2
-            && (type == ACE4_ACCESS_ALLOWED_ACE_TYPE)
Kamil Dudka ad57d2
-            && (wholen == 9))
Kamil Dudka ad57d2
-          num_a_aces++;
Kamil Dudka ad57d2
-        else
Kamil Dudka ad57d2
-          return 1;
Kamil Dudka ad57d2
-
Kamil Dudka ad57d2
-      d_ptr = ROUNDUP (wholen, 4);
Kamil Dudka ad57d2
-      bufp += d_ptr;
Kamil Dudka ad57d2
-      bufs -= d_ptr;
Kamil Dudka ad57d2
-
Kamil Dudka ad57d2
-      /* Make sure we aren't outside our domain */
Kamil Dudka ad57d2
-      if (bufs < 0)
Kamil Dudka ad57d2
-        return -1;
Kamil Dudka ad57d2
-
Kamil Dudka ad57d2
-    }
Kamil Dudka ad57d2
-  return !((num_a_aces <= 3) && (num_d_aces <= 2)
Kamil Dudka ad57d2
-         && (num_a_aces + num_d_aces == num_aces));
Kamil Dudka ad57d2
-
Kamil Dudka ad57d2
-}
Kamil Dudka ad57d2
-
Kamil Dudka ad57d2
 # endif
Kamil Dudka ad57d2
 
Kamil Dudka ad57d2
 #elif USE_ACL && HAVE_FACL && defined GETACL /* Solaris, Cygwin < 2.5, not HP-UX */
Kamil Dudka ad57d2
diff --git a/lib/acl-internal.h b/lib/acl-internal.h
Kamil Dudka ad57d2
index 2a249ff..9353376 100644
Kamil Dudka ad57d2
--- a/lib/acl-internal.h
Kamil Dudka ad57d2
+++ b/lib/acl-internal.h
Kamil Dudka ad57d2
@@ -147,9 +147,6 @@ rpl_acl_set_fd (int fd, acl_t acl)
Kamil Dudka ad57d2
 #   define acl_entries rpl_acl_entries
Kamil Dudka ad57d2
 extern int acl_entries (acl_t);
Kamil Dudka ad57d2
 #  endif
Kamil Dudka ad57d2
-/* Return 1 if given ACL in XDR format is non-trivial
Kamil Dudka ad57d2
- * Return 0 if it is trivial */
Kamil Dudka ad57d2
-extern int acl_nfs4_nontrivial (char *, int);
Kamil Dudka ad57d2
 
Kamil Dudka ad57d2
 #  if HAVE_ACL_TYPE_EXTENDED /* Mac OS X */
Kamil Dudka ad57d2
 /* ACL is an ACL, from a file, stored as type ACL_TYPE_EXTENDED.
Kamil Dudka ad57d2
diff --git a/lib/file-has-acl.c b/lib/file-has-acl.c
Kamil Dudka ad57d2
index 1710234..676523b 100644
Kamil Dudka ad57d2
--- a/lib/file-has-acl.c
Kamil Dudka ad57d2
+++ b/lib/file-has-acl.c
Kamil Dudka ad57d2
@@ -29,14 +29,97 @@
Kamil Dudka ad57d2
 
Kamil Dudka ad57d2
 #include "acl-internal.h"
Kamil Dudka ad57d2
 
Kamil Dudka ad57d2
-#if GETXATTR_WITH_POSIX_ACLS
Kamil Dudka ad57d2
+#if USE_ACL && GETXATTR_WITH_POSIX_ACLS
Kamil Dudka ad57d2
+# include <string.h>
Kamil Dudka ad57d2
+# include <arpa/inet.h>
Kamil Dudka ad57d2
 # include <sys/xattr.h>
Kamil Dudka ad57d2
 # include <linux/xattr.h>
Kamil Dudka ad57d2
-# include <arpa/inet.h>
Kamil Dudka ad57d2
 # ifndef XATTR_NAME_NFSV4_ACL
Kamil Dudka ad57d2
 #  define XATTR_NAME_NFSV4_ACL "system.nfs4_acl"
Kamil Dudka ad57d2
 # endif
Kamil Dudka ad57d2
-# define TRIVIAL_NFS4_ACL_MAX_LENGTH 128
Kamil Dudka ad57d2
+
Kamil Dudka ad57d2
+enum {
Kamil Dudka ad57d2
+  /* ACE4_ACCESS_ALLOWED_ACE_TYPE = 0x00000000, */
Kamil Dudka ad57d2
+  ACE4_ACCESS_DENIED_ACE_TYPE  = 0x00000001,
Kamil Dudka ad57d2
+  ACE4_IDENTIFIER_GROUP        = 0x00000040
Kamil Dudka ad57d2
+};
Kamil Dudka ad57d2
+
Kamil Dudka ad57d2
+/* Return 1 if given ACL in XDR format is non-trivial, 0 if it is trivial.
Kamil Dudka ad57d2
+   -1 upon failure to determine it.  Possibly change errno.  Assume that
Kamil Dudka ad57d2
+   the ACL is valid, except avoid undefined behavior even if invalid.
Kamil Dudka ad57d2
+
Kamil Dudka ad57d2
+   See <https://linux.die.net/man/5/nfs4_acl>.  The NFSv4 acls are
Kamil Dudka ad57d2
+   defined in Internet RFC 7530 and as such, every NFSv4 server
Kamil Dudka ad57d2
+   supporting ACLs should support NFSv4 ACLs (they differ from from
Kamil Dudka ad57d2
+   POSIX draft ACLs).  The ACLs can be obtained via the
Kamil Dudka ad57d2
+   nfsv4-acl-tools, e.g., the nfs4_getfacl command.  Gnulib provides
Kamil Dudka ad57d2
+   only basic support of NFSv4 ACLs, i.e., recognize trivial vs
Kamil Dudka ad57d2
+   nontrivial ACLs.  */
Kamil Dudka ad57d2
+
Kamil Dudka ad57d2
+static int
Kamil Dudka ad57d2
+acl_nfs4_nontrivial (uint32_t *xattr, ssize_t nbytes)
Kamil Dudka ad57d2
+{
Kamil Dudka ad57d2
+  enum { BYTES_PER_NETWORK_UINT = 4};
Kamil Dudka ad57d2
+
Kamil Dudka ad57d2
+  /* Grab the number of aces in the acl.  */
Kamil Dudka ad57d2
+  nbytes -= BYTES_PER_NETWORK_UINT;
Kamil Dudka ad57d2
+  if (nbytes < 0)
Kamil Dudka ad57d2
+    return -1;
Kamil Dudka ad57d2
+  uint32_t num_aces = ntohl (*xattr++);
Kamil Dudka ad57d2
+  if (6 < num_aces)
Kamil Dudka ad57d2
+    return 1;
Kamil Dudka ad57d2
+  int ace_found = 0;
Kamil Dudka ad57d2
+
Kamil Dudka ad57d2
+  for (int ace_n = 0; ace_n < num_aces; ace_n++)
Kamil Dudka ad57d2
+    {
Kamil Dudka ad57d2
+      /* Get the acl type and flag.  Skip the mask; it's too risky to
Kamil Dudka ad57d2
+         test it and it does not seem to be needed.  Get the wholen.  */
Kamil Dudka ad57d2
+      nbytes -= 4 * BYTES_PER_NETWORK_UINT;
Kamil Dudka ad57d2
+      if (nbytes < 0)
Kamil Dudka ad57d2
+        return -1;
Kamil Dudka ad57d2
+      uint32_t type = ntohl (xattr[0]);
Kamil Dudka ad57d2
+      uint32_t flag = ntohl (xattr[1]);
Kamil Dudka ad57d2
+      uint32_t wholen = ntohl (xattr[3]);
Kamil Dudka ad57d2
+      xattr += 4;
Kamil Dudka ad57d2
+      int64_t wholen4 = wholen;
Kamil Dudka ad57d2
+      wholen4 = ((wholen4 + (BYTES_PER_NETWORK_UINT))
Kamil Dudka ad57d2
+                 & ~ (BYTES_PER_NETWORK_UINT - 1));
Kamil Dudka ad57d2
+
Kamil Dudka ad57d2
+      /* Trivial ACLs have only ACE4_ACCESS_ALLOWED_ACE_TYPE or
Kamil Dudka ad57d2
+         ACE4_ACCESS_DENIED_ACE_TYPE.  */
Kamil Dudka ad57d2
+      if (ACE4_ACCESS_DENIED_ACE_TYPE < type)
Kamil Dudka ad57d2
+        return 1;
Kamil Dudka ad57d2
+
Kamil Dudka ad57d2
+      /* RFC 7530 says FLAG should be 0, but be generous to NetApp and
Kamil Dudka ad57d2
+         also accept the group flag.  */
Kamil Dudka ad57d2
+      if (flag & ~ACE4_IDENTIFIER_GROUP)
Kamil Dudka ad57d2
+        return 1;
Kamil Dudka ad57d2
+
Kamil Dudka ad57d2
+      /* Get the who string.  Check NBYTES - WHOLEN4 before storing
Kamil Dudka ad57d2
+         into NBYTES, to avoid truncation on conversion.  */
Kamil Dudka ad57d2
+      if (nbytes - wholen4 < 0)
Kamil Dudka ad57d2
+        return -1;
Kamil Dudka ad57d2
+      nbytes -= wholen4;
Kamil Dudka ad57d2
+
Kamil Dudka ad57d2
+      /* For a trivial ACL, max 6 (typically 3) ACEs, 3 allow, 3 deny.
Kamil Dudka ad57d2
+         Check that there is at most one ACE of each TYPE and WHO.  */
Kamil Dudka ad57d2
+      int who2
Kamil Dudka ad57d2
+        = (wholen == 6 && memcmp (xattr, "OWNER@", 6) == 0 ? 0
Kamil Dudka ad57d2
+           : wholen == 6 && memcmp (xattr, "GROUP@", 6) == 0 ? 2
Kamil Dudka ad57d2
+           : wholen == 9 && memcmp (xattr, "EVERYONE@", 9) == 0 ? 4
Kamil Dudka ad57d2
+           : -1);
Kamil Dudka ad57d2
+      if (who2 < 0)
Kamil Dudka ad57d2
+        return 1;
Kamil Dudka ad57d2
+      int ace_found_bit = 1 << (who2 | type);
Kamil Dudka ad57d2
+      if (ace_found & ace_found_bit)
Kamil Dudka ad57d2
+        return 1;
Kamil Dudka ad57d2
+      ace_found |= ace_found_bit;
Kamil Dudka ad57d2
+
Kamil Dudka ad57d2
+      xattr = (uint32_t *) ((char *) xattr + wholen4);
Kamil Dudka ad57d2
+    }
Kamil Dudka ad57d2
+
Kamil Dudka ad57d2
+  return 0;
Kamil Dudka ad57d2
+}
Kamil Dudka ad57d2
 #endif
Kamil Dudka ad57d2
 
Kamil Dudka ad57d2
 /* Return 1 if NAME has a nontrivial access control list,
Kamil Dudka ad57d2
@@ -56,6 +139,7 @@ file_has_acl (char const *name, struct stat const *sb)
Kamil Dudka ad57d2
 # if GETXATTR_WITH_POSIX_ACLS
Kamil Dudka ad57d2
 
Kamil Dudka ad57d2
       ssize_t ret;
Kamil Dudka ad57d2
+      int initial_errno = errno;
Kamil Dudka ad57d2
 
Kamil Dudka ad57d2
       ret = getxattr (name, XATTR_NAME_POSIX_ACL_ACCESS, NULL, 0);
Kamil Dudka ad57d2
       if (ret < 0 && errno == ENODATA)
Kamil Dudka ad57d2
@@ -73,20 +157,33 @@ file_has_acl (char const *name, struct stat const *sb)
Kamil Dudka ad57d2
         }
Kamil Dudka ad57d2
 
Kamil Dudka ad57d2
       if (ret < 0)
Kamil Dudka ad57d2
-        { /* we might be on NFS, so try to check NFSv4 ACLs too */
Kamil Dudka ad57d2
-          char xattr[TRIVIAL_NFS4_ACL_MAX_LENGTH];
Kamil Dudka ad57d2
-
Kamil Dudka ad57d2
-          errno = 0; /* we need to reset errno set by the previous getxattr() */
Kamil Dudka ad57d2
-          ret = getxattr (name, XATTR_NAME_NFSV4_ACL, xattr, TRIVIAL_NFS4_ACL_MAX_LENGTH);
Kamil Dudka ad57d2
-          if (ret < 0 && errno == ENODATA)
Kamil Dudka ad57d2
-            ret = 0;
Kamil Dudka ad57d2
+        {
Kamil Dudka ad57d2
+          /* Check for NFSv4 ACLs.  The max length of a trivial
Kamil Dudka ad57d2
+             ACL is 6 words for owner, 6 for group, 7 for everyone,
Kamil Dudka ad57d2
+             all times 2 because there are both allow and deny ACEs.
Kamil Dudka ad57d2
+             There are 6 words for owner because of type, flag, mask,
Kamil Dudka ad57d2
+             wholen, "OWNER@"+pad and similarly for group; everyone is
Kamil Dudka ad57d2
+             another word to hold "EVERYONE@".  */
Kamil Dudka ad57d2
+          uint32_t xattr[2 * (6 + 6 + 7)];
Kamil Dudka ad57d2
+
Kamil Dudka ad57d2
+          ret = getxattr (name, XATTR_NAME_NFSV4_ACL, xattr, sizeof xattr);
Kamil Dudka ad57d2
+          if (ret < 0)
Kamil Dudka ad57d2
+            switch (errno)
Kamil Dudka ad57d2
+              {
Kamil Dudka ad57d2
+              case ENODATA: return 0;
Kamil Dudka ad57d2
+              case ERANGE : return 1; /* ACL must be nontrivial.  */
Kamil Dudka ad57d2
+              }
Kamil Dudka ad57d2
           else
Kamil Dudka ad57d2
-            if (ret < 0 && errno == ERANGE)
Kamil Dudka ad57d2
-              return 1;  /* we won't fit into the buffer, so non-trivial ACL is presented */
Kamil Dudka ad57d2
-            else
Kamil Dudka ad57d2
-              if (ret > 0)
Kamil Dudka ad57d2
-                /* looks like trivial ACL, but we need to investigate further */
Kamil Dudka ad57d2
-                return acl_nfs4_nontrivial (xattr, ret);
Kamil Dudka ad57d2
+            {
Kamil Dudka ad57d2
+              /* It looks like a trivial ACL, but investigate further.  */
Kamil Dudka ad57d2
+              ret = acl_nfs4_nontrivial (xattr, ret);
Kamil Dudka ad57d2
+              if (ret < 0)
Kamil Dudka ad57d2
+                {
Kamil Dudka ad57d2
+                  errno = EINVAL;
Kamil Dudka ad57d2
+                  return ret;
Kamil Dudka ad57d2
+                }
Kamil Dudka ad57d2
+              errno = initial_errno;
Kamil Dudka ad57d2
+            }
Kamil Dudka ad57d2
         }
Kamil Dudka ad57d2
       if (ret < 0)
Kamil Dudka ad57d2
         return - acl_errno_valid (errno);
Kamil Dudka ad57d2
-- 
Kamil Dudka ad57d2
2.38.1
Kamil Dudka ad57d2
Kamil Dudka ad57d2
Kamil Dudka ad57d2
From faf965110372c82cd99e9f44f0c64f03cdabb2c1 Mon Sep 17 00:00:00 2001
Kamil Dudka ad57d2
From: Paul Eggert <eggert@cs.ucla.edu>
Kamil Dudka ad57d2
Date: Tue, 27 Dec 2022 20:00:58 -0800
Kamil Dudka ad57d2
Subject: [PATCH 3/3] file-has-acl: fix recently-introduced NFSv4 bug
Kamil Dudka ad57d2
Kamil Dudka ad57d2
* lib/file-has-acl.c (acl_nfs4_nontrivial): Fix off-by-one
Kamil Dudka ad57d2
error when rounding WHOLEN up to next multiple of 4.
Kamil Dudka ad57d2
Pacify GCC 12.2.1 -Wcast-align.
Kamil Dudka ad57d2
Kamil Dudka ad57d2
Upstream-commit: d65e5a8ba77595a598c9ddb8dfa09c4aea732659
Kamil Dudka ad57d2
Signed-off-by: Kamil Dudka <kdudka@redhat.com>
Kamil Dudka ad57d2
---
Kamil Dudka ad57d2
 lib/file-has-acl.c | 9 +++++----
Kamil Dudka ad57d2
 1 file changed, 5 insertions(+), 4 deletions(-)
Kamil Dudka ad57d2
Kamil Dudka ad57d2
diff --git a/lib/file-has-acl.c b/lib/file-has-acl.c
Kamil Dudka ad57d2
index 676523b..7876edc 100644
Kamil Dudka ad57d2
--- a/lib/file-has-acl.c
Kamil Dudka ad57d2
+++ b/lib/file-has-acl.c
Kamil Dudka ad57d2
@@ -81,9 +81,10 @@ acl_nfs4_nontrivial (uint32_t *xattr, ssize_t nbytes)
Kamil Dudka ad57d2
       uint32_t flag = ntohl (xattr[1]);
Kamil Dudka ad57d2
       uint32_t wholen = ntohl (xattr[3]);
Kamil Dudka ad57d2
       xattr += 4;
Kamil Dudka ad57d2
-      int64_t wholen4 = wholen;
Kamil Dudka ad57d2
-      wholen4 = ((wholen4 + (BYTES_PER_NETWORK_UINT))
Kamil Dudka ad57d2
-                 & ~ (BYTES_PER_NETWORK_UINT - 1));
Kamil Dudka ad57d2
+      int whowords = (wholen / BYTES_PER_NETWORK_UINT
Kamil Dudka ad57d2
+                      + (wholen % BYTES_PER_NETWORK_UINT != 0));
Kamil Dudka ad57d2
+      int64_t wholen4 = whowords;
Kamil Dudka ad57d2
+      wholen4 *= BYTES_PER_NETWORK_UINT;
Kamil Dudka ad57d2
 
Kamil Dudka ad57d2
       /* Trivial ACLs have only ACE4_ACCESS_ALLOWED_ACE_TYPE or
Kamil Dudka ad57d2
          ACE4_ACCESS_DENIED_ACE_TYPE.  */
Kamil Dudka ad57d2
@@ -115,7 +116,7 @@ acl_nfs4_nontrivial (uint32_t *xattr, ssize_t nbytes)
Kamil Dudka ad57d2
         return 1;
Kamil Dudka ad57d2
       ace_found |= ace_found_bit;
Kamil Dudka ad57d2
 
Kamil Dudka ad57d2
-      xattr = (uint32_t *) ((char *) xattr + wholen4);
Kamil Dudka ad57d2
+      xattr += whowords;
Kamil Dudka ad57d2
     }
Kamil Dudka ad57d2
 
Kamil Dudka ad57d2
   return 0;
Kamil Dudka ad57d2
-- 
Kamil Dudka ad57d2
2.38.1
Kamil Dudka ad57d2