]> Savannah Git Hosting - gnulib.git/commitdiff
file-has-acl: new function file_has_aclinfo
authorPaul Eggert <eggert@cs.ucla.edu>
Sun, 29 Sep 2024 23:13:53 +0000 (16:13 -0700)
committerPaul Eggert <eggert@cs.ucla.edu>
Mon, 30 Sep 2024 04:15:40 +0000 (21:15 -0700)
This is for GNU coreutils, whose ‘ls’ can be made faster with the
new function because it means (for example) that ‘ls’ needn’t call
both listxattr and lgetxattr in the common case where the file has
no extended attributes.
* lib/acl.h (ACL_SYMLINK_FOLLOW): New constant.
(struct aclinfo): New type.
* lib/file-has-acl.c (UNKNOWN_SECURITY_CONTEXT): New constant.
(USE_LINUX_XATTR): New macro.  When set:
Include stdint.h (for uint32_t).  If also USE_SELINUX_SELINUX_H,
include selinux/selinux.h (for getfilecon).
If HAVE_SMACK, include <sys/smack.h>, otherwise provide substitutes
for smack_smackfs_path and smack_new_label_from_path.
(is_smack_enabled): New function.
(aclinfo_has_xattr): Rename from have_xattr and make it extern.
Use struct aclinfo const * arg instead of char const * and ssize_t.
Work even if ai->size < 0.  Supply a no-op macro on platforms
lacking xattr.
(get_aclinfo): New static function, much of it taken from
coreutils/src/ls.c.
(file_has_aclinfo): New function, which generalizes file_has_acl
to provide more info.  Its body is taken from the old file_has_acl
with new stuff from ls.c.
(file_has_acl): Use it.
(aclinfo_scontext_free, aclinfo_free): Nwew functions, if not
already no-op macros.
* m4/acl.m4 (gl_FUNC_ACL_ARG): Add --without-libsmack option.
(gl_FILE_HAS_ACL): Check for libsmack, selinux.
* m4/selinux-selinux-h.m4 (gl_CHECK_HEADER_SELINUX_SELINUX_H):
New macro, for use by file-has-acl.  Rename HAVE_SELINUX_SELINUX_H
to USE_SELINUX_SELINUX_H to avoid confusion as to whether we have
<selinux/selinux.h>; all uses changed.
(gl_HEADERS_SELINUX_SELINUX_H): Use the new macro.
* modules/file-has-acl (Files): Add m4/selinux-selinux-h.m4.
(Depends-on): Add errno, ssize_t.
* tests/test-file-has-acl.c (main): Add a little test for
file_has_aclinfo.

ChangeLog
lib/acl.h
lib/file-has-acl.c
lib/se-selinux.in.h
m4/acl.m4
m4/selinux-selinux-h.m4
modules/file-has-acl
modules/selinux-h
tests/test-file-has-acl.c

index 071f0f0fc78f3f42d8b5d5e48abf959842f434ec..3368abacb468a533304a853021eb50b72f6f867f 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,43 @@
+2024-09-29  Paul Eggert  <eggert@cs.ucla.edu>
+
+       file-has-acl: new function file_has_aclinfo
+       This is for GNU coreutils, whose ‘ls’ can be made faster with the
+       new function because it means (for example) that ‘ls’ needn’t call
+       both listxattr and lgetxattr in the common case where the file has
+       no extended attributes.
+       * lib/acl.h (ACL_SYMLINK_FOLLOW): New constant.
+       (struct aclinfo): New type.
+       * lib/file-has-acl.c (UNKNOWN_SECURITY_CONTEXT): New constant.
+       (USE_LINUX_XATTR): New macro.  When set:
+       Include stdint.h (for uint32_t).  If also USE_SELINUX_SELINUX_H,
+       include selinux/selinux.h (for getfilecon).
+       If HAVE_SMACK, include <sys/smack.h>, otherwise provide substitutes
+       for smack_smackfs_path and smack_new_label_from_path.
+       (is_smack_enabled): New function.
+       (aclinfo_has_xattr): Rename from have_xattr and make it extern.
+       Use struct aclinfo const * arg instead of char const * and ssize_t.
+       Work even if ai->size < 0.  Supply a no-op macro on platforms
+       lacking xattr.
+       (get_aclinfo): New static function, much of it taken from
+       coreutils/src/ls.c.
+       (file_has_aclinfo): New function, which generalizes file_has_acl
+       to provide more info.  Its body is taken from the old file_has_acl
+       with new stuff from ls.c.
+       (file_has_acl): Use it.
+       (aclinfo_scontext_free, aclinfo_free): Nwew functions, if not
+       already no-op macros.
+       * m4/acl.m4 (gl_FUNC_ACL_ARG): Add --without-libsmack option.
+       (gl_FILE_HAS_ACL): Check for libsmack, selinux.
+       * m4/selinux-selinux-h.m4 (gl_CHECK_HEADER_SELINUX_SELINUX_H):
+       New macro, for use by file-has-acl.  Rename HAVE_SELINUX_SELINUX_H
+       to USE_SELINUX_SELINUX_H to avoid confusion as to whether we have
+       <selinux/selinux.h>; all uses changed.
+       (gl_HEADERS_SELINUX_SELINUX_H): Use the new macro.
+       * modules/file-has-acl (Files): Add m4/selinux-selinux-h.m4.
+       (Depends-on): Add errno, ssize_t.
+       * tests/test-file-has-acl.c (main): Add a little test for
+       file_has_aclinfo.
+
 2024-09-26  Bruno Haible  <bruno@clisp.org>
 
        string-buffer tests: Avoid test failure on native Windows.
