bug-gnulib@gnu.org mirror (unofficial)
 help / color / mirror / Atom feed
* [PATCH] Basic support for checking NFSv4 ACLs in Linux
@ 2022-11-24 17:08 Ondrej Valousek
  2022-11-25  2:46 ` Bruno Haible
  2022-11-25  9:34 ` Andreas Grünbacher
  0 siblings, 2 replies; 40+ messages in thread
From: Ondrej Valousek @ 2022-11-24 17:08 UTC (permalink / raw)
  To: bug-gnulib; +Cc: Ondrej Valousek

Hi GNU lib maintainers,

Attaching the final version of patch introducing a basic checks for non-trivial NFSv4 style ACLs.
It is substantially longer unfortunately - that's why I put most of the code to acl-internal.c where I think
it should be (as similar function for AIX already exists there).
The benefit of this is fairly improved robustness of trivial ACLs detection.
The disadvantage is that it pulls back dependency on libacl for some reason (the code does not
use libacl at all so I expect some linker flag like -as-needed would to the trick).

I have tested it against Netapp and Linux based NFS servers under various conditions.
Works OK to me. Maybe it's not perfect, but it's able to detect 99% cases
where ACLs returned by NFS server are not trivial (i.e. POSIX mode bits does not fully
represent access permissions).

Let's hope the code is not forgotten and will be perhaps merged at some stage.
Ondrej


diff --git a/lib/acl-internal.c b/lib/acl-internal.c
index be244c67a..36e29ac02 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,86 @@ 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
+
+int
+acl_nfs4_nontrivial (char *xattr, int len)
+{
+ 	int ret = 0,wholen,bufs = len;
+        uint32_t flag,type,num_aces = ntohl(*((uint32_t*)(xattr))); /* Grab the number of aces in the acl */
+        char *bufp = xattr;
+        int num_a_aces = 0, num_d_aces = 0;
+
+        ret = 0;
+        bufp += 4;  /* sizeof(uint32_t); */
+        bufs -= 4;
+
+
+	for(uint32_t ace_n = 0; num_aces > ace_n ; ace_n++) {
+		int d_ptr;
+
+                /* 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)){
+                        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))
+                        num_a_aces++;
+                else
+                        return 1;
+
+                d_ptr = (wholen /4)*4;
+                if (wholen % 4 != 0)
+                        d_ptr += 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) ? 0 : 1;
+
+}
+
 # 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 94553fab2..88f1d8f1d 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 e02f0626a..89eaf70aa 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,
@@ -67,6 +72,16 @@ file_has_acl (char const *name, struct stat const *sb)
             return 1;
         }
 
+      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) return acl_nfs4_nontrivial(xattr,ret);     /* looks like trivial ACL, but we need to investigate further */
+      }  
       if (ret < 0)
         return - acl_errno_valid (errno);
       return ret;


^ permalink raw reply related	[flat|nested] 40+ messages in thread
* [PATCH] Basic support for checking NFSv4 ACLs in Linux
@ 2022-12-02 12:40 Ondrej Valousek
  2022-12-02 13:33 ` Bruno Haible
  2022-12-22 17:04 ` Bruno Haible
  0 siblings, 2 replies; 40+ messages in thread
From: Ondrej Valousek @ 2022-12-02 12:40 UTC (permalink / raw)
  To: bug-gnulib, bruno; +Cc: Ondrej Valousek

Hi Bruno,
Thanks for the input, I think it should be OK now, please take a look.
Thanks,
Ondrej

---
 doc/acl-nfsv4.txt  | 17 +++++++++
 lib/acl-internal.c | 95 ++++++++++++++++++++++++++++++++++++++++++++++
 lib/acl-internal.h |  3 ++
 lib/file-has-acl.c | 24 ++++++++----
 4 files changed, 135 insertions(+), 9 deletions(-)
 create mode 100644 doc/acl-nfsv4.txt
diff --git a/doc/acl-nfsv4.txt b/doc/acl-nfsv4.txt
new file mode 100644
index 000000000..71352f58d
--- /dev/null
+++ b/doc/acl-nfsv4.txt
@@ -0,0 +1,17 @@
+General introduction:
+   https://linux.die.net/man/5/nfs4_acl
+
+The NFSv4 acls are defined in RFC7530 and as such, every NFSv4 server supporting ACLs
+will support this kind of ACLs (note the difference from POSIX draft ACLs)
+
+The ACLs can be obtained via the nfsv4-acl-tools, i.e.
+
+$ nfs4_getfacl <file>
+
+# file: <file>
+A::OWNER@:rwaDxtTnNcCy
+A::GROUP@:rwaDxtTnNcy
+A::EVERYONE@:rwaDxtTnNcy
+
+Gnulib is aiming to only provide a basic support of these, i.e. recognize trivial
+and non-trivial ACLs
diff --git a/lib/acl-internal.c b/lib/acl-internal.c
index be244c67a..4c65dffcc 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 94553fab2..5da7c115c 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 e02f0626a..171023488 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,
@@ -67,6 +72,22 @@ file_has_acl (char const *name, struct stat const *sb)
             return 1;
         }
 
