[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
[PATCH] file-has-acl: symlinks can have scontext
From: |
Paul Eggert |
Subject: |
[PATCH] file-has-acl: symlinks can have scontext |
Date: |
Sat, 9 Nov 2024 16:38:51 -0800 |
Problem reported by Pádraig Brady <https://bugs.gnu.org/73418#35>.
* lib/file-has-acl.c (file_has_aclinfo):
With SELinux, symbolic links can have security contexts,
so omit incorrect optimizations assuming that they lack them.
When ! (flags & AC_SYMLINK_FOLLOW), prefer acl_get_link_np to
acl_get_file on platforms that have both APIs,
as this can avoid some races.
---
ChangeLog | 11 ++
lib/file-has-acl.c | 314 ++++++++++++++++++++++-----------------------
2 files changed, 163 insertions(+), 162 deletions(-)
diff --git a/ChangeLog b/ChangeLog
index e881e194dc..1c78de9717 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,14 @@
+2024-11-09 Paul Eggert <eggert@cs.ucla.edu>
+
+ file-has-acl: symlinks can have scontext
+ Problem reported by Pádraig Brady <https://bugs.gnu.org/73418#35>.
+ * lib/file-has-acl.c (file_has_aclinfo):
+ With SELinux, symbolic links can have security contexts,
+ so omit incorrect optimizations assuming that they lack them.
+ When ! (flags & AC_SYMLINK_FOLLOW), prefer acl_get_link_np to
+ acl_get_file on platforms that have both APIs,
+ as this can avoid some races.
+
2024-11-07 Paul Eggert <eggert@cs.ucla.edu>
file-has-acl: minor ENOMEM fixes
diff --git a/lib/file-has-acl.c b/lib/file-has-acl.c
index 980c20249a..9a89e2de72 100644
--- a/lib/file-has-acl.c
+++ b/lib/file-has-acl.c
@@ -336,177 +336,165 @@ file_has_aclinfo (MAYBE_UNUSED char const *restrict
name,
{
unsigned char d_type = flags & UCHAR_MAX;
- /* Symbolic links lack extended attributes and ACLs on all supported
- platforms, so don't bother trying to fetch them. If the platform
- supports not following symlinks this defends against some races
- and avoids a syscall; otherwise this is essential.
-
- First, initialize *AI for cases known to be unsupported. */
+#if USE_LINUX_XATTR
+ int initial_errno = errno;
+ get_aclinfo (name, ai, flags);
- if (!USE_LINUX_XATTR || d_type == DT_LNK)
+ if (ai->size <= 0)
{
- ai->buf = ai->u.__gl_acl_ch;
- ai->size = -1;
- ai->u.err = ENOTSUP;
- ai->scontext = (char *) UNKNOWN_SECURITY_CONTEXT;
- ai->scontext_err = ENOTSUP;
+ errno = ai->size < 0 ? ai->u.err : initial_errno;
+ return ai->size;
}
- /* Now set *AI for cases that might be supported, then check for ACLs. */
+ /* 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. */
+
+ if (!aclinfo_has_xattr (ai, XATTR_NAME_NFSV4_ACL))
+ return
+ (aclinfo_has_xattr (ai, XATTR_NAME_POSIX_ACL_ACCESS)
+ || ((d_type == DT_DIR || d_type == DT_UNKNOWN)
+ && aclinfo_has_xattr (ai, XATTR_NAME_POSIX_ACL_DEFAULT)));
+
+ /* 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)];
+
+ int ret = ((flags & ACL_SYMLINK_FOLLOW ? getxattr : lgetxattr)
+ (name, XATTR_NAME_NFSV4_ACL, buf, sizeof buf));
+ if (ret < 0)
+ switch (errno)
+ {
+ case ENODATA: return 0;
+ case ERANGE : return 1; /* ACL must be nontrivial. */
+ default: return - acl_errno_valid (errno);
+ }
-#if USE_ACL
- if (d_type != DT_LNK)
- {
+ /* It looks like a trivial ACL, but investigate further. */
+ ret = acl_nfs4_nontrivial (buf, ret);
+ errno = ret < 0 ? EINVAL : initial_errno;
+ return ret;
-# if USE_LINUX_XATTR
- int initial_errno = errno;
- get_aclinfo (name, ai, flags);
+#else /* !USE_LINUX_XATTR */
- 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. */
-
- if (!aclinfo_has_xattr (ai, XATTR_NAME_NFSV4_ACL))
- return
- (aclinfo_has_xattr (ai, XATTR_NAME_POSIX_ACL_ACCESS)
- || ((d_type == DT_DIR || d_type == DT_UNKNOWN)
- && aclinfo_has_xattr (ai, XATTR_NAME_POSIX_ACL_DEFAULT)));
-
- /* 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)];
-
- int ret = ((flags & ACL_SYMLINK_FOLLOW ? getxattr : lgetxattr)
- (name, XATTR_NAME_NFSV4_ACL, buf, sizeof buf));
- if (ret < 0)
- switch (errno)
+ ai->buf = ai->u.__gl_acl_ch;
+ ai->size = -1;
+ ai->u.err = ENOTSUP;
+ ai->scontext = (char *) UNKNOWN_SECURITY_CONTEXT;
+ ai->scontext_err = ENOTSUP;
+
+# if USE_ACL
+# if HAVE_ACL_GET_FILE
+
+ {
+ /* POSIX 1003.1e (draft 17 -- abandoned) specific version. */
+ /* Linux, FreeBSD, Mac OS X, IRIX, Tru64, Cygwin >= 2.5 */
+ int ret;
+
+# if HAVE_ACL_EXTENDED_FILE /* Linux */
+ /* On Linux, acl_extended_file is an optimized function: It only
+ makes two calls to getxattr(), one for ACL_TYPE_ACCESS, one for
+ ACL_TYPE_DEFAULT. */
+ ret = ((flags & ACL_SYMLINK_FOLLOW
+ ? acl_extended_file
+ : acl_extended_file_nofollow)
+ (name));
+# elif HAVE_ACL_TYPE_EXTENDED /* Mac OS X */
+ /* On Mac OS X, acl_get_file (name, ACL_TYPE_ACCESS)
+ and acl_get_file (name, ACL_TYPE_DEFAULT)
+ always return NULL / EINVAL. There is no point in making
+ these two useless calls. The real ACL is retrieved through
+ acl_get_file (name, ACL_TYPE_EXTENDED). */
+ acl_t acl = ((flags & AC_SYMLINK_FOLLOW
+ ? acl_get_file
+ : acl_get_link_np)
+ (name, ACL_TYPE_EXTENDED));
+ if (acl)
+ {
+ ret = acl_extended_nontrivial (acl);
+ acl_free (acl);
+ }
+ else
+ ret = -1;
+# else /* FreeBSD, IRIX, Tru64, Cygwin >= 2.5 */
+ acl_t acl = ((flags & AC_SYMLINK_FOLLOW
+ ? acl_get_file
+ : acl_get_link_np)
+ (name, ACL_TYPE_ACCESS));
+ if (acl)
+ {
+ ret = acl_access_nontrivial (acl);
+ int saved_errno = errno;
+ acl_free (acl);
+ errno = saved_errno;
+# if HAVE_ACL_FREE_TEXT /* Tru64 */
+ /* On OSF/1, acl_get_file (name, ACL_TYPE_DEFAULT) always
+ returns NULL with errno not set. There is no point in
+ making this call. */
+# else /* FreeBSD, IRIX, Cygwin >= 2.5 */
+ /* On Linux, FreeBSD, IRIX, acl_get_file (name, ACL_TYPE_ACCESS)
+ and acl_get_file (name, ACL_TYPE_DEFAULT) on a directory
+ either both succeed or both fail; it depends on the
+ file system. Therefore there is no point in making the second
+ call if the first one already failed. */
+ if (ret == 0
+ && (d_type == DT_DIR
+ || (d_type == DT_UNKNOWN && !(flags & _GL_DT_NOTDIR))))
+ {
+ acl = acl_get_file (name, ACL_TYPE_DEFAULT);
+ if (acl)
{
- case ENODATA: return 0;
- case ERANGE : return 1; /* ACL must be nontrivial. */
+# ifdef __CYGWIN__ /* Cygwin >= 2.5 */
+ ret = acl_access_nontrivial (acl);
+ saved_errno = errno;
+ acl_free (acl);
+ errno = saved_errno;
+# else
+ ret = (0 < acl_entries (acl));
+ acl_free (acl);
+# endif
}
- else
- {
- /* It looks like a trivial ACL, but investigate further. */
- ret = acl_nfs4_nontrivial (buf, ret);
- if (ret < 0)
- {
- errno = EINVAL;
- return ret;
- }
- errno = initial_errno;
- }
-
- if (ret < 0)
- return - acl_errno_valid (errno);
- return ret;
- }
-
-# elif HAVE_ACL_GET_FILE
-
- /* POSIX 1003.1e (draft 17 -- abandoned) specific version. */
- /* Linux, FreeBSD, Mac OS X, IRIX, Tru64, Cygwin >= 2.5 */
- int ret;
-
-# if HAVE_ACL_EXTENDED_FILE /* Linux */
- /* On Linux, acl_extended_file is an optimized function: It only
- makes two calls to getxattr(), one for ACL_TYPE_ACCESS, one for
- ACL_TYPE_DEFAULT. */
- ret = ((flags & ACL_SYMLINK_FOLLOW
- ? acl_extended_file
- : acl_extended_file_nofollow)
- (name));
-# elif HAVE_ACL_TYPE_EXTENDED /* Mac OS X */
- /* On Mac OS X, acl_get_file (name, ACL_TYPE_ACCESS)
- and acl_get_file (name, ACL_TYPE_DEFAULT)
- always return NULL / EINVAL. There is no point in making
- these two useless calls. The real ACL is retrieved through
- acl_get_file (name, ACL_TYPE_EXTENDED). */
- acl_t acl = acl_get_file (name, ACL_TYPE_EXTENDED);
- if (acl)
- {
- ret = acl_extended_nontrivial (acl);
- acl_free (acl);
- }
- else
- ret = -1;
-# else /* FreeBSD, IRIX, Tru64, Cygwin >= 2.5 */
- acl_t acl = acl_get_file (name, ACL_TYPE_ACCESS);
- if (acl)
- {
- int saved_errno;
-
- ret = acl_access_nontrivial (acl);
- saved_errno = errno;
- acl_free (acl);
- errno = saved_errno;
-# if HAVE_ACL_FREE_TEXT /* Tru64 */
- /* On OSF/1, acl_get_file (name, ACL_TYPE_DEFAULT) always
- returns NULL with errno not set. There is no point in
- making this call. */
-# else /* FreeBSD, IRIX, Cygwin >= 2.5 */
- /* On Linux, FreeBSD, IRIX, acl_get_file (name, ACL_TYPE_ACCESS)
- and acl_get_file (name, ACL_TYPE_DEFAULT) on a directory
- either both succeed or both fail; it depends on the
- file system. Therefore there is no point in making the second
- call if the first one already failed. */
- if (ret == 0
- && (d_type == DT_DIR
- || (d_type == DT_UNKNOWN && !(flags & _GL_DT_NOTDIR))))
- {
- acl = acl_get_file (name, ACL_TYPE_DEFAULT);
- if (acl)
- {
-# ifdef __CYGWIN__ /* Cygwin >= 2.5 */
- ret = acl_access_nontrivial (acl);
- saved_errno = errno;
- acl_free (acl);
- errno = saved_errno;
-# else
- ret = (0 < acl_entries (acl));
- acl_free (acl);
-# endif
- }
- else
- {
-# ifdef __CYGWIN__ /* Cygwin >= 2.5 */
- if (d_type == DT_UNKNOWN)
- ret = 0;
- else
+ else
+ {
+ ret = -1;
+# ifdef __CYGWIN__ /* Cygwin >= 2.5 */
+ if (d_type == DT_UNKNOWN)
+ ret = 0;
+# endif
+ }
+ }
# endif
- ret = -1;
- }
- }
+ }
+ else
+ ret = -1;
# endif
- }
- else
- ret = -1;
-# endif
- if (ret < 0)
- return - acl_errno_valid (errno);
- return ret;
-# elif HAVE_FACL && defined GETACL /* Solaris, Cygwin < 2.5, not HP-UX */
+ return ret < 0 ? - acl_errno_valid (errno) : ret;
+ }
-# if defined ACL_NO_TRIVIAL
+# else /* !HAVE_ACL_GET_FILE */
+
+ /* The remaining APIs always follow symlinks and operate on
+ platforms where symlinks do not have ACLs, so skip the APIs if
+ NAME is known to be a symlink. */
+ if (d_type != DT_LNK)
+ {
+
+# if HAVE_FACL && defined GETACL /* Solaris, Cygwin < 2.5, not HP-UX */
+
+# ifdef ACL_NO_TRIVIAL
/* Solaris 10 (newer version), which has additional API declared in
<sys/acl.h> (acl_t) and implemented in libsec (acl_set, acl_trivial,
acl_fromtext, ...). */
return acl_trivial (name);
-# else /* Solaris, Cygwin, general case */
+# else /* Solaris, Cygwin, general case */
/* Solaris 2.5 through Solaris 10, Cygwin, and contemporaneous versions
of Unixware. The acl() call returns the access and default ACL both
@@ -579,7 +567,7 @@ file_has_aclinfo (MAYBE_UNUSED char const *restrict name,
free (malloced);
}
-# ifdef ACE_GETACL
+# ifdef ACE_GETACL
/* Solaris also has a different variant of ACLs, used in ZFS and NFSv4
file systems (whereas the other ones are used in UFS file systems).
*/
{
@@ -652,12 +640,12 @@ file_has_aclinfo (MAYBE_UNUSED char const *restrict name,
}
free (malloced);
}
-# endif
+# endif
return 0;
-# endif
+# endif
-# elif HAVE_GETACL /* HP-UX */
+# elif HAVE_GETACL /* HP-UX */
{
struct acl_entry entries[NACLENTRIES];
@@ -700,7 +688,7 @@ file_has_aclinfo (MAYBE_UNUSED char const *restrict name,
}
}
-# if HAVE_ACLV_H /* HP-UX >= 11.11 */
+# if HAVE_ACLV_H /* HP-UX >= 11.11 */
{
struct acl entries[NACLVENTRIES];
@@ -735,9 +723,9 @@ file_has_aclinfo (MAYBE_UNUSED char const *restrict name,
}
}
-# endif
+# endif
-# elif HAVE_ACLX_GET && defined ACL_AIX_WIP /* AIX */
+# elif HAVE_ACLX_GET && defined ACL_AIX_WIP /* AIX */
acl_type_t type;
char aclbuf[1024];
@@ -792,7 +780,7 @@ file_has_aclinfo (MAYBE_UNUSED char const *restrict name,
return -1;
}
-# elif HAVE_STATACL /* older AIX */
+# elif HAVE_STATACL /* older AIX */
union { struct acl a; char room[4096]; } u;
@@ -801,7 +789,7 @@ file_has_aclinfo (MAYBE_UNUSED char const *restrict name,
return acl_nontrivial (&u.a);
-# elif HAVE_ACLSORT /* NonStop Kernel */
+# elif HAVE_ACLSORT /* NonStop Kernel */
{
struct acl entries[NACLENTRIES];
@@ -833,8 +821,10 @@ file_has_aclinfo (MAYBE_UNUSED char const *restrict name,
return acl_nontrivial (count, entries);
}
}
-# endif
+# endif
}
+# endif
+# endif
#endif
return 0;
--
2.43.0
- [PATCH] file-has-acl: symlinks can have scontext,
Paul Eggert <=