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; }