Index: appfsd.c ================================================================== --- appfsd.c +++ appfsd.c @@ -141,11 +141,11 @@ struct { int childcount; } dir; struct { int executable; - int suid; + int suidRoot; int worldaccessible; off_t size; } file; struct { off_t size; @@ -934,11 +934,11 @@ break; case 'f': /* file */ pathinfo->type = APPFS_PATHTYPE_FILE; pathinfo->typeinfo.file.size = 0; pathinfo->typeinfo.file.executable = 0; - pathinfo->typeinfo.file.suid = 0; + pathinfo->typeinfo.file.suidRoot = 0; pathinfo->typeinfo.file.worldaccessible = 0; Tcl_DictObjGet(interp, attrs_dict, attr_key_size, &attr_value); if (attr_value != NULL) { tcl_ret = Tcl_GetWideIntFromObj(NULL, attr_value, &attr_value_wide); @@ -955,11 +955,11 @@ case 'x': pathinfo->typeinfo.file.executable = 1; break; case 'U': - pathinfo->typeinfo.file.suid = 1; + pathinfo->typeinfo.file.suidRoot = 1; break; case '-': pathinfo->typeinfo.file.worldaccessible = 1; @@ -1150,10 +1150,11 @@ return(0); } static int appfs_fuse_getattr(const char *path, struct stat *stbuf) { struct appfs_pathinfo pathinfo; + int changeOwnerToUserIfPackaged; int retval; retval = 0; APPFS_DEBUG("Enter (path = %s, ...)", path); @@ -1188,24 +1189,28 @@ stbuf->st_ctime = pathinfo.time; stbuf->st_atime = pathinfo.time; stbuf->st_ino = pathinfo.inode; stbuf->st_mode = 0; + changeOwnerToUserIfPackaged = 1; + switch (pathinfo.type) { case APPFS_PATHTYPE_DIRECTORY: stbuf->st_mode = S_IFDIR | 0555; stbuf->st_nlink = 2 + pathinfo.typeinfo.dir.childcount; break; case APPFS_PATHTYPE_FILE: + stbuf->st_mode = S_IFREG | 0444; + if (pathinfo.typeinfo.file.executable) { - stbuf->st_mode = S_IFREG | 0555; - } else { - stbuf->st_mode = S_IFREG | 0444; + stbuf->st_mode |= 0111; } - if (pathinfo.typeinfo.file.suid) { - stbuf->st_mode = S_IFREG | 04000; + if (pathinfo.typeinfo.file.suidRoot) { + changeOwnerToUserIfPackaged = 0; + + stbuf->st_mode |= 04000; } if (pathinfo.typeinfo.file.worldaccessible) { stbuf->st_mode &= ~077; } @@ -1237,11 +1242,11 @@ retval = -EIO; break; } - if (pathinfo.packaged) { + if (pathinfo.packaged && changeOwnerToUserIfPackaged) { stbuf->st_uid = appfs_get_fsuid(); stbuf->st_gid = appfs_get_fsgid(); stbuf->st_mode |= 0200; } @@ -2008,10 +2013,17 @@ fuse_opt_add_arg(args, "-odefault_permissions,fsname=appfs,subtype=appfsd,use_ino,kernel_cache,entry_timeout=0,attr_timeout=0,big_writes,intr,hard_remove"); if (getuid() == 0) { fuse_opt_parse(args, NULL, NULL, NULL); fuse_opt_add_arg(args, "-oallow_other"); + + /* + * This should generally be avoided, but if there are security + * concerns suid can be disabled completely on the commandline + */ + fuse_opt_parse(args, NULL, NULL, NULL); + fuse_opt_add_arg(args, "-osuid"); } while ((ch = getopt(argc, argv, "dfshvo:")) != -1) { switch (ch) { case 'v': Index: appfsd.tcl ================================================================== --- appfsd.tcl +++ appfsd.tcl @@ -38,10 +38,19 @@ # User-replaceable function get the home directory of the current user proc get_homedir {} { return [::appfsd::get_homedir] } + + # User-replacable function to update permissions + proc change_perms {file perms} { + if {[info exists ::appfs::user::add_perms($file)]} { + append perms $::appfs::user::add_perms($file) + } + + return $perms + } } namespace eval ::appfs { variable cachedir "/tmp/appfs-cache" variable ttl 3600 @@ -753,10 +762,14 @@ set retval(childcount) [llength [getchildren $path]] } "file" { set retval(type) "file" set retval(size) $localpathinfo(size) + + # Once the user writes to a file, all its other + # attributes (such as suid) are lost + _as_user { if {[file executable $localpath]} { set retval(perms) "x-" } else { set retval(perms) "-" @@ -791,10 +804,15 @@ if {$directory == "" && $file == ""} { array set retval [list type directory] } ::appfs::db eval {SELECT type, time, source, size, perms FROM files WHERE package_sha1 = $pathinfo(package_sha1) AND file_directory = $directory AND file_name = $file;} retval {} + + # Allow an administrator to supply additional permissions to remote files + if {[info exists retval(perms)]} { + set retval(perms) [::appfs::user::change_perms $path $retval(perms)] + } if {[info exists retval(type)] && $retval(type) == "directory"} { set retval(childcount) [llength [getchildren $path]] }