Hello, Nowaday, I'm considering about a philosophical theme. In my understanding, file-metadata includes _filename_ similar to filesize, update-timestamp, and so on. Most of permissions for reading metadata are defined as 'getattr' in SELinux. But permission for reading filename is defined as 'read' of parent directory, 'getattr' of each child-entries are not evaluated. It seems a bit curious behavior for me. Why can an unauthorized process be allowed to know whether the file exists or not ? I think it's worthwhile to conceal the existence of files from unauthorized processes. This patch enables to conceal the unauthorized files. [1/2] LSM_metadata_protection_v1.patch - add security_file_readdir(), it's called when readdir() write each filename to userspace. - add security_inode_lookup(), it's called when filename is resolved by path_walk(). [2/2] SELinux_metadata_protection_v1.patch - add checking 'getattr' permission on file_readdirand inode_lookup hooks. Thanks, any comments please, and have a good year :) ---- without Metadata Protection patch -------- [root@saba test]# uname -r 2.6.14-1.1653_FC4 [root@saba test]# id -Z root:system_r:unconfined_t [root@saba test]# ls -lZ drwxr-xr-x root root root:object_r:tmp_t dir1 drwxr-xr-x root root root:object_r:hidden_file_t dir2 -rw-r--r-- root root root:object_r:tmp_t file1 -rw-r--r-- root root root:object_r:hidden_file_t file2 -rw-r--r-- root root root:object_r:hidden_file_t file3 -rw-r--r-- root root root:object_r:tmp_t file4 [root@saba test]# setenforce 1 [root@saba test]# ls -lZ <-- readdir() contains unauthorized filenames. drwxr-xr-x root root root:object_r:tmp_t dir1 ?--------- ? ? dir2 -rw-r--r-- root root root:object_r:tmp_t file1 ?--------- ? ? file2 <-- getattr is not allowed ?--------- ? ? file3 -rw-r--r-- root root root:object_r:tmp_t file4 [root@saba test]# cat file2 cat: file2: Permission denied <-- process can know "file2 exists!". [root@saba test]# ---- with Metadata Protection patch -------- [root@saba test]# uname -r 2.6.14.5-selinux.mp [root@saba test]# id -Z root:system_r:unconfined_t [root@saba test]# ls -lZ drwxr-xr-x root root root:object_r:tmp_t dir1 drwxr-xr-x root root root:object_r:hidden_file_t dir2 -rw-r--r-- root root root:object_r:tmp_t file1 -rw-r--r-- root root root:object_r:hidden_file_t file2 -rw-r--r-- root root root:object_r:hidden_file_t file3 -rw-r--r-- root root root:object_r:tmp_t file4 [root@saba test]# setenforce 1 [root@saba test]# ls -lZ <-- readdir() doesn't contain unauthorized filenames. drwxr-xr-x root root root:object_r:tmp_t dir1 -rw-r--r-- root root root:object_r:tmp_t file1 -rw-r--r-- root root root:object_r:tmp_t file4 [root@saba test]# cat file2 cat: file2: No such file or directory <-- process can not know whether file2 exists. [root@saba test]# -- KaiGai Kohei <kaigai@private> --- linux-2.6.14.5-selinux/security/selinux/hooks.c 2005-12-31 05:38:32.000000000 -0500 +++ linux-2.6.14.5-selinux.mp/security/selinux/hooks.c 2005-12-31 05:11:12.000000000 -0500 @@ -2009,6 +2009,11 @@ static int selinux_inode_init_security(s return 0; } +static int selinux_inode_lookup(struct vfsmount *mnt, struct dentry *dentry) +{ + return dentry_has_perm(current, mnt, dentry, FILE__GETATTR) ? -ENOENT : 0; +} + static int selinux_inode_create(struct inode *dir, struct dentry *dentry, int mask) { return may_create(dir, dentry, SECCLASS_FILE); @@ -2336,6 +2341,28 @@ static int selinux_file_permission(struc file_mask_to_av(inode->i_mode, mask)); } +static int selinux_file_readdir(struct file *dir, struct qstr *qname) +{ + struct dentry *dentry; + int rc = -ENOENT; + + if (qname->len==1 && qname->name[0]=='.') { + dentry = dget(dir->f_dentry); + } else if (qname->len==2 && qname->name[0]=='.' && qname->name[1]=='.') { + dentry = dget(dir->f_dentry->d_parent); + } else { + dentry = lookup_hash(qname, dir->f_dentry); + } + if (!IS_ERR(dentry)) { + if (dentry->d_inode) + rc = dentry_has_perm(current, dir->f_vfsmnt, dentry, FILE__GETATTR); + dput(dentry); + } else { + rc = PTR_ERR(dentry); + } + return selinux_enforcing ? rc : 0; +} + static int selinux_file_alloc_security(struct file *file) { return file_alloc_security(file); @@ -4274,6 +4301,7 @@ static struct security_operations selinu .inode_alloc_security = selinux_inode_alloc_security, .inode_free_security = selinux_inode_free_security, .inode_init_security = selinux_inode_init_security, + .inode_lookup = selinux_inode_lookup, .inode_create = selinux_inode_create, .inode_link = selinux_inode_link, .inode_unlink = selinux_inode_unlink, @@ -4297,6 +4325,7 @@ static struct security_operations selinu .inode_listsecurity = selinux_inode_listsecurity, .file_permission = selinux_file_permission, + .file_readdir = selinux_file_readdir, .file_alloc_security = selinux_file_alloc_security, .file_free_security = selinux_file_free_security, .file_ioctl = selinux_file_ioctl, --- linux-2.6.14.5-selinux/include/linux/security.h 2005-12-31 05:38:32.000000000 -0500 +++ linux-2.6.14.5-selinux.mp/include/linux/security.h 2005-12-31 05:35:26.000000000 -0500 @@ -269,6 +269,11 @@ struct swap_info_struct; * Returns 0 if @name and @value have been successfully set, * -EOPNOTSUPP if no security attribute is needed, or * -ENOMEM on memory allocation failure. + * @ inode_lookup + * Check permission before resolving a filename. + * @mnt is the vfsmount where the dentry was looked up + * @dentry contains the dentry structure for the file. + * Return 0 if permission is granted. * @inode_create: * Check permission to create a regular file. * @dir contains inode structure of the parent of the new file. @@ -422,6 +427,13 @@ struct swap_info_struct; * @file contains the file structure being accessed. * @mask contains the requested permissions. * Return 0 if permission is granted. + * @file_readdir + * Check permission while an readdir operation. + * This hook is called for each child entry. If reading filename of + * the child entry is not permitted, a filldir operation will skipped. + * @dir contains the file structure of the parent directory. + * @qname contains the qstr structure of the name of child entry. + * Return 0 if permission is granted. * @file_alloc_security: * Allocate and attach a security structure to the file->f_security field. * The security field is initialized to NULL when the structure is first @@ -1068,6 +1080,7 @@ struct security_operations { void (*inode_free_security) (struct inode *inode); int (*inode_init_security) (struct inode *inode, struct inode *dir, char **name, void **value, size_t *len); + int (*inode_lookup) (struct vfsmount *mnt, struct dentry *dentry); int (*inode_create) (struct inode *dir, struct dentry *dentry, int mode); int (*inode_link) (struct dentry *old_dentry, @@ -1099,6 +1112,7 @@ struct security_operations { int (*inode_listsecurity)(struct inode *inode, char *buffer, size_t buffer_size); int (*file_permission) (struct file * file, int mask); + int (*file_readdir) (struct file * dir, struct qstr *qname); int (*file_alloc_security) (struct file * file); void (*file_free_security) (struct file * file); int (*file_ioctl) (struct file * file, unsigned int cmd, @@ -1426,7 +1440,15 @@ static inline int security_inode_init_se return -EOPNOTSUPP; return security_ops->inode_init_security (inode, dir, name, value, len); } - + +static inline int security_inode_lookup (struct vfsmount *mnt, + struct dentry *dentry) +{ + if (unlikely (IS_PRIVATE (dentry->d_inode))) + return 0; + return security_ops->inode_lookup(mnt, dentry); +} + static inline int security_inode_create (struct inode *dir, struct dentry *dentry, int mode) @@ -1609,6 +1631,25 @@ static inline int security_file_permissi return security_ops->file_permission (file, mask); } +extern int security_file_filldir (void *buffer, const char *name, int namelen, + loff_t offset, ino_t ino, unsigned int d_type); +typedef struct { + struct file *dir; + void *buffer; + filldir_t filler; +} security_filldir_t; + +static inline int security_file_readdir (struct file *dir, void *buffer, filldir_t filler) +{ + security_filldir_t private; + + private.dir = dir; + private.buffer = buffer; + private.filler = filler; + + return dir->f_op->readdir (dir, &private, security_file_filldir); +} + static inline int security_file_alloc (struct file *file) { return security_ops->file_alloc_security (file); @@ -2112,7 +2153,13 @@ static inline int security_inode_init_se { return -EOPNOTSUPP; } - + +static inline int security_inode_lookup (struct dentry *dentry, + struct vfsmount *mnt) +{ + return 0; +} + static inline int security_inode_create (struct inode *dir, struct dentry *dentry, int mode) @@ -2245,6 +2292,11 @@ static inline int security_file_permissi return 0; } +static inline int security_file_readdir (struct file *dir, void *buffer, filldir_t filldir) +{ + return dir->f_op->readdir(dir, buffer, filldir); +} + static inline int security_file_alloc (struct file *file) { return 0; --- linux-2.6.14.5-selinux/fs/readdir.c 2005-12-26 19:26:33.000000000 -0500 +++ linux-2.6.14.5-selinux.mp/fs/readdir.c 2005-12-29 20:26:55.000000000 -0500 @@ -33,7 +33,11 @@ int vfs_readdir(struct file *file, filld down(&inode->i_sem); res = -ENOENT; if (!IS_DEADDIR(inode)) { - res = file->f_op->readdir(file, buf, filler); + /* NOTE: + When LSM was not enable, security_file_readdir() + is same as 'file->f_op->readdir()'. + */ + res = security_file_readdir(file, buf, filler); file_accessed(file); } up(&inode->i_sem); --- linux-2.6.14.5-selinux/fs/namei.c 2005-12-26 19:26:33.000000000 -0500 +++ linux-2.6.14.5-selinux.mp/fs/namei.c 2005-12-31 05:10:04.000000000 -0500 @@ -812,6 +812,9 @@ static fastcall int __link_path_walk(con inode = next.dentry->d_inode; if (!inode) goto out_dput; + err = security_inode_lookup(next.mnt, next.dentry); + if (err) + goto out_dput; err = -ENOTDIR; if (!inode->i_op) goto out_dput; @@ -862,6 +865,11 @@ last_component: if (err) break; inode = next.dentry->d_inode; + if (inode) { + err = security_inode_lookup(next.mnt, next.dentry); + if (err) + break; + } if ((lookup_flags & LOOKUP_FOLLOW) && inode && inode->i_op && inode->i_op->follow_link) { err = do_follow_link(&next, nd); --- linux-2.6.14.5-selinux/security/security.c 2005-12-26 19:26:33.000000000 -0500 +++ linux-2.6.14.5-selinux.mp/security/security.c 2005-12-29 20:26:55.000000000 -0500 @@ -195,6 +195,23 @@ int capable(int cap) return 1; } +int security_file_filldir (void *buffer, const char *name, int namelen, + loff_t offset, ino_t ino, unsigned int d_type) +{ + security_filldir_t *private = (security_filldir_t *)buffer; + struct file *dir = private->dir; + struct qstr qname; + + qname.name = name; + qname.len = namelen; + qname.hash = full_name_hash(qname.name, qname.len); + + if (!security_ops->file_readdir(dir, &qname)) + return private->filler(private->buffer, name, namelen, offset, ino, d_type); + + return 0; /* skip filler if LSM denied. */ +} + EXPORT_SYMBOL_GPL(register_security); EXPORT_SYMBOL_GPL(unregister_security); EXPORT_SYMBOL_GPL(mod_reg_security); --- linux-2.6.14.5-selinux/security/dummy.c 2005-12-31 05:38:32.000000000 -0500 +++ linux-2.6.14.5-selinux.mp/security/dummy.c 2005-12-31 05:10:35.000000000 -0500 @@ -264,6 +264,11 @@ static int dummy_inode_init_security (st return -EOPNOTSUPP; } +static int dummy_inode_lookup (struct vfsmount *mnt, struct dentry *dentry) +{ + return 0; +} + static int dummy_inode_create (struct inode *inode, struct dentry *dentry, int mask) { @@ -397,6 +402,11 @@ static int dummy_file_permission (struct return 0; } +static int dummy_file_readdir (struct file *dir, struct dentry *child) +{ + return 0; +} + static int dummy_file_alloc_security (struct file *file) { return 0; @@ -854,6 +864,7 @@ void security_fixup_ops (struct security set_to_dummy_if_null(ops, inode_alloc_security); set_to_dummy_if_null(ops, inode_free_security); set_to_dummy_if_null(ops, inode_init_security); + set_to_dummy_if_null(ops, inode_lookup); set_to_dummy_if_null(ops, inode_create); set_to_dummy_if_null(ops, inode_link); set_to_dummy_if_null(ops, inode_unlink); @@ -877,6 +888,7 @@ void security_fixup_ops (struct security set_to_dummy_if_null(ops, inode_setsecurity); set_to_dummy_if_null(ops, inode_listsecurity); set_to_dummy_if_null(ops, file_permission); + set_to_dummy_if_null(ops, file_readdir); set_to_dummy_if_null(ops, file_alloc_security); set_to_dummy_if_null(ops, file_free_security); set_to_dummy_if_null(ops, file_ioctl);
This archive was generated by hypermail 2.1.3 : Sat Dec 31 2005 - 10:15:58 PST