Skip to contentSkip to conversation

acl: Preserving the setuid/setgid/sticky bits

PATCH Andreas Gruenbacher 2 participants 3 messages Jun 17, 2009 – Jun 23, 2009

lore
Share & tools
lore
#0 lore raw

Hello,

(Copying the xfs list for a wider audience while moving to the new list;
please reply to [email protected].)

in a recent bug report, we found out that setfacl --restore sometimes destroys the suid/sgid bits. This has been fixed [1], but problem that setfacl --restore cannot restore the setuid/setgid/sticky bits still remains: getfacl simply does not include this information in its output.

[1] Avoid unnecessary but destructive chown calls,

    http://git.savannah.gnu.org/cgit/acl.git/commit/?id=45833cc

Based on an earlier version from Brandon, I have created a patch which causes getfacl to include the special bits in its output, and setfacl to restore the bits when possible. The proposed format for special flags in the getfacl output would be:

	$ cd /
	$ getfacl bin/ping tmp
	# file: bin/ping
	# owner: root
	# group: root
	# flags: s--
	user::rwx
	group::r-x
	other::r-x

# file: tmp
# owner: root
# group: root
# flags: --t
user::rwx
group::rwx
other::rwx

In the current version, getfacl only includes the new flags: comment for files which have any of the special set. Setfacl --restore clears all special bits if there is no flags: comment, and sets them accordingly otherwise. (Without --restore, setfacl disregards such comments, just like it disregards the other comments.)

Does this extension look reasonable?

Any objections to changing the behavior of --restore to clear the special flags in case there is no flags: comment? (And if so, wow would you like this solved instead?)

Thanks,
Andreas

commit 5f60ceef5ed39872207cb52528ccc98f40ef76f7

Author: Andreas Gruenbacher <[email protected]>
Date:   Mon Jun 8 17:04:48 2009 +0200

Include the S_ISUID, S_ISGID, S_ISVTX flags in the getfacl output, and restore them with "setfacl --restore=file".

getfacl/getfacl.c +14 -0
index ab050ba..ba03686 100644
@@ -423,6 +423,18 @@ acl_get_file_mode(const char *path_p)
 	return acl_from_mode(st.st_mode);
 }
 