index 475231c2db7e3e5165af9412fa86f0e109cf5f8f..07ab04250d45cdf395d21e74d414ddfa1290cd09 100644 (file)
--- a/lib/acl.h
+++ b/lib/acl.h
 extern "C" {
 #endif
 
+/* Follow symlinks when getting an ACL.  */
+enum { ACL_SYMLINK_FOLLOW = 1 };
+
+/* Information about an ACL.  */
+struct aclinfo
+{
+  /* If 'size' is nonnegative, a buffer holding the concatenation
+     of extended attribute names, each terminated by NUL
+     (either u.__gl_acl_ch, or heap-allocated).  */
+  char *buf;
+
+  /* The number of useful bytes at the start of buf, counting trailing NULs.
+     If negative, there was an error in getting the ACL info,
+     and u.err is the corresponding errno.  */
+  ssize_t size;
+
+  /* The allocated size of buf.  This is sizeof u.__gl_acl_ch if the
+     buffer is not heap-allocated, and is larger otherwise.
+     For internal use only.  */
+  ssize_t __gl_acl_alloc;
+
+  /* Security context string.  Do not modify its contents.  */
+  char *scontext;
+  /* Security context errno value.  It is zero if there was no
+     error getting the security context.  When nonzero, scontext is "?".  */
+  int scontext_err;
+
+  union
+  {
+    /* An errno value, when there was an error getting the ACL info.  */
+    int err;
+
+    /* A small array of char, big enough for most listxattr results.
+       The size is somewhat arbitrary.  For internal use only.  */
+    char __gl_acl_ch[152];
+  } u;
+};
 
 bool acl_errno_valid (int) _GL_ATTRIBUTE_CONST;
 int file_has_acl (char const *, struct stat const *);
+int file_has_aclinfo (char const *, struct stat const *, struct aclinfo *, int);
+
+#if USE_ACL && HAVE_LINUX_XATTR_H && HAVE_LISTXATTR
+bool aclinfo_has_xattr (struct aclinfo const *, char const *)
+  _GL_ATTRIBUTE_PURE;
+void aclinfo_free (struct aclinfo *);
+#else
+# define aclinfo_has_xattr(ai, xattr) false
+# define aclinfo_free(ai) ((void) 0)
+#endif
+#if (USE_ACL && HAVE_LINUX_XATTR_H && HAVE_LISTXATTR \
+     && (HAVE_SMACK || USE_SELINUX_SELINUX_H))
+void aclinfo_scontext_free (char *);
+#else
+# define aclinfo_scontext_free(s) ((void) 0)
+#endif
 
 int qset_acl (char const *, int, mode_t);
 int xset_acl (char const *, int, mode_t);
index 06759a4948c1dea0648cc04c7ca9b052538fd110..89faba565f459525eb35691abfb0664d7c0df37b 100644 (file)
 #include "attribute.h"
 #include "minmax.h"
 
