diff --git a/include/ntfs-3g/security.h b/include/ntfs-3g/security.h index 4aa3e076..0d913413 100644 --- a/include/ntfs-3g/security.h +++ b/include/ntfs-3g/security.h @@ -48,7 +48,7 @@ struct CACHED_PERMISSIONS { gid_t gid; le32 inh_fileid; le32 inh_dirid; - unsigned int mode:9; + unsigned int mode:12; unsigned int valid:1; } ; @@ -149,7 +149,7 @@ int ntfs_get_owner_mode(struct SECURITY_CONTEXT *scx, const char *path, ntfs_inode *ni, struct stat*); int ntfs_set_mode(struct SECURITY_CONTEXT *scx, const char *path, ntfs_inode *ni, mode_t mode); -BOOL ntfs_allowed_access(struct SECURITY_CONTEXT *scx, const char *path, +int ntfs_allowed_access(struct SECURITY_CONTEXT *scx, const char *path, ntfs_inode *ni, int accesstype); BOOL ntfs_allowed_dir_access(struct SECURITY_CONTEXT *scx, const char *path, int accesstype); diff --git a/libntfs-3g/security.c b/libntfs-3g/security.c index d8ba9ab4..2e9fc2e9 100644 --- a/libntfs-3g/security.c +++ b/libntfs-3g/security.c @@ -83,31 +83,40 @@ /* flags which are set to mean exec, write or read */ -#define FILE_READ (FILE_READ_DATA | FILE_READ_EA) -#define FILE_WRITE (FILE_WRITE_DATA | FILE_APPEND_DATA | FILE_WRITE_EA) +#define FILE_READ FILE_READ_DATA +#define FILE_WRITE (FILE_WRITE_DATA | FILE_APPEND_DATA) #define FILE_EXEC (FILE_EXECUTE) -#define DIR_READ (FILE_LIST_DIRECTORY | FILE_READ_EA) -#define DIR_WRITE (FILE_ADD_FILE | FILE_ADD_SUBDIRECTORY \ - | FILE_WRITE_EA | FILE_DELETE_CHILD) +#define DIR_READ FILE_LIST_DIRECTORY +#define DIR_WRITE (FILE_ADD_FILE | FILE_ADD_SUBDIRECTORY | FILE_DELETE_CHILD) #define DIR_EXEC (FILE_TRAVERSE) - /* flags interpreted as meaning exec, write or read */ + /* flags which must be different to mean sticky bit */ +#define FILE_STICKY (FILE_WRITE_DATA | FILE_APPEND_DATA) +#define DIR_STICKY (FILE_ADD_SUBDIRECTORY | FILE_DELETE_CHILD) + /* flags which must be different to mean setuid or setgid */ +#define FILE_SETUID (FILE_WRITE_ATTRIBUTES | FILE_WRITE_EA) +#define FILE_SETGID (FILE_WRITE_ATTRIBUTES | FILE_WRITE_EA) + + /* flags tested for meaning exec, write or read */ + /* tests for write allow for interpretation of a sticky bit */ #define FILE_GREAD (FILE_READ | GENERIC_READ) -#define FILE_GWRITE (FILE_WRITE | GENERIC_WRITE) +#define FILE_GWRITE (FILE_WRITE_DATA | GENERIC_WRITE) #define FILE_GEXEC (FILE_EXEC | GENERIC_EXECUTE) #define DIR_GREAD (DIR_READ | GENERIC_READ) -#define DIR_GWRITE (DIR_WRITE | GENERIC_WRITE) +#define DIR_GWRITE (FILE_ADD_FILE | GENERIC_WRITE) #define DIR_GEXEC (DIR_EXEC | GENERIC_EXECUTE) /* standard owner (and administrator) rights */ -#define OWNER_RIGHTS (DELETE | READ_CONTROL | WRITE_DAC | WRITE_OWNER | SYNCHRONIZE \ - | FILE_READ_ATTRIBUTES | FILE_WRITE_ATTRIBUTES) +#define OWNER_RIGHTS (DELETE | READ_CONTROL | WRITE_DAC | WRITE_OWNER \ + | SYNCHRONIZE \ + | FILE_READ_ATTRIBUTES | FILE_WRITE_ATTRIBUTES \ + | FILE_READ_EA | FILE_WRITE_EA) /* standard world rights */ -#define WORLD_RIGHTS (READ_CONTROL | FILE_READ_ATTRIBUTES); +#define WORLD_RIGHTS (READ_CONTROL | FILE_READ_ATTRIBUTES | FILE_READ_EA) /* inheritance flags for files and directories */ @@ -1626,7 +1635,7 @@ static const struct CACHED_SECURID *enter_securid(struct SECURITY_CONTEXT *scx, struct CACHED_SECURID *previous; struct CACHED_SECURID *before; - mode &= 0777; + mode &= 07777; cache = *scx->pseccache; if (cache || (cache = create_caches(scx, le32_to_cpu(securid)))) { @@ -1781,7 +1790,7 @@ static struct CACHED_PERMISSIONS *enter_cache(struct SECURITY_CONTEXT *scx, - pcache->head.first]; cacheentry->uid = uid; cacheentry->gid = gid; - cacheentry->mode = mode & 0777; + cacheentry->mode = mode & 07777; cacheentry->inh_fileid = cpu_to_le32(0); cacheentry->inh_dirid = cpu_to_le32(0); cacheentry->valid = 1; @@ -1798,7 +1807,7 @@ static struct CACHED_PERMISSIONS *enter_cache(struct SECURITY_CONTEXT *scx, if (cacheentry) { cacheentry->uid = uid; cacheentry->gid = gid; - cacheentry->mode = mode & 0777; + cacheentry->mode = mode & 07777; cacheentry->inh_fileid = cpu_to_le32(0); cacheentry->inh_dirid = cpu_to_le32(0); cacheentry->valid = 1; @@ -1962,6 +1971,15 @@ static char *retrievesecurityattr(ntfs_volume *vol, SII_INDEX_KEY id) * On Windows, these ACE's are processed normally, though they * are redundant (as owner and group are the same), but this has * no impact on administrator rights + * + * Special cases : + * - a directory with sticky bit is represented as a directory in + * which world can add directories and not delete or conversely + * - a file with sticky bit is represented as a file which the + * owner can write to and not append or conversely + * - a file with setuid or setgid is represented as a file with + * the right to write extended attributes different from the + * right to write standard attributes */ static int buildacls(char *secattr, int offs, mode_t mode, int isdir, @@ -2021,6 +2039,10 @@ static int buildacls(char *secattr, int offs, mode_t mode, int isdir, grants |= FILE_WRITE; if (mode & S_IRUSR) grants |= FILE_READ; + if (mode & S_ISVTX) + grants ^= FILE_APPEND_DATA; + if (mode & S_ISUID) + grants ^= FILE_WRITE_EA; } pgace->size = cpu_to_le16(usidsz + 8); pgace->mask = cpu_to_le32(grants); @@ -2088,7 +2110,8 @@ static int buildacls(char *secattr, int offs, mode_t mode, int isdir, /* but present if owner is administrator */ if (adminowns - || (((mode >> 3) ^ mode) & 7)) { + || (((mode >> 3) ^ mode) & 7) + || (mode & S_ISGID)) { pgace = (ACCESS_ALLOWED_ACE*)&secattr[offs + pos]; pgace->type = ACCESS_ALLOWED_ACE_TYPE; grants = WORLD_RIGHTS; @@ -2108,6 +2131,10 @@ static int buildacls(char *secattr, int offs, mode_t mode, int isdir, grants |= FILE_WRITE; if (mode & S_IRGRP) grants |= FILE_READ; + if (mode & S_ISVTX) + grants ^= FILE_APPEND_DATA; + if (mode & S_ISGID) + grants ^= FILE_WRITE_EA; } pgace->size = cpu_to_le16(gsidsz + 8); pgace->mask = cpu_to_le32(grants); @@ -2164,6 +2191,8 @@ static int buildacls(char *secattr, int offs, mode_t mode, int isdir, grants |= DIR_WRITE; if (mode & S_IROTH) grants |= DIR_READ; + if (mode & S_ISVTX) + grants ^= FILE_ADD_SUBDIRECTORY; } else { pgace->flags = FILE_INHERITANCE; if (mode & S_IXOTH) @@ -2414,6 +2443,14 @@ static int merge_permissions(ntfs_inode *ni, /* read if any of readdata or generic read */ if (owner & FILE_GREAD) perm |= S_IRUSR; + /* sticky if write different from append */ + if (((owner & FILE_STICKY) + && ((owner & FILE_STICKY) != FILE_STICKY))) + perm |= S_ISVTX; + /* setuid if write_ea different from write_attr */ + if (((owner & FILE_SETUID) + && ((owner & FILE_SETUID) != FILE_SETUID))) + perm |= S_ISUID; } } /* build group permission */ @@ -2438,6 +2475,10 @@ static int merge_permissions(ntfs_inode *ni, /* read if any of readdata */ if (group & FILE_GREAD) perm |= S_IRGRP; + /* setgid if write_ea different from write_attr */ + if (((group & FILE_SETGID) + && ((group & FILE_SETGID) != FILE_SETGID))) + perm |= S_ISGID; } } /* build world permission */ @@ -2452,6 +2493,10 @@ static int merge_permissions(ntfs_inode *ni, /* read if any of list */ if (world & DIR_GREAD) perm |= S_IROTH; + /* sticky if adddir different from delete_child */ + if (((world & DIR_STICKY) + && ((world & DIR_STICKY) != DIR_STICKY))) + perm |= S_ISVTX; } else { /* exec if execute */ if (world & FILE_GEXEC) @@ -2703,7 +2748,7 @@ static int ntfs_get_perm(struct SECURITY_CONTEXT *scx, int perm; if (!scx->usermapping || !scx->uid) - perm = 0777; + perm = 07777; else { /* check whether available in cache */ cached = fetch_cache(scx,ni); @@ -2752,12 +2797,12 @@ static int ntfs_get_perm(struct SECURITY_CONTEXT *scx, } if (perm >= 0) { if (uid == scx->uid) - perm &= 0700; + perm &= 07700; else if (gid == scx->gid) - perm &= 070; + perm &= 07070; else - perm &= 007; + perm &= 07007; } } return (perm); @@ -2780,7 +2825,7 @@ int ntfs_get_owner_mode(struct SECURITY_CONTEXT *scx, int perm; if (!scx->usermapping) - perm = 0777; + perm = 07777; else { /* check whether available in cache */ cached = fetch_cache(scx,ni); @@ -2788,7 +2833,7 @@ int ntfs_get_owner_mode(struct SECURITY_CONTEXT *scx, perm = cached->mode; stbuf->st_uid = cached->uid; stbuf->st_gid = cached->gid; - stbuf->st_mode = (stbuf->st_mode & ~0777) + perm; + stbuf->st_mode = (stbuf->st_mode & ~07777) + perm; } else { perm = -1; /* default to error */ securattr = getsecurityattr(scx->vol, path, ni); @@ -2818,7 +2863,7 @@ int ntfs_get_owner_mode(struct SECURITY_CONTEXT *scx, stbuf->st_uid = findowner(scx,usid); stbuf->st_gid = findgroup(scx,gsid); stbuf->st_mode = - (stbuf->st_mode & ~0777) + perm; + (stbuf->st_mode & ~07777) + perm; enter_cache(scx, ni, stbuf->st_uid, stbuf->st_gid, perm); } @@ -2851,7 +2896,7 @@ int ntfs_set_owner_mode(struct SECURITY_CONTEXT *scx, ntfs_inode *ni, /* check whether target securid is known in cache */ if (test_nino_flag(ni, v3_Extensions)) { - cached = fetch_securid(scx, uid, gid, mode & 0777); + cached = fetch_securid(scx, uid, gid, mode & 07777); /* quite simple, if we are lucky */ if (cached) { ni->security_id = cached->securid; @@ -3032,12 +3077,13 @@ int ntfs_sd_add_everyone(ntfs_inode *ni) /* * Check whether user can access a file in a specific way * - * Always returns true is user is root or if no user mapping - * has been defined - * Sets errno if there is a problem or if access is not allowed + * Returns 1 if user is root or if no user mapping has been defined + * 2 if sticky and accesstype is S_IWRITE + S_IEXEC + S_ISVTX + * 0 and sets errno if there is a problem or if access + * is not allowed */ -BOOL ntfs_allowed_access(struct SECURITY_CONTEXT *scx, +int ntfs_allowed_access(struct SECURITY_CONTEXT *scx, const char *path, ntfs_inode *ni, int accesstype) /* access type required (S_Ixxx values) */ { @@ -3072,6 +3118,13 @@ BOOL ntfs_allowed_access(struct SECURITY_CONTEXT *scx, allow = ((perm & (S_IRUSR | S_IRGRP | S_IROTH)) != 0) && ((perm & (S_IWUSR | S_IWGRP | S_IWOTH)) != 0); break; + case S_IWRITE + S_IEXEC + S_ISVTX: + if (perm & S_ISVTX) + allow = 2; + else + allow = ((perm & (S_IWUSR | S_IWGRP | S_IWOTH)) != 0) + && ((perm & (S_IXUSR | S_IXGRP | S_IXOTH)) != 0); + break; default: /* BUG ! */ allow = 0; break; @@ -3086,17 +3139,18 @@ BOOL ntfs_allowed_access(struct SECURITY_CONTEXT *scx, * Check whether user can access the parent directory * of a file in a specific way * - * Always returns true is user is root or if no user mapping - * has been defined + * Returns true if user is root or if no user mapping has been defined + * * Sets errno if there is a problem or if not allowed */ BOOL ntfs_allowed_dir_access(struct SECURITY_CONTEXT *scx, const char *path, int accesstype) { - BOOL allow; + int allow; char *dirpath; char *name; + ntfs_inode *ni; ntfs_inode *dir_ni; allow = 0; @@ -3108,9 +3162,23 @@ BOOL ntfs_allowed_dir_access(struct SECURITY_CONTEXT *scx, *++name = 0; dir_ni = ntfs_pathname_to_inode(scx->vol, NULL, dirpath); if (dir_ni) { - allow = ntfs_allowed_access(scx,path, + allow = ntfs_allowed_access(scx,dirpath, dir_ni, accesstype); ntfs_inode_close(dir_ni); + /* + * for a sticky directory, have to retry + * and check whether file itself is writeable + */ + if ((accesstype == (S_IWRITE + S_IEXEC + S_ISVTX)) + && (allow == 2)) { + ni = ntfs_pathname_to_inode(scx->vol, NULL, + path); + if (ni) { + allow = ntfs_allowed_access(scx,path, + ni, S_IWRITE); + ntfs_inode_close(ni); + } + } } free(dirpath); } diff --git a/src/ntfs-3g.c b/src/ntfs-3g.c index 8adefb45..6fb834a4 100644 --- a/src/ntfs-3g.c +++ b/src/ntfs-3g.c @@ -964,7 +964,7 @@ static int ntfs_fuse_create(const char *org_path, dev_t typemode, dev_t dev, char *path; ntfschar *stream_name; int stream_name_len; - dev_t type = typemode & ~0777; + dev_t type = typemode & ~07777; mode_t perm; struct SECURITY_CONTEXT security; int res = 0, uname_len, utarget_len; @@ -1132,7 +1132,7 @@ static int ntfs_fuse_mknod(const char *org_path, mode_t mode, dev_t dev) goto exit; } if (!stream_name_len) - res = ntfs_fuse_create(path, mode & (S_IFMT | 0777), dev, NULL); + res = ntfs_fuse_create(path, mode & (S_IFMT | 07777), dev, NULL); else res = ntfs_fuse_create_stream(path, stream_name, stream_name_len); @@ -1304,12 +1304,14 @@ static int ntfs_fuse_unlink(const char *org_path) return stream_name_len; /* JPA deny unlinking if directory is not writable and executable */ if (!ntfs_fuse_fill_security_context(&security) - || ntfs_allowed_dir_access(&security,path,S_IEXEC + S_IWRITE)) { + || ntfs_allowed_dir_access(&security, path, + S_IEXEC + S_IWRITE + S_ISVTX)) { if (!stream_name_len) res = ntfs_fuse_rm(path); else res = ntfs_fuse_rm_stream(path, stream_name, stream_name_len); - } + } else + res = -errno; free(path); if (stream_name_len) free(stream_name); @@ -1433,7 +1435,7 @@ static int ntfs_fuse_mkdir(const char *path, { if (ntfs_fuse_is_named_data_stream(path)) return -EINVAL; /* n/a for named data streams. */ - return ntfs_fuse_create(path, S_IFDIR | (mode & 0777), 0, NULL); + return ntfs_fuse_create(path, S_IFDIR | (mode & 07777), 0, NULL); } static int ntfs_fuse_rmdir(const char *path) @@ -2571,7 +2573,6 @@ int main(int argc, char *argv[]) ntfs_log_info("User mapping built\n"); else ntfs_log_info("Failed to build user mapping\n"); - } fuse_loop(fh);