Skip to contentSkip to conversation

make call_usermodehelper a bit more "safe"

PATCH In review Greg KH 4 participants 19 messages Jan 16, 2017 – Jan 19, 2017

Subsystems (9)
lore
Share & tools
lore

Greg KH

author maintainer
#0 lore raw

cover make call_usermodehelper a bit more "safe"

Hi all,

Here's a second cut at my attempt to make call_usermodehelper a bit more "safe". It includes some patches from my previous series, and one new one. In all, this is a much smaller patchset, with better functionality in the end.

The issue is that if you end up getting write access to kernel memory, if you change the string '/sbin/hotplug' to point to '/home/hacked/my_binary', then the next uevent that the system makes will call this binary instead of the "trusted" one.

This series addresses this issue by doing two different things. The first 2 patches move a lot of existing call_usermodehelper binaries to read-only memory, preventing them from being able to be changed at all.

The last patch introduces a new configuration option, STATIC_USERMODEHELPER. This option routes all call_usermodehelper() calls to a single userspace binary. That binary can then filter/mediate/blacklist/whitelist/whatever the "real" usermodehelper binaries and call them as needed (it determines the real one by looking at the first argument.)

The location of this new binary can be set with the STATIC_USERMODEHELPER_PATH configuration option.

If the user wants call_usermodehelper() to be disabled entirely, STATIC_USERMODEHELPER_PATH can be set to "", which will cause all call_usermodehelper() calls to do nothing, but return successful.

Many thanks to the reviewers of the last patch series for their hints on how to mark strings properly to live in read-only memory always, and to Neil Brown for the idea of STATIC_USERMODEHELPER.

If there are no complaints about these patches, I'll take them through my driver-core tree.

thanks,

greg k-h

author maintainer
#1 lore raw

PATCH 1/3 kmod: make usermodehelper path a const string

From: Greg Kroah-Hartman <[email protected]>

This is in preparation for making it so that usermode helper programs can't be changed, if desired, by userspace. We will tackle the mess of cleaning up the write-ability of argv and env later, that's going to take more work, for much less gain...

---
 include/linux/kmod.h | 7 ++++---
 kernel/kmod.c        | 4 ++--
 2 files changed, 6 insertions(+), 5 deletions(-)