+      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;


^ permalink raw reply related	[flat|nested] 40+ messages in thread
* [PATCH] Basic support for checking NFSv4 ACLs in Linux
@ 2022-12-01 14:24 Ondrej Valousek
  2022-12-02  0:58 ` Bruno Haible
  0 siblings, 1 reply; 40+ messages in thread
From: Ondrej Valousek @ 2022-12-01 14:24 UTC (permalink / raw)
  To: bug-gnulib, bruno; +Cc: Ondrej Valousek

Hi Bruno,
Could you review one more time?
Thanks,
Ondrej

---
 doc/acl-nfsv4.txt  | 17 +++++++++
 lib/acl-internal.c | 95 ++++++++++++++++++++++++++++++++++++++++++++++
 lib/acl-internal.h |  3 ++
 lib/file-has-acl.c | 24 ++++++++----
 4 files changed, 135 insertions(+), 9 deletions(-)
 create mode 100644 doc/acl-nfsv4.txt

diff --git a/doc/acl-nfsv4.txt b/doc/acl-nfsv4.txt
new file mode 100644
index 000000000..71352f58d
--- /dev/null
+++ b/doc/acl-nfsv4.txt
@@ -0,0 +1,17 @@
+General introduction:
+   https://linux.die.net/man/5/nfs4_acl
+
+The NFSv4 acls are defined in RFC7530 and as such, every NFSv4 server supporting ACLs
+will support this kind of ACLs (note the difference from POSIX draft ACLs)
+
+The ACLs can be obtained via the nfsv4-acl-tools, i.e.
+
+$ nfs4_getfacl <file>
+
+# file: <file>
+A::OWNER@:rwaDxtTnNcCy
+A::GROUP@:rwaDxtTnNcy
+A::EVERYONE@:rwaDxtTnNcy
+
+Gnulib is aiming to only provide a basic support of these, i.e. recognize trivial
+and non-trivial ACLs
diff --git a/lib/acl-internal.c b/lib/acl-internal.c
index be244c67a..b4172fe6d 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,104 @@ 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 ret = 0,
+      wholen,
+      bufs = len;
+  uint32_t flag,
+           type,
+           num_aces = ntohl (*((uint32_t*)(xattr))); /* Grab the number of aces in the acl */
+  char *bufp = xattr;
+  int num_a_aces = 0,
+      num_d_aces = 0;
+
+  ret = 0;
+  bufp += 4;  /* sizeof(uint32_t); */
+  bufs -= 4;
+
+  for (uint32_t ace_n = 0; num_aces > ace_n ; ace_n++)
+    {
+      int d_ptr;
+
+      /* 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) ? 0 : 1;
+
+}
+
 # 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 94553fab2..b674c7702 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 e02f0626a..82830ba4a 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,
@@ -67,6 +72,22 @@ file_has_acl (char const *name, struct stat const *sb)
             return 1;
         }
 
+      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;


^ permalink raw reply related	[flat|nested] 40+ messages in thread
* [PATCH] Basic support for checking NFSv4 ACLs in Linux
@ 2022-12-01  9:50 Ondrej Valousek
  2022-12-01 11:52 ` Bruno Haible
  0 siblings, 1 reply; 40+ messages in thread
From: Ondrej Valousek @ 2022-12-01  9:50 UTC (permalink / raw)
  To: bug-gnulib, bruno; +Cc: Ondrej Valousek

Hi Bruno,
Just sending the modified patch (fixed formatting + few suggestions from Andreas included)
Does it look OK now?
Thanks,
Ondrej

---
 doc/acl-nfsv4.txt  | 17 +++++++++
 lib/acl-internal.c | 95 ++++++++++++++++++++++++++++++++++++++++++++++
 lib/acl-internal.h |  3 ++
 lib/file-has-acl.c | 24 ++++++++----
 m4/acl.m4          |  5 ++-
 5 files changed, 135 insertions(+), 9 deletions(-)
 create mode 100644 doc/acl-nfsv4.txt