-#if USE_ACL && HAVE_LINUX_XATTR_H && HAVE_LISTXATTR
+static char const UNKNOWN_SECURITY_CONTEXT[] = "?";
+
+#define USE_LINUX_XATTR (USE_ACL && HAVE_LINUX_XATTR_H && HAVE_LISTXATTR)
+
+#if USE_LINUX_XATTR
+# if USE_SELINUX_SELINUX_H
+#  include <selinux/selinux.h>
+# endif
 # include <stdckdint.h>
+# include <stdint.h>
 # include <string.h>
 # include <arpa/inet.h>
 # include <sys/xattr.h>
 #  define XATTR_NAME_POSIX_ACL_DEFAULT "system.posix_acl_default"
 # endif
 
+# ifdef HAVE_SMACK
+#  include <sys/smack.h>
+# else
+static char const *
+smack_smackfs_path (void)
+{
+  return NULL;
+}
+static ssize_t
+smack_new_label_from_path (MAYBE_UNUSED const char *path,
+                           MAYBE_UNUSED const char *xattr,
+                           MAYBE_UNUSED int follow, MAYBE_UNUSED char **label)
+{
+  return -1;
+}
+# endif
+static bool
+is_smack_enabled (void)
+{
+ return !!smack_smackfs_path ();
+}
+
 enum {
   /* ACE4_ACCESS_ALLOWED_ACE_TYPE = 0x00000000, */
   ACE4_ACCESS_DENIED_ACE_TYPE  = 0x00000001,
   ACE4_IDENTIFIER_GROUP        = 0x00000040
 };
 
-/* Return true if ATTR is in the set represented by the NUL-terminated
-   strings in LISTBUF, which is of size LISTSIZE.  */
+/* Does AI's xattr set contain XATTR?  */
 
-ATTRIBUTE_PURE static bool
-have_xattr (char const *attr, char const *listbuf, ssize_t listsize)
+bool
+aclinfo_has_xattr (struct aclinfo const *ai, char const *xattr)
 {
-  char const *blim = listbuf + listsize;
-  for (char const *b = listbuf; b < blim; b += strlen (b) + 1)
-    for (char const *a = attr; *a == *b; a++, b++)
-      if (!*a)
-        return true;
+  if (0 < ai->size)
+    {
+      char const *blim = ai->buf + ai->size;
+      for (char const *b = ai->buf; b < blim; b += strlen (b) + 1)
+        for (char const *a = xattr; *a == *b; a++, b++)
+          if (!*a)
+            return true;
+    }
   return false;
 }
 
