Skip to contentSkip to conversation

cap_syslog: don't refuse cap_sys_admin for now (v3)

PATCH In review Serge E. Hallyn 4 participants 11 messages Feb 10, 2011 – Aug 8, 2011

lore
Share & tools
lore
#0 lore raw

PATCH 1/1 cap_syslog: don't refuse cap_sys_admin for now (v3)

In commit ce6ada35bdf710d16582cc4869c26722547e6f11, Serge messed up userspace backward compatibility. As Gergely pointed out, userspace which was doing the right thing and dropping all but cap_sys_admin before calling syslog is now breaking.

At 2.6.39 or 2.6.40, let's add a sysctl which defaults to 1. When 0, if the user has cap_sys_admin but no cap_syslog, return -EPERM. When 1 in that case, allow. Alternatively, as David pointed out, just leaving the behavior as below is still very useful.

Please apply.

---
 kernel/printk.c |   26 ++++++++++++++++----------
 1 files changed, 16 insertions(+), 10 deletions(-)
kernel/printk.c +16 -10
index 2ddbdc7..bc56386 100644
@@ -274,12 +274,24 @@ int do_syslog(int type, char __user *buf, int len, bool from_file)
 	 * at open time.
 	 */
 	if (type == SYSLOG_ACTION_OPEN || !from_file) {
-		if (dmesg_restrict && !capable(CAP_SYSLOG))
-			goto warn; /* switch to return -EPERM after 2.6.39 */
+		if (dmesg_restrict && !capable(CAP_SYSLOG)) {
+			/* remove after 2.6.39 */
+			if (capable(CAP_SYS_ADMIN))
+				WARN_ONCE(1, "Attempt to access syslog with CAP_SYS_ADMIN "
+				  "but no CAP_SYSLOG (deprecated).\n");
+			else
+				return -EPERM;
+		}
 		if ((type != SYSLOG_ACTION_READ_ALL &&
 		     type != SYSLOG_ACTION_SIZE_BUFFER) &&
-		    !capable(CAP_SYSLOG))
-			goto warn; /* switch to return -EPERM after 2.6.39 */
+		     !capable(CAP_SYSLOG)) {
+			/* remove after 2.6.39 */
+			if (capable(CAP_SYS_ADMIN))
+				WARN_ONCE(1, "Attempt to access syslog with CAP_SYS_ADMIN "
+				  "but no CAP_SYSLOG (deprecated).\n");
+			else
+				return -EPERM;
+		}
 	}
 
 	error = security_syslog(type);
@@ -423,12 +435,6 @@ int do_syslog(int type, char __user *buf, int len, bool from_file)
 	}
 out:
 	return error;
-warn:
-	/* remove after 2.6.39 */
-	if (capable(CAP_SYS_ADMIN))
-		WARN_ONCE(1, "Attempt to access syslog with CAP_SYS_ADMIN "
-		  "but no CAP_SYSLOG (deprecated and denied).\n");
-	return -EPERM;
 }
 
 SYSCALL_DEFINE3(syslog, int, type, char __user *, buf, int, len)
Signature · 1 line
1.7.2.3
#1 lore raw
Quoted 2 lines · Serge E. Hallyn

Please apply.

Hmm. So I detest the duplication and the ugly resulting code. It was a bit hard to follow before, now it's just nasty.

Why not make this all into some nicer helper functions instead - something simple like the attached?

UNTESTED! I'm not going to apply it unless I get acks/tested-by's.

Linus

 kernel/printk.c |   54 +++++++++++++++++++++++++++++++++++-------------------
 1 files changed, 35 insertions(+), 19 deletions(-)
kernel/printk.c +35 -19
index 2ddbdc7..f51214c 100644
@@ -262,25 +262,47 @@ int dmesg_restrict = 1;
 int dmesg_restrict;
 #endif
 
