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, 1 Dec 2022 10:50:49 +0100

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.])



reply via email to

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