An often found problem with selinux in the wild is the use of things like stdout redirection to a file. As an example it may be perfectly reasonable for a user to run a daemon in the foreground for debugging and pipe the output to a file in /tmp. But it would unreasonable for that daemon to directly open a file in /tmp. Currently SELinux sees both of these as the same security operation. By separating the open permission from the r/w permission we are able to more broadly grant r/w permissions while still being able to see and stop a number of attack vectors and misbehaving programs. --- This patch makes use of Paul Moore's new capability map but that was completely untested by me. I actually just added a new selinuxfs file to turn these checks on and off at will during my testing and never even defined the permissions in my running policy. So to say the least testing is a bit short. Does it look right to you on first glance Paul? It did however work. If prog1 opened a bunch of files and then execve'd prog2 we would have. prog1 need open+r/w and prog2 just needs r/w. security/selinux/hooks.c | 33 +++++++++++++++++++++++++- security/selinux/include/av_perm_to_string.h | 5 ++++ security/selinux/include/av_permissions.h | 5 ++++ security/selinux/include/security.h | 2 + security/selinux/ss/services.c | 3 ++ 5 files changed, 47 insertions(+), 1 deletions(-) diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c index 75c2e99..fd911fe 100644 --- a/security/selinux/hooks.c +++ b/security/selinux/hooks.c @@ -1616,6 +1616,37 @@ static inline u32 file_to_av(struct file *file) return av; } +/* + * Convert a Linux file to an access vector and include the correct open + * open permission. + */ +static inline u32 open_file_to_av(struct file *file) +{ + struct inode *inode = file->f_path.dentry->d_inode; + u32 av = file_to_av(file); + + if (selinux_policycap_openperm) { + /* + * lnk files and socks do not really have an 'open' + */ + if (S_ISREG(inode->i_mode)) + av |= FILE__OPEN; + else if (S_ISCHR(inode->i_mode)) + av |= CHR_FILE__OPEN; + else if (S_ISBLK(inode->i_mode)) + av |= BLK_FILE__OPEN; + else if (S_ISFIFO(inode->i_mode)) + av |= FIFO_FILE__OPEN; + else if (S_ISDIR(inode->i_mode)) + av |= DIR__OPEN; + else { + printk(KERN_EMERG "inside open_file_to_av with unknown mode:%x\n", inode->i_mode); + BUG(); + } + } + return av; +} + /* Hook functions begin here. */ static int selinux_ptrace(struct task_struct *parent, struct task_struct *child) @@ -3031,7 +3062,7 @@ static int selinux_dentry_open(struct file *file) * new inode label or new policy. * This check is not redundant - do not remove. */ - return inode_has_perm(current, inode, file_to_av(file), NULL); + return inode_has_perm(current, inode, open_file_to_av(file), NULL); } /* task security operations */ diff --git a/security/selinux/include/av_perm_to_string.h b/security/selinux/include/av_perm_to_string.h index d569669..1223b4f 100644 --- a/security/selinux/include/av_perm_to_string.h +++ b/security/selinux/include/av_perm_to_string.h @@ -14,12 +14,17 @@ S_(SECCLASS_DIR, DIR__REPARENT, "reparent") S_(SECCLASS_DIR, DIR__SEARCH, "search") S_(SECCLASS_DIR, DIR__RMDIR, "rmdir") + S_(SECCLASS_DIR, DIR__OPEN, "open") S_(SECCLASS_FILE, FILE__EXECUTE_NO_TRANS, "execute_no_trans") S_(SECCLASS_FILE, FILE__ENTRYPOINT, "entrypoint") S_(SECCLASS_FILE, FILE__EXECMOD, "execmod") + S_(SECCLASS_FILE, FILE__OPEN, "open") S_(SECCLASS_CHR_FILE, CHR_FILE__EXECUTE_NO_TRANS, "execute_no_trans") S_(SECCLASS_CHR_FILE, CHR_FILE__ENTRYPOINT, "entrypoint") S_(SECCLASS_CHR_FILE, CHR_FILE__EXECMOD, "execmod") + S_(SECCLASS_CHR_FILE, CHR_FILE__OPEN, "open") + S_(SECCLASS_BLK_FILE, BLK_FILE__OPEN, "open") + S_(SECCLASS_FIFO_FILE, FIFO_FILE__OPEN, "open") S_(SECCLASS_FD, FD__USE, "use") S_(SECCLASS_TCP_SOCKET, TCP_SOCKET__CONNECTTO, "connectto") S_(SECCLASS_TCP_SOCKET, TCP_SOCKET__NEWCONN, "newconn") diff --git a/security/selinux/include/av_permissions.h b/security/selinux/include/av_permissions.h index 75b4131..c4c5116 100644 --- a/security/selinux/include/av_permissions.h +++ b/security/selinux/include/av_permissions.h @@ -79,6 +79,7 @@ #define DIR__REPARENT 0x00080000UL #define DIR__SEARCH 0x00100000UL #define DIR__RMDIR 0x00200000UL +#define DIR__OPEN 0x00400000UL #define FILE__IOCTL 0x00000001UL #define FILE__READ 0x00000002UL #define FILE__WRITE 0x00000004UL @@ -99,6 +100,7 @@ #define FILE__EXECUTE_NO_TRANS 0x00020000UL #define FILE__ENTRYPOINT 0x00040000UL #define FILE__EXECMOD 0x00080000UL +#define FILE__OPEN 0x00100000UL #define LNK_FILE__IOCTL 0x00000001UL #define LNK_FILE__READ 0x00000002UL #define LNK_FILE__WRITE 0x00000004UL @@ -136,6 +138,7 @@ #define CHR_FILE__EXECUTE_NO_TRANS 0x00020000UL #define CHR_FILE__ENTRYPOINT 0x00040000UL #define CHR_FILE__EXECMOD 0x00080000UL +#define CHR_FILE__OPEN 0x00100000UL #define BLK_FILE__IOCTL 0x00000001UL #define BLK_FILE__READ 0x00000002UL #define BLK_FILE__WRITE 0x00000004UL @@ -153,6 +156,7 @@ #define BLK_FILE__SWAPON 0x00004000UL #define BLK_FILE__QUOTAON 0x00008000UL #define BLK_FILE__MOUNTON 0x00010000UL +#define BLK_FILE__OPEN 0x00020000UL #define SOCK_FILE__IOCTL 0x00000001UL #define SOCK_FILE__READ 0x00000002UL #define SOCK_FILE__WRITE 0x00000004UL @@ -187,6 +191,7 @@ #define FIFO_FILE__SWAPON 0x00004000UL #define FIFO_FILE__QUOTAON 0x00008000UL #define FIFO_FILE__MOUNTON 0x00010000UL +#define FIFO_FILE__OPEN 0x00020000UL #define FD__USE 0x00000001UL #define SOCKET__IOCTL 0x00000001UL #define SOCKET__READ 0x00000002UL diff --git a/security/selinux/include/avc.h b/security/selinux/include/avc.h diff --git a/security/selinux/include/security.h b/security/selinux/include/security.h index 837ce42..fa30f53 100644 --- a/security/selinux/include/security.h +++ b/security/selinux/include/security.h @@ -43,11 +43,13 @@ extern int selinux_mls_enabled; /* Policy capabilities */ enum { POLICYDB_CAPABILITY_NETPEER, + POLICYDB_CAPABILITY_OPENPERM, __POLICYDB_CAPABILITY_MAX }; #define POLICYDB_CAPABILITY_MAX (__POLICYDB_CAPABILITY_MAX - 1) extern int selinux_policycap_netpeer; +extern int selinux_policycap_openperm; int security_load_policy(void * data, size_t len); diff --git a/security/selinux/selinuxfs.c b/security/selinux/selinuxfs.c diff --git a/security/selinux/ss/services.c b/security/selinux/ss/services.c index f374186..23a61f1 100644 --- a/security/selinux/ss/services.c +++ b/security/selinux/ss/services.c @@ -61,6 +61,7 @@ extern void selnl_notify_policyload(u32 seqno); unsigned int policydb_loaded_version; int selinux_policycap_netpeer; +int selinux_policycap_openperm; /* * This is declared in avc.c @@ -1306,6 +1307,8 @@ static void security_load_policycaps(void) { selinux_policycap_netpeer = ebitmap_get_bit(&policydb.policycaps, POLICYDB_CAPABILITY_NETPEER); + selinux_policycap_openperm = ebitmap_get_bit(&policydb.policycaps, + POLICYDB_CAPABILITY_OPENPERM); } extern void selinux_complete_init(void); -- This message was distributed to subscribers of the selinux mailing list. If you no longer wish to subscribe, send mail to majordomo@xxxxxxxxxxxxx with the words "unsubscribe selinux" without quotes as the message.