1 /* Test whether a file has a nontrivial ACL.  -*- coding: utf-8 -*-
2 
3    Copyright (C) 2002-2003, 2005-2021 Free Software Foundation, Inc.
4 
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.
9 
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.
14 
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/>.
17 
18    Written by Paul Eggert, Andreas Grünbacher, and Bruno Haible.  */
19 
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
25 
26 #include <config.h>
27 
28 #include "acl.h"
29 
30 #include "acl-internal.h"
31 
32 #if GETXATTR_WITH_POSIX_ACLS
33 # include <sys/xattr.h>
34 # include <linux/xattr.h>
35 #endif
36 
37 /* Return 1 if NAME has a nontrivial access control list,
38    0 if ACLs are not supported, or if NAME has no or only a base ACL,
39    and -1 (setting errno) on error.  Note callers can determine
40    if ACLs are not supported as errno is set in that case also.
41    SB must be set to the stat buffer of NAME,
42    obtained through stat() or lstat().  */
43 
44 int
file_has_acl(char const * name,struct stat const * sb)45 file_has_acl (char const *name, struct stat const *sb)
46 {
47 #if USE_ACL
48   if (! S_ISLNK (sb->st_mode))
49     {
50 
51 # if GETXATTR_WITH_POSIX_ACLS
52 
53       ssize_t ret;
54 
55       ret = getxattr (name, XATTR_NAME_POSIX_ACL_ACCESS, NULL, 0);
56       if (ret < 0 && errno == ENODATA)
57         ret = 0;
58       else if (ret > 0)
59         return 1;
60 
61       if (ret == 0 && S_ISDIR (sb->st_mode))
62         {
63           ret = getxattr (name, XATTR_NAME_POSIX_ACL_DEFAULT, NULL, 0);
64           if (ret < 0 && errno == ENODATA)
65             ret = 0;
66           else if (ret > 0)
67             return 1;
68         }
69 
70       if (ret < 0)
71         return - acl_errno_valid (errno);
72       return ret;
73 
74 # elif HAVE_ACL_GET_FILE
75 
76       /* POSIX 1003.1e (draft 17 -- abandoned) specific version.  */
77       /* Linux, FreeBSD, Mac OS X, IRIX, Tru64, Cygwin >= 2.5 */
78       int ret;
79 
80       if (HAVE_ACL_EXTENDED_FILE) /* Linux */
81         {
82           /* On Linux, acl_extended_file is an optimized function: It only
83              makes two calls to getxattr(), one for ACL_TYPE_ACCESS, one for
84              ACL_TYPE_DEFAULT.  */
85           ret = acl_extended_file (name);
86         }
87       else /* FreeBSD, Mac OS X, IRIX, Tru64, Cygwin >= 2.5 */
88         {
89 #  if HAVE_ACL_TYPE_EXTENDED /* Mac OS X */
90           /* On Mac OS X, acl_get_file (name, ACL_TYPE_ACCESS)
91              and acl_get_file (name, ACL_TYPE_DEFAULT)
92              always return NULL / EINVAL.  There is no point in making
93              these two useless calls.  The real ACL is retrieved through
94              acl_get_file (name, ACL_TYPE_EXTENDED).  */
95           acl_t acl = acl_get_file (name, ACL_TYPE_EXTENDED);
96           if (acl)
97             {
98               ret = acl_extended_nontrivial (acl);
99               acl_free (acl);
100             }
101           else
102             ret = -1;
103 #  else /* FreeBSD, IRIX, Tru64, Cygwin >= 2.5 */
104           acl_t acl = acl_get_file (name, ACL_TYPE_ACCESS);
105           if (acl)
106             {
107               int saved_errno;
108 
109               ret = acl_access_nontrivial (acl);
110               saved_errno = errno;
111               acl_free (acl);
112               errno = saved_errno;
113 #   if HAVE_ACL_FREE_TEXT /* Tru64 */
114               /* On OSF/1, acl_get_file (name, ACL_TYPE_DEFAULT) always
115                  returns NULL with errno not set.  There is no point in
116                  making this call.  */
117 #   else /* FreeBSD, IRIX, Cygwin >= 2.5 */
118               /* On Linux, FreeBSD, IRIX, acl_get_file (name, ACL_TYPE_ACCESS)
119                  and acl_get_file (name, ACL_TYPE_DEFAULT) on a directory
120                  either both succeed or both fail; it depends on the
121                  file system.  Therefore there is no point in making the second
122                  call if the first one already failed.  */
123               if (ret == 0 && S_ISDIR (sb->st_mode))
124                 {
125                   acl = acl_get_file (name, ACL_TYPE_DEFAULT);
126                   if (acl)
127                     {
128 #    ifdef __CYGWIN__ /* Cygwin >= 2.5 */
129                       ret = acl_access_nontrivial (acl);
130                       saved_errno = errno;
131                       acl_free (acl);
132                       errno = saved_errno;
133 #    else
134                       ret = (0 < acl_entries (acl));
135                       acl_free (acl);
136 #    endif
137                     }
138                   else
139                     ret = -1;
140                 }
141 #   endif
142             }
143           else
144             ret = -1;
145 #  endif
146         }
147       if (ret < 0)
148         return - acl_errno_valid (errno);
149       return ret;
150 
151 # elif HAVE_FACL && defined GETACL /* Solaris, Cygwin < 2.5, not HP-UX */
152 
153 #  if defined ACL_NO_TRIVIAL
154 
155       /* Solaris 10 (newer version), which has additional API declared in
156          <sys/acl.h> (acl_t) and implemented in libsec (acl_set, acl_trivial,
157          acl_fromtext, ...).  */
158       return acl_trivial (name);
159 
160 #  else /* Solaris, Cygwin, general case */
161 
162       /* Solaris 2.5 through Solaris 10, Cygwin, and contemporaneous versions
163          of Unixware.  The acl() call returns the access and default ACL both
164          at once.  */
165       {
166         /* Initially, try to read the entries into a stack-allocated buffer.
167            Use malloc if it does not fit.  */
168         enum
169           {
170             alloc_init = 4000 / sizeof (aclent_t), /* >= 3 */
171             alloc_max = MIN (INT_MAX, SIZE_MAX / sizeof (aclent_t))
172           };
173         aclent_t buf[alloc_init];
174         size_t alloc = alloc_init;
175         aclent_t *entries = buf;
176         aclent_t *malloced = NULL;
177         int count;
178 
179         for (;;)
180           {
181             count = acl (name, GETACL, alloc, entries);
182             if (count < 0 && errno == ENOSPC)
183               {
184                 /* Increase the size of the buffer.  */
185                 free (malloced);
186                 if (alloc > alloc_max / 2)
187                   {
188                     errno = ENOMEM;
189                     return -1;
190                   }
191                 alloc = 2 * alloc; /* <= alloc_max */
192                 entries = malloced =
193                   (aclent_t *) malloc (alloc * sizeof (aclent_t));
194                 if (entries == NULL)
195                   {
196                     errno = ENOMEM;
197                     return -1;
198                   }
199                 continue;
200               }
201             break;
202           }
203         if (count < 0)
204           {
205             if (errno == ENOSYS || errno == ENOTSUP)
206               ;
207             else
208               {
209                 int saved_errno = errno;
210                 free (malloced);
211                 errno = saved_errno;
212                 return -1;
213               }
214           }
215         else if (count == 0)
216           ;
217         else
218           {
219             /* Don't use MIN_ACL_ENTRIES:  It's set to 4 on Cygwin, but Cygwin
220                returns only 3 entries for files with no ACL.  But this is safe:
221                If there are more than 4 entries, there cannot be only the
222                "user::", "group::", "other:", and "mask:" entries.  */
223             if (count > 4)
224               {
225                 free (malloced);
226                 return 1;
227               }
228 
229             if (acl_nontrivial (count, entries))
230               {
231                 free (malloced);
232                 return 1;
233               }
234           }
235         free (malloced);
236       }
237 
238 #   ifdef ACE_GETACL
239       /* Solaris also has a different variant of ACLs, used in ZFS and NFSv4
240          file systems (whereas the other ones are used in UFS file systems).  */
241       {
242         /* Initially, try to read the entries into a stack-allocated buffer.
243            Use malloc if it does not fit.  */
244         enum
245           {
246             alloc_init = 4000 / sizeof (ace_t), /* >= 3 */
247             alloc_max = MIN (INT_MAX, SIZE_MAX / sizeof (ace_t))
248           };
249         ace_t buf[alloc_init];
250         size_t alloc = alloc_init;
251         ace_t *entries = buf;
252         ace_t *malloced = NULL;
253         int count;
254 
255         for (;;)
256           {
257             count = acl (name, ACE_GETACL, alloc, entries);
258             if (count < 0 && errno == ENOSPC)
259               {
260                 /* Increase the size of the buffer.  */
261                 free (malloced);
262                 if (alloc > alloc_max / 2)
263                   {
264                     errno = ENOMEM;
265                     return -1;
266                   }
267                 alloc = 2 * alloc; /* <= alloc_max */
268                 entries = malloced = (ace_t *) malloc (alloc * sizeof (ace_t));
269                 if (entries == NULL)
270                   {
271                     errno = ENOMEM;
272                     return -1;
273                   }
274                 continue;
275               }
276             break;
277           }
278         if (count < 0)
279           {
280             if (errno == ENOSYS || errno == EINVAL)
281               ;
282             else
283               {
284                 int saved_errno = errno;
285                 free (malloced);
286                 errno = saved_errno;
287                 return -1;
288               }
289           }
290         else if (count == 0)
291           ;
292         else
293           {
294             /* In the old (original Solaris 10) convention:
295                If there are more than 3 entries, there cannot be only the
296                ACE_OWNER, ACE_GROUP, ACE_OTHER entries.
297                In the newer Solaris 10 and Solaris 11 convention:
298                If there are more than 6 entries, there cannot be only the
299                ACE_OWNER, ACE_GROUP, ACE_EVERYONE entries, each once with
300                NEW_ACE_ACCESS_ALLOWED_ACE_TYPE and once with
301                NEW_ACE_ACCESS_DENIED_ACE_TYPE.  */
302             if (count > 6)
303               {
304                 free (malloced);
305                 return 1;
306               }
307 
308             if (acl_ace_nontrivial (count, entries))
309               {
310                 free (malloced);
311                 return 1;
312               }
313           }
314         free (malloced);
315       }
316 #   endif
317 
318       return 0;
319 #  endif
320 
321 # elif HAVE_GETACL /* HP-UX */
322 
323       {
324         struct acl_entry entries[NACLENTRIES];
325         int count;
326 
327         count = getacl (name, NACLENTRIES, entries);
328 
329         if (count < 0)
330           {
331             /* ENOSYS is seen on newer HP-UX versions.
332                EOPNOTSUPP is typically seen on NFS mounts.
333                ENOTSUP was seen on Quantum StorNext file systems (cvfs).  */
334             if (errno == ENOSYS || errno == EOPNOTSUPP || errno == ENOTSUP)
335               ;
336             else
337               return -1;
338           }
339         else if (count == 0)
340           return 0;
341         else /* count > 0 */
342           {
343             if (count > NACLENTRIES)
344               /* If NACLENTRIES cannot be trusted, use dynamic memory
345                  allocation.  */
346               abort ();
347 
348             /* If there are more than 3 entries, there cannot be only the
349                (uid,%), (%,gid), (%,%) entries.  */
350             if (count > 3)
351               return 1;
352 
353             {
354               struct stat statbuf;
355 
356               if (stat (name, &statbuf) == -1 && errno != EOVERFLOW)
357                 return -1;
358 
359               return acl_nontrivial (count, entries);
360             }
361           }
362       }
363 
364 #  if HAVE_ACLV_H /* HP-UX >= 11.11 */
365 
366       {
367         struct acl entries[NACLVENTRIES];
368         int count;
369 
370         count = acl ((char *) name, ACL_GET, NACLVENTRIES, entries);
371 
372         if (count < 0)
373           {
374             /* EOPNOTSUPP is seen on NFS in HP-UX 11.11, 11.23.
375                EINVAL is seen on NFS in HP-UX 11.31.  */
376             if (errno == ENOSYS || errno == EOPNOTSUPP || errno == EINVAL)
377               ;
378             else
379               return -1;
380           }
381         else if (count == 0)
382           return 0;
383         else /* count > 0 */
384           {
385             if (count > NACLVENTRIES)
386               /* If NACLVENTRIES cannot be trusted, use dynamic memory
387                  allocation.  */
388               abort ();
389 
390             /* If there are more than 4 entries, there cannot be only the
391                four base ACL entries.  */
392             if (count > 4)
393               return 1;
394 
395             return aclv_nontrivial (count, entries);
396           }
397       }
398 
399 #  endif
400 
401 # elif HAVE_ACLX_GET && defined ACL_AIX_WIP /* AIX */
402 
403       acl_type_t type;
404       char aclbuf[1024];
405       void *acl = aclbuf;
406       size_t aclsize = sizeof (aclbuf);
407       mode_t mode;
408 
409       for (;;)
410         {
411           /* The docs say that type being 0 is equivalent to ACL_ANY, but it
412              is not true, in AIX 5.3.  */
413           type.u64 = ACL_ANY;
414           if (aclx_get (name, 0, &type, aclbuf, &aclsize, &mode) >= 0)
415             break;
416           if (errno == ENOSYS)
417             return 0;
418           if (errno != ENOSPC)
419             {
420               if (acl != aclbuf)
421                 {
422                   int saved_errno = errno;
423                   free (acl);
424                   errno = saved_errno;
425                 }
426               return -1;
427             }
428           aclsize = 2 * aclsize;
429           if (acl != aclbuf)
430             free (acl);
431           acl = malloc (aclsize);
432           if (acl == NULL)
433             {
434               errno = ENOMEM;
435               return -1;
436             }
437         }
438 
439       if (type.u64 == ACL_AIXC)
440         {
441           int result = acl_nontrivial ((struct acl *) acl);
442           if (acl != aclbuf)
443             free (acl);
444           return result;
445         }
446       else if (type.u64 == ACL_NFS4)
447         {
448           int result = acl_nfs4_nontrivial ((nfs4_acl_int_t *) acl);
449           if (acl != aclbuf)
450             free (acl);
451           return result;
452         }
453       else
454         {
455           /* A newer type of ACL has been introduced in the system.
456              We should better support it.  */
457           if (acl != aclbuf)
458             free (acl);
459           errno = EINVAL;
460           return -1;
461         }
462 
463 # elif HAVE_STATACL /* older AIX */
464 
465       union { struct acl a; char room[4096]; } u;
466 
467       if (statacl ((char *) name, STX_NORMAL, &u.a, sizeof (u)) < 0)
468         return -1;
469 
470       return acl_nontrivial (&u.a);
471 
472 # elif HAVE_ACLSORT /* NonStop Kernel */
473 
474       {
475         struct acl entries[NACLENTRIES];
476         int count;
477 
478         count = acl ((char *) name, ACL_GET, NACLENTRIES, entries);
479 
480         if (count < 0)
481           {
482             if (errno == ENOSYS || errno == ENOTSUP)
483               ;
484             else
485               return -1;
486           }
487         else if (count == 0)
488           return 0;
489         else /* count > 0 */
490           {
491             if (count > NACLENTRIES)
492               /* If NACLENTRIES cannot be trusted, use dynamic memory
493                  allocation.  */
494               abort ();
495 
496             /* If there are more than 4 entries, there cannot be only the
497                four base ACL entries.  */
498             if (count > 4)
499               return 1;
500 
501             return acl_nontrivial (count, entries);
502           }
503       }
504 
505 # endif
506     }
507 #endif
508 
509   return 0;
510 }
511