+/* Get attributes of the file NAME into AI.
+   If FLAGS & ACL_SYMLINK_FOLLOW, follow symbolic links.  */
+static void
+get_aclinfo (char const *name, struct aclinfo *ai, int flags)
+{
+  int scontext_err = ENOTSUP;
+  ai->buf = ai->u.__gl_acl_ch;
+  ai->__gl_acl_alloc = sizeof ai->u.__gl_acl_ch;
+
+  ssize_t (*lsxattr) (char const *, char *, size_t)
+    = (flags & ACL_SYMLINK_FOLLOW ? listxattr : llistxattr);
+  while (true)
+    {
+      ai->size = lsxattr (name, ai->buf, ai->__gl_acl_alloc);
+      if (0 < ai->size)
+        break;
+      ai->u.err = ai->size < 0 ? errno : 0;
+      if (! (ai->size < 0 && ai->u.err == ERANGE))
+        break;
+
+      /* The buffer was too small.  Find how large it should have been.  */
+      ssize_t size = lsxattr (name, NULL, 0);
+      if (size <= 0)
+        {
+          ai->size = size;
+          ai->u.err = size < 0 ? errno : 0;
+          break;
+        }
+
+      /* Grow allocation to at least 'size'.  Grow it by a nontrivial
+         amount, to defend against denial of service by an adversary
+         that fiddles with ACLs.  */
+      ssize_t larger_alloc;
+      if (ckd_add (&larger_alloc, ai->__gl_acl_alloc, ai->__gl_acl_alloc >> 1))
+        {
+          ai->u.err = ENOMEM;
+          break;
+        }
+      if (ai->buf != ai->u.__gl_acl_ch)
+        {
+          free (ai->buf);
+          ai->buf = ai->u.__gl_acl_ch;
+          ai->__gl_acl_alloc = sizeof ai->u.__gl_acl_ch;
+        }
+      ssize_t newalloc = MAX (size, larger_alloc);
+      if (SIZE_MAX < newalloc)
+        {
+          ai->u.err = ENOMEM;
+          break;
+        }
+      char *newbuf = malloc (newalloc);
+      if (!newbuf)
+        {
+          ai->u.err = errno;
+          break;
+        }
+      ai->buf = newbuf;
+      ai->__gl_acl_alloc = newalloc;
+    }
+
+  if (0 < ai->size)
+    {
+      if (is_smack_enabled ())
+        {
+          if (aclinfo_has_xattr (ai, XATTR_NAME_SMACK))
+            {
+              ssize_t r = smack_new_label_from_path (name, "security.SMACK64",
+                                                     flags & ACL_SYMLINK_FOLLOW,
+                                                     &ai->scontext);
+              scontext_err = r < 0 ? errno : 0;
+            }
+        }
+      else
+        {
+# if USE_SELINUX_SELINUX_H
+          if (aclinfo_has_xattr (ai, XATTR_NAME_SELINUX))
+            {
+              ssize_t r =
+                ((flags & ACL_SYMLINK_FOLLOW ? getfilecon : lgetfilecon)
+                 (name, &ai->scontext));
+              scontext_err = r < 0 ? errno : 0;
+#  ifndef SE_SELINUX_INLINE
+              /* Gnulib's selinux-h module is not in use, so getfilecon and
+                 lgetfilecon can misbehave, be it via an old version of
+                 libselinux where these would return 0 and set the result
+                 context to NULL, or via a modern kernel+lib operating on a
+                 file from a disk whose attributes were set by a kernel from
+                 around 2006.  In that latter case, the functions return a
+                 length of 10 for the "unlabeled" context.  Map both failures
+                 to a return value of -1, and set errno to ENOTSUP in the
+                 first case, and ENODATA in the latter.  */
+              if (r == 0)
+                scontext_err = ENOTSUP;
+              if (r == 10 && memcmp (ai->scontext, "unlabeled", 10) == 0)
+                {
+                  freecon (ai->scontext);
+                  scontext_err = ENODATA;
+                }
+#  endif
+            }
+# endif
+        }
+    }
+  ai->scontext_err = scontext_err;
+  if (scontext_err)
+    ai->scontext = (char *) UNKNOWN_SECURITY_CONTEXT;
+}
+
+# ifndef aclinfo_scontext_free
+/* Free the pointer that file_has_aclinfo put into scontext.
+   However, do nothing if the argument is a null pointer;
+   This lets the caller replace the scontext member with a null pointer if it
+   is willing to own the member and call this function later.  */
+void
+aclinfo_scontext_free (char *scontext)
+{
+  if (scontext != UNKNOWN_SECURITY_CONTEXT)
+    {
+      if (is_smack_enabled ())
+        free (scontext);
+      else if (scontext)
+        freecon (scontext);
+    }
+}
+# endif
+
+/* Free AI's heap storage.  */
+void
+aclinfo_free (struct aclinfo *ai)
+{
+  if (ai->buf != ai->u.__gl_acl_ch)
+    free (ai->buf);
+  aclinfo_scontext_free (ai->scontext);
+}
+
 /* Return 1 if given ACL in XDR format is non-trivial, 0 if it is trivial.
    -1 upon failure to determine it.  Possibly change errno.  Assume that
    the ACL is valid, except avoid undefined behavior even if invalid.
@@ -151,86 +318,50 @@ acl_nfs4_nontrivial (uint32_t *xattr, ssize_t nbytes)
    and -1 (setting errno) on error.  Note callers can determine
    if ACLs are not supported as errno is set in that case also.
    SB must be set to the stat buffer of NAME,
-   obtained through stat() or lstat().  */
-
+   obtained through stat() or lstat().
+   Set *AI to the ACL info if available.
+   If FLAGS & AT_SYMLINK_FOLLOW, SB was gotten via stat and follow symlinks;
+   otherwise, SB was gotten vi lstat and do not follow them.  */
 int
