Package: afuse
Version: 0.2-2
Severity: grave
Tags: security
Justification: user security hole

Hi

A privilege escalation has been reported against afuse.
This issue is CVE-2008-2232.

Here is some additional information:

afuse accepts a command line of the form
  afuse /path -o mount_template="mount-script %m %r" \
      unmount_template="unmount-script %m %r"
It replaces %m with the mountpoint and %r with the next component of the
pathname being accessed.  These interpolated strings are inserted inside
double quotes, but metacharacters within them are not escaped.  The
resulting string is then passed to system() and executed by the shell.

Therefore, an attacker with read access to the afuse filesystem can gain
the privileges of its owner, using paths such as
  /path/";arbitrary command;"
  /path/`arbitrary command`

The patch attached is from the original is from the original reporter
Anders Kaseorg, please honour him in the changelog.

When you fix this issue, please mention the CVE id in your changelog.

Cheers
Steffen
diff -ur afuse-0.2.orig/src/afuse.c afuse-0.2/src/afuse.c
--- afuse-0.2.orig/src/afuse.c	2008-02-18 17:16:32.000000000 -0500
+++ afuse-0.2/src/afuse.c	2008-07-10 21:50:06.000000000 -0400
@@ -280,14 +280,19 @@
 }
 
 
-// !!FIXME!! allow escaping of %'s
 // Note: this method strips out quotes and applies them itself as should be appropriate
-char *expand_template(const char *template, const char *mount_point, const char *root_name)
+bool run_template(const char *template, const char *mount_point, const char *root_name)
 {
 	int len = 0;
+	int nargs = 1;
 	int i;
-	char *expanded_name;
-	char *expanded_name_start;
+	char *buf;
+	char *p;
+	char **args;
+	char **arg;
+	bool quote = false;
+	pid_t pid;
+	int status;
 
 	// calculate length
 	for(i = 0; template[i]; i++)
@@ -295,53 +300,100 @@
 			switch(template[i + 1])
 			{
 				case 'm':
-					len += strlen(mount_point) + 2;
+					len += strlen(mount_point);
 					i++;
 					break;
 				case 'r':
-					len += strlen(root_name) + 2;
+					len += strlen(root_name);
+					i++;
+					break;
+				case '%':
+					len++;
 					i++;
 					break;
 			}
-		} else if(template[i] != '"')
+		} else if(template[i] == ' ' && !quote) {
+			len++;
+			nargs++;
+		} else if(template[i] == '"')
+			quote = !quote;
+		else if(template[i] == '\\' && template[i + 1])
+			len++, i++;
+		else
 			len++;
 
-	expanded_name_start = expanded_name = my_malloc(len + 1);
+	buf = my_malloc(len + 1);
+	args = my_malloc((nargs + 1) * sizeof(*args));
+
+	p = buf;
+	arg = args;
+	*arg++ = p;
 
 	for(i = 0; template[i]; i++)
 		if(template[i] == '%') {
-			int j = 0;
 			switch(template[i + 1])
 			{
 				case 'm':
-					*expanded_name++ = '"';
-					while(mount_point[j])
-						*expanded_name++ = mount_point[j++];
-					*expanded_name++ = '"';
+					strcpy(p, mount_point);
+					p += strlen(mount_point);
 					i++;
 					break;
 				case 'r':
-					*expanded_name++ = '"';
-					while(root_name[j])
-						*expanded_name++ = root_name[j++];
-					*expanded_name++ = '"';
+					strcpy(p, root_name);
+					p += strlen(root_name);
+					i++;
+					break;
+				case '%':
+					*p++ = '%';
 					i++;
 					break;
 			}
-		} else if(template[i] != '"')
-			*expanded_name++ = template[i];
-
-	*expanded_name = '\0';
-
-	return expanded_name_start;
+		} else if(template[i] == ' ' && !quote) {
+			*p++ = '\0';
+			*arg++ = p;
+		} else if(template[i] == '"')
+			quote = !quote;
+		else if(template[i] == '\\' && template[i + 1])
+			*p++ = template[++i];
+		else
+			*p++ = template[i];
+
+	*p = '\0';
+	*arg = NULL;
+
+	pid = fork();
+	if(pid == -1) {
+		fprintf(stderr, "Failed to fork (%s)\n", strerror(errno));
+		free(args);
+		free(buf);
+		return false;
+	}
+	if(pid == 0) {
+		execvp(args[0], args);
+		abort();
+	}
+	pid = waitpid(pid, &status, 0);
+	if(pid == -1) {
+		fprintf(stderr, "Failed to waitpid (%s)\n", strerror(errno));
+		free(args);
+		free(buf);
+		return false;
+	}
+	if(!WIFEXITED(status) || WEXITSTATUS(status) != 0) {
+		fprintf(stderr, "Failed to invoke command: %s\n", args[0]);
+		free(args);
+		free(buf);
+		return false;
+	}
+	free(args);
+	free(buf);
+	return true;
 }
 
 mount_list_t *do_mount(const char *root_name)
 {
 	char *mount_point;
-	char *mount_command;
 	mount_list_t *mount;
-	int sysret;
 
 	fprintf(stderr, "Mounting: %s\n", root_name);
 
@@ -351,57 +403,33 @@
 		return NULL;
 	}
 
-	mount_command = expand_template(user_options.mount_command_template,
-	                                mount_point, root_name);
-	sysret = system(mount_command);
-
-	fprintf(stderr, "sysret: %.8x\n", sysret);
-
-	if(sysret) {
-		fprintf(stderr, "Failed to invoke mount command: '%s' (%s)\n",
-			mount_command, sysret != -1 ?
-				"Error executing mount" :
-				strerror(errno));
-
+	if(!run_template(user_options.mount_command_template,
+			 mount_point, root_name)) {
 		// remove the now unused directory
 		if( rmdir(mount_point) == -1 )
 			fprintf(stderr, "Failed to remove mount point dir: %s (%s)",
 				mount_point, strerror(errno));
 
-		free(mount_command);
 		free(mount_point);
 		return NULL;
 	}
 
 	mount = add_mount(root_name, mount_point);
-
-	free(mount_command);
 	return mount;
 }
 
 int do_umount(mount_list_t *mount)
 {
-	char *unmount_command;
-	int sysret;
-
 	fprintf(stderr, "Unmounting: %s\n", mount->root_name);
 
-	unmount_command = expand_template(user_options.unmount_command_template,
-	                                  mount->mount_point, mount->root_name);
-	sysret = system(unmount_command);
-	if(sysret) {
-		fprintf(stderr, "Failed to invoke unmount command: '%s' (%s)\n",
-		        unmount_command, sysret != -1 ?
-			               "Error executing mount" :
-				       strerror(errno));
-		/* Still unmount anyway */
-	}
+	run_template(user_options.unmount_command_template,
+		     mount->mount_point, mount->root_name);
+	/* Still unmount anyway */
 
 	if( rmdir(mount->mount_point) == -1 )
 		fprintf(stderr, "Failed to remove mount point dir: %s (%s)",
 				mount->mount_point, strerror(errno));
 	remove_mount(mount);
-	free(unmount_command);
 	return 1;
 }
 

Reply via email to