1 /* Test whether a file has a nontrivial ACL.  -*- coding: utf-8 -*-
2 
3    Copyright (C) 2002-2003, 2005-2018 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 */
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 */
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 */
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 */
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                       ret = (0 < acl_entries (acl));
129                       acl_free (acl);
130                     }
131                   else
132                     ret = -1;
133                 }
134 #   endif
135             }
136           else
137             ret = -1;
138 #  endif
139         }
140       if (ret < 0)
141         return - acl_errno_valid (errno);
142       return ret;
143 
144 # elif HAVE_FACL && defined GETACL /* Solaris, Cygwin, not HP-UX */
145 
146 #  if defined ACL_NO_TRIVIAL
147 
148       /* Solaris 10 (newer version), which has additional API declared in
149          <sys/acl.h> (acl_t) and implemented in libsec (acl_set, acl_trivial,
150          acl_fromtext, ...).  */
151       return acl_trivial (name);
152 
153 #  else /* Solaris, Cygwin, general case */
154 
155       /* Solaris 2.5 through Solaris 10, Cygwin, and contemporaneous versions
156          of Unixware.  The acl() call returns the access and default ACL both
157          at once.  */
158       {
159         /* Initially, try to read the entries into a stack-allocated buffer.
160            Use malloc if it does not fit.  */
161         enum
162           {
163             alloc_init = 4000 / sizeof (aclent_t), /* >= 3 */
164             alloc_max = MIN (INT_MAX, SIZE_MAX / sizeof (aclent_t))
165           };
166         aclent_t buf[alloc_init];
167         size_t alloc = alloc_init;
168         aclent_t *entries = buf;
169         aclent_t *malloced = NULL;
170         int count;
171 
172         for (;;)
173           {
174             count = acl (name, GETACL, alloc, entries);
175             if (count < 0 && errno == ENOSPC)
176               {
177                 /* Increase the size of the buffer.  */
178                 free (malloced);
179                 if (alloc > alloc_max / 2)
180                   {
181                     errno = ENOMEM;
182                     return -1;
183                   }
184                 alloc = 2 * alloc; /* <= alloc_max */
185                 entries = malloced =
186                   (aclent_t *) malloc (alloc * sizeof (aclent_t));
187                 if (entries == NULL)
188                   {
189                     errno = ENOMEM;
190                     return -1;
191                   }
192                 continue;
193               }
194             break;
195           }
196         if (count < 0)
197           {
198             if (errno == ENOSYS || errno == ENOTSUP)
199               ;
200             else
201               {
202                 int saved_errno = errno;
203                 free (malloced);
204                 errno = saved_errno;
205                 return -1;
206               }
207           }
208         else if (count == 0)
209           ;
210         else
211           {
212             /* Don't use MIN_ACL_ENTRIES:  It's set to 4 on Cygwin, but Cygwin
213                returns only 3 entries for files with no ACL.  But this is safe:
214                If there are more than 4 entries, there cannot be only the
215                "user::", "group::", "other:", and "mask:" entries.  */
216             if (count > 4)
217               {
218                 free (malloced);
219                 return 1;
220               }
221 
222             if (acl_nontrivial (count, entries))
223               {
224                 free (malloced);
225                 return 1;
226               }
227           }
228         free (malloced);
229       }
230 
231 #   ifdef ACE_GETACL
232       /* Solaris also has a different variant of ACLs, used in ZFS and NFSv4
233          file systems (whereas the other ones are used in UFS file systems).  */
234       {
235         /* Initially, try to read the entries into a stack-allocated buffer.
236            Use malloc if it does not fit.  */
237         enum
238           {
239             alloc_init = 4000 / sizeof (ace_t), /* >= 3 */
240             alloc_max = MIN (INT_MAX, SIZE_MAX / sizeof (ace_t))
241           };
242         ace_t buf[alloc_init];
243         size_t alloc = alloc_init;
244         ace_t *entries = buf;
245         ace_t *malloced = NULL;
246         int count;
247 
248         for (;;)
249           {
250             count = acl (name, ACE_GETACL, alloc, entries);
251             if (count < 0 && errno == ENOSPC)
252               {
253                 /* Increase the size of the buffer.  */
254                 free (malloced);
255                 if (alloc > alloc_max / 2)
256                   {
257                     errno = ENOMEM;
258                     return -1;
259                   }
260                 alloc = 2 * alloc; /* <= alloc_max */
261                 entries = malloced = (ace_t *) malloc (alloc * sizeof (ace_t));
262                 if (entries == NULL)
263                   {
264                     errno = ENOMEM;
265                     return -1;
266                   }
267                 continue;
268               }
269             break;
270           }
271         if (count < 0)
272           {
273             if (errno == ENOSYS || errno == EINVAL)
274               ;
275             else
276               {
277                 int saved_errno = errno;
278                 free (malloced);
279                 errno = saved_errno;
280                 return -1;
281               }
282           }
283         else if (count == 0)
284           ;
285         else
286           {
287             /* In the old (original Solaris 10) convention:
288                If there are more than 3 entries, there cannot be only the
289                ACE_OWNER, ACE_GROUP, ACE_OTHER entries.
290                In the newer Solaris 10 and Solaris 11 convention:
291                If there are more than 6 entries, there cannot be only the
292                ACE_OWNER, ACE_GROUP, ACE_EVERYONE entries, each once with
293                NEW_ACE_ACCESS_ALLOWED_ACE_TYPE and once with
294                NEW_ACE_ACCESS_DENIED_ACE_TYPE.  */
295             if (count > 6)
296               {
297                 free (malloced);
298                 return 1;
299               }
300 
301             if (acl_ace_nontrivial (count, entries))
302               {
303                 free (malloced);
304                 return 1;
305               }
306           }
307         free (malloced);
308       }
309 #   endif
310 
311       return 0;
312 #  endif
313 
314 # elif HAVE_GETACL /* HP-UX */
315 
316       {
317         struct acl_entry entries[NACLENTRIES];
318         int count;
319 
320         count = getacl (name, NACLENTRIES, entries);
321 
322         if (count < 0)
323           {
324             /* ENOSYS is seen on newer HP-UX versions.
325                EOPNOTSUPP is typically seen on NFS mounts.
326                ENOTSUP was seen on Quantum StorNext file systems (cvfs).  */
327             if (errno == ENOSYS || errno == EOPNOTSUPP || errno == ENOTSUP)
328               ;
329             else
330               return -1;
331           }
332         else if (count == 0)
333           return 0;
334         else /* count > 0 */
335           {
336             if (count > NACLENTRIES)
337               /* If NACLENTRIES cannot be trusted, use dynamic memory
338                  allocation.  */
339               abort ();
340 
341             /* If there are more than 3 entries, there cannot be only the
342                (uid,%), (%,gid), (%,%) entries.  */
343             if (count > 3)
344               return 1;
345 
346             {
347               struct stat statbuf;
348 
349               if (stat (name, &statbuf) < 0)
350                 return -1;
351 
352               return acl_nontrivial (count, entries);
353             }
354           }
355       }
356 
357 #  if HAVE_ACLV_H /* HP-UX >= 11.11 */
358 
359       {
360         struct acl entries[NACLVENTRIES];
361         int count;
362 
363         count = acl ((char *) name, ACL_GET, NACLVENTRIES, entries);
364 
365         if (count < 0)
366           {
367             /* EOPNOTSUPP is seen on NFS in HP-UX 11.11, 11.23.
368                EINVAL is seen on NFS in HP-UX 11.31.  */
369             if (errno == ENOSYS || errno == EOPNOTSUPP || errno == EINVAL)
370               ;
371             else
372               return -1;
373           }
374         else if (count == 0)
375           return 0;
376         else /* count > 0 */
377           {
378             if (count > NACLVENTRIES)
379               /* If NACLVENTRIES cannot be trusted, use dynamic memory
380                  allocation.  */
381               abort ();
382 
383             /* If there are more than 4 entries, there cannot be only the
384                four base ACL entries.  */
385             if (count > 4)
386               return 1;
387 
388             return aclv_nontrivial (count, entries);
389           }
390       }
391 
392 #  endif
393 
394 # elif HAVE_ACLX_GET && defined ACL_AIX_WIP /* AIX */
395 
396       acl_type_t type;
397       char aclbuf[1024];
398       void *acl = aclbuf;
399       size_t aclsize = sizeof (aclbuf);
400       mode_t mode;
401 
402       for (;;)
403         {
404           /* The docs say that type being 0 is equivalent to ACL_ANY, but it
405              is not true, in AIX 5.3.  */
406           type.u64 = ACL_ANY;
407           if (aclx_get (name, 0, &type, aclbuf, &aclsize, &mode) >= 0)
408             break;
409           if (errno == ENOSYS)
410             return 0;
411           if (errno != ENOSPC)
412             {
413               if (acl != aclbuf)
414                 {
415                   int saved_errno = errno;
416                   free (acl);
417                   errno = saved_errno;
418                 }
419               return -1;
420             }
421           aclsize = 2 * aclsize;
422           if (acl != aclbuf)
423             free (acl);
424           acl = malloc (aclsize);
425           if (acl == NULL)
426             {
427               errno = ENOMEM;
428               return -1;
429             }
430         }
431 
432       if (type.u64 == ACL_AIXC)
433         {
434           int result = acl_nontrivial ((struct acl *) acl);
435           if (acl != aclbuf)
436             free (acl);
437           return result;
438         }
439       else if (type.u64 == ACL_NFS4)
440         {
441           int result = acl_nfs4_nontrivial ((nfs4_acl_int_t *) acl);
442           if (acl != aclbuf)
443             free (acl);
444           return result;
445         }
446       else
447         {
448           /* A newer type of ACL has been introduced in the system.
449              We should better support it.  */
450           if (acl != aclbuf)
451             free (acl);
452           errno = EINVAL;
453           return -1;
454         }
455 
456 # elif HAVE_STATACL /* older AIX */
457 
458       union { struct acl a; char room[4096]; } u;
459 
460       if (statacl ((char *) name, STX_NORMAL, &u.a, sizeof (u)) < 0)
461         return -1;
462 
463       return acl_nontrivial (&u.a);
464 
465 # elif HAVE_ACLSORT /* NonStop Kernel */
466 
467       {
468         struct acl entries[NACLENTRIES];
469         int count;
470 
471         count = acl ((char *) name, ACL_GET, NACLENTRIES, entries);
472 
473         if (count < 0)
474           {
475             if (errno == ENOSYS || errno == ENOTSUP)
476               ;
477             else
478               return -1;
479           }
480         else if (count == 0)
481           return 0;
482         else /* count > 0 */
483           {
484             if (count > NACLENTRIES)
485               /* If NACLENTRIES cannot be trusted, use dynamic memory
486                  allocation.  */
487               abort ();
488 
489             /* If there are more than 4 entries, there cannot be only the
490                four base ACL entries.  */
491             if (count > 4)
492               return 1;
493 
494             return acl_nontrivial (count, entries);
495           }
496       }
497 
498 # endif
499     }
500 #endif
501 
502   return 0;
503 }
504