1 /* Test whether a file has a nontrivial ACL. -*- coding: utf-8 -*-
3 Copyright (C) 2002-2003, 2005-2024 Free Software Foundation, Inc.
5 This program is free software: you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation, either version 3 of the License, or
8 (at your option) any later version.
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU General Public License for more details.
15 You should have received a copy of the GNU General Public License
16 along with this program. If not, see <https://www.gnu.org/licenses/>.
18 Written by Paul Eggert, Andreas Grünbacher, and Bruno Haible. */
20 /* Without this pragma, gcc 4.7.0 20120126 may suggest that the
21 file_has_acl function might be candidate for attribute 'const' */
22 #if (__GNUC__ == 4 && 6 <= __GNUC_MINOR__) || 4 < __GNUC__
23 # pragma GCC diagnostic ignored "-Wsuggest-attribute=const"
30 #include "acl-internal.h"
31 #include "attribute.h"
34 #if USE_ACL && HAVE_LINUX_XATTR_H && HAVE_LISTXATTR
35 # include <stdckdint.h>
37 # include <arpa/inet.h>
38 # include <sys/xattr.h>
39 # include <linux/xattr.h>
40 # ifndef XATTR_NAME_NFSV4_ACL
41 # define XATTR_NAME_NFSV4_ACL "system.nfs4_acl"
43 # ifndef XATTR_NAME_POSIX_ACL_ACCESS
44 # define XATTR_NAME_POSIX_ACL_ACCESS "system.posix_acl_access"
46 # ifndef XATTR_NAME_POSIX_ACL_DEFAULT
47 # define XATTR_NAME_POSIX_ACL_DEFAULT "system.posix_acl_default"
51 /* ACE4_ACCESS_ALLOWED_ACE_TYPE = 0x00000000, */
52 ACE4_ACCESS_DENIED_ACE_TYPE
= 0x00000001,
53 ACE4_IDENTIFIER_GROUP
= 0x00000040
56 /* Return true if ATTR is in the set represented by the NUL-terminated
57 strings in LISTBUF, which is of size LISTSIZE. */
59 ATTRIBUTE_PURE
static bool
60 have_xattr (char const *attr
, char const *listbuf
, ssize_t listsize
)
62 char const *blim
= listbuf
+ listsize
;
63 for (char const *b
= listbuf
; b
< blim
; b
+= strlen (b
) + 1)
64 for (char const *a
= attr
; *a
== *b
; a
++, b
++)
70 /* Return 1 if given ACL in XDR format is non-trivial, 0 if it is trivial.
71 -1 upon failure to determine it. Possibly change errno. Assume that
72 the ACL is valid, except avoid undefined behavior even if invalid.
74 See <https://linux.die.net/man/5/nfs4_acl>. The NFSv4 acls are
75 defined in Internet RFC 7530 and as such, every NFSv4 server
76 supporting ACLs should support NFSv4 ACLs (they differ from from
77 POSIX draft ACLs). The ACLs can be obtained via the
78 nfsv4-acl-tools, e.g., the nfs4_getfacl command. Gnulib provides
79 only basic support of NFSv4 ACLs, i.e., recognize trivial vs
83 acl_nfs4_nontrivial (uint32_t *xattr
, ssize_t nbytes
)
85 enum { BYTES_PER_NETWORK_UINT
= 4};
87 /* Grab the number of aces in the acl. */
88 nbytes
-= BYTES_PER_NETWORK_UINT
;
91 uint32_t num_aces
= ntohl (*xattr
++);
96 for (int ace_n
= 0; ace_n
< num_aces
; ace_n
++)
98 /* Get the acl type and flag. Skip the mask; it's too risky to
99 test it and it does not seem to be needed. Get the wholen. */
100 nbytes
-= 4 * BYTES_PER_NETWORK_UINT
;
103 uint32_t type
= ntohl (xattr
[0]);
104 uint32_t flag
= ntohl (xattr
[1]);
105 uint32_t wholen
= ntohl (xattr
[3]);
107 int whowords
= (wholen
/ BYTES_PER_NETWORK_UINT
108 + (wholen
% BYTES_PER_NETWORK_UINT
!= 0));
109 int64_t wholen4
= whowords
;
110 wholen4
*= BYTES_PER_NETWORK_UINT
;
112 /* Trivial ACLs have only ACE4_ACCESS_ALLOWED_ACE_TYPE or
113 ACE4_ACCESS_DENIED_ACE_TYPE. */
114 if (ACE4_ACCESS_DENIED_ACE_TYPE
< type
)
117 /* RFC 7530 says FLAG should be 0, but be generous to NetApp and
118 also accept the group flag. */
119 if (flag
& ~ACE4_IDENTIFIER_GROUP
)
122 /* Get the who string. Check NBYTES - WHOLEN4 before storing
123 into NBYTES, to avoid truncation on conversion. */
124 if (nbytes
- wholen4
< 0)
128 /* For a trivial ACL, max 6 (typically 3) ACEs, 3 allow, 3 deny.
129 Check that there is at most one ACE of each TYPE and WHO. */
131 = (wholen
== 6 && memcmp (xattr
, "OWNER@", 6) == 0 ? 0
132 : wholen
== 6 && memcmp (xattr
, "GROUP@", 6) == 0 ? 2
133 : wholen
== 9 && memcmp (xattr
, "EVERYONE@", 9) == 0 ? 4
137 int ace_found_bit
= 1 << (who2
| type
);
138 if (ace_found
& ace_found_bit
)
140 ace_found
|= ace_found_bit
;
149 /* Return 1 if NAME has a nontrivial access control list,
150 0 if ACLs are not supported, or if NAME has no or only a base ACL,
151 and -1 (setting errno) on error. Note callers can determine
152 if ACLs are not supported as errno is set in that case also.
153 SB must be set to the stat buffer of NAME,
154 obtained through stat() or lstat(). */
157 file_has_acl (char const *name
, struct stat
const *sb
)
160 if (! S_ISLNK (sb
->st_mode
))
163 # if HAVE_LINUX_XATTR_H && HAVE_LISTXATTR
164 int initial_errno
= errno
;
166 /* The max length of a trivial NFSv4 ACL is 6 words for owner,
167 6 for group, 7 for everyone, all times 2 because there are
168 both allow and deny ACEs. There are 6 words for owner
169 because of type, flag, mask, wholen, "OWNER@"+pad and
170 similarly for group; everyone is another word to hold
172 typedef uint32_t trivial_NFSv4_xattr_buf
[2 * (6 + 6 + 7)];
174 /* A buffer large enough to hold any trivial NFSv4 ACL,
175 and also useful as a small array of char. */
177 trivial_NFSv4_xattr_buf xattr
;
178 char ch
[sizeof (trivial_NFSv4_xattr_buf
)];
181 char *listbuf
= stackbuf
.ch
;
182 ssize_t listbufsize
= sizeof stackbuf
.ch
;
183 char *heapbuf
= NULL
;
186 /* Use listxattr first, as this means just one syscall in the
187 typical case where the file lacks an ACL. Try stackbuf
188 first, falling back on malloc if stackbuf is too small. */
189 while ((listsize
= listxattr (name
, listbuf
, listbufsize
)) < 0
193 ssize_t newsize
= listxattr (name
, NULL
, 0);
197 /* Grow LISTBUFSIZE to at least NEWSIZE. Grow it by a
198 nontrivial amount too, to defend against denial of
199 service by an adversary that fiddles with ACLs. */
200 bool overflow
= ckd_add (&listbufsize
, listbufsize
, listbufsize
>> 1);
201 listbufsize
= MAX (listbufsize
, newsize
);
202 if (overflow
|| SIZE_MAX
< listbufsize
)
208 listbuf
= heapbuf
= malloc (listbufsize
);
213 /* In Fedora 39, a file can have both NFSv4 and POSIX ACLs,
214 but if it has an NFSv4 ACL that's the one that matters.
215 In earlier Fedora the two types of ACLs were mutually exclusive.
216 Attempt to work correctly on both kinds of systems. */
218 = 0 < listsize
&& have_xattr (XATTR_NAME_NFSV4_ACL
, listbuf
, listsize
);
220 = (listsize
<= 0 ? listsize
222 || have_xattr (XATTR_NAME_POSIX_ACL_ACCESS
, listbuf
, listsize
)
223 || (S_ISDIR (sb
->st_mode
)
224 && have_xattr (XATTR_NAME_POSIX_ACL_DEFAULT
,
225 listbuf
, listsize
))));
228 /* If there is an NFSv4 ACL, follow up with a getxattr syscall
229 to see whether the NFSv4 ACL is nontrivial. */
232 ret
= getxattr (name
, XATTR_NAME_NFSV4_ACL
,
233 stackbuf
.xattr
, sizeof stackbuf
.xattr
);
237 case ENODATA
: return 0;
238 case ERANGE
: return 1; /* ACL must be nontrivial. */
242 /* It looks like a trivial ACL, but investigate further. */
243 ret
= acl_nfs4_nontrivial (stackbuf
.xattr
, ret
);
249 errno
= initial_errno
;
253 return - acl_errno_valid (errno
);
256 # elif HAVE_ACL_GET_FILE
258 /* POSIX 1003.1e (draft 17 -- abandoned) specific version. */
259 /* Linux, FreeBSD, Mac OS X, IRIX, Tru64, Cygwin >= 2.5 */
262 if (HAVE_ACL_EXTENDED_FILE
) /* Linux */
264 /* On Linux, acl_extended_file is an optimized function: It only
265 makes two calls to getxattr(), one for ACL_TYPE_ACCESS, one for
267 ret
= acl_extended_file (name
);
269 else /* FreeBSD, Mac OS X, IRIX, Tru64, Cygwin >= 2.5 */
271 # if HAVE_ACL_TYPE_EXTENDED /* Mac OS X */
272 /* On Mac OS X, acl_get_file (name, ACL_TYPE_ACCESS)
273 and acl_get_file (name, ACL_TYPE_DEFAULT)
274 always return NULL / EINVAL. There is no point in making
275 these two useless calls. The real ACL is retrieved through
276 acl_get_file (name, ACL_TYPE_EXTENDED). */
277 acl_t acl
= acl_get_file (name
, ACL_TYPE_EXTENDED
);
280 ret
= acl_extended_nontrivial (acl
);
285 # else /* FreeBSD, IRIX, Tru64, Cygwin >= 2.5 */
286 acl_t acl
= acl_get_file (name
, ACL_TYPE_ACCESS
);
291 ret
= acl_access_nontrivial (acl
);
295 # if HAVE_ACL_FREE_TEXT /* Tru64 */
296 /* On OSF/1, acl_get_file (name, ACL_TYPE_DEFAULT) always
297 returns NULL with errno not set. There is no point in
299 # else /* FreeBSD, IRIX, Cygwin >= 2.5 */
300 /* On Linux, FreeBSD, IRIX, acl_get_file (name, ACL_TYPE_ACCESS)
301 and acl_get_file (name, ACL_TYPE_DEFAULT) on a directory
302 either both succeed or both fail; it depends on the
303 file system. Therefore there is no point in making the second
304 call if the first one already failed. */
305 if (ret
== 0 && S_ISDIR (sb
->st_mode
))
307 acl
= acl_get_file (name
, ACL_TYPE_DEFAULT
);
310 # ifdef __CYGWIN__ /* Cygwin >= 2.5 */
311 ret
= acl_access_nontrivial (acl
);
316 ret
= (0 < acl_entries (acl
));
330 return - acl_errno_valid (errno
);
333 # elif HAVE_FACL && defined GETACL /* Solaris, Cygwin < 2.5, not HP-UX */
335 # if defined ACL_NO_TRIVIAL
337 /* Solaris 10 (newer version), which has additional API declared in
338 <sys/acl.h> (acl_t) and implemented in libsec (acl_set, acl_trivial,
339 acl_fromtext, ...). */
340 return acl_trivial (name
);
342 # else /* Solaris, Cygwin, general case */
344 /* Solaris 2.5 through Solaris 10, Cygwin, and contemporaneous versions
345 of Unixware. The acl() call returns the access and default ACL both
348 /* Initially, try to read the entries into a stack-allocated buffer.
349 Use malloc if it does not fit. */
352 alloc_init
= 4000 / sizeof (aclent_t
), /* >= 3 */
353 alloc_max
= MIN (INT_MAX
, SIZE_MAX
/ sizeof (aclent_t
))
355 aclent_t buf
[alloc_init
];
356 size_t alloc
= alloc_init
;
357 aclent_t
*entries
= buf
;
358 aclent_t
*malloced
= NULL
;
363 count
= acl (name
, GETACL
, alloc
, entries
);
364 if (count
< 0 && errno
== ENOSPC
)
366 /* Increase the size of the buffer. */
368 if (alloc
> alloc_max
/ 2)
373 alloc
= 2 * alloc
; /* <= alloc_max */
375 (aclent_t
*) malloc (alloc
* sizeof (aclent_t
));
387 if (errno
== ENOSYS
|| errno
== ENOTSUP
)
399 /* Don't use MIN_ACL_ENTRIES: It's set to 4 on Cygwin, but Cygwin
400 returns only 3 entries for files with no ACL. But this is safe:
401 If there are more than 4 entries, there cannot be only the
402 "user::", "group::", "other:", and "mask:" entries. */
409 if (acl_nontrivial (count
, entries
))
419 /* Solaris also has a different variant of ACLs, used in ZFS and NFSv4
420 file systems (whereas the other ones are used in UFS file systems). */
422 /* Initially, try to read the entries into a stack-allocated buffer.
423 Use malloc if it does not fit. */
426 alloc_init
= 4000 / sizeof (ace_t
), /* >= 3 */
427 alloc_max
= MIN (INT_MAX
, SIZE_MAX
/ sizeof (ace_t
))
429 ace_t buf
[alloc_init
];
430 size_t alloc
= alloc_init
;
431 ace_t
*entries
= buf
;
432 ace_t
*malloced
= NULL
;
437 count
= acl (name
, ACE_GETACL
, alloc
, entries
);
438 if (count
< 0 && errno
== ENOSPC
)
440 /* Increase the size of the buffer. */
442 if (alloc
> alloc_max
/ 2)
447 alloc
= 2 * alloc
; /* <= alloc_max */
448 entries
= malloced
= (ace_t
*) malloc (alloc
* sizeof (ace_t
));
460 if (errno
== ENOSYS
|| errno
== EINVAL
)
472 /* In the old (original Solaris 10) convention:
473 If there are more than 3 entries, there cannot be only the
474 ACE_OWNER, ACE_GROUP, ACE_OTHER entries.
475 In the newer Solaris 10 and Solaris 11 convention:
476 If there are more than 6 entries, there cannot be only the
477 ACE_OWNER, ACE_GROUP, ACE_EVERYONE entries, each once with
478 NEW_ACE_ACCESS_ALLOWED_ACE_TYPE and once with
479 NEW_ACE_ACCESS_DENIED_ACE_TYPE. */
486 if (acl_ace_nontrivial (count
, entries
))
499 # elif HAVE_GETACL /* HP-UX */
502 struct acl_entry entries
[NACLENTRIES
];
505 count
= getacl (name
, NACLENTRIES
, entries
);
509 /* ENOSYS is seen on newer HP-UX versions.
510 EOPNOTSUPP is typically seen on NFS mounts.
511 ENOTSUP was seen on Quantum StorNext file systems (cvfs). */
512 if (errno
== ENOSYS
|| errno
== EOPNOTSUPP
|| errno
== ENOTSUP
)
521 if (count
> NACLENTRIES
)
522 /* If NACLENTRIES cannot be trusted, use dynamic memory
526 /* If there are more than 3 entries, there cannot be only the
527 (uid,%), (%,gid), (%,%) entries. */
534 if (stat (name
, &statbuf
) == -1 && errno
!= EOVERFLOW
)
537 return acl_nontrivial (count
, entries
);
542 # if HAVE_ACLV_H /* HP-UX >= 11.11 */
545 struct acl entries
[NACLVENTRIES
];
548 count
= acl ((char *) name
, ACL_GET
, NACLVENTRIES
, entries
);
552 /* EOPNOTSUPP is seen on NFS in HP-UX 11.11, 11.23.
553 EINVAL is seen on NFS in HP-UX 11.31. */
554 if (errno
== ENOSYS
|| errno
== EOPNOTSUPP
|| errno
== EINVAL
)
563 if (count
> NACLVENTRIES
)
564 /* If NACLVENTRIES cannot be trusted, use dynamic memory
568 /* If there are more than 4 entries, there cannot be only the
569 four base ACL entries. */
573 return aclv_nontrivial (count
, entries
);
579 # elif HAVE_ACLX_GET && defined ACL_AIX_WIP /* AIX */
584 size_t aclsize
= sizeof (aclbuf
);
589 /* The docs say that type being 0 is equivalent to ACL_ANY, but it
590 is not true, in AIX 5.3. */
592 if (aclx_get (name
, 0, &type
, aclbuf
, &aclsize
, &mode
) >= 0)
602 aclsize
= 2 * aclsize
;
605 acl
= malloc (aclsize
);
613 if (type
.u64
== ACL_AIXC
)
615 int result
= acl_nontrivial ((struct acl
*) acl
);
620 else if (type
.u64
== ACL_NFS4
)
622 int result
= acl_nfs4_nontrivial ((nfs4_acl_int_t
*) acl
);
629 /* A newer type of ACL has been introduced in the system.
630 We should better support it. */
637 # elif HAVE_STATACL /* older AIX */
639 union { struct acl a
; char room
[4096]; } u
;
641 if (statacl ((char *) name
, STX_NORMAL
, &u
.a
, sizeof (u
)) < 0)
644 return acl_nontrivial (&u
.a
);
646 # elif HAVE_ACLSORT /* NonStop Kernel */
649 struct acl entries
[NACLENTRIES
];
652 count
= acl ((char *) name
, ACL_GET
, NACLENTRIES
, entries
);
656 if (errno
== ENOSYS
|| errno
== ENOTSUP
)
665 if (count
> NACLENTRIES
)
666 /* If NACLENTRIES cannot be trusted, use dynamic memory
670 /* If there are more than 4 entries, there cannot be only the
671 four base ACL entries. */
675 return acl_nontrivial (count
, entries
);