exp2l: Work around a NetBSD 10.0/i386 bug.
[gnulib.git] / lib / file-has-acl.c
blob898fb030d1de9b9f6e886e099f29c98b31310c58
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"
24 #endif
26 #include <config.h>
28 #include "acl.h"
30 #include "acl-internal.h"
31 #include "attribute.h"
32 #include "minmax.h"
34 #if USE_ACL && HAVE_LINUX_XATTR_H && HAVE_LISTXATTR
35 # include <stdckdint.h>
36 # include <string.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"
42 # endif
43 # ifndef XATTR_NAME_POSIX_ACL_ACCESS
44 # define XATTR_NAME_POSIX_ACL_ACCESS "system.posix_acl_access"
45 # endif
46 # ifndef XATTR_NAME_POSIX_ACL_DEFAULT
47 # define XATTR_NAME_POSIX_ACL_DEFAULT "system.posix_acl_default"
48 # endif
50 enum {
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++)
65 if (!*a)
66 return true;
67 return false;
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
80 nontrivial ACLs. */
82 static int
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;
89 if (nbytes < 0)
90 return -1;
91 uint32_t num_aces = ntohl (*xattr++);
92 if (6 < num_aces)
93 return 1;
94 int ace_found = 0;
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;
101 if (nbytes < 0)
102 return -1;
103 uint32_t type = ntohl (xattr[0]);
104 uint32_t flag = ntohl (xattr[1]);
105 uint32_t wholen = ntohl (xattr[3]);
106 xattr += 4;
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)
115 return 1;
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)
120 return 1;
122 /* Get the who string. Check NBYTES - WHOLEN4 before storing
123 into NBYTES, to avoid truncation on conversion. */
124 if (nbytes - wholen4 < 0)
125 return -1;
126 nbytes -= wholen4;
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. */
130 int who2
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
134 : -1);
135 if (who2 < 0)
136 return 1;
137 int ace_found_bit = 1 << (who2 | type);
138 if (ace_found & ace_found_bit)
139 return 1;
140 ace_found |= ace_found_bit;
142 xattr += whowords;
145 return 0;
147 #endif
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)
159 #if USE_ACL
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
171 "EVERYONE@". */
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. */
176 union {
177 trivial_NFSv4_xattr_buf xattr;
178 char ch[sizeof (trivial_NFSv4_xattr_buf)];
179 } stackbuf;
181 char *listbuf = stackbuf.ch;
182 ssize_t listbufsize = sizeof stackbuf.ch;
183 char *heapbuf = NULL;
184 ssize_t listsize;
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
190 && errno == ERANGE)
192 free (heapbuf);
193 ssize_t newsize = listxattr (name, NULL, 0);
194 if (newsize <= 0)
195 return newsize;
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)
204 errno = ENOMEM;
205 return -1;
208 listbuf = heapbuf = malloc (listbufsize);
209 if (!listbuf)
210 return -1;
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. */
217 bool nfsv4_acl
218 = 0 < listsize && have_xattr (XATTR_NAME_NFSV4_ACL, listbuf, listsize);
219 int ret
220 = (listsize <= 0 ? listsize
221 : (nfsv4_acl
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))));
226 free (heapbuf);
228 /* If there is an NFSv4 ACL, follow up with a getxattr syscall
229 to see whether the NFSv4 ACL is nontrivial. */
230 if (nfsv4_acl)
232 ret = getxattr (name, XATTR_NAME_NFSV4_ACL,
233 stackbuf.xattr, sizeof stackbuf.xattr);
234 if (ret < 0)
235 switch (errno)
237 case ENODATA: return 0;
238 case ERANGE : return 1; /* ACL must be nontrivial. */
240 else
242 /* It looks like a trivial ACL, but investigate further. */
243 ret = acl_nfs4_nontrivial (stackbuf.xattr, ret);
244 if (ret < 0)
246 errno = EINVAL;
247 return ret;
249 errno = initial_errno;
252 if (ret < 0)
253 return - acl_errno_valid (errno);
254 return ret;
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 */
260 int ret;
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
266 ACL_TYPE_DEFAULT. */
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);
278 if (acl)
280 ret = acl_extended_nontrivial (acl);
281 acl_free (acl);
283 else
284 ret = -1;
285 # else /* FreeBSD, IRIX, Tru64, Cygwin >= 2.5 */
286 acl_t acl = acl_get_file (name, ACL_TYPE_ACCESS);
287 if (acl)
289 int saved_errno;
291 ret = acl_access_nontrivial (acl);
292 saved_errno = errno;
293 acl_free (acl);
294 errno = saved_errno;
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
298 making this call. */
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);
308 if (acl)
310 # ifdef __CYGWIN__ /* Cygwin >= 2.5 */
311 ret = acl_access_nontrivial (acl);
312 saved_errno = errno;
313 acl_free (acl);
314 errno = saved_errno;
315 # else
316 ret = (0 < acl_entries (acl));
317 acl_free (acl);
318 # endif
320 else
321 ret = -1;
323 # endif
325 else
326 ret = -1;
327 # endif
329 if (ret < 0)
330 return - acl_errno_valid (errno);
331 return ret;
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
346 at once. */
348 /* Initially, try to read the entries into a stack-allocated buffer.
349 Use malloc if it does not fit. */
350 enum
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;
359 int count;
361 for (;;)
363 count = acl (name, GETACL, alloc, entries);
364 if (count < 0 && errno == ENOSPC)
366 /* Increase the size of the buffer. */
367 free (malloced);
368 if (alloc > alloc_max / 2)
370 errno = ENOMEM;
371 return -1;
373 alloc = 2 * alloc; /* <= alloc_max */
374 entries = malloced =
375 (aclent_t *) malloc (alloc * sizeof (aclent_t));
376 if (entries == NULL)
378 errno = ENOMEM;
379 return -1;
381 continue;
383 break;
385 if (count < 0)
387 if (errno == ENOSYS || errno == ENOTSUP)
389 else
391 free (malloced);
392 return -1;
395 else if (count == 0)
397 else
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. */
403 if (count > 4)
405 free (malloced);
406 return 1;
409 if (acl_nontrivial (count, entries))
411 free (malloced);
412 return 1;
415 free (malloced);
418 # ifdef ACE_GETACL
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. */
424 enum
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;
433 int count;
435 for (;;)
437 count = acl (name, ACE_GETACL, alloc, entries);
438 if (count < 0 && errno == ENOSPC)
440 /* Increase the size of the buffer. */
441 free (malloced);
442 if (alloc > alloc_max / 2)
444 errno = ENOMEM;
445 return -1;
447 alloc = 2 * alloc; /* <= alloc_max */
448 entries = malloced = (ace_t *) malloc (alloc * sizeof (ace_t));
449 if (entries == NULL)
451 errno = ENOMEM;
452 return -1;
454 continue;
456 break;
458 if (count < 0)
460 if (errno == ENOSYS || errno == EINVAL)
462 else
464 free (malloced);
465 return -1;
468 else if (count == 0)
470 else
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. */
480 if (count > 6)
482 free (malloced);
483 return 1;
486 if (acl_ace_nontrivial (count, entries))
488 free (malloced);
489 return 1;
492 free (malloced);
494 # endif
496 return 0;
497 # endif
499 # elif HAVE_GETACL /* HP-UX */
502 struct acl_entry entries[NACLENTRIES];
503 int count;
505 count = getacl (name, NACLENTRIES, entries);
507 if (count < 0)
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)
514 else
515 return -1;
517 else if (count == 0)
518 return 0;
519 else /* count > 0 */
521 if (count > NACLENTRIES)
522 /* If NACLENTRIES cannot be trusted, use dynamic memory
523 allocation. */
524 abort ();
526 /* If there are more than 3 entries, there cannot be only the
527 (uid,%), (%,gid), (%,%) entries. */
528 if (count > 3)
529 return 1;
532 struct stat statbuf;
534 if (stat (name, &statbuf) == -1 && errno != EOVERFLOW)
535 return -1;
537 return acl_nontrivial (count, entries);
542 # if HAVE_ACLV_H /* HP-UX >= 11.11 */
545 struct acl entries[NACLVENTRIES];
546 int count;
548 count = acl ((char *) name, ACL_GET, NACLVENTRIES, entries);
550 if (count < 0)
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)
556 else
557 return -1;
559 else if (count == 0)
560 return 0;
561 else /* count > 0 */
563 if (count > NACLVENTRIES)
564 /* If NACLVENTRIES cannot be trusted, use dynamic memory
565 allocation. */
566 abort ();
568 /* If there are more than 4 entries, there cannot be only the
569 four base ACL entries. */
570 if (count > 4)
571 return 1;
573 return aclv_nontrivial (count, entries);
577 # endif
579 # elif HAVE_ACLX_GET && defined ACL_AIX_WIP /* AIX */
581 acl_type_t type;
582 char aclbuf[1024];
583 void *acl = aclbuf;
584 size_t aclsize = sizeof (aclbuf);
585 mode_t mode;
587 for (;;)
589 /* The docs say that type being 0 is equivalent to ACL_ANY, but it
590 is not true, in AIX 5.3. */
591 type.u64 = ACL_ANY;
592 if (aclx_get (name, 0, &type, aclbuf, &aclsize, &mode) >= 0)
593 break;
594 if (errno == ENOSYS)
595 return 0;
596 if (errno != ENOSPC)
598 if (acl != aclbuf)
599 free (acl);
600 return -1;
602 aclsize = 2 * aclsize;
603 if (acl != aclbuf)
604 free (acl);
605 acl = malloc (aclsize);
606 if (acl == NULL)
608 errno = ENOMEM;
609 return -1;
613 if (type.u64 == ACL_AIXC)
615 int result = acl_nontrivial ((struct acl *) acl);
616 if (acl != aclbuf)
617 free (acl);
618 return result;
620 else if (type.u64 == ACL_NFS4)
622 int result = acl_nfs4_nontrivial ((nfs4_acl_int_t *) acl);
623 if (acl != aclbuf)
624 free (acl);
625 return result;
627 else
629 /* A newer type of ACL has been introduced in the system.
630 We should better support it. */
631 if (acl != aclbuf)
632 free (acl);
633 errno = EINVAL;
634 return -1;
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)
642 return -1;
644 return acl_nontrivial (&u.a);
646 # elif HAVE_ACLSORT /* NonStop Kernel */
649 struct acl entries[NACLENTRIES];
650 int count;
652 count = acl ((char *) name, ACL_GET, NACLENTRIES, entries);
654 if (count < 0)
656 if (errno == ENOSYS || errno == ENOTSUP)
658 else
659 return -1;
661 else if (count == 0)
662 return 0;
663 else /* count > 0 */
665 if (count > NACLENTRIES)
666 /* If NACLENTRIES cannot be trusted, use dynamic memory
667 allocation. */
668 abort ();
670 /* If there are more than 4 entries, there cannot be only the
671 four base ACL entries. */
672 if (count > 4)
673 return 1;
675 return acl_nontrivial (count, entries);
679 # endif
681 #endif
683 return 0;