+static int syslog_action_restricted(int type)
+{
+	if (dmesg_restrict)
+		return 1;
+	/* Unless restricted, we allow "read all" and "get buffer size" for everybody */
+	return type != SYSLOG_ACTION_READ_ALL && type != SYSLOG_ACTION_SIZE_BUFFER;
+}
+
+static int check_syslog_permissions(int type, bool from_file)
+{
+	/*
+	 * If this is from /proc/kmsg and we've already opened it, then we've
+	 * already the capabilities checks at open time.
+	 */
+	if (from_file && type != SYSLOG_ACTION_OPEN)
+		return 0;
+
+	if (syslog_action_restricted(type)) {
+		if (capable(CAP_SYSLOG))
+			return 0;
+		/* For historical reasons, accept CAP_SYS_ADMIN too, with a warning */
+		if (capable(CAP_SYS_ADMIN)) {
+			WARN_ONCE(1, "Attempt to access syslog with CAP_SYS_ADMIN "
+				 "but no CAP_SYSLOG (deprecated).\n");
+			return 0;
+		}
+		return -EPERM;
+	}
+	return 0;
+}
+
 int do_syslog(int type, char __user *buf, int len, bool from_file)
 {
 	unsigned i, j, limit, count;
 	int do_clear = 0;
 	char c;
-	int error = 0;
+	int error;
 
-	/*
-	 * If this is from /proc/kmsg we only do the capabilities checks
-	 * at open time.
-	 */
-	if (type == SYSLOG_ACTION_OPEN || !from_file) {
-		if (dmesg_restrict && !capable(CAP_SYSLOG))
-			goto warn; /* switch to return -EPERM after 2.6.39 */
-		if ((type != SYSLOG_ACTION_READ_ALL &&
-		     type != SYSLOG_ACTION_SIZE_BUFFER) &&
-		    !capable(CAP_SYSLOG))
-			goto warn; /* switch to return -EPERM after 2.6.39 */
-	}
+	error = check_syslog_permissions(type, from_file);
+	if (error)
+		goto out;
 
 	error = security_syslog(type);
 	if (error)
@@ -423,12 +445,6 @@ int do_syslog(int type, char __user *buf, int len, bool from_file)
 	}
 out:
 	return error;
-warn:
-	/* remove after 2.6.39 */
-	if (capable(CAP_SYS_ADMIN))
-		WARN_ONCE(1, "Attempt to access syslog with CAP_SYS_ADMIN "
-		  "but no CAP_SYSLOG (deprecated and denied).\n");
-	return -EPERM;
 }
 
 SYSCALL_DEFINE3(syslog, int, type, char __user *, buf, int, len)
author Acked-by
#2 lore raw
Quoted 7 lines · Linus Torvalds
Quoted 2 lines · Serge E. Hallyn

Please apply.

Hmm. So I detest the duplication and the ugly resulting code. It was a bit hard to follow before, now it's just nasty.

Quoted 6 lines

Why not make this all into some nicer helper functions instead - something simple like the attached?

UNTESTED! I'm not going to apply it unless I get acks/tested-by's.

Linus

It looks correct (and much nicer indeed)

Setting up a box to actually test on...

thanks,
-serge

Quoted 81 lines
 kernel/printk.c |   54 +++++++++++++++++++++++++++++++++++-------------------
 1 files changed, 35 insertions(+), 19 deletions(-)
kernel/printk.c +35 -19
index 2ddbdc7..f51214c 100644
@@ -262,25 +262,47 @@ int dmesg_restrict = 1;
 int dmesg_restrict;
 #endif
 
+static int syslog_action_restricted(int type)
+{
+	if (dmesg_restrict)
+		return 1;
+	/* Unless restricted, we allow "read all" and "get buffer size" for everybody */
+	return type != SYSLOG_ACTION_READ_ALL && type != SYSLOG_ACTION_SIZE_BUFFER;
+}
+
+static int check_syslog_permissions(int type, bool from_file)
+{
+	/*
+	 * If this is from /proc/kmsg and we've already opened it, then we've
+	 * already the capabilities checks at open time.
+	 */
+	if (from_file && type != SYSLOG_ACTION_OPEN)
+		return 0;
+
+	if (syslog_action_restricted(type)) {
+		if (capable(CAP_SYSLOG))
+			return 0;
+		/* For historical reasons, accept CAP_SYS_ADMIN too, with a warning */
+		if (capable(CAP_SYS_ADMIN)) {
+			WARN_ONCE(1, "Attempt to access syslog with CAP_SYS_ADMIN "
+				 "but no CAP_SYSLOG (deprecated).\n");
+			return 0;
+		}
+		return -EPERM;
+	}
+	return 0;
+}
+
 int do_syslog(int type, char __user *buf, int len, bool from_file)
 {
 	unsigned i, j, limit, count;
 	int do_clear = 0;
 	char c;
-	int error = 0;
+	int error;
 
-	/*
-	 * If this is from /proc/kmsg we only do the capabilities checks
-	 * at open time.
-	 */
-	if (type == SYSLOG_ACTION_OPEN || !from_file) {
-		if (dmesg_restrict && !capable(CAP_SYSLOG))
-			goto warn; /* switch to return -EPERM after 2.6.39 */
-		if ((type != SYSLOG_ACTION_READ_ALL &&
-		     type != SYSLOG_ACTION_SIZE_BUFFER) &&
-		    !capable(CAP_SYSLOG))
-			goto warn; /* switch to return -EPERM after 2.6.39 */
-	}
+	error = check_syslog_permissions(type, from_file);
+	if (error)
+		goto out;
 
 	error = security_syslog(type);
 	if (error)
@@ -423,12 +445,6 @@ int do_syslog(int type, char __user *buf, int len, bool from_file)
 	}
 out:
 	return error;