include/linux/kmod.h +4 -3
index fcfd2bf14d3f..c4e441e00db5 100644
@@ -56,7 +56,7 @@ struct file;
 struct subprocess_info {
 	struct work_struct work;
 	struct completion *complete;
-	char *path;
+	const char *path;
 	char **argv;
 	char **envp;
 	int wait;
@@ -67,10 +67,11 @@ struct subprocess_info {
 };
 
 extern int
-call_usermodehelper(char *path, char **argv, char **envp, int wait);
+call_usermodehelper(const char *path, char **argv, char **envp, int wait);
 
 extern struct subprocess_info *
-call_usermodehelper_setup(char *path, char **argv, char **envp, gfp_t gfp_mask,
+call_usermodehelper_setup(const char *path, char **argv, char **envp,
+			  gfp_t gfp_mask,
 			  int (*init)(struct subprocess_info *info, struct cred *new),
 			  void (*cleanup)(struct subprocess_info *), void *data);
 
kernel/kmod.c +2 -2
index d45c96073afb..426a614e97fe 100644
@@ -516,7 +516,7 @@ static void helper_unlock(void)
  * Function must be runnable in either a process context or the
  * context in which call_usermodehelper_exec is called.
  */
-struct subprocess_info *call_usermodehelper_setup(char *path, char **argv,
+struct subprocess_info *call_usermodehelper_setup(const char *path, char **argv,
 		char **envp, gfp_t gfp_mask,
 		int (*init)(struct subprocess_info *info, struct cred *new),
 		void (*cleanup)(struct subprocess_info *info),
@@ -613,7 +613,7 @@ EXPORT_SYMBOL(call_usermodehelper_exec);
  * This function is the equivalent to use call_usermodehelper_setup() and
  * call_usermodehelper_exec().
  */
-int call_usermodehelper(char *path, char **argv, char **envp, int wait)
+int call_usermodehelper(const char *path, char **argv, char **envp, int wait)
 {
 	struct subprocess_info *info;
 	gfp_t gfp_mask = (wait == UMH_NO_WAIT) ? GFP_ATOMIC : GFP_KERNEL;
Signature · 1 line
2.11.0
author maintainer
#2 lore raw

PATCH 2/3 Make static usermode helper binaries constant

In reply to Greg KH

From: Greg Kroah-Hartman <[email protected]>

There are a number of usermode helper binaries that are "hard coded" in the kernel today, so mark them as "const" to make it harder for someone to change where the variables point to.

Cc Benjamin Herrenschmidt <[email protected]>
Cc Thomas Sailer <[email protected]>
Cc "Rafael J. Wysocki" <[email protected]>
Cc Johan Hovold <[email protected]>
Cc Alex Elder <[email protected]>
Cc "J. Bruce Fields" <[email protected]>
Cc Jeff Layton <[email protected]>
Cc David Howells <[email protected]>
---
 drivers/macintosh/windfarm_core.c          |  4 ++--
 drivers/net/hamradio/baycom_epp.c          | 10 +++++++---
 drivers/pnp/pnpbios/core.c                 |  5 +++--
 drivers/staging/greybus/svc_watchdog.c     |  4 ++--
 drivers/staging/rtl8192e/rtl8192e/rtl_dm.c |  8 ++++----
 fs/nfsd/nfs4layouts.c                      |  6 ++++--
 security/keys/request_key.c                |  7 ++++---
 7 files changed, 26 insertions(+), 18 deletions(-)
drivers/macintosh/windfarm_core.c +2 -2
index 465d770ab0bb..5e013d781a74 100644
@@ -74,8 +74,8 @@ static inline void wf_notify(int event, void *param)
 
 static int wf_critical_overtemp(void)
 {
-	static char * critical_overtemp_path = "/sbin/critical_overtemp";
-	char *argv[] = { critical_overtemp_path, NULL };
+	static char const critical_overtemp_path[] = "/sbin/critical_overtemp";
+	char *argv[] = { (char *)critical_overtemp_path, NULL };
 	static char *envp[] = { "HOME=/",
 				"TERM=linux",
 				"PATH=/sbin:/usr/sbin:/bin:/usr/bin",
drivers/net/hamradio/baycom_epp.c +7 -3
index 7d054697b199..594fa1407e29 100644
@@ -299,7 +299,7 @@ static inline void baycom_int_freq(struct baycom_state *bc)
  *    eppconfig_path should be setable  via /proc/sys.
  */
 
-static char eppconfig_path[256] = "/usr/sbin/eppfpga";
+static char const eppconfig_path[] = "/usr/sbin/eppfpga";
 
 static char *envp[] = { "HOME=/", "TERM=linux", "PATH=/usr/bin:/bin", NULL };
 
@@ -308,8 +308,12 @@ static int eppconfig(struct baycom_state *bc)
 {
 	char modearg[256];
 	char portarg[16];
-        char *argv[] = { eppconfig_path, "-s", "-p", portarg, "-m", modearg,
-			 NULL };
+        char *argv[] = {
+		(char *)eppconfig_path,
+		"-s",
+		"-p", portarg,
+		"-m", modearg,
+		NULL };
 
 	/* set up arguments */
 	sprintf(modearg, "%sclk,%smodem,fclk=%d,bps=%d,divider=%d%s,extstat",
drivers/pnp/pnpbios/core.c +3 -2
index c38a5b9733c8..0ced908e7aa8 100644
@@ -98,6 +98,7 @@ static struct completion unload_sem;
  */
 static int pnp_dock_event(int dock, struct pnp_docking_station_info *info)
 {
+	static char const sbin_pnpbios[] = "/sbin/pnpbios";
 	char *argv[3], **envp, *buf, *scratch;
 	int i = 0, value;
 
@@ -112,7 +113,7 @@ static int pnp_dock_event(int dock, struct pnp_docking_station_info *info)
 	 * integrated into the driver core and use the usual infrastructure
 	 * like sysfs and uevents
 	 */
-	argv[0] = "/sbin/pnpbios";
+	argv[0] = (char *)sbin_pnpbios;
 	argv[1] = "dock";
 	argv[2] = NULL;
 
@@ -139,7 +140,7 @@ static int pnp_dock_event(int dock, struct pnp_docking_station_info *info)
 			   info->location_id, info->serial, info->capabilities);
 	envp[i] = NULL;
 
-	value = call_usermodehelper(argv [0], argv, envp, UMH_WAIT_EXEC);
+	value = call_usermodehelper(sbin_pnpbios, argv, envp, UMH_WAIT_EXEC);
 	kfree(buf);
 	kfree(envp);
 	return 0;
drivers/staging/greybus/svc_watchdog.c +2 -2
index 3729460fb954..12cef5c06e27 100644
@@ -44,14 +44,14 @@ static int svc_watchdog_pm_notifier(struct notifier_block *notifier,
 
 static void greybus_reset(struct work_struct *work)
 {
-	static char start_path[256] = "/system/bin/start";
+	static char const start_path[] = "/system/bin/start";
 	static char *envp[] = {
 		"HOME=/",
 		"PATH=/sbin:/vendor/bin:/system/sbin:/system/bin:/system/xbin",
 		NULL,
 	};
 	static char *argv[] = {
-		start_path,
+		(char *)start_path,
 		"unipro_reset",
 		NULL,
 	};
drivers/staging/rtl8192e/rtl8192e/rtl_dm.c +4 -4
index 9bc284812c30..dbb58fb16482 100644
@@ -268,8 +268,8 @@ void rtl92e_dm_watchdog(struct net_device *dev)
 static void _rtl92e_dm_check_ac_dc_power(struct net_device *dev)
 {
 	struct r8192_priv *priv = rtllib_priv(dev);
-	static char *ac_dc_script = "/etc/acpi/wireless-rtl-ac-dc-power.sh";
-	char *argv[] = {ac_dc_script, DRV_NAME, NULL};
+	static char const ac_dc_script[] = "/etc/acpi/wireless-rtl-ac-dc-power.sh";
+	char *argv[] = {(char *)ac_dc_script, DRV_NAME, NULL};
 	static char *envp[] = {"HOME=/",
 			"TERM=linux",
 			"PATH=/usr/bin:/bin",
@@ -1823,7 +1823,7 @@ static void _rtl92e_dm_check_rf_ctrl_gpio(void *data)
 	enum rt_rf_power_state eRfPowerStateToSet;
 	bool bActuallySet = false;
 	char *argv[3];
-	static char *RadioPowerPath = "/etc/acpi/events/RadioPower.sh";
+	static char const RadioPowerPath[] = "/etc/acpi/events/RadioPower.sh";
 	static char *envp[] = {"HOME=/", "TERM=linux", "PATH=/usr/bin:/bin",
 			       NULL};
 
@@ -1862,7 +1862,7 @@ static void _rtl92e_dm_check_rf_ctrl_gpio(void *data)
 		else
 			argv[1] = "RFON";
 
-		argv[0] = RadioPowerPath;
+		argv[0] = (char *)RadioPowerPath;
 		argv[2] = NULL;
 		call_usermodehelper(RadioPowerPath, argv, envp, UMH_WAIT_PROC);
 	}
fs/nfsd/nfs4layouts.c +4 -2
index 596205d939a1..e06a4ae5f3ad 100644
@@ -613,6 +613,7 @@ nfsd4_cb_layout_fail(struct nfs4_layout_stateid *ls)
 {
 	struct nfs4_client *clp = ls->ls_stid.sc_client;
 	char addr_str[INET6_ADDRSTRLEN];
+	static char const nfsd_recall_failed[] = "/sbin/nfsd-recall-failed";
 	static char *envp[] = {
 		"HOME=/",
 		"TERM=linux",
@@ -628,12 +629,13 @@ nfsd4_cb_layout_fail(struct nfs4_layout_stateid *ls)
 		"nfsd: client %s failed to respond to layout recall. "
 		"  Fencing..\n", addr_str);
 
-	argv[0] = "/sbin/nfsd-recall-failed";
+	argv[0] = (char *)nfsd_recall_failed;
 	argv[1] = addr_str;
 	argv[2] = ls->ls_file->f_path.mnt->mnt_sb->s_id;
 	argv[3] = NULL;
 
-	error = call_usermodehelper(argv[0], argv, envp, UMH_WAIT_PROC);
+	error = call_usermodehelper(nfsd_recall_failed, argv, envp,
+				    UMH_WAIT_PROC);
 	if (error) {
 		printk(KERN_ERR "nfsd: fence failed for client %s: %d!\n",
 			addr_str, error);
security/keys/request_key.c +4 -3
index 43affcf10b22..9822e500d50d 100644
@@ -72,7 +72,7 @@ static void umh_keys_cleanup(struct subprocess_info *info)
 /*
  * Call a usermode helper with a specific session keyring.
  */
-static int call_usermodehelper_keys(char *path, char **argv, char **envp,
+static int call_usermodehelper_keys(const char *path, char **argv, char **envp,
 					struct key *session_keyring, int wait)
 {
 	struct subprocess_info *info;
@@ -95,6 +95,7 @@ static int call_sbin_request_key(struct key_construction *cons,
 				 const char *op,
 				 void *aux)
 {
+	static char const request_key[] = "/sbin/request-key";
 	const struct cred *cred = current_cred();
 	key_serial_t prkey, sskey;
 	struct key *key = cons->key, *authkey = cons->authkey, *keyring,
@@ -161,7 +162,7 @@ static int call_sbin_request_key(struct key_construction *cons,
 
 	/* set up the argument list */
 	i = 0;
-	argv[i++] = "/sbin/request-key";
+	argv[i++] = (char *)request_key;
 	argv[i++] = (char *) op;
 	argv[i++] = key_str;
 	argv[i++] = uid_str;
@@ -172,7 +173,7 @@ static int call_sbin_request_key(struct key_construction *cons,
 	argv[i] = NULL;
 
 	/* do it */
-	ret = call_usermodehelper_keys(argv[0], argv, envp, keyring,
+	ret = call_usermodehelper_keys(request_key, argv, envp, keyring,
 				       UMH_WAIT_PROC);
 	kdebug("usermode -> 0x%x", ret);
 	if (ret >= 0) {
Signature · 1 line
2.11.0
#3 lore raw

Re: [PATCH 2/3] Make static usermode helper binaries constant

Quoted 6 lines · Greg KH

From: Greg Kroah-Hartman <[email protected]>

There are a number of usermode helper binaries that are "hard coded" in the kernel today, so mark them as "const" to make it harder for someone to change where the variables point to.

...

Quoted 11 lines
drivers/pnp/pnpbios/core.c +1 -0
@@ -98,6 +98,7 @@ static struct completion unload_sem;
  */
 static int pnp_dock_event(int dock, struct pnp_docking_station_info *info)
 {
+	static char const sbin_pnpbios[] = "/sbin/pnpbios";
 	char *argv[3], **envp, *buf, *scratch;
 	int i = 0, value;
 

drivers/pnp/pnpbios/core.c static int pnp_dock_event(int dock, struct pnp_docking_station_info *info) · line 116

Quoted 5 lines
drivers/pnp/pnpbios/core.c +1 -1
@@ -112,7 +113,7 @@ static int pnp_dock_event(int dock, struct pnp_docking_station_info *info)
 	 * integrated into the driver core and use the usual infrastructure
 	 * like sysfs and uevents
 	 */
-	argv[0] = "/sbin/pnpbios";
+	argv[0] = (char *)sbin_pnpbios;

So here and elsewhere, can attackers write to argv[0] instead of to the memory where the string lives?

Apologies if I'm rehashing earlier discussion, I did a quick search of archives but could easily have missed something.

--b.

author maintainer
#4 lore raw

Re: [PATCH 2/3] Make static usermode helper binaries constant

Quoted 18 lines · J. Bruce Fields
Quoted 6 lines · Greg KH

From: Greg Kroah-Hartman <[email protected]>

There are a number of usermode helper binaries that are "hard coded" in the kernel today, so mark them as "const" to make it harder for someone to change where the variables point to.

...

Quoted 10 lines
drivers/pnp/pnpbios/core.c +1 -0
@@ -98,6 +98,7 @@ static struct completion unload_sem;
  */
 static int pnp_dock_event(int dock, struct pnp_docking_station_info *info)
 {
+	static char const sbin_pnpbios[] = "/sbin/pnpbios";
 	char *argv[3], **envp, *buf, *scratch;
 	int i = 0, value;
 
Quoted 9 lines

drivers/pnp/pnpbios/core.c static int pnp_dock_event(int dock, struct pnp_docking_station_info *info) · line 116

Quoted 6 lines
drivers/pnp/pnpbios/core.c +1 -1
@@ -112,7 +113,7 @@ static int pnp_dock_event(int dock, struct pnp_docking_station_info *info)
 	 * integrated into the driver core and use the usual infrastructure
 	 * like sysfs and uevents
 	 */
-	argv[0] = "/sbin/pnpbios";
+	argv[0] = (char *)sbin_pnpbios;

So here and elsewhere, can attackers write to argv[0] instead of to the memory where the string lives?

Yes, they could, it would be a very "tight" race to do that (have to write after the assignment and before the call_usermodehelper_exec() runs). However, the kernel does not run argv[0], it just passes it to the binary you specify in path, so for this example, the correct program would still be run by the kernel.

But, if you do worry about this type of attack, then enable the option I created in patch 3/3 here, which will funnel all calls into a single userspace binary where you can then filter on argv[0] to see if you want to run the binary or not to prevent this type of attack.

Quoted 2 lines

Apologies if I'm rehashing earlier discussion, I did a quick search of archives but could easily have missed something.

No problem at all, hopefully I've explained it better now.

thanks,

greg k-h

#5 lore raw

Re: [PATCH 2/3] Make static usermode helper binaries constant

Quoted 29 lines · Greg KH
Quoted 18 lines · J. Bruce Fields
Quoted 6 lines · Greg KH

From: Greg Kroah-Hartman <[email protected]>

There are a number of usermode helper binaries that are "hard coded" in the kernel today, so mark them as "const" to make it harder for someone to change where the variables point to.

...

Quoted 10 lines
drivers/pnp/pnpbios/core.c +1 -0
@@ -98,6 +98,7 @@ static struct completion unload_sem;
  */
 static int pnp_dock_event(int dock, struct pnp_docking_station_info *info)
 {
+	static char const sbin_pnpbios[] = "/sbin/pnpbios";
 	char *argv[3], **envp, *buf, *scratch;
 	int i = 0, value;
 
Quoted 9 lines

drivers/pnp/pnpbios/core.c static int pnp_dock_event(int dock, struct pnp_docking_station_info *info) · line 116

Quoted 6 lines
drivers/pnp/pnpbios/core.c +1 -1
@@ -112,7 +113,7 @@ static int pnp_dock_event(int dock, struct pnp_docking_station_info *info)
 	 * integrated into the driver core and use the usual infrastructure
 	 * like sysfs and uevents
 	 */
-	argv[0] = "/sbin/pnpbios";
+	argv[0] = (char *)sbin_pnpbios;

So here and elsewhere, can attackers write to argv[0] instead of to the memory where the string lives?

Quoted 5 lines

Yes, they could, it would be a very "tight" race to do that (have to write after the assignment and before the call_usermodehelper_exec() runs). However, the kernel does not run argv[0], it just passes it to the binary you specify in path, so for this example, the correct program would still be run by the kernel.

In this case it's argv[0] that will be passed to call_usermodehelper as path, but.... OK, this argv array and the various function call arguments are all just data on the stack, so I guess it's all about equivalent.

So we're assuming an attacker that can write to a static location in memory but can't write to the right part of the stack at the right time. I'm no expert at this kind of thing but it seems plausible that assumption could apply in cases that matter.

Quoted 9 lines

But, if you do worry about this type of attack, then enable the option I created in patch 3/3 here, which will funnel all calls into a single userspace binary where you can then filter on argv[0] to see if you want to run the binary or not to prevent this type of attack.

Quoted 2 lines

Apologies if I'm rehashing earlier discussion, I did a quick search of archives but could easily have missed something.

No problem at all, hopefully I've explained it better now.

Thanks!

--b.

author maintainer
#6 lore raw

Re: [PATCH 2/3] Make static usermode helper binaries constant

Quoted 36 lines · J. Bruce Fields
Quoted 29 lines · Greg KH
Quoted 18 lines · J. Bruce Fields
Quoted 6 lines · Greg KH

From: Greg Kroah-Hartman <[email protected]>

There are a number of usermode helper binaries that are "hard coded" in the kernel today, so mark them as "const" to make it harder for someone to change where the variables point to.

...

Quoted 10 lines
drivers/pnp/pnpbios/core.c +1 -0
@@ -98,6 +98,7 @@ static struct completion unload_sem;
  */
 static int pnp_dock_event(int dock, struct pnp_docking_station_info *info)
 {
+	static char const sbin_pnpbios[] = "/sbin/pnpbios";
 	char *argv[3], **envp, *buf, *scratch;
 	int i = 0, value;
 
Quoted 9 lines

drivers/pnp/pnpbios/core.c static int pnp_dock_event(int dock, struct pnp_docking_station_info *info) · line 116

Quoted 6 lines
drivers/pnp/pnpbios/core.c +1 -1
@@ -112,7 +113,7 @@ static int pnp_dock_event(int dock, struct pnp_docking_station_info *info)
 	 * integrated into the driver core and use the usual infrastructure
 	 * like sysfs and uevents
 	 */
-	argv[0] = "/sbin/pnpbios";
+	argv[0] = (char *)sbin_pnpbios;

So here and elsewhere, can attackers write to argv[0] instead of to the memory where the string lives?

Quoted 5 lines

Yes, they could, it would be a very "tight" race to do that (have to write after the assignment and before the call_usermodehelper_exec() runs). However, the kernel does not run argv[0], it just passes it to the binary you specify in path, so for this example, the correct program would still be run by the kernel.

Quoted 4 lines

In this case it's argv[0] that will be passed to call_usermodehelper as path, but.... OK, this argv array and the various function call arguments are all just data on the stack, so I guess it's all about equivalent.

Kind of, nice catch, I'll change the call to usermodehelper to use sbin_pnpbios here, as that's the right thing to do.

Quoted 4 lines

So we're assuming an attacker that can write to a static location in memory but can't write to the right part of the stack at the right time. I'm no expert at this kind of thing but it seems plausible that assumption could apply in cases that matter.

And again, if you really are worried about this, just use the new kconfig option that allows you to filter all of this in userspace :)

thanks,

greg k-h

author maintainer
#7 lore raw

Re: [kernel-hardening] Re: [PATCH 2/3] Make static usermode helper binaries constant

Quoted 37 lines · Greg KH
Quoted 36 lines · J. Bruce Fields
Quoted 34 lines · Greg KH
Quoted 18 lines · J. Bruce Fields
Quoted 6 lines · Greg KH

From: Greg Kroah-Hartman <[email protected]>

There are a number of usermode helper binaries that are "hard coded" in the kernel today, so mark them as "const" to make it harder for someone to change where the variables point to.

...

Quoted 10 lines
drivers/pnp/pnpbios/core.c +1 -0
@@ -98,6 +98,7 @@ static struct completion unload_sem;
  */
 static int pnp_dock_event(int dock, struct pnp_docking_station_info *info)
 {
+	static char const sbin_pnpbios[] = "/sbin/pnpbios";
 	char *argv[3], **envp, *buf, *scratch;
 	int i = 0, value;
 
Quoted 9 lines

drivers/pnp/pnpbios/core.c static int pnp_dock_event(int dock, struct pnp_docking_station_info *info) · line 116

Quoted 6 lines
drivers/pnp/pnpbios/core.c +1 -1
@@ -112,7 +113,7 @@ static int pnp_dock_event(int dock, struct pnp_docking_station_info *info)
 	 * integrated into the driver core and use the usual infrastructure
 	 * like sysfs and uevents
 	 */
-	argv[0] = "/sbin/pnpbios";
+	argv[0] = (char *)sbin_pnpbios;

So here and elsewhere, can attackers write to argv[0] instead of to the memory where the string lives?

Yes, they could, it would be a very "tight" race to do that (have to write after the assignment and before the call_usermodehelper_exec() runs). However, the kernel does not run argv[0], it just passes it to the binary you specify in path, so for this example, the correct program would still be run by the kernel.

Quoted 7 lines
Quoted 4 lines

In this case it's argv[0] that will be passed to call_usermodehelper as path, but.... OK, this argv array and the various function call arguments are all just data on the stack, so I guess it's all about equivalent.

Kind of, nice catch, I'll change the call to usermodehelper to use sbin_pnpbios here, as that's the right thing to do.

Oops, no, the patch was doing the right thing here, you missed the next chunk of the patch:

@@ -139,7 +140,7 @@ static int pnp_dock_event(int dock, struct pnp_docking_station_info *info)

                           info->location_id, info->serial, info->capabilities);
        envp[i] = NULL;

- value = call_usermodehelper(argv [0], argv, envp, UMH_WAIT_EXEC);

+       value = call_usermodehelper(sbin_pnpbios, argv, envp, UMH_WAIT_EXEC);
        kfree(buf);
        kfree(envp);
        return 0;

So it's ok.

thanks,

greg k-h

#8 lore raw

Re: [kernel-hardening] Re: [PATCH 2/3] Make static usermode helper binaries constant

Quoted 43 lines · Greg KH
Quoted 42 lines · Greg KH
Quoted 40 lines · J. Bruce Fields
Quoted 29 lines · Greg KH
Quoted 18 lines · J. Bruce Fields
Quoted 6 lines · Greg KH

From: Greg Kroah-Hartman <[email protected]>

There are a number of usermode helper binaries that are "hard coded" in the kernel today, so mark them as "const" to make it harder for someone to change where the variables point to.

...

Quoted 10 lines
drivers/pnp/pnpbios/core.c +1 -0
@@ -98,6 +98,7 @@ static struct completion unload_sem;
  */
 static int pnp_dock_event(int dock, struct pnp_docking_station_info *info)
 {
+	static char const sbin_pnpbios[] = "/sbin/pnpbios";
 	char *argv[3], **envp, *buf, *scratch;
 	int i = 0, value;
 
Quoted 9 lines

drivers/pnp/pnpbios/core.c static int pnp_dock_event(int dock, struct pnp_docking_station_info *info) · line 116

Quoted 6 lines
drivers/pnp/pnpbios/core.c +1 -1
@@ -112,7 +113,7 @@ static int pnp_dock_event(int dock, struct pnp_docking_station_info *info)
 	 * integrated into the driver core and use the usual infrastructure
 	 * like sysfs and uevents
 	 */
-	argv[0] = "/sbin/pnpbios";
+	argv[0] = (char *)sbin_pnpbios;

So here and elsewhere, can attackers write to argv[0] instead of to the memory where the string lives?

Quoted 5 lines

Yes, they could, it would be a very "tight" race to do that (have to write after the assignment and before the call_usermodehelper_exec() runs). However, the kernel does not run argv[0], it just passes it to the binary you specify in path, so for this example, the correct program would still be run by the kernel.

In this case it's argv[0] that will be passed to call_usermodehelper as path, but.... OK, this argv array and the various function call arguments are all just data on the stack, so I guess it's all about equivalent.

Quoted 5 lines
Quoted 2 lines

Kind of, nice catch, I'll change the call to usermodehelper to use sbin_pnpbios here, as that's the right thing to do.

Oops, no, the patch was doing the right thing here, you missed the next chunk of the patch:

Oh, got it, thanks.

--b.

Quoted 18 lines
drivers/pnp/pnpbios/core.c +1 -1
@@ -139,7 +140,7 @@ static int pnp_dock_event(int dock, struct pnp_docking_station_info *info)
                           info->location_id, info->serial, info->capabilities);
        envp[i] = NULL;
 
-       value = call_usermodehelper(argv [0], argv, envp, UMH_WAIT_EXEC);
+       value = call_usermodehelper(sbin_pnpbios, argv, envp, UMH_WAIT_EXEC);
        kfree(buf);
        kfree(envp);
        return 0;

So it's ok.

thanks,

greg k-h

#9 lore raw

Re: [PATCH 2/3] Make static usermode helper binaries constant

In reply to Greg KH
Quoted 170 lines · Greg KH

From: Greg Kroah-Hartman <[email protected]>

There are a number of usermode helper binaries that are "hard coded" in the kernel today, so mark them as "const" to make it harder for someone to change where the variables point to.

Cc Benjamin Herrenschmidt <[email protected]>
Cc Thomas Sailer <[email protected]>
Cc "Rafael J. Wysocki" <[email protected]>
Cc Johan Hovold <[email protected]>
Cc Alex Elder <[email protected]>
Cc "J. Bruce Fields" <[email protected]>
Cc Jeff Layton <[email protected]>
Cc David Howells <[email protected]>
---
 drivers/macintosh/windfarm_core.c          |  4 ++--
 drivers/net/hamradio/baycom_epp.c          | 10 +++++++---
 drivers/pnp/pnpbios/core.c                 |  5 +++--
 drivers/staging/greybus/svc_watchdog.c     |  4 ++--
 drivers/staging/rtl8192e/rtl8192e/rtl_dm.c |  8 ++++----
 fs/nfsd/nfs4layouts.c                      |  6 ++++--
 security/keys/request_key.c                |  7 ++++---
 7 files changed, 26 insertions(+), 18 deletions(-)
drivers/macintosh/windfarm_core.c +2 -2
index 465d770ab0bb..5e013d781a74 100644
@@ -74,8 +74,8 @@ static inline void wf_notify(int event, void *param)
 
 static int wf_critical_overtemp(void)
 {
-	static char * critical_overtemp_path = "/sbin/critical_overtemp";
-	char *argv[] = { critical_overtemp_path, NULL };
+	static char const critical_overtemp_path[] = "/sbin/critical_overtemp";
+	char *argv[] = { (char *)critical_overtemp_path, NULL };
 	static char *envp[] = { "HOME=/",
 				"TERM=linux",
 				"PATH=/sbin:/usr/sbin:/bin:/usr/bin",
drivers/net/hamradio/baycom_epp.c +7 -3
index 7d054697b199..594fa1407e29 100644
@@ -299,7 +299,7 @@ static inline void baycom_int_freq(struct baycom_state *bc)
  *    eppconfig_path should be setable  via /proc/sys.
  */
 
-static char eppconfig_path[256] = "/usr/sbin/eppfpga";
+static char const eppconfig_path[] = "/usr/sbin/eppfpga";
 
 static char *envp[] = { "HOME=/", "TERM=linux", "PATH=/usr/bin:/bin", NULL };
 
@@ -308,8 +308,12 @@ static int eppconfig(struct baycom_state *bc)
 {
 	char modearg[256];
 	char portarg[16];
-        char *argv[] = { eppconfig_path, "-s", "-p", portarg, "-m", modearg,
-			 NULL };
+        char *argv[] = {
+		(char *)eppconfig_path,
+		"-s",
+		"-p", portarg,
+		"-m", modearg,
+		NULL };
 
 	/* set up arguments */
 	sprintf(modearg, "%sclk,%smodem,fclk=%d,bps=%d,divider=%d%s,extstat",
drivers/pnp/pnpbios/core.c +3 -2
index c38a5b9733c8..0ced908e7aa8 100644
@@ -98,6 +98,7 @@ static struct completion unload_sem;
  */
 static int pnp_dock_event(int dock, struct pnp_docking_station_info *info)
 {
+	static char const sbin_pnpbios[] = "/sbin/pnpbios";
 	char *argv[3], **envp, *buf, *scratch;
 	int i = 0, value;
 
@@ -112,7 +113,7 @@ static int pnp_dock_event(int dock, struct pnp_docking_station_info *info)
 	 * integrated into the driver core and use the usual infrastructure
 	 * like sysfs and uevents
 	 */
-	argv[0] = "/sbin/pnpbios";
+	argv[0] = (char *)sbin_pnpbios;
 	argv[1] = "dock";
 	argv[2] = NULL;
 
@@ -139,7 +140,7 @@ static int pnp_dock_event(int dock, struct pnp_docking_station_info *info)
 			   info->location_id, info->serial, info->capabilities);
 	envp[i] = NULL;
 
-	value = call_usermodehelper(argv [0], argv, envp, UMH_WAIT_EXEC);
+	value = call_usermodehelper(sbin_pnpbios, argv, envp, UMH_WAIT_EXEC);
 	kfree(buf);
 	kfree(envp);
 	return 0;
drivers/staging/greybus/svc_watchdog.c +2 -2
index 3729460fb954..12cef5c06e27 100644
@@ -44,14 +44,14 @@ static int svc_watchdog_pm_notifier(struct notifier_block *notifier,
 
 static void greybus_reset(struct work_struct *work)
 {
-	static char start_path[256] = "/system/bin/start";
+	static char const start_path[] = "/system/bin/start";
 	static char *envp[] = {
 		"HOME=/",
 		"PATH=/sbin:/vendor/bin:/system/sbin:/system/bin:/system/xbin",
 		NULL,
 	};
 	static char *argv[] = {
-		start_path,
+		(char *)start_path,
 		"unipro_reset",
 		NULL,
 	};
drivers/staging/rtl8192e/rtl8192e/rtl_dm.c +4 -4
index 9bc284812c30..dbb58fb16482 100644
@@ -268,8 +268,8 @@ void rtl92e_dm_watchdog(struct net_device *dev)
 static void _rtl92e_dm_check_ac_dc_power(struct net_device *dev)
 {
 	struct r8192_priv *priv = rtllib_priv(dev);
-	static char *ac_dc_script = "/etc/acpi/wireless-rtl-ac-dc-power.sh";
-	char *argv[] = {ac_dc_script, DRV_NAME, NULL};
+	static char const ac_dc_script[] = "/etc/acpi/wireless-rtl-ac-dc-power.sh";
+	char *argv[] = {(char *)ac_dc_script, DRV_NAME, NULL};
 	static char *envp[] = {"HOME=/",
 			"TERM=linux",
 			"PATH=/usr/bin:/bin",
@@ -1823,7 +1823,7 @@ static void _rtl92e_dm_check_rf_ctrl_gpio(void *data)
 	enum rt_rf_power_state eRfPowerStateToSet;
 	bool bActuallySet = false;
 	char *argv[3];
-	static char *RadioPowerPath = "/etc/acpi/events/RadioPower.sh";
+	static char const RadioPowerPath[] = "/etc/acpi/events/RadioPower.sh";
 	static char *envp[] = {"HOME=/", "TERM=linux", "PATH=/usr/bin:/bin",
 			       NULL};
 
@@ -1862,7 +1862,7 @@ static void _rtl92e_dm_check_rf_ctrl_gpio(void *data)
 		else
 			argv[1] = "RFON";
 
-		argv[0] = RadioPowerPath;
+		argv[0] = (char *)RadioPowerPath;
 		argv[2] = NULL;
 		call_usermodehelper(RadioPowerPath, argv, envp, UMH_WAIT_PROC);
 	}
fs/nfsd/nfs4layouts.c +2 -1
index 596205d939a1..e06a4ae5f3ad 100644
@@ -613,6 +613,7 @@ nfsd4_cb_layout_fail(struct nfs4_layout_stateid *ls)
 {
 	struct nfs4_client *clp = ls->ls_stid.sc_client;
 	char addr_str[INET6_ADDRSTRLEN];
+	static char const nfsd_recall_failed[] = "/sbin/nfsd-recall-failed";
 	static char *envp[] = {
 		"HOME=/",
 		"TERM=linux",
@@ -628,12 +629,13 @@ nfsd4_cb_layout_fail(struct nfs4_layout_stateid *ls)
 		"nfsd: client %s failed to respond to layout recall. "
 		"  Fencing..\n", addr_str);
 
-	argv[0] = "/sbin/nfsd-recall-failed";
+	argv[0] = (char *)nfsd_recall_failed;

fs/nfsd/nfs4layouts.c nfsd4_cb_layout_fail(struct nfs4_layout_stateid *ls) · line 641

Quoted 10 lines
fs/nfsd/nfs4layouts.c +2 -1
 	argv[1] = addr_str;
 	argv[2] = ls->ls_file->f_path.mnt->mnt_sb->s_id;
 	argv[3] = NULL;
 
-	error = call_usermodehelper(argv[0], argv, envp, UMH_WAIT_PROC);
+	error = call_usermodehelper(nfsd_recall_failed, argv, envp,
+				    UMH_WAIT_PROC);
 	if (error) {
 		printk(KERN_ERR "nfsd: fence failed for client %s: %d!\n",
 			addr_str, error);

Do we need a similar fix in nfsd4_umh_cltrack_upcall?

Quoted 39 lines
security/keys/request_key.c +4 -3
index 43affcf10b22..9822e500d50d 100644
@@ -72,7 +72,7 @@ static void umh_keys_cleanup(struct subprocess_info *info)
 /*
  * Call a usermode helper with a specific session keyring.
  */
-static int call_usermodehelper_keys(char *path, char **argv, char **envp,
+static int call_usermodehelper_keys(const char *path, char **argv, char **envp,
 					struct key *session_keyring, int wait)
 {
 	struct subprocess_info *info;
@@ -95,6 +95,7 @@ static int call_sbin_request_key(struct key_construction *cons,
 				 const char *op,
 				 void *aux)
 {
+	static char const request_key[] = "/sbin/request-key";
 	const struct cred *cred = current_cred();
 	key_serial_t prkey, sskey;
 	struct key *key = cons->key, *authkey = cons->authkey, *keyring,
@@ -161,7 +162,7 @@ static int call_sbin_request_key(struct key_construction *cons,
 
 	/* set up the argument list */
 	i = 0;
-	argv[i++] = "/sbin/request-key";
+	argv[i++] = (char *)request_key;
 	argv[i++] = (char *) op;
 	argv[i++] = key_str;
 	argv[i++] = uid_str;
@@ -172,7 +173,7 @@ static int call_sbin_request_key(struct key_construction *cons,
 	argv[i] = NULL;
 
 	/* do it */
-	ret = call_usermodehelper_keys(argv[0], argv, envp, keyring,
+	ret = call_usermodehelper_keys(request_key, argv, envp, keyring,
 				       UMH_WAIT_PROC);
 	kdebug("usermode -> 0x%x", ret);
 	if (ret >= 0) {
Signature · 1 line
Jeff Layton <[email protected]>
author maintainer
#10 lore raw

Re: [PATCH 2/3] Make static usermode helper binaries constant

Quoted 39 lines · Jeff Layton
Quoted 38 lines · Greg KH

From: Greg Kroah-Hartman <[email protected]>

There are a number of usermode helper binaries that are "hard coded" in the kernel today, so mark them as "const" to make it harder for someone to change where the variables point to.

Cc Benjamin Herrenschmidt <[email protected]>
Cc Thomas Sailer <[email protected]>
Cc "Rafael J. Wysocki" <[email protected]>
Cc Johan Hovold <[email protected]>
Cc Alex Elder <[email protected]>
Cc "J. Bruce Fields" <[email protected]>
Cc Jeff Layton <[email protected]>
Cc David Howells <[email protected]>
fs/nfsd/nfs4layouts.c +2 -1
@@ -613,6 +613,7 @@ nfsd4_cb_layout_fail(struct nfs4_layout_stateid *ls)
 {
 	struct nfs4_client *clp = ls->ls_stid.sc_client;
 	char addr_str[INET6_ADDRSTRLEN];
+	static char const nfsd_recall_failed[] = "/sbin/nfsd-recall-failed";
 	static char *envp[] = {
 		"HOME=/",
 		"TERM=linux",
@@ -628,12 +629,13 @@ nfsd4_cb_layout_fail(struct nfs4_layout_stateid *ls)
 		"nfsd: client %s failed to respond to layout recall. "
 		"  Fencing..\n", addr_str);
 
-	argv[0] = "/sbin/nfsd-recall-failed";
+	argv[0] = (char *)nfsd_recall_failed;
 	argv[1] = addr_str;
 	argv[2] = ls->ls_file->f_path.mnt->mnt_sb->s_id;
 	argv[3] = NULL;
 
Quoted 8 lines

fs/nfsd/nfs4layouts.c nfsd4_cb_layout_fail(struct nfs4_layout_stateid *ls) · line 641

Quoted 6 lines
fs/nfsd/nfs4layouts.c +2 -1
-	error = call_usermodehelper(argv[0], argv, envp, UMH_WAIT_PROC);
+	error = call_usermodehelper(nfsd_recall_failed, argv, envp,
+				    UMH_WAIT_PROC);
 	if (error) {
 		printk(KERN_ERR "nfsd: fence failed for client %s: %d!\n",
 			addr_str, error);

Do we need a similar fix in nfsd4_umh_cltrack_upcall?

Not that I can tell, as the call_usermodehelper() binary it calls is dynamically created (it's not a static string). Unless I'm misreading the code?

thanks,

greg k-h

#11 lore raw

Re: [PATCH 2/3] Make static usermode helper binaries constant

Quoted 49 lines · Greg KH
Quoted 39 lines · Jeff Layton
Quoted 38 lines · Greg KH

From: Greg Kroah-Hartman <[email protected]>

There are a number of usermode helper binaries that are "hard coded" in the kernel today, so mark them as "const" to make it harder for someone to change where the variables point to.

Cc Benjamin Herrenschmidt <[email protected]>
Cc Thomas Sailer <[email protected]>
Cc "Rafael J. Wysocki" <[email protected]>
Cc Johan Hovold <[email protected]>
Cc Alex Elder <[email protected]>
Cc "J. Bruce Fields" <[email protected]>
Cc Jeff Layton <[email protected]>
Cc David Howells <[email protected]>
fs/nfsd/nfs4layouts.c +2 -1
@@ -613,6 +613,7 @@ nfsd4_cb_layout_fail(struct nfs4_layout_stateid *ls)
 {
 	struct nfs4_client *clp = ls->ls_stid.sc_client;
 	char addr_str[INET6_ADDRSTRLEN];
+	static char const nfsd_recall_failed[] = "/sbin/nfsd-recall-failed";
 	static char *envp[] = {
 		"HOME=/",
 		"TERM=linux",
@@ -628,12 +629,13 @@ nfsd4_cb_layout_fail(struct nfs4_layout_stateid *ls)
 		"nfsd: client %s failed to respond to layout recall. "
 		"  Fencing..\n", addr_str);
 
-	argv[0] = "/sbin/nfsd-recall-failed";
+	argv[0] = (char *)nfsd_recall_failed;
 	argv[1] = addr_str;
 	argv[2] = ls->ls_file->f_path.mnt->mnt_sb->s_id;
 	argv[3] = NULL;
 
Quoted 8 lines

fs/nfsd/nfs4layouts.c nfsd4_cb_layout_fail(struct nfs4_layout_stateid *ls) · line 641

Quoted 6 lines
fs/nfsd/nfs4layouts.c +2 -1
-	error = call_usermodehelper(argv[0], argv, envp, UMH_WAIT_PROC);
+	error = call_usermodehelper(nfsd_recall_failed, argv, envp,
+				    UMH_WAIT_PROC);
 	if (error) {
 		printk(KERN_ERR "nfsd: fence failed for client %s: %d!\n",
 			addr_str, error);

Do we need a similar fix in nfsd4_umh_cltrack_upcall?

Quoted 4 lines

Not that I can tell, as the call_usermodehelper() binary it calls is dynamically created (it's not a static string). Unless I'm misreading the code?

It's a module_param_string:

static char cltrack_prog[PATH_MAX] = "/sbin/nfsdcltrack"; module_param_string(cltrack_prog, cltrack_prog, sizeof(cltrack_prog), S_IRUGO|S_IWUSR); MODULE_PARM_DESC(cltrack_prog, "Path to the nfsdcltrack upcall program");

Maybe we should consider deprecating that module parameter and make it const as well? I added it when I first developed that code, but I doubt anyone legitimately sets it.

Signature · 1 line
Jeff Layton <[email protected]>
author maintainer
#12 lore raw

Re: [PATCH 2/3] Make static usermode helper binaries constant

Quoted 63 lines · Jeff Layton
Quoted 49 lines · Greg KH
Quoted 39 lines · Jeff Layton
Quoted 38 lines · Greg KH

From: Greg Kroah-Hartman <[email protected]>

There are a number of usermode helper binaries that are "hard coded" in the kernel today, so mark them as "const" to make it harder for someone to change where the variables point to.

Cc Benjamin Herrenschmidt <[email protected]>
Cc Thomas Sailer <[email protected]>
Cc "Rafael J. Wysocki" <[email protected]>
Cc Johan Hovold <[email protected]>
Cc Alex Elder <[email protected]>
Cc "J. Bruce Fields" <[email protected]>
Cc Jeff Layton <[email protected]>
Cc David Howells <[email protected]>
fs/nfsd/nfs4layouts.c +2 -1
@@ -613,6 +613,7 @@ nfsd4_cb_layout_fail(struct nfs4_layout_stateid *ls)
 {
 	struct nfs4_client *clp = ls->ls_stid.sc_client;
 	char addr_str[INET6_ADDRSTRLEN];
+	static char const nfsd_recall_failed[] = "/sbin/nfsd-recall-failed";
 	static char *envp[] = {
 		"HOME=/",
 		"TERM=linux",
@@ -628,12 +629,13 @@ nfsd4_cb_layout_fail(struct nfs4_layout_stateid *ls)
 		"nfsd: client %s failed to respond to layout recall. "
 		"  Fencing..\n", addr_str);
 
-	argv[0] = "/sbin/nfsd-recall-failed";
+	argv[0] = (char *)nfsd_recall_failed;
 	argv[1] = addr_str;
 	argv[2] = ls->ls_file->f_path.mnt->mnt_sb->s_id;
 	argv[3] = NULL;
 
Quoted 8 lines

fs/nfsd/nfs4layouts.c nfsd4_cb_layout_fail(struct nfs4_layout_stateid *ls) · line 641

Quoted 6 lines
fs/nfsd/nfs4layouts.c +2 -1
-	error = call_usermodehelper(argv[0], argv, envp, UMH_WAIT_PROC);
+	error = call_usermodehelper(nfsd_recall_failed, argv, envp,
+				    UMH_WAIT_PROC);
 	if (error) {
 		printk(KERN_ERR "nfsd: fence failed for client %s: %d!\n",
 			addr_str, error);

Do we need a similar fix in nfsd4_umh_cltrack_upcall?

Quoted 4 lines

Not that I can tell, as the call_usermodehelper() binary it calls is dynamically created (it's not a static string). Unless I'm misreading the code?

It's a module_param_string:

static char cltrack_prog[PATH_MAX] = "/sbin/nfsdcltrack"; module_param_string(cltrack_prog, cltrack_prog, sizeof(cltrack_prog), S_IRUGO|S_IWUSR); MODULE_PARM_DESC(cltrack_prog, "Path to the nfsdcltrack upcall program");

Quoted 3 lines

Maybe we should consider deprecating that module parameter and make it const as well? I added it when I first developed that code, but I doubt anyone legitimately sets it.

That's fine with me, but was outside of the scope of this patch, I was not trying to change any existing functionality :)

thanks,

greg k-h

author maintainer
#13 lore raw

PATCH 3/3 Introduce STATIC_USERMODEHELPER to mediate call_usermodehelper()

In reply to Greg KH

From: Greg Kroah-Hartman <[email protected]>

Some usermode helper applications are defined at kernel build time, while others can be changed at runtime. To provide a sane way to filter these, add a new kernel option "STATIC_USERMODEHELPER". This option routes all call_usermodehelper() calls through this binary, no matter what the caller wishes to have called.

The new binary (by default set to /sbin/usermode-helper, but can be changed through the STATIC_USERMODEHELPER_PATH option) can properly filter the requested programs to be run by the kernel by looking at the first argument that is passed to it. All other options should then be passed onto the proper program if so desired.

To disable all call_usermodehelper() calls by the kernel, set STATIC_USERMODEHELPER_PATH to an empty string.

Thanks to Neil Brown for the idea of this feature.

---
 kernel/kmod.c    | 14 ++++++++++++++
 security/Kconfig | 35 +++++++++++++++++++++++++++++++++++
 2 files changed, 49 insertions(+)
kernel/kmod.c +14 -0
index 426a614e97fe..0c407f905ca4 100644
@@ -528,7 +528,12 @@ struct subprocess_info *call_usermodehelper_setup(const char *path, char **argv,
 		goto out;
 
 	INIT_WORK(&sub_info->work, call_usermodehelper_exec_work);
+
+#ifdef CONFIG_STATIC_USERMODEHELPER
+	sub_info->path = CONFIG_STATIC_USERMODEHELPER_PATH;
+#else
 	sub_info->path = path;
+#endif
 	sub_info->argv = argv;
 	sub_info->envp = envp;
 
@@ -566,6 +571,15 @@ int call_usermodehelper_exec(struct subprocess_info *sub_info, int wait)
 		retval = -EBUSY;
 		goto out;
 	}
+
+	/*
+	 * If there is no binary for us to call, then just return and get out of
+	 * here.  This allows us to set STATIC_USERMODEHELPER_PATH to "" and
+	 * disable all call_usermodehelper() calls.
+	 */
+	if (strlen(sub_info->path) == 0)
+		goto out;
+
 	/*
 	 * Set the completion pointer only if there is a waiter.
 	 * This makes it possible to use umh_complete to free
security/Kconfig +35 -0
index 118f4549404e..d900f47eaa68 100644
@@ -158,6 +158,41 @@ config HARDENED_USERCOPY_PAGESPAN
 	  been removed. This config is intended to be used only while
 	  trying to find such users.
 
+config STATIC_USERMODEHELPER
+	bool "Force all usermode helper calls through a single binary"
+	help
+	  By default, the kernel can call many different userspace
+	  binary programs through the "usermode helper" kernel
+	  interface.  Some of these binaries are statically defined
+	  either in the kernel code itself, or as a kernel configuration
+	  option.  However, some of these are dynamically created at
+	  runtime, or can be modified after the kernel has started up.
+	  To provide an additional layer of security, route all of these
+	  calls through a single executable that can not have its name
+	  changed.
+
+	  Note, it is up to this single binary to then call the relevant
+	  "real" usermode helper binary, based on the first argument
+	  passed to it.  If desired, this program can filter and pick
+	  and choose what real programs are called.
+
+	  If you wish for all usermode helper programs are to be
+	  disabled, choose this option and then set
+	  STATIC_USERMODEHELPER_PATH to an empty string.
+
+config STATIC_USERMODEHELPER_PATH
+	string "Path to the static usermode helper binary"
+	depends on STATIC_USERMODEHELPER
+	default "/sbin/usermode-helper"
+	help
+	  The binary called by the kernel when any usermode helper
+	  program is wish to be run.  The "real" application's name will
+	  be in the first argument passed to this program on the command
+	  line.
+
+	  If you wish for all usermode helper programs to be disabled,
+	  specify an empty string here (i.e. "").
+
 source security/selinux/Kconfig
 source security/smack/Kconfig
 source security/tomoyo/Kconfig
Signature · 1 line
2.11.0
#14 lore raw

Re: [PATCH 3/3] Introduce STATIC_USERMODEHELPER to mediate call_usermodehelper()

Quoted 95 lines · Greg KH

From: Greg Kroah-Hartman <[email protected]>

Some usermode helper applications are defined at kernel build time, while others can be changed at runtime. To provide a sane way to filter these, add a new kernel option "STATIC_USERMODEHELPER". This option routes all call_usermodehelper() calls through this binary, no matter what the caller wishes to have called.

The new binary (by default set to /sbin/usermode-helper, but can be changed through the STATIC_USERMODEHELPER_PATH option) can properly filter the requested programs to be run by the kernel by looking at the first argument that is passed to it. All other options should then be passed onto the proper program if so desired.

To disable all call_usermodehelper() calls by the kernel, set STATIC_USERMODEHELPER_PATH to an empty string.

Thanks to Neil Brown for the idea of this feature.

---
 kernel/kmod.c    | 14 ++++++++++++++
 security/Kconfig | 35 +++++++++++++++++++++++++++++++++++
 2 files changed, 49 insertions(+)
kernel/kmod.c +14 -0
index 426a614e97fe..0c407f905ca4 100644
@@ -528,7 +528,12 @@ struct subprocess_info *call_usermodehelper_setup(const char *path, char **argv,
 		goto out;
 
 	INIT_WORK(&sub_info->work, call_usermodehelper_exec_work);
+
+#ifdef CONFIG_STATIC_USERMODEHELPER
+	sub_info->path = CONFIG_STATIC_USERMODEHELPER_PATH;
+#else
 	sub_info->path = path;
+#endif
 	sub_info->argv = argv;
 	sub_info->envp = envp;
 
@@ -566,6 +571,15 @@ int call_usermodehelper_exec(struct subprocess_info *sub_info, int wait)
 		retval = -EBUSY;
 		goto out;
 	}
+
+	/*
+	 * If there is no binary for us to call, then just return and get out of
+	 * here.  This allows us to set STATIC_USERMODEHELPER_PATH to "" and
+	 * disable all call_usermodehelper() calls.
+	 */
+	if (strlen(sub_info->path) == 0)
+		goto out;
+
 	/*
 	 * Set the completion pointer only if there is a waiter.
 	 * This makes it possible to use umh_complete to free
security/Kconfig +28 -0
index 118f4549404e..d900f47eaa68 100644
@@ -158,6 +158,41 @@ config HARDENED_USERCOPY_PAGESPAN
 	  been removed. This config is intended to be used only while
 	  trying to find such users.
 
+config STATIC_USERMODEHELPER
+	bool "Force all usermode helper calls through a single binary"
+	help
+	  By default, the kernel can call many different userspace
+	  binary programs through the "usermode helper" kernel
+	  interface.  Some of these binaries are statically defined
+	  either in the kernel code itself, or as a kernel configuration
+	  option.  However, some of these are dynamically created at
+	  runtime, or can be modified after the kernel has started up.
+	  To provide an additional layer of security, route all of these
+	  calls through a single executable that can not have its name
+	  changed.
+
+	  Note, it is up to this single binary to then call the relevant
+	  "real" usermode helper binary, based on the first argument
+	  passed to it.  If desired, this program can filter and pick
+	  and choose what real programs are called.
+
+	  If you wish for all usermode helper programs are to be
+	  disabled, choose this option and then set
+	  STATIC_USERMODEHELPER_PATH to an empty string.
+
+config STATIC_USERMODEHELPER_PATH
+	string "Path to the static usermode helper binary"
+	depends on STATIC_USERMODEHELPER
+	default "/sbin/usermode-helper"
+	help
+	  The binary called by the kernel when any usermode helper

security/Kconfig config HARDENED_USERCOPY_PAGESPAN · line 198

Quoted 10 lines
security/Kconfig +7 -0
+	  program is wish to be run.  The "real" application's name will
+	  be in the first argument passed to this program on the command
+	  line.
+
+	  If you wish for all usermode helper programs to be disabled,
+	  specify an empty string here (i.e. "").
+
 source security/selinux/Kconfig
 source security/smack/Kconfig
 source security/tomoyo/Kconfig

I like the core of this idea (having a single dispatch binary) a lot. It seems like a good way to limit the attack surface. We could even consider signing this binary as well, etc...

I'm less excited though about using the binary pathnames in argv[0]. Would it be better to pass a non-path string of some sort that the binary would have to turn into a path to the binary to exec?

Signature · 1 line
Jeff Layton <[email protected]>
author maintainer
#15 lore raw

Re: [PATCH 3/3] Introduce STATIC_USERMODEHELPER to mediate call_usermodehelper()

Quoted 102 lines · Jeff Layton
Quoted 101 lines · Greg KH

From: Greg Kroah-Hartman <[email protected]>

Some usermode helper applications are defined at kernel build time, while others can be changed at runtime. To provide a sane way to filter these, add a new kernel option "STATIC_USERMODEHELPER". This option routes all call_usermodehelper() calls through this binary, no matter what the caller wishes to have called.

The new binary (by default set to /sbin/usermode-helper, but can be changed through the STATIC_USERMODEHELPER_PATH option) can properly filter the requested programs to be run by the kernel by looking at the first argument that is passed to it. All other options should then be passed onto the proper program if so desired.

To disable all call_usermodehelper() calls by the kernel, set STATIC_USERMODEHELPER_PATH to an empty string.

Thanks to Neil Brown for the idea of this feature.

---
 kernel/kmod.c    | 14 ++++++++++++++
 security/Kconfig | 35 +++++++++++++++++++++++++++++++++++
 2 files changed, 49 insertions(+)
kernel/kmod.c +14 -0
index 426a614e97fe..0c407f905ca4 100644
@@ -528,7 +528,12 @@ struct subprocess_info *call_usermodehelper_setup(const char *path, char **argv,
 		goto out;
 
 	INIT_WORK(&sub_info->work, call_usermodehelper_exec_work);
+
+#ifdef CONFIG_STATIC_USERMODEHELPER
+	sub_info->path = CONFIG_STATIC_USERMODEHELPER_PATH;
+#else
 	sub_info->path = path;
+#endif
 	sub_info->argv = argv;
 	sub_info->envp = envp;
 
@@ -566,6 +571,15 @@ int call_usermodehelper_exec(struct subprocess_info *sub_info, int wait)
 		retval = -EBUSY;
 		goto out;
 	}
+
+	/*
+	 * If there is no binary for us to call, then just return and get out of
+	 * here.  This allows us to set STATIC_USERMODEHELPER_PATH to "" and
+	 * disable all call_usermodehelper() calls.
+	 */
+	if (strlen(sub_info->path) == 0)
+		goto out;
+
 	/*
 	 * Set the completion pointer only if there is a waiter.
 	 * This makes it possible to use umh_complete to free
security/Kconfig +34 -0
index 118f4549404e..d900f47eaa68 100644
@@ -158,6 +158,41 @@ config HARDENED_USERCOPY_PAGESPAN
 	  been removed. This config is intended to be used only while
 	  trying to find such users.
 
+config STATIC_USERMODEHELPER
+	bool "Force all usermode helper calls through a single binary"
+	help
+	  By default, the kernel can call many different userspace
+	  binary programs through the "usermode helper" kernel
+	  interface.  Some of these binaries are statically defined
+	  either in the kernel code itself, or as a kernel configuration
+	  option.  However, some of these are dynamically created at
+	  runtime, or can be modified after the kernel has started up.
+	  To provide an additional layer of security, route all of these
+	  calls through a single executable that can not have its name
+	  changed.
+
+	  Note, it is up to this single binary to then call the relevant
+	  "real" usermode helper binary, based on the first argument
+	  passed to it.  If desired, this program can filter and pick
+	  and choose what real programs are called.
+
+	  If you wish for all usermode helper programs are to be
+	  disabled, choose this option and then set
+	  STATIC_USERMODEHELPER_PATH to an empty string.
+
+config STATIC_USERMODEHELPER_PATH
+	string "Path to the static usermode helper binary"
+	depends on STATIC_USERMODEHELPER
+	default "/sbin/usermode-helper"
+	help
+	  The binary called by the kernel when any usermode helper
+	  program is wish to be run.  The "real" application's name will
+	  be in the first argument passed to this program on the command
+	  line.
+
+	  If you wish for all usermode helper programs to be disabled,
+	  specify an empty string here (i.e. "").
Quoted 8 lines

security/Kconfig config HARDENED_USERCOPY_PAGESPAN · line 198

Quoted 4 lines
security/Kconfig +1 -0
+
 source security/selinux/Kconfig
 source security/smack/Kconfig
 source security/tomoyo/Kconfig

I like the core of this idea (having a single dispatch binary) a lot. It seems like a good way to limit the attack surface. We could even consider signing this binary as well, etc...

Or use a read-only partition that is checked with dm-verity at boot so you know it's safe. Lots of ways to protect this file.

Quoted 3 lines

I'm less excited though about using the binary pathnames in argv[0]. Would it be better to pass a non-path string of some sort that the binary would have to turn into a path to the binary to exec?

What exactly do you mean by "non-path" string? You can do whatever you want with the argv[0] value in your new binary, but rewriting all of the users of this interface in the kernel to pass in some other type of value instead of a full path, that doesn't make much sense (hint a path is just as good as anything else.)

thanks,

greg k-h

Acked-by
#16 lore raw

Re: [PATCH 3/3] Introduce STATIC_USERMODEHELPER to mediate call_usermodehelper()

Quoted 112 lines · Greg KH
Quoted 102 lines · Jeff Layton
Quoted 101 lines · Greg KH

From: Greg Kroah-Hartman <[email protected]>

Some usermode helper applications are defined at kernel build time, while others can be changed at runtime. To provide a sane way to filter these, add a new kernel option "STATIC_USERMODEHELPER". This option routes all call_usermodehelper() calls through this binary, no matter what the caller wishes to have called.

The new binary (by default set to /sbin/usermode-helper, but can be changed through the STATIC_USERMODEHELPER_PATH option) can properly filter the requested programs to be run by the kernel by looking at the first argument that is passed to it. All other options should then be passed onto the proper program if so desired.

To disable all call_usermodehelper() calls by the kernel, set STATIC_USERMODEHELPER_PATH to an empty string.

Thanks to Neil Brown for the idea of this feature.

---
 kernel/kmod.c    | 14 ++++++++++++++
 security/Kconfig | 35 +++++++++++++++++++++++++++++++++++
 2 files changed, 49 insertions(+)
kernel/kmod.c +14 -0
index 426a614e97fe..0c407f905ca4 100644
@@ -528,7 +528,12 @@ struct subprocess_info *call_usermodehelper_setup(const char *path, char **argv,
 		goto out;
 
 	INIT_WORK(&sub_info->work, call_usermodehelper_exec_work);
+
+#ifdef CONFIG_STATIC_USERMODEHELPER
+	sub_info->path = CONFIG_STATIC_USERMODEHELPER_PATH;
+#else
 	sub_info->path = path;
+#endif
 	sub_info->argv = argv;
 	sub_info->envp = envp;
 
@@ -566,6 +571,15 @@ int call_usermodehelper_exec(struct subprocess_info *sub_info, int wait)
 		retval = -EBUSY;
 		goto out;
 	}
+
+	/*
+	 * If there is no binary for us to call, then just return and get out of
+	 * here.  This allows us to set STATIC_USERMODEHELPER_PATH to "" and
+	 * disable all call_usermodehelper() calls.
+	 */
+	if (strlen(sub_info->path) == 0)
+		goto out;
+
 	/*
 	 * Set the completion pointer only if there is a waiter.
 	 * This makes it possible to use umh_complete to free
security/Kconfig +34 -0
index 118f4549404e..d900f47eaa68 100644
@@ -158,6 +158,41 @@ config HARDENED_USERCOPY_PAGESPAN
 	  been removed. This config is intended to be used only while
 	  trying to find such users.
 
+config STATIC_USERMODEHELPER
+	bool "Force all usermode helper calls through a single binary"
+	help
+	  By default, the kernel can call many different userspace
+	  binary programs through the "usermode helper" kernel
+	  interface.  Some of these binaries are statically defined
+	  either in the kernel code itself, or as a kernel configuration
+	  option.  However, some of these are dynamically created at
+	  runtime, or can be modified after the kernel has started up.
+	  To provide an additional layer of security, route all of these
+	  calls through a single executable that can not have its name
+	  changed.
+
+	  Note, it is up to this single binary to then call the relevant
+	  "real" usermode helper binary, based on the first argument
+	  passed to it.  If desired, this program can filter and pick
+	  and choose what real programs are called.
+
+	  If you wish for all usermode helper programs are to be
+	  disabled, choose this option and then set
+	  STATIC_USERMODEHELPER_PATH to an empty string.
+
+config STATIC_USERMODEHELPER_PATH
+	string "Path to the static usermode helper binary"
+	depends on STATIC_USERMODEHELPER
+	default "/sbin/usermode-helper"
+	help
+	  The binary called by the kernel when any usermode helper
+	  program is wish to be run.  The "real" application's name will
+	  be in the first argument passed to this program on the command
+	  line.
+
+	  If you wish for all usermode helper programs to be disabled,
+	  specify an empty string here (i.e. "").
Quoted 8 lines

security/Kconfig config HARDENED_USERCOPY_PAGESPAN · line 198

Quoted 4 lines
security/Kconfig +1 -0
+
 source security/selinux/Kconfig
 source security/smack/Kconfig
 source security/tomoyo/Kconfig

I like the core of this idea (having a single dispatch binary) a lot. It seems like a good way to limit the attack surface. We could even consider signing this binary as well, etc...

Quoted 3 lines

Or use a read-only partition that is checked with dm-verity at boot so you know it's safe. Lots of ways to protect this file.

...and the binary could be even more helpful too -- drop capabilities or change task creds before calling exec, for some binaries for instance.

This may even provide a way to allow the upcalls to switch to the appropriate namespaces maybe based on info passed in env vars?

In any case, there are a lot of useful possibilities here.

Quoted 11 lines
Quoted 3 lines

I'm less excited though about using the binary pathnames in argv[0]. Would it be better to pass a non-path string of some sort that the binary would have to turn into a path to the binary to exec?

What exactly do you mean by "non-path" string? You can do whatever you want with the argv[0] value in your new binary, but rewriting all of the users of this interface in the kernel to pass in some other type of value instead of a full path, that doesn't make much sense (hint a path is just as good as anything else.)

I guess I'm thinking that this new binary is an opportunity to formalize the usermode helper upcall stuff to be more of a real ABI. It's rather haphazard now.

So yeah, the pathname strings would work fine as dispatch keys, but it is a rather ugly interface. It might be nicer to pass in some set of opaque string tokens so that the upcalls being requested have a real meaning that's not defined by the legacy pathnames?

Hmm...I guess in that case we could pass this token along in the env array as well. Fair enough, I think this is fine.

author maintainer
#17 lore raw
In reply to Greg KH
Quoted 6 lines · Greg KH

Hi all,

Here's a second cut at my attempt to make call_usermodehelper a bit more "safe". It includes some patches from my previous series, and one new one. In all, this is a much smaller patchset, with better functionality in the end.

And of course, I miscounted the patches, there are only 3 patches in this series, yet I said 0/4 in this email, sorry about that.

greg k-h

Acked-by on the v1 series
#18 lore raw
In reply to Greg KH
Quoted 30 lines · Greg KH

Hi all,

Here's a second cut at my attempt to make call_usermodehelper a bit more "safe". It includes some patches from my previous series, and one new one. In all, this is a much smaller patchset, with better functionality in the end.

The issue is that if you end up getting write access to kernel memory, if you change the string '/sbin/hotplug' to point to '/home/hacked/my_binary', then the next uevent that the system makes will call this binary instead of the "trusted" one.

This series addresses this issue by doing two different things. The first 2 patches move a lot of existing call_usermodehelper binaries to read-only memory, preventing them from being able to be changed at all.

The last patch introduces a new configuration option, STATIC_USERMODEHELPER. This option routes all call_usermodehelper() calls to a single userspace binary. That binary can then filter/mediate/blacklist/whitelist/whatever the "real" usermodehelper binaries and call them as needed (it determines the real one by looking at the first argument.)

The location of this new binary can be set with the STATIC_USERMODEHELPER_PATH configuration option.

If the user wants call_usermodehelper() to be disabled entirely, STATIC_USERMODEHELPER_PATH can be set to "", which will cause all call_usermodehelper() calls to do nothing, but return successful.

Quoted 6 lines

Many thanks to the reviewers of the last patch series for their hints on how to mark strings properly to live in read-only memory always, and to Neil Brown for the idea of STATIC_USERMODEHELPER.

If there are no complaints about these patches, I'll take them through my driver-core tree.

I like it! More things into .rodata. :)

Consider all three patches:

-Kees

Signature · 2 lines
Kees Cook
Nexus Security