bug-gnulib
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[PATCH] Basic support for checking NFSv4 ACLs in Linux


From: Ondrej Valousek
Subject: [PATCH] Basic support for checking NFSv4 ACLs in Linux
Date: Thu, 24 Nov 2022 18:08:23 +0100

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;



reply via email to

[Prev in Thread] Current Thread [Next in Thread]