diff --git a/doc/acl-nfsv4.txt b/doc/acl-nfsv4.txt
new file mode 100644
index 000000000..552a62ca3
--- /dev/null
+++ b/doc/acl-nfsv4.txt
@@ -0,0 +1,17 @@
+General introduction:
+   https://linux.die.net/man/5/nfs4_acl
+
+The NFSv4 acls are defined in RFC7530 and as such, every NFSv4 server supporting ACLs
+will support this kind of ACLs (note the difference from POSIX draft ACLs)
+
+The ACLs can be obtained via the nfsv4-acl-tools, i.e.
+
+$ nfs4_getfacl <file>
+
+# file: <file>
+A::OWNER@:rwaDxtTnNcCy
+A::GROUP@:rwaDxtTnNcy
+A::EVERYONE@:rwaDxtTnNcy
+
+GNULib is aiming to only provide a basic support of these, i.e. recognize trivial
+and non-trivial ACLs
diff --git a/lib/acl-internal.c b/lib/acl-internal.c
index be244c67a..86df38854 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,98 @@ 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 ret = 0,wholen,bufs = len;
+  uint32_t flag,type,num_aces = ntohl(*((uint32_t*)(xattr))); /* Grab the number of aces in the acl */
+  char *bufp = xattr;
+  int num_a_aces = 0, num_d_aces = 0;
+
+  ret = 0;
+  bufp += 4;  /* sizeof(uint32_t); */
+  bufs -= 4;
+
+  for(uint32_t ace_n = 0; num_aces > ace_n ; ace_n++) {
+    int d_ptr;
+
+    /* 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) ? 0 : 1;
+
+}
+
 # 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 94553fab2..88f1d8f1d 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 e02f0626a..8e43dd70f 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,
@@ -67,6 +72,22 @@ file_has_acl (char const *name, struct stat const *sb)
             return 1;
         }
 
+      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)
+              return acl_nfs4_nontrivial(xattr,ret);
+              /* looks like trivial ACL, but we need to investigate further */
+        }
       if (ret < 0)
         return - acl_errno_valid (errno);
       return ret;
diff --git a/m4/acl.m4 b/m4/acl.m4
index 8909442d7..9445edf9c 100644
--- a/m4/acl.m4
+++ b/m4/acl.m4
@@ -197,7 +197,10 @@ AC_DEFUN([gl_FILE_HAS_ACL],
          [gl_cv_getxattr_with_posix_acls=yes])])
   fi
   if test "$gl_cv_getxattr_with_posix_acls" = yes; then
-    LIB_HAS_ACL=
+    dnl We need to pull libacl back to make linker happy, but strictly
+    dnl speaking, we do not need it
+    LIB_HAS_ACL=$LIB_ACL
+    gl_need_lib_has_acl=1
     AC_DEFINE([GETXATTR_WITH_POSIX_ACLS], 1,
       [Define to 1 if getxattr works with XATTR_NAME_POSIX_ACL_ACCESS
        and XATTR_NAME_POSIX_ACL_DEFAULT.])


^ permalink raw reply related	[flat|nested] 40+ messages in thread
* [PATCH] Basic support for checking NFSv4 ACLs in Linux
@ 2022-11-14  8:16 Ondrej Valousek
  2022-11-14 12:49 ` Andreas Grünbacher
  2022-11-15  2:45 ` Paul Eggert
  0 siblings, 2 replies; 40+ messages in thread
From: Ondrej Valousek @ 2022-11-14  8:16 UTC (permalink / raw)
  To: bug-gnulib, andreas.gruenbacher; +Cc: Ondrej Valousek

Hi Andreas/all
Would you support the following version of the patch?
It is substantially longer unfortunately - that's why I put most of the code to acl-internal.c where I think
it should be (as similar function for AIX already exists there).
The benefit of this is fairly improved robustness of trivial ACLs detection.
The disadvantage is that it pulls back dependency on libacl for some reason (the code does not
use libacl at all so I expect some linker flag like -as-needed would to the trick).
Please let me know.
Ondrej