-file_has_acl (char const *name, struct stat const *sb)
+file_has_aclinfo (char const *name, struct stat const *sb,
+                  struct aclinfo *ai, int flags)
 {
-#if USE_ACL
-  if (! S_ISLNK (sb->st_mode))
-    {
-
-# if HAVE_LINUX_XATTR_H && HAVE_LISTXATTR
-      int initial_errno = errno;
-
-      /* The max length of a trivial NFSv4 ACL is 6 words for owner,
-         6 for group, 7 for everyone, all times 2 because there are
-         both allow and deny ACEs.  There are 6 words for owner
-         because of type, flag, mask, wholen, "OWNER@"+pad and
-         similarly for group; everyone is another word to hold
-         "EVERYONE@".  */
-      typedef uint32_t trivial_NFSv4_xattr_buf[2 * (6 + 6 + 7)];
-
-      /* A buffer large enough to hold any trivial NFSv4 ACL,
-         and also useful as a small array of char.  */
-      union {
-        trivial_NFSv4_xattr_buf xattr;
-        char ch[sizeof (trivial_NFSv4_xattr_buf)];
-      } stackbuf;
-
-      char *listbuf = stackbuf.ch;
-      ssize_t listbufsize = sizeof stackbuf.ch;
-      char *heapbuf = NULL;
-      ssize_t listsize;
-
-      /* Use listxattr first, as this means just one syscall in the
-         typical case where the file lacks an ACL.  Try stackbuf
-         first, falling back on malloc if stackbuf is too small.  */
-      while ((listsize = listxattr (name, listbuf, listbufsize)) < 0
-             && errno == ERANGE)
-        {
-          free (heapbuf);
-          ssize_t newsize = listxattr (name, NULL, 0);
-          if (newsize <= 0)
-            return newsize;
-
-          /* Grow LISTBUFSIZE to at least NEWSIZE.  Grow it by a
-             nontrivial amount too, to defend against denial of
-             service by an adversary that fiddles with ACLs.  */
-          bool overflow = ckd_add (&listbufsize, listbufsize, listbufsize >> 1);
-          listbufsize = MAX (listbufsize, newsize);
-          if (overflow || SIZE_MAX < listbufsize)
-            {
-              errno = ENOMEM;
-              return -1;
-            }
-
-          listbuf = heapbuf = malloc (listbufsize);
-          if (!listbuf)
-            return -1;
-        }
+#if USE_LINUX_XATTR
+  int initial_errno = errno;
+  get_aclinfo (name, ai, flags);
 
+  if (ai->size <= 0)
+    {
+      errno = ai->size < 0 ? ai->u.err : initial_errno;
+      return ai->size;
+    }
+  else
+    {
       /* In Fedora 39, a file can have both NFSv4 and POSIX ACLs,
          but if it has an NFSv4 ACL that's the one that matters.
          In earlier Fedora the two types of ACLs were mutually exclusive.
          Attempt to work correctly on both kinds of systems.  */
-      bool nfsv4_acl
-        = 0 < listsize && have_xattr (XATTR_NAME_NFSV4_ACL, listbuf, listsize);
+      bool nfsv4_acl = aclinfo_has_xattr (ai, XATTR_NAME_NFSV4_ACL);
       int ret
-        = (listsize <= 0 ? listsize
+        = (ai->size <= 0 ? ai->size
            : (nfsv4_acl
-              || have_xattr (XATTR_NAME_POSIX_ACL_ACCESS, listbuf, listsize)
+              || aclinfo_has_xattr (ai, XATTR_NAME_POSIX_ACL_ACCESS)
               || (S_ISDIR (sb->st_mode)
-                  && have_xattr (XATTR_NAME_POSIX_ACL_DEFAULT,
-                                 listbuf, listsize))));
-      free (heapbuf);
+                  && aclinfo_has_xattr (ai, XATTR_NAME_POSIX_ACL_DEFAULT))));
 
       /* If there is an NFSv4 ACL, follow up with a getxattr syscall
          to see whether the NFSv4 ACL is nontrivial.  */
       if (nfsv4_acl)
         {
-          ret = getxattr (name, XATTR_NAME_NFSV4_ACL,
-                          stackbuf.xattr, sizeof stackbuf.xattr);
+          /* A buffer large enough to hold any trivial NFSv4 ACL.
+             The max length of a trivial NFSv4 ACL is 6 words for owner,
+             6 for group, 7 for everyone, all times 2 because there are both
+             allow and deny ACEs.  There are 6 words for owner because of
+             type, flag, mask, wholen, "OWNER@"+pad and similarly for group;
+             everyone is another word to hold "EVERYONE@".  */
+          uint32_t buf[2 * (6 + 6 + 7)];
+
+          ret = getxattr (name, XATTR_NAME_NFSV4_ACL, buf, sizeof buf);
           if (ret < 0)
             switch (errno)
               {
@@ -240,7 +371,7 @@ file_has_acl (char const *name, struct stat const *sb)
           else
             {
               /* It looks like a trivial ACL, but investigate further.  */
-              ret = acl_nfs4_nontrivial (stackbuf.xattr, ret);
+              ret = acl_nfs4_nontrivial (buf, ret);
               if (ret < 0)
                 {
                   errno = EINVAL;
@@ -252,8 +383,18 @@ file_has_acl (char const *name, struct stat const *sb)
       if (ret < 0)
         return - acl_errno_valid (errno);
       return ret;
+    }
+#else
+  ai->size = -1;
+  ai->u.err = ENOTSUP;
+  ai->scontext = (char *) UNKNOWN_SECURITY_CONTEXT;
+  ai->scontext_err = ENOTSUP;
+#endif
 
-# elif HAVE_ACL_GET_FILE
+#if USE_ACL
+  if (! S_ISLNK (sb->st_mode))
+    {
+# if HAVE_ACL_GET_FILE
 
       /* POSIX 1003.1e (draft 17 -- abandoned) specific version.  */
       /* Linux, FreeBSD, Mac OS X, IRIX, Tru64, Cygwin >= 2.5 */
@@ -682,3 +823,18 @@ file_has_acl (char const *name, struct stat const *sb)
 
   return 0;
 }
+
+/* Return 1 if NAME has a nontrivial access control list,
+   0 if ACLs are not supported, or if NAME has no or only a base ACL,
+   and -1 (setting errno) on error.  Note callers can determine
+   if ACLs are not supported as errno is set in that case also.
+   SB must be set to the stat buffer of NAME,
+   obtained through stat() or lstat().  */
+int
+file_has_acl (char const *name, struct stat const *sb)
+{
+  struct aclinfo ai;
+  int r = file_has_aclinfo (name, sb, &ai, 0);
+  aclinfo_free (&ai);
+  return r;
+}
index 76d3762fd473644e25715bf84a056912598ba6d4..80795b107d02318c70d88f47ecf3999e0f6876ea 100644 (file)
@@ -19,7 +19,7 @@
 #endif
 @PRAGMA_COLUMNS@
 
-#if @HAVE_SELINUX_SELINUX_H@
+#if @USE_SELINUX_SELINUX_H@
 
 #@INCLUDE_NEXT@ @NEXT_SELINUX_SELINUX_H@
 
index be88f1b8313a02cfa16b9ff1486b3ab45407d5d1..4328bd20a1bae69c51350da9a2d8f91adafc2f26 100644 (file)
--- a/m4/acl.m4
+++ b/m4/acl.m4
@@ -1,5 +1,5 @@
 # acl.m4
-# serial 31
+# serial 32
 dnl Copyright (C) 2002, 2004-2024 Free Software Foundation, Inc.
 dnl This file is free software; the Free Software Foundation
 dnl gives unlimited permission to copy and/or distribute it,
@@ -15,9 +15,12 @@ AC_DEFUN([gl_FUNC_ACL_ARG],
   AC_ARG_ENABLE([acl],
     AS_HELP_STRING([[--disable-acl]], [do not support ACLs]),
     , [enable_acl=auto])
+  AC_ARG_WITH([libsmack],
+    [AS_HELP_STRING([--without-libsmack],
+       [do not use libsmack, even on systems that have it])]
+    [], [with_libsmack=maybe])
 ])
 
-
 AC_DEFUN_ONCE([gl_FUNC_ACL],
 [
   AC_REQUIRE([gl_FUNC_ACL_ARG])
@@ -189,9 +192,35 @@ AC_DEFUN([gl_FILE_HAS_ACL],
   AC_CHECK_HEADERS_ONCE([linux/xattr.h])
   AC_CHECK_FUNCS_ONCE([listxattr])
   FILE_HAS_ACL_LIB=
-  AS_CASE([$enable_acl,$ac_cv_header_linux_xattr_h,$ac_cv_func_listxattr],
-    [no,*,*], [],
-    [*,yes,yes], [],
+
+  gl_file_has_acl_uses_smack=no
+  AS_CASE([$enable_acl,$with_libsmack,$ac_cv_header_linux_xattr_h,$ac_cv_func_listxattr],
+    [no,* | *,no,*], [],
+    [*,*,yes,yes],
+      [AC_CHECK_HEADER([sys/smack.h],
+         [gl_saved_LIBS=$LIBS
+          AC_SEARCH_LIBS([smack_new_label_from_path], [smack],
+            [AC_DEFINE([HAVE_SMACK], [1],
+               [Define to 1 if libsmack is usable.])
+             AS_CASE([$ac_cv_search_smack_new_label_from_path],
+               ["none required"], [],
+               [FILE_HAS_ACL_LIB=$ac_cv_search_new_label_from_path])
+             gl_file_has_acl_uses_smack=yes],
+            [AS_CASE([$with_libsmack],
+               [yes], [AC_MSG_ERROR([libsmack not found or unusable])])])
+          LIBS=$gl_saved_LIBS])])
+
+  gl_file_has_acl_uses_selinux=no
+  AS_CASE([$enable_acl,$with_selinux,$ac_cv_header_linux_xattr_h,$ac_cv_func_listxattr],
+    [no,* | *,no,*], [],
+    [*,*,yes,yes],
+      [gl_CHECK_HEADER_SELINUX_SELINUX_H
+       AS_IF([test "$USE_SELINUX_SELINUX_H" -ne 0 ],
+         [FILE_HAS_ACL_LIB="$FILE_HAS_ACL_LIB $LIB_SELINUX"
+          gl_file_has_acl_uses_selinux=yes])])
+
+  AS_CASE([$enable_acl,$gl_file_has_acl_uses_selinux,$gl_file_has_acl_uses_smack],
+    [no,* | *,yes,* | *,yes], [],
     [*],
       [dnl Set gl_need_lib_has_acl to a nonempty value, so that any
        dnl later gl_FUNC_ACL call will set FILE_HAS_ACL_LIB=$LIB_ACL.
index 91fdc434f2e5bc5de2b351f441eb4375ea31c092..a141defef0f8c682feebacab548406431aec4a60 100644 (file)
@@ -1,5 +1,5 @@
 # selinux-selinux-h.m4
-# serial 8   -*- Autoconf -*-
+# serial 9   -*- Autoconf -*-
 dnl Copyright (C) 2006-2007, 2009-2024 Free Software Foundation, Inc.
 dnl This file is free software; the Free Software Foundation
 dnl gives unlimited permission to copy and/or distribute it,
@@ -12,16 +12,8 @@ dnl with or without modifications, as long as this notice is preserved.
 
 AC_DEFUN([gl_HEADERS_SELINUX_SELINUX_H],
 [
-  AC_REQUIRE([gl_LIBSELINUX])
+  AC_REQUIRE([gl_CHECK_HEADER_SELINUX_SELINUX_H])
   if test "$with_selinux" != no; then
-    AC_CHECK_HEADERS([selinux/selinux.h])
-
-    if test $ac_cv_header_selinux_selinux_h = yes; then
-      HAVE_SELINUX_SELINUX_H=1
-    else
-      HAVE_SELINUX_SELINUX_H=0
-    fi
-
     if test "$ac_cv_header_selinux_selinux_h" = yes; then
       # We do have <selinux/selinux.h>, so do compile getfilecon.c
       # and arrange to use its wrappers.
@@ -39,6 +31,22 @@ AC_DEFUN([gl_HEADERS_SELINUX_SELINUX_H],
       AC_DEFINE([fgetfilecon_raw], [rpl_fgetfilecon_raw],
                 [Always use our fgetfilecon_raw wrapper.])
     fi
+  fi
+])
+
+# Check for <selinux/selinux.h>, if necessary.
+
+AC_DEFUN([gl_CHECK_HEADER_SELINUX_SELINUX_H],
+[
+  AC_REQUIRE([gl_LIBSELINUX])
+  if test "$with_selinux" != no; then
+    AC_CHECK_HEADERS_ONCE([selinux/selinux.h])
+
+    if test $ac_cv_header_selinux_selinux_h = yes; then
+      USE_SELINUX_SELINUX_H=1
+    else
+      USE_SELINUX_SELINUX_H=0
+    fi
 
     case "$ac_cv_search_setfilecon:$ac_cv_header_selinux_selinux_h" in
       no:*) # already warned
@@ -50,9 +58,11 @@ AC_DEFUN([gl_HEADERS_SELINUX_SELINUX_H],
   else
     # Do as if <selinux/selinux.h> does not exist, even if
     # AC_CHECK_HEADERS_ONCE has already determined that it exists.
-    HAVE_SELINUX_SELINUX_H=0
+    USE_SELINUX_SELINUX_H=0
   fi
-  AC_SUBST([HAVE_SELINUX_SELINUX_H])
+  AC_SUBST([USE_SELINUX_SELINUX_H])
+  AC_DEFINE_UNQUOTED([USE_SELINUX_SELINUX_H], [$USE_SELINUX_SELINUX_H],
+    [Define to 1 if <selinux/selinux.h> should be used, to 0 otherwise.])])
 ])
 
 AC_DEFUN([gl_LIBSELINUX],
index ec93313dfdde378f3bdc275755955c16053a6c65..8adb310bd0ed334e3d024229af5c7dc352bbc088 100644 (file)
@@ -5,13 +5,16 @@ Files:
 lib/acl-internal.h
 lib/file-has-acl.c
 m4/acl.m4
+m4/selinux-selinux-h.m4
 
 Depends-on:
 acl-permissions
 attribute
+errno
 extern-inline
 minmax
 free-posix
+ssize_t
 stat
 stdbool
 stdckdint
index d4e89cc4fc1801366cba2def0f03e1f5eee3acb6..941673d1ee4be9f1d13e99328b6ad2968b5546c1 100644 (file)
@@ -34,11 +34,11 @@ selinux/selinux.h: se-selinux.in.h $(top_builddir)/config.status
        $(AM_V_GEN)$(MKDIR_P) '%reldir%/selinux'
        $(AM_V_at)$(SED_HEADER_STDOUT) \
              -e 's|@''GUARD_PREFIX''@|${gl_include_guard_prefix}|g' \
-             -e 's/@''HAVE_SELINUX_SELINUX_H''@/$(HAVE_SELINUX_SELINUX_H)/g' \
              -e 's|@''INCLUDE_NEXT''@|$(INCLUDE_NEXT)|g' \
              -e 's|@''PRAGMA_SYSTEM_HEADER''@|@PRAGMA_SYSTEM_HEADER@|g' \
              -e 's|@''PRAGMA_COLUMNS''@|@PRAGMA_COLUMNS@|g' \
              -e 's|@''NEXT_SELINUX_SELINUX_H''@|$(NEXT_SELINUX_SELINUX_H)|g' \
+             -e 's/@''USE_SELINUX_SELINUX_H''@/$(USE_SELINUX_SELINUX_H)/g' \
              $(srcdir)/se-selinux.in.h > $@-t
        $(AM_V_at)mv $@-t $@
 MOSTLYCLEANFILES += selinux/selinux.h selinux/selinux.h-t
index 1a10286cd2d5eb79931882ccce9a7655f736b871..c0766609421ac1bd6e8bdcf633aa5d615e3ae33d 100644 (file)
@@ -22,6 +22,7 @@
 
 #include <signal.h>
 #include <stdio.h>
+#include <string.h>
 #include <stdlib.h>
 #include <unistd.h>
 #include <sys/types.h>
@@ -64,6 +65,16 @@ main (int argc, char *argv[])
         fprintf (stderr, "could not access the ACL of file \"%s\"\n", file);
         exit (EXIT_FAILURE);
       }
+
+    struct aclinfo ai;
+    int reti = file_has_aclinfo (file, &statbuf, &ai, 0);
+    if (reti != ret)
+      {
+        fprintf (stderr, "file_has_aclinfo failed for \"%s\"\n", file);
+        exit (EXIT_FAILURE);
+      }
+    aclinfo_free (&ai);
+
     printf ("%s\n", ret ? "yes" : "no");
   }
 #else