From mboxrd@z Thu Jan 1 00:00:00 1970 Path: news.gmane.io!.POSTED.blaine.gmane.org!not-for-mail From: Lars Ingebrigtsen Newsgroups: gmane.emacs.bugs Subject: bug#33847: 27.0.50; emacsclient does not find server socket Date: Thu, 22 Jul 2021 19:05:11 +0200 Message-ID: <878s1yi8jc.fsf@gnus.org> References: <8a6fc59c-08b3-e274-4fb1-74674c79540a@cs.ucla.edu> <9ebcad22-2cdb-46fb-4be9-efc4ad234b6d@cs.ucla.edu> <27d2f2eb-8956-4088-f3ec-5ff6c2cf2e8f@cs.ucla.edu> <87eeo27v6k.fsf@gnus.org> <83lfi79klf.fsf@gnu.org> <831rjya5yy.fsf@gnu.org> <8333533f-08ec-acd4-2fbf-f06e78591e98@cs.ucla.edu> <83v9h99awv.fsf@gnu.org> <878s1yjy20.fsf@gnus.org> <83lf5y2t78.fsf@gnu.org> Mime-Version: 1.0 Content-Type: multipart/mixed; boundary="=-=-=" Injection-Info: ciao.gmane.io; posting-host="blaine.gmane.org:116.202.254.214"; logging-data="14149"; mail-complaints-to="usenet@ciao.gmane.io" User-Agent: Gnus/5.13 (Gnus v5.13) Emacs/28.0.50 (gnu/linux) Cc: teika@gmx.com, eggert@cs.ucla.edu, 33847@debbugs.gnu.org, ulm@gentoo.org To: Eli Zaretskii Original-X-From: bug-gnu-emacs-bounces+geb-bug-gnu-emacs=m.gmane-mx.org@gnu.org Thu Jul 22 19:06:12 2021 Return-path: Envelope-to: geb-bug-gnu-emacs@m.gmane-mx.org Original-Received: from lists.gnu.org ([209.51.188.17]) by ciao.gmane.io with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.92) (envelope-from ) id 1m6c9D-0003Qz-4p for geb-bug-gnu-emacs@m.gmane-mx.org; Thu, 22 Jul 2021 19:06:11 +0200 Original-Received: from localhost ([::1]:34938 helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1m6c9B-0004UK-6u for geb-bug-gnu-emacs@m.gmane-mx.org; Thu, 22 Jul 2021 13:06:09 -0400 Original-Received: from eggs.gnu.org ([2001:470:142:3::10]:50698) by lists.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1m6c94-0004Th-RX for bug-gnu-emacs@gnu.org; Thu, 22 Jul 2021 13:06:02 -0400 Original-Received: from debbugs.gnu.org ([209.51.188.43]:58231) by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_128_GCM_SHA256:128) (Exim 4.90_1) (envelope-from ) id 1m6c94-0003l9-Je for bug-gnu-emacs@gnu.org; Thu, 22 Jul 2021 13:06:02 -0400 Original-Received: from Debian-debbugs by debbugs.gnu.org with local (Exim 4.84_2) (envelope-from ) id 1m6c94-0002F2-Dy for bug-gnu-emacs@gnu.org; Thu, 22 Jul 2021 13:06:02 -0400 X-Loop: help-debbugs@gnu.org Resent-From: Lars Ingebrigtsen Original-Sender: "Debbugs-submit" Resent-CC: bug-gnu-emacs@gnu.org Resent-Date: Thu, 22 Jul 2021 17:06:02 +0000 Resent-Message-ID: Resent-Sender: help-debbugs@gnu.org X-GNU-PR-Message: followup 33847 X-GNU-PR-Package: emacs X-GNU-PR-Keywords: patch Original-Received: via spool by 33847-submit@debbugs.gnu.org id=B33847.16269735288566 (code B ref 33847); Thu, 22 Jul 2021 17:06:02 +0000 Original-Received: (at 33847) by debbugs.gnu.org; 22 Jul 2021 17:05:28 +0000 Original-Received: from localhost ([127.0.0.1]:41544 helo=debbugs.gnu.org) by debbugs.gnu.org with esmtp (Exim 4.84_2) (envelope-from ) id 1m6c8V-0002E3-1h for submit@debbugs.gnu.org; Thu, 22 Jul 2021 13:05:28 -0400 Original-Received: from quimby.gnus.org ([95.216.78.240]:57116) by debbugs.gnu.org with esmtp (Exim 4.84_2) (envelope-from ) id 1m6c8R-0002Dm-Hr for 33847@debbugs.gnu.org; Thu, 22 Jul 2021 13:05:25 -0400 DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=gnus.org; s=20200322; h=Content-Type:MIME-Version:Message-ID:In-Reply-To:Date: References:Subject:Cc:To:From:Sender:Reply-To:Content-Transfer-Encoding: Content-ID:Content-Description:Resent-Date:Resent-From:Resent-Sender: Resent-To:Resent-Cc:Resent-Message-ID:List-Id:List-Help:List-Unsubscribe: List-Subscribe:List-Post:List-Owner:List-Archive; bh=sqkv0mqkEdtgEuid6pxSoQ3Tien33rdwrXHJLySqNPU=; b=RVKp/RQsYG703/ZDxzxogpfeZF g9bVDkC9O+EnvnzTsqSdi+r3GIQvJgxKI+31tz28rECUNTayGRTF6M2pGNKX//vPNIBE0V1a8FsNm JKcSTy/XF2NSvUx84NMtPC2dqdb9U65BvvZ1vQmSt8Fw7Yu2Kp8AZQCCJ53YGEkI7VHU=; Original-Received: from cm-84.212.220.105.getinternet.no ([84.212.220.105] helo=elva) by quimby.gnus.org with esmtpsa (TLS1.3:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.92) (envelope-from ) id 1m6c8G-000502-Rx; Thu, 22 Jul 2021 19:05:16 +0200 X-Now-Playing: Pet Shop Boys's _Hotspot_: "Happy people" In-Reply-To: <83lf5y2t78.fsf@gnu.org> (Eli Zaretskii's message of "Thu, 22 Jul 2021 19:45:31 +0300") X-BeenThere: debbugs-submit@debbugs.gnu.org X-Mailman-Version: 2.1.18 Precedence: list X-BeenThere: bug-gnu-emacs@gnu.org List-Id: "Bug reports for GNU Emacs, the Swiss army knife of text editors" List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: bug-gnu-emacs-bounces+geb-bug-gnu-emacs=m.gmane-mx.org@gnu.org Original-Sender: "bug-gnu-emacs" Xref: news.gmane.io gmane.emacs.bugs:210533 Archived-At: --=-=-= Content-Type: text/plain Eli Zaretskii writes: > Looks like you didn't attach the right patch? Yup; 2nd attempt now. -- (domestic pets only, the antidote for overdose, milk.) bloggy blog: http://lars.ingebrigtsen.no --=-=-= Content-Type: text/x-diff; charset=utf-8 Content-Disposition: inline; filename=client.patch Content-Transfer-Encoding: quoted-printable diff --git a/admin/merge-gnulib b/admin/merge-gnulib index 1c8b442700..c12e83dd2f 100755 --- a/admin/merge-gnulib +++ b/admin/merge-gnulib @@ -33,7 +33,7 @@ GNULIB_MODULES=3D crypto/md5-buffer crypto/sha1-buffer crypto/sha256-buffer crypto/sha512-= buffer d-type diffseq double-slash-root dtoastr dtotimespec dup2 environ execinfo explicit_bzero faccessat - fchmodat fcntl fcntl-h fdopendir + fchmodat fcntl fcntl-h fdopendir file-has-acl filemode filename filevercmp flexmember fpieee free-posix fstatat fsusage fsync futimens getloadavg getopt-gnu getrandom gettime gettimeofday gitlog-to-changelog diff --git a/lib-src/emacsclient.c b/lib-src/emacsclient.c index 12ced4aadb..3972cbba45 100644 --- a/lib-src/emacsclient.c +++ b/lib-src/emacsclient.c @@ -80,6 +80,7 @@ Copyright (C) 1986-1987, 1994, 1999-2021 Free Software Fo= undation, Inc. #include #include =20 +#include #include #include #include @@ -91,6 +92,10 @@ Copyright (C) 1986-1987, 1994, 1999-2021 Free Software F= oundation, Inc. # pragma GCC diagnostic ignored "-Wformat-truncation=3D2" #endif =20 +#ifndef O_PATH +# define O_PATH O_SEARCH +#endif + /* Name used to invoke this program. */ static char const *progname; @@ -1128,24 +1133,74 @@ process_grouping (void) =20 #ifdef SOCKETS_IN_FILE_SYSTEM =20 -/* Return the file status of NAME, ordinarily a socket. - It should be owned by UID. Return one of the following: - >0 - 'stat' failed with this errno value - -1 - isn't owned by us - 0 - success: none of the above */ +/* A local socket address. The union avoids the need to cast. */ +union local_sockaddr +{ + struct sockaddr_un un; + struct sockaddr sa; +}; + +/* Relative to the directory DIRFD, connect the socket file named ADDR + to the socket S. Return 0 if successful, -1 if DIRFD is not + AT_FDCWD and DIRFD's permissions would allow a symlink attack, an + errno otherwise. */ =20 static int -socket_status (const char *name, uid_t uid) +connect_socket (int dirfd, char const *addr, int s, uid_t uid) { - struct stat statbfr; + int sock_status =3D 0; =20 - if (stat (name, &statbfr) !=3D 0) - return errno; + union local_sockaddr server; + if (sizeof server.un.sun_path <=3D strlen (addr)) + return ENAMETOOLONG; + server.un.sun_family =3D AF_UNIX; + strcpy (server.un.sun_path, addr); =20 - if (statbfr.st_uid !=3D uid) - return -1; + /* If -1, WDFD is not set yet. If nonnegative, WDFD is a file + descriptor for the initial working directory. Otherwise -1 - WDFD is + the error number for the initial working directory. */ + static int wdfd =3D -1; =20 - return 0; + if (dirfd !=3D AT_FDCWD) + { + /* Fail if DIRFD's permissions are bogus. */ + struct stat st; + if (fstat (dirfd, &st) !=3D 0) + return errno; + if (st.st_uid !=3D uid || (st.st_mode & (S_IWGRP | S_IWOTH))) + return -1; + + if (wdfd =3D=3D -1) + { + /* Save the initial working directory. */ + wdfd =3D open (".", O_PATH | O_CLOEXEC); + if (wdfd < 0) + wdfd =3D -1 - errno; + } + if (wdfd < 0) + return -1 - wdfd; + if (fchdir (dirfd) !=3D 0) + return errno; + + /* Fail if DIRFD has an ACL, which means its permissions are + almost surely bogus. */ + int has_acl =3D file_has_acl (".", &st); + if (has_acl) + sock_status =3D has_acl < 0 ? errno : -1; + } + + if (!sock_status) + sock_status =3D connect (s, &server.sa, sizeof server.un) =3D=3D 0 ? 0= : errno; + + /* Fail immediately if we cannot change back to the initial working + directory, as that can mess up the rest of execution. */ + if (dirfd !=3D AT_FDCWD && fchdir (wdfd) !=3D 0) + { + message (true, "%s: .: %s\n", progname, strerror (errno)); + exit (EXIT_FAILURE); + } + + return sock_status; } =20 @@ -1322,32 +1377,49 @@ act_on_signals (HSOCKET emacs_socket) } } =20 -/* Create in SOCKNAME (of size SOCKNAMESIZE) a name for a local socket. - The first TMPDIRLEN bytes of SOCKNAME are already initialized to be - the name of a temporary directory. Use UID and SERVER_NAME to - concoct the name. Return the total length of the name if successful, - -1 if it does not fit (and store a truncated name in that case). - Fail if TMPDIRLEN is out of range. */ +enum { socknamesize =3D sizeof ((struct sockaddr_un *) NULL)->sun_path }; + +/* Given a local socket S, create in *SOCKNAME a name for a local socket + and connect to that socket. The first TMPDIRLEN bytes of *SOCKNAME are + already initialized to be the name of a temporary directory. + Use UID and SERVER_NAME to concoct the name. Return 0 if + successful, -1 if the socket's parent directory is not safe, and an + errno if there is some other problem. */ =20 static int -local_sockname (char *sockname, int socknamesize, int tmpdirlen, - uintmax_t uid, char const *server_name) +local_sockname (int s, char sockname[socknamesize], int tmpdirlen, + uid_t uid, char const *server_name) { /* If ! (0 <=3D TMPDIRLEN && TMPDIRLEN < SOCKNAMESIZE) the truncated temporary directory name is already in SOCKNAME, so nothing more need be stored. */ - if (0 <=3D tmpdirlen) - { - int remaining =3D socknamesize - tmpdirlen; - if (0 < remaining) - { - int suffixlen =3D snprintf (&sockname[tmpdirlen], remaining, - "/emacs%"PRIuMAX"/%s", uid, server_name); - if (0 <=3D suffixlen && suffixlen < remaining) - return tmpdirlen + suffixlen; - } - } - return -1; + if (! (0 <=3D tmpdirlen && tmpdirlen < socknamesize)) + return ENAMETOOLONG; + + /* Put the full address name into the buffer, since the caller might + need it for diagnostics. But don't overrun the buffer. */ + uintmax_t uidmax =3D uid; + int emacsdirlen; + int suffixlen =3D snprintf (sockname + tmpdirlen, socknamesize - tmpdirl= en, + "/emacs%"PRIuMAX"%n/%s", uidmax, &emacsdirlen, + server_name); + if (! (0 <=3D suffixlen && suffixlen < socknamesize - tmpdirlen)) + return ENAMETOOLONG; + + /* Make sure the address's parent directory is not a symlink and is + this user's directory and does not let others write to it; this + fends off some symlink attacks. To avoid races, keep the parent + directory open while checking. */ + char *emacsdirend =3D sockname + tmpdirlen + emacsdirlen; + *emacsdirend =3D '\0'; + int dir =3D openat (AT_FDCWD, sockname, + O_PATH | O_DIRECTORY | O_NOFOLLOW | O_CLOEXEC); + *emacsdirend =3D '/'; + if (dir < 0) + return errno; + int sock_status =3D connect_socket (dir, server_name, s, uid); + close (dir); + return sock_status; } =20 /* Create a local socket for SERVER_NAME and connect it to Emacs. If @@ -1358,28 +1430,43 @@ local_sockname (char *sockname, int socknamesize, i= nt tmpdirlen, static HSOCKET set_local_socket (char const *server_name) { - union { - struct sockaddr_un un; - struct sockaddr sa; - } server =3D {{ .sun_family =3D AF_UNIX }}; + union local_sockaddr server; + int sock_status; char *sockname =3D server.un.sun_path; - enum { socknamesize =3D sizeof server.un.sun_path }; int tmpdirlen =3D -1; int socknamelen =3D -1; uid_t uid =3D geteuid (); bool tmpdir_used =3D false; + int s =3D cloexec_socket (AF_UNIX, SOCK_STREAM, 0); + if (s < 0) + { + message (true, "%s: can't create socket: %s\n", + progname, strerror (errno)); + fail (); + } =20 if (strchr (server_name, '/') || (ISSLASH ('\\') && strchr (server_name, '\\'))) - socknamelen =3D snprintf (sockname, socknamesize, "%s", server_name); + { + socknamelen =3D snprintf (sockname, socknamesize, "%s", server_name); + sock_status =3D (0 <=3D socknamelen && socknamelen < socknamesize + ? connect_socket (AT_FDCWD, sockname, s, 0) + : ENAMETOOLONG); + } else { /* socket_name is a file name component. */ + sock_status =3D ENOENT; char const *xdg_runtime_dir =3D egetenv ("XDG_RUNTIME_DIR"); if (xdg_runtime_dir) - socknamelen =3D snprintf (sockname, socknamesize, "%s/emacs/%s", - xdg_runtime_dir, server_name); - else + { + socknamelen =3D snprintf (sockname, socknamesize, "%s/emacs/%s", + xdg_runtime_dir, server_name); + sock_status =3D (0 <=3D socknamelen && socknamelen < socknamesize + ? connect_socket (AT_FDCWD, sockname, s, 0) + : ENAMETOOLONG); + } + if (sock_status =3D=3D ENOENT) { char const *tmpdir =3D egetenv ("TMPDIR"); if (tmpdir) @@ -1398,23 +1485,24 @@ set_local_socket (char const *server_name) if (tmpdirlen < 0) tmpdirlen =3D snprintf (sockname, socknamesize, "/tmp"); } - socknamelen =3D local_sockname (sockname, socknamesize, tmpdirlen, + sock_status =3D local_sockname (s, sockname, tmpdirlen, uid, server_name); tmpdir_used =3D true; } } =20 - if (! (0 <=3D socknamelen && socknamelen < socknamesize)) + if (sock_status =3D=3D 0) + return s; + + if (sock_status =3D=3D ENAMETOOLONG) { message (true, "%s: socket-name %s... too long\n", progname, socknam= e); fail (); } =20 - /* See if the socket exists, and if it's owned by us. */ - int sock_status =3D socket_status (sockname, uid); - if (sock_status) + if (tmpdir_used) { - /* Failing that, see if LOGNAME or USER exist and differ from + /* See whether LOGNAME or USER exist and differ from our euid. If so, look for a socket based on the UID associated with the name. This is reminiscent of the logic that init_editfns uses to set the global Vuser_full_name. */ @@ -1431,48 +1519,26 @@ set_local_socket (char const *server_name) if (pw && pw->pw_uid !=3D uid) { /* We're running under su, apparently. */ - socknamelen =3D local_sockname (sockname, socknamesize, tmpdirlen, + sock_status =3D local_sockname (s, sockname, tmpdirlen, pw->pw_uid, server_name); - if (socknamelen < 0) + if (sock_status =3D=3D 0) + return s; + if (sock_status =3D=3D ENAMETOOLONG) { message (true, "%s: socket-name %s... too long\n", progname, sockname); exit (EXIT_FAILURE); } - - sock_status =3D socket_status (sockname, uid); } } } =20 - if (sock_status =3D=3D 0) - { - HSOCKET s =3D cloexec_socket (AF_UNIX, SOCK_STREAM, 0); - if (s < 0) - { - message (true, "%s: socket: %s\n", progname, strerror (errno)); - return INVALID_SOCKET; - } - if (connect (s, &server.sa, sizeof server.un) !=3D 0) - { - message (true, "%s: connect: %s\n", progname, strerror (errno)); - CLOSE_SOCKET (s); - return INVALID_SOCKET; - } - - struct stat connect_stat; - if (fstat (s, &connect_stat) !=3D 0) - sock_status =3D errno; - else if (connect_stat.st_uid =3D=3D uid) - return s; - else - sock_status =3D -1; - - CLOSE_SOCKET (s); - } + close (s); =20 - if (sock_status < 0) - message (true, "%s: Invalid socket owner\n", progname); + if (sock_status =3D=3D -1) + message (true, + "%s: Invalid permissions on parent directory of socket: %s\n", + progname, sockname); else if (sock_status =3D=3D ENOENT) { if (tmpdir_used) @@ -1502,7 +1568,7 @@ set_local_socket (char const *server_name) } } else - message (true, "%s: can't stat %s: %s\n", + message (true, "%s: can't connect to %s: %s\n", progname, sockname, strerror (sock_status)); =20 return INVALID_SOCKET; diff --git a/lib/file-has-acl.c b/lib/file-has-acl.c new file mode 100644 index 0000000000..c667ae9d24 --- /dev/null +++ b/lib/file-has-acl.c @@ -0,0 +1,510 @@ +/* Test whether a file has a nontrivial ACL. -*- coding: utf-8 -*- + + Copyright (C) 2002-2003, 2005-2020 Free Software Foundation, Inc. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + + Written by Paul Eggert, Andreas Gr=C3=BCnbacher, and Bruno Haible. */ + +/* Without this pragma, gcc 4.7.0 20120126 may suggest that the + file_has_acl function might be candidate for attribute 'const' */ +#if (__GNUC__ =3D=3D 4 && 6 <=3D __GNUC_MINOR__) || 4 < __GNUC__ +# pragma GCC diagnostic ignored "-Wsuggest-attribute=3Dconst" +#endif + +#include + +#include "acl.h" + +#include "acl-internal.h" + +#if GETXATTR_WITH_POSIX_ACLS +# include +# include +#endif + +/* 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) +{ +#if USE_ACL + if (! S_ISLNK (sb->st_mode)) + { + +# if GETXATTR_WITH_POSIX_ACLS + + ssize_t ret; + + ret =3D getxattr (name, XATTR_NAME_POSIX_ACL_ACCESS, NULL, 0); + if (ret < 0 && errno =3D=3D ENODATA) + ret =3D 0; + else if (ret > 0) + return 1; + + if (ret =3D=3D 0 && S_ISDIR (sb->st_mode)) + { + ret =3D getxattr (name, XATTR_NAME_POSIX_ACL_DEFAULT, NULL, 0); + if (ret < 0 && errno =3D=3D ENODATA) + ret =3D 0; + else if (ret > 0) + return 1; + } + + 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 >=3D 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 f= or + ACL_TYPE_DEFAULT. */ + ret =3D acl_extended_file (name); + } + else /* FreeBSD, Mac OS X, IRIX, Tru64, Cygwin >=3D 2.5 */ + { +# if 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 =3D acl_get_file (name, ACL_TYPE_EXTENDED); + if (acl) + { + ret =3D acl_extended_nontrivial (acl); + acl_free (acl); + } + else + ret =3D -1; +# else /* FreeBSD, IRIX, Tru64, Cygwin >=3D 2.5 */ + acl_t acl =3D acl_get_file (name, ACL_TYPE_ACCESS); + if (acl) + { + int saved_errno; + + ret =3D acl_access_nontrivial (acl); + saved_errno =3D errno; + acl_free (acl); + errno =3D 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 >=3D 2.5 */ + /* On Linux, FreeBSD, IRIX, acl_get_file (name, ACL_TYPE_ACC= ESS) + 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 s= econd + call if the first one already failed. */ + if (ret =3D=3D 0 && S_ISDIR (sb->st_mode)) + { + acl =3D acl_get_file (name, ACL_TYPE_DEFAULT); + if (acl) + { +# ifdef __CYGWIN__ /* Cygwin >=3D 2.5 */ + ret =3D acl_access_nontrivial (acl); + saved_errno =3D errno; + acl_free (acl); + errno =3D saved_errno; +# else + ret =3D (0 < acl_entries (acl)); + acl_free (acl); +# endif + } + else + ret =3D -1; + } +# endif + } + else + ret =3D -1; +# endif + } + if (ret < 0) + return - acl_errno_valid (errno); + return ret; + +# elif HAVE_FACL && defined GETACL /* Solaris, Cygwin < 2.5, not HP-UX */ + +# if defined ACL_NO_TRIVIAL + + /* Solaris 10 (newer version), which has additional API declared in + (acl_t) and implemented in libsec (acl_set, acl_trivi= al, + acl_fromtext, ...). */ + return acl_trivial (name); + +# else /* Solaris, Cygwin, general case */ + + /* Solaris 2.5 through Solaris 10, Cygwin, and contemporaneous versi= ons + of Unixware. The acl() call returns the access and default ACL b= oth + at once. */ + { + /* Initially, try to read the entries into a stack-allocated buffe= r. + Use malloc if it does not fit. */ + enum + { + alloc_init =3D 4000 / sizeof (aclent_t), /* >=3D 3 */ + alloc_max =3D MIN (INT_MAX, SIZE_MAX / sizeof (aclent_t)) + }; + aclent_t buf[alloc_init]; + size_t alloc =3D alloc_init; + aclent_t *entries =3D buf; + aclent_t *malloced =3D NULL; + int count; + + for (;;) + { + count =3D acl (name, GETACL, alloc, entries); + if (count < 0 && errno =3D=3D ENOSPC) + { + /* Increase the size of the buffer. */ + free (malloced); + if (alloc > alloc_max / 2) + { + errno =3D ENOMEM; + return -1; + } + alloc =3D 2 * alloc; /* <=3D alloc_max */ + entries =3D malloced =3D + (aclent_t *) malloc (alloc * sizeof (aclent_t)); + if (entries =3D=3D NULL) + { + errno =3D ENOMEM; + return -1; + } + continue; + } + break; + } + if (count < 0) + { + if (errno =3D=3D ENOSYS || errno =3D=3D ENOTSUP) + ; + else + { + int saved_errno =3D errno; + free (malloced); + errno =3D saved_errno; + return -1; + } + } + else if (count =3D=3D 0) + ; + else + { + /* Don't use MIN_ACL_ENTRIES: It's set to 4 on Cygwin, but Cy= gwin + returns only 3 entries for files with no ACL. But this is = safe: + If there are more than 4 entries, there cannot be only the + "user::", "group::", "other:", and "mask:" entries. */ + if (count > 4) + { + free (malloced); + return 1; + } + + if (acl_nontrivial (count, entries)) + { + free (malloced); + return 1; + } + } + free (malloced); + } + +# ifdef ACE_GETACL + /* Solaris also has a different variant of ACLs, used in ZFS and NFS= v4 + file systems (whereas the other ones are used in UFS file systems= ). */ + { + /* Initially, try to read the entries into a stack-allocated buffe= r. + Use malloc if it does not fit. */ + enum + { + alloc_init =3D 4000 / sizeof (ace_t), /* >=3D 3 */ + alloc_max =3D MIN (INT_MAX, SIZE_MAX / sizeof (ace_t)) + }; + ace_t buf[alloc_init]; + size_t alloc =3D alloc_init; + ace_t *entries =3D buf; + ace_t *malloced =3D NULL; + int count; + + for (;;) + { + count =3D acl (name, ACE_GETACL, alloc, entries); + if (count < 0 && errno =3D=3D ENOSPC) + { + /* Increase the size of the buffer. */ + free (malloced); + if (alloc > alloc_max / 2) + { + errno =3D ENOMEM; + return -1; + } + alloc =3D 2 * alloc; /* <=3D alloc_max */ + entries =3D malloced =3D (ace_t *) malloc (alloc * sizeof = (ace_t)); + if (entries =3D=3D NULL) + { + errno =3D ENOMEM; + return -1; + } + continue; + } + break; + } + if (count < 0) + { + if (errno =3D=3D ENOSYS || errno =3D=3D EINVAL) + ; + else + { + int saved_errno =3D errno; + free (malloced); + errno =3D saved_errno; + return -1; + } + } + else if (count =3D=3D 0) + ; + else + { + /* In the old (original Solaris 10) convention: + If there are more than 3 entries, there cannot be only the + ACE_OWNER, ACE_GROUP, ACE_OTHER entries. + In the newer Solaris 10 and Solaris 11 convention: + If there are more than 6 entries, there cannot be only the + ACE_OWNER, ACE_GROUP, ACE_EVERYONE entries, each once with + NEW_ACE_ACCESS_ALLOWED_ACE_TYPE and once with + NEW_ACE_ACCESS_DENIED_ACE_TYPE. */ + if (count > 6) + { + free (malloced); + return 1; + } + + if (acl_ace_nontrivial (count, entries)) + { + free (malloced); + return 1; + } + } + free (malloced); + } +# endif + + return 0; +# endif + +# elif HAVE_GETACL /* HP-UX */ + + { + struct acl_entry entries[NACLENTRIES]; + int count; + + count =3D getacl (name, NACLENTRIES, entries); + + if (count < 0) + { + /* ENOSYS is seen on newer HP-UX versions. + EOPNOTSUPP is typically seen on NFS mounts. + ENOTSUP was seen on Quantum StorNext file systems (cvfs). = */ + if (errno =3D=3D ENOSYS || errno =3D=3D EOPNOTSUPP || errno = =3D=3D ENOTSUP) + ; + else + return -1; + } + else if (count =3D=3D 0) + return 0; + else /* count > 0 */ + { + if (count > NACLENTRIES) + /* If NACLENTRIES cannot be trusted, use dynamic memory + allocation. */ + abort (); + + /* If there are more than 3 entries, there cannot be only the + (uid,%), (%,gid), (%,%) entries. */ + if (count > 3) + return 1; + + { + struct stat statbuf; + + if (stat (name, &statbuf) < 0) + return -1; + + return acl_nontrivial (count, entries); + } + } + } + +# if HAVE_ACLV_H /* HP-UX >=3D 11.11 */ + + { + struct acl entries[NACLVENTRIES]; + int count; + + count =3D acl ((char *) name, ACL_GET, NACLVENTRIES, entries); + + if (count < 0) + { + /* EOPNOTSUPP is seen on NFS in HP-UX 11.11, 11.23. + EINVAL is seen on NFS in HP-UX 11.31. */ + if (errno =3D=3D ENOSYS || errno =3D=3D EOPNOTSUPP || errno = =3D=3D EINVAL) + ; + else + return -1; + } + else if (count =3D=3D 0) + return 0; + else /* count > 0 */ + { + if (count > NACLVENTRIES) + /* If NACLVENTRIES cannot be trusted, use dynamic memory + allocation. */ + abort (); + + /* If there are more than 4 entries, there cannot be only the + four base ACL entries. */ + if (count > 4) + return 1; + + return aclv_nontrivial (count, entries); + } + } + +# endif + +# elif HAVE_ACLX_GET && defined ACL_AIX_WIP /* AIX */ + + acl_type_t type; + char aclbuf[1024]; + void *acl =3D aclbuf; + size_t aclsize =3D sizeof (aclbuf); + mode_t mode; + + for (;;) + { + /* The docs say that type being 0 is equivalent to ACL_ANY, but = it + is not true, in AIX 5.3. */ + type.u64 =3D ACL_ANY; + if (aclx_get (name, 0, &type, aclbuf, &aclsize, &mode) >=3D 0) + break; + if (errno =3D=3D ENOSYS) + return 0; + if (errno !=3D ENOSPC) + { + if (acl !=3D aclbuf) + { + int saved_errno =3D errno; + free (acl); + errno =3D saved_errno; + } + return -1; + } + aclsize =3D 2 * aclsize; + if (acl !=3D aclbuf) + free (acl); + acl =3D malloc (aclsize); + if (acl =3D=3D NULL) + { + errno =3D ENOMEM; + return -1; + } + } + + if (type.u64 =3D=3D ACL_AIXC) + { + int result =3D acl_nontrivial ((struct acl *) acl); + if (acl !=3D aclbuf) + free (acl); + return result; + } + else if (type.u64 =3D=3D ACL_NFS4) + { + int result =3D acl_nfs4_nontrivial ((nfs4_acl_int_t *) acl); + if (acl !=3D aclbuf) + free (acl); + return result; + } + else + { + /* A newer type of ACL has been introduced in the system. + We should better support it. */ + if (acl !=3D aclbuf) + free (acl); + errno =3D EINVAL; + return -1; + } + +# elif HAVE_STATACL /* older AIX */ + + union { struct acl a; char room[4096]; } u; + + if (statacl ((char *) name, STX_NORMAL, &u.a, sizeof (u)) < 0) + return -1; + + return acl_nontrivial (&u.a); + +# elif HAVE_ACLSORT /* NonStop Kernel */ + + { + struct acl entries[NACLENTRIES]; + int count; + + count =3D acl ((char *) name, ACL_GET, NACLENTRIES, entries); + + if (count < 0) + { + if (errno =3D=3D ENOSYS || errno =3D=3D ENOTSUP) + ; + else + return -1; + } + else if (count =3D=3D 0) + return 0; + else /* count > 0 */ + { + if (count > NACLENTRIES) + /* If NACLENTRIES cannot be trusted, use dynamic memory + allocation. */ + abort (); + + /* If there are more than 4 entries, there cannot be only the + four base ACL entries. */ + if (count > 4) + return 1; + + return acl_nontrivial (count, entries); + } + } + +# endif + } +#endif + + return 0; +} diff --git a/lib/gnulib.mk.in b/lib/gnulib.mk.in index 07736f9b8b..0b9aaf6d9e 100644 --- a/lib/gnulib.mk.in +++ b/lib/gnulib.mk.in @@ -98,6 +98,7 @@ # fcntl \ # fcntl-h \ # fdopendir \ +# file-has-acl \ # filemode \ # filename \ # filevercmp \ @@ -1788,6 +1789,16 @@ EXTRA_libgnu_a_SOURCES +=3D fdopendir.c endif ## end gnulib module fdopendir =20 +## begin gnulib module file-has-acl +ifeq (,$(OMIT_GNULIB_MODULE_file-has-acl)) + +libgnu_a_SOURCES +=3D file-has-acl.c + +EXTRA_DIST +=3D acl-internal.h + +endif +## end gnulib module file-has-acl + ## begin gnulib module filemode ifeq (,$(OMIT_GNULIB_MODULE_filemode)) =20 diff --git a/m4/gnulib-comp.m4 b/m4/gnulib-comp.m4 index cd6f7b4bbd..05e7faa993 100644 --- a/m4/gnulib-comp.m4 +++ b/m4/gnulib-comp.m4 @@ -89,6 +89,7 @@ AC_DEFUN # Code from module fcntl: # Code from module fcntl-h: # Code from module fdopendir: + # Code from module file-has-acl: # Code from module filemode: # Code from module filename: # Code from module filevercmp: @@ -287,6 +288,7 @@ AC_DEFUN fi gl_DIRENT_MODULE_INDICATOR([fdopendir]) gl_MODULE_INDICATOR([fdopendir]) + gl_FILE_HAS_ACL gl_FILEMODE AC_C_FLEXIBLE_ARRAY_MEMBER gl_FUNC_FPENDING @@ -1045,6 +1047,7 @@ AC_DEFUN lib/fcntl.c lib/fcntl.in.h lib/fdopendir.c + lib/file-has-acl.c lib/filemode.c lib/filemode.h lib/filename.h --=-=-=--