diff --git a/lib/acl-internal.c b/lib/acl-internal.c
index be244c67a..13f1dfdb3 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,81 @@ 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
+
+int
+acl_nfs4_nontrivial (char *xattr, int len)
+{
+ 	int ret = 0,wholen,d_ptr,bufs = len;
+        u_int32_t type,num_aces = (u_int32_t)ntohl(*((u_int32_t*)(xattr))); /* Grab the number of aces in the acl */
+        char *bufp = xattr;
+        int num_a_aces = 0, num_d_aces = 0;
+
+        ret = 0;
+        d_ptr = sizeof(u_int32_t);
+        bufp += d_ptr;
+        bufs -= d_ptr;
+
+
+	for(u_int32_t ace_n = 0; num_aces > ace_n ; ace_n++) {
+                /* Get the acl type */
+                if(bufs <= 0) {
+                        errno = EINVAL;
+                        return -1;
+                }
+
+                type = (u_int32_t)ntohl(*((u_int32_t*)bufp));
+
+                d_ptr = 3*sizeof(u_int32_t); /* we skip flag and mask - not relevant for our needs */
+                bufp += d_ptr;
+                bufs -= d_ptr;
+
+                if (bufs <= 0) {
+                        errno = EINVAL;
+                        return -1;
+                }
+
+                wholen = (u_int32_t)ntohl(*((u_int32_t*)bufp));
+
+                d_ptr = sizeof(u_int32_t);
+                bufp += d_ptr;
+                bufs -= d_ptr;
+
+                /* Get the who string */
+                if (bufs <= 0) {
+                        errno = EINVAL;
+                        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)){
+                        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))
+                        num_a_aces++;
+                else
+                        return 1;
+
+                d_ptr = ((wholen / sizeof(u_int32_t))*sizeof(u_int32_t));
+                if (wholen % sizeof(u_int32_t) != 0)
+                        d_ptr += sizeof(u_int32_t);
+
+                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) ? 0 : 1;
+
+}
+
 # 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 94553fab2..88f1d8f1d 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 e02f0626a..4d46e7a9d 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,
@@ -67,6 +72,17 @@ file_has_acl (char const *name, struct stat const *sb)
             return 1;
         }
 
+      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) return -1;
+	  else return acl_nfs4_nontrivial(xattr,ret);     /* looks like trivial ACL, but we need to investigate further */
+      }  
       if (ret < 0)
         return - acl_errno_valid (errno);
       return ret;


^ permalink raw reply related	[flat|nested] 40+ messages in thread
[parent not found: <20221109152951.1003859-1-ondrej.valousek.xm@renesas.com>]
* [PATCH] Basic support for checking NFSv4 ACLs in Linux
@ 2022-11-09 15:32 Ondrej Valousek
  0 siblings, 0 replies; 40+ messages in thread
From: Ondrej Valousek @ 2022-11-09 15:32 UTC (permalink / raw)
  To: bug-gnulib; +Cc: Ondrej Valousek

As per suggestions from Andreas, I am attaching a simplified version of the patch implementing basic
checks for non-trivial NFSv4 acls

---
 lib/file-has-acl.c | 24 ++++++++++++++++++++++++
 1 file changed, 24 insertions(+)

diff --git a/lib/file-has-acl.c b/lib/file-has-acl.c
index e02f0626a..37b3aaa30 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_LENGTH 80
 #endif
 
 /* Return 1 if NAME has a nontrivial access control list,
@@ -67,6 +72,14 @@ file_has_acl (char const *name, struct stat const *sb)
             return 1;
         }
 
+      if (ret < 0) { /* we might be on NFS, so try to check NFSv4 ACLs too */
+	  char xattr[TRIVIAL_NFS4_ACL_LENGTH];
+
+	  ret = getxattr (name, XATTR_NAME_NFSV4_ACL, xattr, TRIVIAL_NFS4_ACL_LENGTH);
+	  if (ret < 0 && errno == ENODATA)
+              ret = 0;
+	  else ret = errno == ERANGE;  /* we won't fit into the buffer, so non-trivial ACL is presented */
+      }  
       if (ret < 0)
         return - acl_errno_valid (errno);
       return ret;
-- 
2.37.3