-warn:
-	/* remove after 2.6.39 */
-	if (capable(CAP_SYS_ADMIN))
-		WARN_ONCE(1, "Attempt to access syslog with CAP_SYS_ADMIN "
-		  "but no CAP_SYSLOG (deprecated and denied).\n");
-	return -EPERM;
 }
 
 SYSCALL_DEFINE3(syslog, int, type, char __user *, buf, int, len)
Reviewed-by
#3 lore raw
In reply to Linus Torvalds
Quoted 11 lines · Linus Torvalds
Quoted 2 lines · Serge E. Hallyn

Please apply.

Hmm. So I detest the duplication and the ugly resulting code. It was a bit hard to follow before, now it's just nasty.

Why not make this all into some nicer helper functions instead - something simple like the attached?

UNTESTED! I'm not going to apply it unless I get acks/tested-by's.

Signature · 2 lines
James Morris
<[email protected]>
author Acked-by Serge Hallyn Tested-by Serge Hallyn
#4 lore raw
In reply to Linus Torvalds
Quoted 11 lines · Linus Torvalds
Quoted 2 lines · Serge E. Hallyn

Please apply.

Hmm. So I detest the duplication and the ugly resulting code. It was a bit hard to follow before, now it's just nasty.

Why not make this all into some nicer helper functions instead - something simple like the attached?

UNTESTED! I'm not going to apply it unless I get acks/tested-by's.

I tested using klogctl with actions 10, 3 and 5. Without dmesg_restrict set, an unprivileged program could get the size and read-all, but not clear the buffer. With either CAP_SYS_ADMIN or CAP_SYSLOG, it could do all three. With dmesg_restrict set, the capabilities were needed for all actions.

Worked perfectly.

thanks,
-serge

Quoted 83 lines

Linus

 kernel/printk.c |   54 +++++++++++++++++++++++++++++++++++-------------------
 1 files changed, 35 insertions(+), 19 deletions(-)
kernel/printk.c +35 -19
index 2ddbdc7..f51214c 100644
@@ -262,25 +262,47 @@ int dmesg_restrict = 1;
 int dmesg_restrict;
 #endif
 
+static int syslog_action_restricted(int type)
+{
+	if (dmesg_restrict)
+		return 1;
+	/* Unless restricted, we allow "read all" and "get buffer size" for everybody */
+	return type != SYSLOG_ACTION_READ_ALL && type != SYSLOG_ACTION_SIZE_BUFFER;
+}
+
+static int check_syslog_permissions(int type, bool from_file)
+{
+	/*
+	 * If this is from /proc/kmsg and we've already opened it, then we've
+	 * already the capabilities checks at open time.
+	 */
+	if (from_file && type != SYSLOG_ACTION_OPEN)
+		return 0;
+
+	if (syslog_action_restricted(type)) {
+		if (capable(CAP_SYSLOG))
+			return 0;
+		/* For historical reasons, accept CAP_SYS_ADMIN too, with a warning */
+		if (capable(CAP_SYS_ADMIN)) {
+			WARN_ONCE(1, "Attempt to access syslog with CAP_SYS_ADMIN "
+				 "but no CAP_SYSLOG (deprecated).\n");
+			return 0;
+		}
+		return -EPERM;
+	}
+	return 0;
+}
+
 int do_syslog(int type, char __user *buf, int len, bool from_file)
 {
 	unsigned i, j, limit, count;
 	int do_clear = 0;
 	char c;
-	int error = 0;
+	int error;
 
-	/*
-	 * If this is from /proc/kmsg we only do the capabilities checks
-	 * at open time.
-	 */
-	if (type == SYSLOG_ACTION_OPEN || !from_file) {
-		if (dmesg_restrict && !capable(CAP_SYSLOG))
-			goto warn; /* switch to return -EPERM after 2.6.39 */
-		if ((type != SYSLOG_ACTION_READ_ALL &&
-		     type != SYSLOG_ACTION_SIZE_BUFFER) &&
-		    !capable(CAP_SYSLOG))
-			goto warn; /* switch to return -EPERM after 2.6.39 */
-	}
+	error = check_syslog_permissions(type, from_file);
+	if (error)
+		goto out;
 
 	error = security_syslog(type);
 	if (error)
@@ -423,12 +445,6 @@ int do_syslog(int type, char __user *buf, int len, bool from_file)
 	}
 out:
 	return error;