+static const char *
+flagstr(mode_t mode)
+{
+	static char str[4];
+
+	str[0] = (mode & S_ISUID) ? 's' : '-';
+	str[1] = (mode & S_ISGID) ? 's' : '-';
+	str[2] = (mode & S_ISVTX) ? 't' : '-';
+	str[3] = '\0';
+	return str;
+}
+
 int do_print(const char *path_p, const struct stat *st, int walk_flags, void *unused)
 {
 	const char *default_prefix = NULL;
@@ -498,6 +510,8 @@ int do_print(const char *path_p, const struct stat *st, int walk_flags, void *un
 			       xquote(user_name(st->st_uid, opt_numeric)));
 			printf("# group: %s\n",
 			       xquote(group_name(st->st_gid, opt_numeric)));
+			if (st->st_mode & (S_ISVTX | S_ISUID | S_ISGID))
+				printf("# flags: %s\n", flagstr(st->st_mode));
 		}
 		if (acl != NULL) {
 			char *acl_text = acl_to_any_text(acl, NULL, '\n',
setfacl/Makefile +1 -1
index 46b74d9..c44e7c0 100644
@@ -21,7 +21,7 @@ include $(TOPDIR)/include/builddefs
 
 LTCOMMAND = setfacl
 CFILES = setfacl.c do_set.c sequence.c parse.c
-HFILES = sequence.h parse.h
+HFILES = sequence.h parse.h do_set.h
 
 LLDLIBS = $(LIBMISC) $(LIBACL) $(LIBATTR)
 LTDEPENDENCIES = $(LIBMISC) $(LIBACL)
setfacl/do_set.c +12 -9
index b9c0ce7..d518d76 100644
@@ -34,6 +34,7 @@
 #include <dirent.h>
 #include <ftw.h>
 #include "sequence.h"
+#include "do_set.h"
 #include "parse.h"
 #include "config.h"
 #include "walk_tree.h"
@@ -262,7 +263,7 @@ do_set(
 	int walk_flags,
 	void *arg)
 {
-	const seq_t seq = (const seq_t)arg;
+	struct do_set_args *args = arg;
 	acl_t old_acl = NULL, old_default_acl = NULL;
 	acl_t acl = NULL, default_acl = NULL;
 	acl_t *xacl, *old_xacl;
@@ -290,7 +291,7 @@ do_set(
 		return 0;
 
 	/* Execute the commands in seq (read ACLs on demand) */
-	error = seq_get_cmd(seq, SEQ_FIRST_CMD, &cmd);
+	error = seq_get_cmd(args->seq, SEQ_FIRST_CMD, &cmd);
 	if (error == 0)
 		return 0;
 	while (error == 1) {
@@ -357,7 +358,7 @@ do_set(
 				goto fail;
 		}
 
-		error = seq_get_cmd(seq, SEQ_NEXT_CMD, &cmd);
+		error = seq_get_cmd(args->seq, SEQ_NEXT_CMD, &cmd);
 	}
 
 	if (error < 0)
@@ -467,19 +468,21 @@ do_set(
 		goto cleanup;
 	}
 	if (acl) {
+		mode_t mode = 0;
+		int equiv_mode;
+
+		equiv_mode = acl_equiv_mode(acl, &mode);
+
 		if (acl_set_file(path_p, ACL_TYPE_ACCESS, acl) != 0) {
 			if (errno == ENOSYS || errno == ENOTSUP) {
-				int saved_errno = errno;
-				mode_t mode;
-
-				if (acl_equiv_mode(acl, &mode) != 0) {
-					errno = saved_errno;
+				if (equiv_mode != 0)
 					goto fail;
-				} else if (chmod(path_p, mode) != 0)
+				else if (chmod(path_p, mode) != 0)
 					goto fail;
 			} else
 				goto fail;
 		}
+		args->mode = mode;
 	}
 	if (default_acl) {
 		if (S_ISDIR(st->st_mode)) {
setfacl/do_set.h new file +36 -0
new file mode 100644
index 0000000..2ea25a8
@@ -0,0 +1,36 @@
+/*
+  File: do_set.h
+  (Linux Access Control List Management)
+
+  Copyright (C) 2009 by Andreas Gruenbacher
+  <[email protected]>
+
+  This program is free software; you can redistribute it and/or
+  modify it under the terms of the GNU Lesser General Public
+  License as published by the Free Software Foundation; either
+  version 2.1 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
+  Lesser General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public
+  License along with this library; if not, write to the Free Software
+  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+#ifndef __DO_SET_H
+#define __DO_SET_H
+
+#include "sequence.h"
+
+struct do_set_args {
+	seq_t seq;
+	mode_t mode;
+};
+
+extern int do_set(const char *path_p, const struct stat *stat_p, int flags,
+		  void *arg);
+
+#endif  /* __DO_SET_H */
setfacl/parse.c +27 -1
index daa32e2..4df1a19 100644
@@ -410,7 +410,8 @@ read_acl_comments(
 	int *line,
 	char **path_p,
 	uid_t *uid_p,
-	gid_t *gid_p)
+	gid_t *gid_p,
+	mode_t *flags)
 {
 	int c;
 	/*
@@ -429,6 +430,8 @@ read_acl_comments(
 		*uid_p = ACL_UNDEFINED_ID;
 	if (gid_p)
 		*gid_p = ACL_UNDEFINED_ID;
+	if (flags)
+		*flags = 0;
 
 	for(;;) {
 		c = fgetc(file);
@@ -493,6 +496,29 @@ read_acl_comments(
 				if (get_gid(unquote(cp), gid_p) != 0)
 					continue;
 			}
+		} else if (strncmp(cp, "flags:", 6) == 0) {
+			mode_t f = 0;
+
+			cp += 6;
+			SKIP_WS(cp);
+
+			if (cp[0] == 's')
+				f |= S_ISUID;
+			else if (cp[0] != '-')
+				goto fail;
+			if (cp[1] == 's')
+				f |= S_ISGID;
+			else if (cp[1] != '-')
+				goto fail;
+			if (cp[2] == 't')
+				f |= S_ISVTX;
+			else if (cp[2] != '-')
+				goto fail;
+			if (cp[3] != '\0')
+				goto fail;
+
+			if (flags)
+				*flags = f;
 		}
 	}
 	if (ferror(file))
setfacl/parse.h +2 -1
index b6b7e01..b2e68b4 100644
@@ -64,7 +64,8 @@ read_acl_comments(
 	int *line,
 	char **path_p,
 	uid_t *uid_p,
-	gid_t *gid_p);
+	gid_t *gid_p,
+	mode_t *flags);
 int
 read_acl_seq(
 	FILE *file,
setfacl/setfacl.c +33 -17

118 lines not on this page · raw message

_______________________________________________
xfs mailing list
[email protected]
http://oss.sgi.com/mailman/listinfo/xfs

#1 lore raw
Quoted 7 lines · Andreas Gruenbacher

In the current version, getfacl only includes the new flags: comment for files which have any of the special set. Setfacl --restore clears all special bits if there is no flags: comment, and sets them accordingly otherwise. (Without --restore, setfacl disregards such comments, just like it disregards the other comments.)

Does this extension look reasonable?

Seems reasonable to me.

Quoted 3 lines

Any objections to changing the behavior of --restore to clear the special flags in case there is no flags: comment? (And if so, how would you like this solved instead?)

Will need to put a block around the getfacl mode output in the POSIXLY_CORRECT case. Other than that SUID/SGID/sticky restore if the comment exists makes sense to me.

Quoted 16 lines
(quoted diff) +5 -5
@@ -467,19 +468,21 @@ do_set(
 		goto cleanup;
 	}
 	if (acl) {
+		mode_t mode = 0;
+		int equiv_mode;
+
+		equiv_mode = acl_equiv_mode(acl, &mode);
+
 		if (acl_set_file(path_p, ACL_TYPE_ACCESS, acl) != 0) {
 			if (errno == ENOSYS || errno == ENOTSUP) {
-				int saved_errno = errno;
-				mode_t mode;
-
-				if (acl_equiv_mode(acl, &mode) != 0) {
-					errno = saved_errno;

Review do_set( · line 486

Quoted 10 lines
(quoted diff) +3 -1
+				if (equiv_mode != 0)
 					goto fail;
-				} else if (chmod(path_p, mode) != 0)
+				else if (chmod(path_p, mode) != 0)
 					goto fail;
 			} else
 				goto fail;
 		}
+		args->mode = mode;
 	}

These is a bit of a bug here. If the acl in do_set is the same as the current acl then we will never reach here and mode will remain 0.

restore() from setfacl.c will OR this 0 mode with the flags from the comment and chmod, obviously creating an incorrect result.

Here is the fix in restore()

New patch attached with this fix and a test case that will break with the original patch applied.

+ if (!args.mode)
+ args.mode = st.st_mode;
+ args.mode &= (S_IRWXU | S_IRWXG | S_IRWXO);
+ if (chmod(path_p, flags | args.mode) != 0) {
+ fprintf(stderr, _("%s: %s: Cannot change
"
+ "mode: %s\n"),
+ progname, xquote(path_p,
"\n\r"),
+ strerror(errno));
+ status = 1;
+ }
+ }

Cheers,

Brandon

Quoted 1 line

From 9186e94b12b5b9a4fde37cb54328259788c78b94 Mon Sep 17 00:00:00 2001

From: Brandon Philips <[email protected]>
Date: Mon, 22 Jun 2009 22:24:56 -0700
Subject: [PATCH] Include the S_ISUID, S_ISGID, S_ISVTX flags in the getfacl output, and restore them with "setfacl --restore=file".

---
 getfacl/getfacl.c       |   14 ++++++++++++
 setfacl/Makefile        |    2 +-
 setfacl/do_set.c        |   21 ++++++++++--------
 setfacl/parse.c         |   28 ++++++++++++++++++++++++-
 setfacl/parse.h         |    3 +-
 setfacl/setfacl.c       |   53 +++++++++++++++++++++++++++++++---------------
 test/sbits-restore.test |   22 +++++++++++++++++++
 7 files changed, 114 insertions(+), 29 deletions(-)
 create mode 100644 test/sbits-restore.test
getfacl/getfacl.c +14 -0
index fc650e3..e267414 100644
@@ -423,6 +423,18 @@ acl_get_file_mode(const char *path_p)
 	return acl_from_mode(st.st_mode);
 }
 
+static const char *
+flagstr(mode_t mode)
+{
+	static char str[4];
+
+	str[0] = (mode & S_ISUID) ? 's' : '-';
+	str[1] = (mode & S_ISGID) ? 's' : '-';
+	str[2] = (mode & S_ISVTX) ? 't' : '-';
+	str[3] = '\0';
+	return str;
+}
+
 int do_print(const char *path_p, const struct stat *st, int walk_flags, void *unused)
 {
 	const char *default_prefix = NULL;
@@ -498,6 +510,8 @@ int do_print(const char *path_p, const struct stat *st, int walk_flags, void *un
 			       xquote(user_name(st->st_uid, opt_numeric), " \t\n\r"));
 			printf("# group: %s\n",
 			       xquote(group_name(st->st_gid, opt_numeric), " \t\n\r"));
+			if (st->st_mode & (S_ISVTX | S_ISUID | S_ISGID))
+				printf("# flags: %s\n", flagstr(st->st_mode));
 		}
 		if (acl != NULL) {
 			char *acl_text = acl_to_any_text(acl, NULL, '\n',
setfacl/Makefile +1 -1
index 46b74d9..c44e7c0 100644
@@ -21,7 +21,7 @@ include $(TOPDIR)/include/builddefs
 
 LTCOMMAND = setfacl
 CFILES = setfacl.c do_set.c sequence.c parse.c
-HFILES = sequence.h parse.h
+HFILES = sequence.h parse.h do_set.h
 
 LLDLIBS = $(LIBMISC) $(LIBACL) $(LIBATTR)
 LTDEPENDENCIES = $(LIBMISC) $(LIBACL)
setfacl/do_set.c +12 -9
index b9c0ce7..3e7e982 100644
@@ -34,6 +34,7 @@
 #include <dirent.h>
 #include <ftw.h>
 #include "sequence.h"
+#include "do_set.h"
 #include "parse.h"
 #include "config.h"
 #include "walk_tree.h"
@@ -262,7 +263,7 @@ do_set(
 	int walk_flags,
 	void *arg)
 {
-	const seq_t seq = (const seq_t)arg;
+	struct do_set_args *args = arg;
 	acl_t old_acl = NULL, old_default_acl = NULL;
 	acl_t acl = NULL, default_acl = NULL;
 	acl_t *xacl, *old_xacl;
@@ -290,7 +291,7 @@ do_set(
 		return 0;
 
 	/* Execute the commands in seq (read ACLs on demand) */
-	error = seq_get_cmd(seq, SEQ_FIRST_CMD, &cmd);
+	error = seq_get_cmd(args->seq, SEQ_FIRST_CMD, &cmd);
 	if (error == 0)
 		return 0;
 	while (error == 1) {
@@ -357,7 +358,7 @@ do_set(
 				goto fail;
 		}
 
-		error = seq_get_cmd(seq, SEQ_NEXT_CMD, &cmd);
+		error = seq_get_cmd(args->seq, SEQ_NEXT_CMD, &cmd);
 	}
 
 	if (error < 0)
@@ -467,19 +468,21 @@ do_set(
 		goto cleanup;
 	}
 	if (acl) {
+		int equiv_mode;
+		mode_t mode = 0;
+
+		equiv_mode = acl_equiv_mode(acl, &mode);
+
 		if (acl_set_file(path_p, ACL_TYPE_ACCESS, acl) != 0) {
 			if (errno == ENOSYS || errno == ENOTSUP) {
-				int saved_errno = errno;
-				mode_t mode;
-
-				if (acl_equiv_mode(acl, &mode) != 0) {
-					errno = saved_errno;
+				if (equiv_mode != 0)
 					goto fail;
-				} else if (chmod(path_p, mode) != 0)
+				else if (chmod(path_p, mode) != 0)
 					goto fail;
 			} else
 				goto fail;
 		}
+		args->mode = mode;
 	}
 	if (default_acl) {
 		if (S_ISDIR(st->st_mode)) {
setfacl/parse.c +27 -1
index daa32e2..4df1a19 100644
@@ -410,7 +410,8 @@ read_acl_comments(
 	int *line,
 	char **path_p,
 	uid_t *uid_p,
-	gid_t *gid_p)
+	gid_t *gid_p,
+	mode_t *flags)
 {
 	int c;
 	/*
@@ -429,6 +430,8 @@ read_acl_comments(
 		*uid_p = ACL_UNDEFINED_ID;
 	if (gid_p)
 		*gid_p = ACL_UNDEFINED_ID;
+	if (flags)
+		*flags = 0;
 
 	for(;;) {
 		c = fgetc(file);
@@ -493,6 +496,29 @@ read_acl_comments(
 				if (get_gid(unquote(cp), gid_p) != 0)
 					continue;
 			}
+		} else if (strncmp(cp, "flags:", 6) == 0) {
+			mode_t f = 0;
+
+			cp += 6;
+			SKIP_WS(cp);
+
+			if (cp[0] == 's')
+				f |= S_ISUID;
+			else if (cp[0] != '-')
+				goto fail;
+			if (cp[1] == 's')
+				f |= S_ISGID;
+			else if (cp[1] != '-')
+				goto fail;
+			if (cp[2] == 't')
+				f |= S_ISVTX;
+			else if (cp[2] != '-')
+				goto fail;
+			if (cp[3] != '\0')
+				goto fail;
+
+			if (flags)
+				*flags = f;
 		}
 	}
 	if (ferror(file))
setfacl/parse.h +2 -1
index b6b7e01..b2e68b4 100644
@@ -64,7 +64,8 @@ read_acl_comments(
 	int *line,
 	char **path_p,
 	uid_t *uid_p,
-	gid_t *gid_p);
+	gid_t *gid_p,
+	mode_t *flags);
 int
 read_acl_seq(
 	FILE *file,
setfacl/setfacl.c +36 -17

121 lines not on this page · raw message

test/sbits-restore.test new file +22 -0
new file mode 100644
index 0000000..e5e4fb2
@@ -0,0 +1,22 @@
+Ensure setting of SUID/SGID/sticky via --restore works
+
+	$ umask 022
+	$ mkdir d
+	$ touch d/g
+	$ touch d/u
+	$ chmod u+s d/u
+	$ chmod g+s d/g
+	$ chmod +t d
+	$ getfacl -R d > d.acl
+	$ rm -R d
+	$ mkdir d
+	$ touch d/g
+	$ touch d/u
+	$ setfacl --restore d.acl
+	$ ls -dl d | awk '{print $1}'
+	> drwxr-xr-t
+	$ ls -dl d/u | awk '{print $1}'
+	> -rwSr--r--
+	$ ls -dl d/g | awk '{print $1}'
+	> -rw-r-Sr--
+	$ rm -Rf d
Signature · 1 line
1.6.2

_______________________________________________
xfs mailing list
[email protected]
http://oss.sgi.com/mailman/listinfo/xfs

#2 lore raw
Quoted 3 lines · On Tuesday 23 June 2009 07:32:21 Brandon Philips wrote:

Will need to put a block around the getfacl mode output in the POSIXLY_CORRECT case. Other than that SUID/SGID/sticky restore if the comment exists makes sense to me.

Alright, fine by me.

Quoted 2 lines

New patch attached with this fix and a test case that will break with the original patch applied.

Thanks, Brandon.

I have committed this to the master branch now, together with a man page update.

Cheers,
Andreas

_______________________________________________
xfs mailing list
[email protected]
http://oss.sgi.com/mailman/listinfo/xfs