^ permalink raw reply related	[flat|nested] 40+ messages in thread
* [PATCH] Basic support for checking NFSv4 ACLs in Linux
@ 2022-10-27  9:34 Ondrej Valousek
  2022-10-27 19:52 ` Bruno Haible
  0 siblings, 1 reply; 40+ messages in thread
From: Ondrej Valousek @ 2022-10-27  9:34 UTC (permalink / raw)
  To: bug-gnulib; +Cc: Ondrej Valousek

---
 lib/file-has-acl.c | 24 ++++++++++++++++++++++++
 1 file changed, 24 insertions(+)

diff --git a/lib/file-has-acl.c b/lib/file-has-acl.c
index e02f0626a..a6144e52e 100644
--- a/lib/file-has-acl.c
+++ b/lib/file-has-acl.c
@@ -32,6 +32,10 @@
 #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
 #endif
 
 /* Return 1 if NAME has a nontrivial access control list,
@@ -67,6 +71,26 @@ file_has_acl (char const *name, struct stat const *sb)
             return 1;
         }
 
+      if (ret < 0) { /* we might be on NFS, so try to check NFSv4 ACLs too */
+	  ret = getxattr (name, XATTR_NAME_NFSV4_ACL, NULL, 0);
+	  if (ret < 0 && errno == ENODATA)
+              ret = 0;
+	  else if (ret > 0) {
+              char *xattr;
+	      xattr = malloc(ret);
+	      if (!xattr) {
+		ret = -1;
+	      } else {
+                ret = getxattr (name, XATTR_NAME_NFSV4_ACL, xattr, ret);
+		if (ret < 0) ret = -1;
+		else {
+		    u_int32_t num_aces = (u_int32_t)ntohl(*((u_int32_t*)(xattr))); /* Grab the number of aces in the acl */
+		    ret = num_aces > 3;
+		}
+		free(xattr);
+	     }
+	  }
+      }  
       if (ret < 0)
         return - acl_errno_valid (errno);
       return ret;
-- 
2.37.3



^ permalink raw reply related	[flat|nested] 40+ messages in thread

end of thread, other threads:[~2022-12-28 17:10 UTC | newest]

Thread overview: 40+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2022-11-24 17:08 [PATCH] Basic support for checking NFSv4 ACLs in Linux Ondrej Valousek
2022-11-25  2:46 ` Bruno Haible
2022-11-25  9:34 ` Andreas Grünbacher
2022-11-25 10:17   ` Andreas Grünbacher
2022-11-28  7:29     ` Ondrej Valousek
2022-11-29 10:58       ` Andreas Grünbacher
  -- strict thread matches above, loose matches on Subject: below --
2022-12-02 12:40 Ondrej Valousek
2022-12-02 13:33 ` Bruno Haible
2022-12-22 17:04 ` Bruno Haible
2022-12-23 23:32   ` Paul Eggert
2022-12-24 13:00     ` Ondrej Valousek
2022-12-24 19:27       ` Paul Eggert
2022-12-28  4:13     ` Paul Eggert
2022-12-28  9:07       ` Ondrej Valousek
2022-12-28 17:09         ` Paul Eggert
2022-12-01 14:24 Ondrej Valousek
2022-12-02  0:58 ` Bruno Haible
2022-12-01  9:50 Ondrej Valousek
2022-12-01 11:52 ` Bruno Haible
2022-11-14  8:16 Ondrej Valousek
2022-11-14 12:49 ` Andreas Grünbacher
2022-11-15  9:17   ` Ondrej Valousek
2022-11-15 12:24     ` Andreas Grünbacher
2022-11-15 12:35     ` Andreas Grünbacher
2022-11-15 12:46       ` Ondrej Valousek
2022-11-15 13:14         ` Andreas Grünbacher
2022-11-16  9:51           ` Ondrej Valousek
2022-11-15  2:45 ` Paul Eggert
2022-11-15  7:00   ` Andreas Grünbacher
     [not found] <20221109152951.1003859-1-ondrej.valousek.xm@renesas.com>
     [not found] ` <CAHpGcM+=+9Qp1umqzmP-aXHbEPtu8xB_hYP6kNk8UY52WOXpKA@mail.gmail.com>
     [not found]   ` <TY1PR01MB1850006B3019A6BA823B5859D9019@TY1PR01MB1850.jpnprd01.prod.outlook.com>
     [not found]     ` <CAHpGcMKSM7Sgc3jnexdRSajFhC8q0pTcg+M7LNpJs8cMRBgjqQ@mail.gmail.com>
2022-11-11  8:40       ` Ondrej Valousek
2022-11-13 19:32         ` Paul Eggert
2022-11-09 15:32 Ondrej Valousek
2022-10-27  9:34 Ondrej Valousek
2022-10-27 19:52 ` Bruno Haible
2022-10-28 14:33   ` Ondrej Valousek
2022-10-30 18:36     ` Paul Eggert
2022-10-31  8:05       ` Ondrej Valousek
2022-10-31 19:36         ` Paul Eggert
2022-11-07 12:45           ` Ondrej Valousek
2022-11-08 22:11             ` Andreas Grünbacher

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).