-warn:
-	/* remove after 2.6.39 */
-	if (capable(CAP_SYS_ADMIN))
-		WARN_ONCE(1, "Attempt to access syslog with CAP_SYS_ADMIN "
-		  "but no CAP_SYSLOG (deprecated and denied).\n");
-	return -EPERM;
 }
 
 SYSCALL_DEFINE3(syslog, int, type, char __user *, buf, int, len)
#5 lore raw

[PATCH/RFC] cap_syslog: make CAP_SYS_ADMIN deprecation notice less alarming

In reply to Linus Torvalds

syslog-ng versions in active use assume that CAP_SYS_ADMIN is sufficent to access syslog. As a result, ever since CAP_SYSLOG was introduce in v2.6.38-rc1~429^2~18 (security: Define CAP_SYSLOG, 2010-11-25), they have triggered a warning, complete with kernel backtrace.

v2.6.38-rc5~46 (cap_syslog: accept CAP_SYS_ADMIN for now, 2011-02-10) made things a little better by removing the regression in behavior, just keeping the WARN_ONCE. But still, this is a warning that adds many lines to syslog, sets a taint flag, and alarms sysadmins when nothing worse has happened than use of an old userspace with a recent kernel.

Convert the WARN_ONCE to a printk_once to avoid this while continuing to give userspace developers a hint that this is an unwanted backward-compatibility feature and won't be around forever.

---
Hi,

Nothing urgent about this, but it seems to be a frequently[1] reported[2] source of unnecessary worry. Thoughts?

[1] http://thread.gmane.org/gmane.linux.kernel/1145040
[2] http://thread.gmane.org/gmane.linux.kernel/1153808

Context: http://bugs.debian.org/cgi-bin/bugreport.cgi?msg=32;bug=636501

 kernel/printk.c |    3 ++-
 1 files changed, 2 insertions(+), 1 deletions(-)
