From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.2 (2018-09-13) on dcvr.yhbt.net X-Spam-Level: X-Spam-ASN: AS22989 209.51.188.0/24 X-Spam-Status: No, score=-3.9 required=3.0 tests=AWL,BAYES_00, HEADER_FROM_DIFFERENT_DOMAINS,MAILING_LIST_MULTI,RCVD_IN_DNSWL_MED, RCVD_IN_MSPIKE_H2,SPF_HELO_PASS,SPF_PASS shortcircuit=no autolearn=ham autolearn_force=no version=3.4.2 Received: from lists.gnu.org (lists.gnu.org [209.51.188.17]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by dcvr.yhbt.net (Postfix) with ESMTPS id 85C951F910 for ; Thu, 24 Nov 2022 17:21:37 +0000 (UTC) Received: from localhost ([::1] helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1oyFuk-0007Lq-3b; Thu, 24 Nov 2022 12:21:30 -0500 Received: from eggs.gnu.org ([2001:470:142:3::10]) by lists.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1oyFui-0007Ld-JZ for bug-gnulib@gnu.org; Thu, 24 Nov 2022 12:21:28 -0500 Received: from haproxy.adestotech.com ([217.163.77.122]) by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1oyFuf-0001X5-Qw for bug-gnulib@gnu.org; Thu, 24 Nov 2022 12:21:28 -0500 Received: from skynet19.adestotech.com (unknown [192.168.129.19]) by haproxy.adestotech.com (Postfix) with ESMTP id 52E94A16D0; Thu, 24 Nov 2022 17:21:20 +0000 (GMT) From: Ondrej Valousek To: bug-gnulib@gnu.org Cc: Ondrej Valousek Subject: [PATCH] Basic support for checking NFSv4 ACLs in Linux Date: Thu, 24 Nov 2022 18:08:23 +0100 Message-Id: <20221124170822.1329029-1-ondrej.valousek.xm@renesas.com> X-Mailer: git-send-email 2.37.3 MIME-Version: 1.0 Content-Transfer-Encoding: 8bit Received-SPF: pass client-ip=217.163.77.122; envelope-from=ondrej.valousek.xm@renesas.com; helo=haproxy.adestotech.com X-Spam_score_int: 11 X-Spam_score: 1.1 X-Spam_bar: + X-Spam_report: (1.1 / 5.0 requ) AC_FROM_MANY_DOTS=2.999, BAYES_00=-1.9, SPF_HELO_NONE=0.001, SPF_PASS=-0.001 autolearn=no autolearn_force=no X-Spam_action: no action X-BeenThere: bug-gnulib@gnu.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: Gnulib discussion list List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: bug-gnulib-bounces+normalperson=yhbt.net@gnu.org Sender: bug-gnulib-bounces+normalperson=yhbt.net@gnu.org 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 +# include + # 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 # include +# include +#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;