kernel/printk.c +2 -1
index 37dff342..db64c951 100644
@@ -318,7 +318,8 @@ static int check_syslog_permissions(int type, bool from_file)
 			return 0;
 		/* For historical reasons, accept CAP_SYS_ADMIN too, with a warning */
 		if (capable(CAP_SYS_ADMIN)) {
-			WARN_ONCE(1, "Attempt to access syslog with CAP_SYS_ADMIN "
+			printk_once(KERN_WARNING
+				 "Attempt to access syslog with CAP_SYS_ADMIN "
 				 "but no CAP_SYSLOG (deprecated).\n");
 			return 0;
 		}
Signature · 1 line
1.7.6
#6 lore raw

Re: [PATCH/RFC] cap_syslog: make CAP_SYS_ADMIN deprecation notice less alarming

Quoted 3 lines · Jonathan Nieder

Convert the WARN_ONCE to a printk_once to avoid this while continuing to give userspace developers a hint that this is an unwanted backward-compatibility feature and won't be around forever.

Seems reasonable to me.

Signature · 2 lines
James Morris
<[email protected]>
author Acked-by
#7 lore raw

Re: [PATCH/RFC] cap_syslog: make CAP_SYS_ADMIN deprecation notice less alarming

In reply to Jonathan Nieder
Quoted 10 lines · Jonathan Nieder

syslog-ng versions in active use assume that CAP_SYS_ADMIN is sufficent to access syslog. As a result, ever since CAP_SYSLOG was introduce in v2.6.38-rc1~429^2~18 (security: Define CAP_SYSLOG, 2010-11-25), they have triggered a warning, complete with kernel backtrace.

v2.6.38-rc5~46 (cap_syslog: accept CAP_SYS_ADMIN for now, 2011-02-10) made things a little better by removing the regression in behavior, just keeping the WARN_ONCE. But still, this is a warning that adds many lines to syslog, sets a taint flag, and alarms sysadmins when

Sets the taint flag? That's a bit over the top, so:

Quoted 7 lines

nothing worse has happened than use of an old userspace with a recent kernel.

Convert the WARN_ONCE to a printk_once to avoid this while continuing to give userspace developers a hint that this is an unwanted backward-compatibility feature and won't be around forever.

Quoted 5 lines

thanks,
-serge

Quoted 31 lines

---
Hi,

Nothing urgent about this, but it seems to be a frequently[1] reported[2] source of unnecessary worry. Thoughts?

[1] http://thread.gmane.org/gmane.linux.kernel/1145040
[2] http://thread.gmane.org/gmane.linux.kernel/1153808

Context: http://bugs.debian.org/cgi-bin/bugreport.cgi?msg=32;bug=636501

 kernel/printk.c |    3 ++-
 1 files changed, 2 insertions(+), 1 deletions(-)
kernel/printk.c +2 -1
index 37dff342..db64c951 100644
@@ -318,7 +318,8 @@ static int check_syslog_permissions(int type, bool from_file)
 			return 0;
 		/* For historical reasons, accept CAP_SYS_ADMIN too, with a warning */
 		if (capable(CAP_SYS_ADMIN)) {
-			WARN_ONCE(1, "Attempt to access syslog with CAP_SYS_ADMIN "
+			printk_once(KERN_WARNING
+				 "Attempt to access syslog with CAP_SYS_ADMIN "
 				 "but no CAP_SYSLOG (deprecated).\n");
 			return 0;
 		}
Signature · 1 line
1.7.6
#8 lore raw

Re: [PATCH/RFC] cap_syslog: make CAP_SYS_ADMIN deprecation notice less alarming

In reply to Jonathan Nieder
Quoted 3 lines · Jonathan Nieder

Convert the WARN_ONCE to a printk_once to avoid this while continuing to give userspace developers a hint that this is an unwanted backward-compatibility feature and won't be around forever.

Applied to
git://git.kernel.org/pub/scm/linux/kernel/git/jmorris/security-testing-2.6#next-queue

Signature · 2 lines
James Morris
<[email protected]>
#9 lore raw

Re: [PATCH/RFC] cap_syslog: make CAP_SYS_ADMIN deprecation notice less alarming

In reply to Jonathan Nieder
Quoted 4 lines · Jonathan Nieder

Convert the WARN_ONCE to a printk_once to avoid this while continuing to give userspace developers a hint that this is an unwanted backward-compatibility feature and won't be around forever.

The problem with this one is that converting away from a warning doesn't just get rid of all the ugliness, it gets rid of some of the *useful* information in the warning too.

In particular, when you have a warning like "Attempting to access syslog", you'd better tell people *who* is attempting to access syslog, so that they can fix it or complain to the right person.

And the warning does show what process it is (in addition to stacktrace, tainting etc that we don't want). Your printk_once() conversion does not.

Linus

#10 lore raw

PATCH v2 1/1 cap_syslog: don't use WARN_ONCE for CAP_SYS_ADMIN deprecation warning

syslog-ng versions before 3.3.0beta1 (2011-05-12) assume that CAP_SYS_ADMIN is sufficient to access syslog, so ever since CAP_SYSLOG was introduced (2010-11-25) they have triggered a warning. v2.6.38-rc5~46 (cap_syslog: accept CAP_SYS_ADMIN for now, 2011-02-10) improved matters a little by making syslog-ng work again, just keeping the WARN_ONCE(). But still, this is a warning that writes a stack trace we don't care about to syslog, sets a taint flag, and alarms sysadmins when nothing worse has happened than use of an old userspace with a recent kernel.

Convert the WARN_ONCE to a printk_once to avoid that while continuing to give userspace developers a hint that this is an unwanted backward-compatibility feature and won't be around forever.

---

Quoted 7 lines · Linus Torvalds

The problem with this one is that converting away from a warning doesn't just get rid of all the ugliness, it gets rid of some of the *useful* information in the warning too.

In particular, when you have a warning like "Attempting to access syslog", you'd better tell people *who* is attempting to access syslog, so that they can fix it or complain to the right person.

True. I can't imagine people wanting to know much more than the process's command line and pid, which is basically what the WARN_ONCE gives (though it also gives registers). Here's an updated patch.

I've carried over the acks from last time; hopefully that's okay. Thanks for your help.

 kernel/printk.c |    6 ++++--
 1 files changed, 4 insertions(+), 2 deletions(-)
kernel/printk.c +4 -2
index 37dff342..836a2ae0 100644
@@ -318,8 +318,10 @@ static int check_syslog_permissions(int type, bool from_file)
 			return 0;
 		/* For historical reasons, accept CAP_SYS_ADMIN too, with a warning */
 		if (capable(CAP_SYS_ADMIN)) {
-			WARN_ONCE(1, "Attempt to access syslog with CAP_SYS_ADMIN "
-				 "but no CAP_SYSLOG (deprecated).\n");
+			printk_once(KERN_WARNING "%s (%d): "
+				 "Attempt to access syslog with CAP_SYS_ADMIN "
+				 "but no CAP_SYSLOG (deprecated).\n",
+				 current->comm, task_pid_nr(current));
 			return 0;
 		}
 		return -EPERM;
Signature · 1 line
1.7.6