From 4e256541b8cdd8bf8f5eeff701529ca55b3c459a Mon Sep 17 00:00:00 2001 From: Kees Cook Date: Mon, 1 Feb 2010 21:21:17 -0800 Subject: [PATCH] syslog: distinguish between /proc/kmsg and syscalls This allows the LSM to distinguish between syslog functions originating from /proc/kmsg access and direct syscalls. By default, the commoncaps will now no longer require CAP_SYS_ADMIN to read an opened /proc/kmsg file descriptor. For example the kernel syslog reader can now drop privileges after opening /proc/kmsg, instead of staying privileged with CAP_SYS_ADMIN. MAC systems that implement security_syslog have unchanged behavior. diff -urN linux-2.6.32-fan32d-r10202.old/fs/proc/kmsg.c linux-2.6.32-fan32d-r10202/fs/proc/kmsg.c --- linux-2.6.32-fan32d-r10202.old/fs/proc/kmsg.c 2014-12-17 18:31:31.000000000 +0300 +++ linux-2.6.32-fan32d-r10202/fs/proc/kmsg.c 2014-12-17 18:33:16.000000000 +0300 @@ -12,6 +12,7 @@ #include #include #include +#include #include #include #include @@ -22,31 +23,30 @@ extern wait_queue_head_t log_wait; -extern int do_syslog(int type, char __user *bug, int count); - static int kmsg_open(struct inode * inode, struct file * file) { - return do_syslog(1,NULL,0); + return do_syslog(1, NULL, 0, SYSLOG_CONTEXT_PROC); } static int kmsg_release(struct inode * inode, struct file * file) { - (void) do_syslog(0,NULL,0); + (void) do_syslog(0, NULL, 0, SYSLOG_CONTEXT_PROC); return 0; } static ssize_t kmsg_read(struct file *file, char __user *buf, size_t count, loff_t *ppos) { - if ((file->f_flags & O_NONBLOCK) && !do_syslog(9, NULL, 0)) + if ((file->f_flags & O_NONBLOCK) && + !do_syslog(9, NULL, 0, SYSLOG_CONTEXT_PROC)) return -EAGAIN; - return do_syslog(2, buf, count); + return do_syslog(2, buf, count, SYSLOG_CONTEXT_PROC); } static unsigned int kmsg_poll(struct file *file, poll_table *wait) { poll_wait(file, &ve_log_wait, wait); - if (do_syslog(9, NULL, 0)) + if (do_syslog(9, NULL, 0, SYSLOG_CONTEXT_PROC)) return POLLIN | POLLRDNORM; return 0; } diff -urN linux-2.6.32-fan32d-r10202.old/include/linux/security.h linux-2.6.32-fan32d-r10202/include/linux/security.h --- linux-2.6.32-fan32d-r10202.old/include/linux/security.h 2014-12-17 18:31:32.000000000 +0300 +++ linux-2.6.32-fan32d-r10202/include/linux/security.h 2014-12-17 18:33:16.000000000 +0300 @@ -76,7 +76,7 @@ extern int cap_task_setscheduler(struct task_struct *p, int policy, struct sched_param *lp); extern int cap_task_setioprio(struct task_struct *p, int ioprio); extern int cap_task_setnice(struct task_struct *p, int nice); -extern int cap_syslog(int type); +extern int cap_syslog(int type, int context); extern int cap_vm_enough_memory(struct mm_struct *mm, long pages); struct msghdr; @@ -1341,6 +1341,7 @@ * logging to the console. * See the syslog(2) manual page for an explanation of the @type values. * @type contains the type of action. + * @context contains the context of action (/proc or syscall). * Return 0 if permission is granted. * @settime: * Check permission to change the system time. @@ -1455,7 +1456,7 @@ int (*sysctl) (struct ctl_table *table, int op); int (*quotactl) (int cmds, int type, int id, struct super_block *sb); int (*quota_on) (struct dentry *dentry); - int (*syslog) (int type); + int (*syslog) (int type, int context); int (*settime) (const struct timespec *ts, const struct timezone *tz); int (*vm_enough_memory) (struct mm_struct *mm, long pages); @@ -1755,7 +1756,7 @@ int security_sysctl(struct ctl_table *table, int op); int security_quotactl(int cmds, int type, int id, struct super_block *sb); int security_quota_on(struct dentry *dentry); -int security_syslog(int type); +int security_syslog(int type, int context); int security_settime(const struct timespec *ts, const struct timezone *tz); int security_vm_enough_memory(long pages); int security_vm_enough_memory_mm(struct mm_struct *mm, long pages); @@ -2005,9 +2006,9 @@ return 0; } -static inline int security_syslog(int type) +static inline int security_syslog(int type, int context) { - return cap_syslog(type); + return cap_syslog(type, context); } static inline int security_settime(const struct timespec *ts, diff -urN linux-2.6.32-fan32d-r10202.old/include/linux/syslog.h linux-2.6.32-fan32d-r10202/include/linux/syslog.h --- linux-2.6.32-fan32d-r10202.old/include/linux/syslog.h 1970-01-01 03:00:00.000000000 +0300 +++ linux-2.6.32-fan32d-r10202/include/linux/syslog.h 2014-12-17 18:33:16.000000000 +0300 @@ -0,0 +1,29 @@ +/* Syslog internals + * + * Copyright 2010 Canonical, Ltd. + * Author: Kees Cook + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#ifndef _LINUX_SYSLOG_H +#define _LINUX_SYSLOG_H + +#define SYSLOG_CONTEXT_SYSCALL 0 +#define SYSLOG_CONTEXT_PROC 1 + +int do_syslog(int type, char __user *buf, int count, int context); + +#endif /* _LINUX_SYSLOG_H */ diff -urN linux-2.6.32-fan32d-r10202.old/kernel/printk.c linux-2.6.32-fan32d-r10202/kernel/printk.c --- linux-2.6.32-fan32d-r10202.old/kernel/printk.c 2014-12-17 18:31:37.000000000 +0300 +++ linux-2.6.32-fan32d-r10202/kernel/printk.c 2014-12-17 18:33:16.000000000 +0300 @@ -33,6 +33,7 @@ #include #include #include +#include #include #include @@ -375,7 +376,7 @@ * 9 -- Return number of unread characters in the log buffer * 10 -- Return size of the log buffer */ -int do_syslog(int type, char __user *buf, int len) +int do_syslog(int type, char __user *buf, int len, int context) { unsigned i, j, limit, count; int do_clear = 0; @@ -385,7 +386,7 @@ if (!ve_is_super(get_exec_env()) && (type == 6 || type == 7)) goto out; - error = security_syslog(type); + error = security_syslog(type, context); if (error) return error; @@ -527,7 +528,7 @@ SYSCALL_DEFINE3(syslog, int, type, char __user *, buf, int, len) { - return do_syslog(type, buf, len); + return do_syslog(type, buf, len, SYSLOG_CONTEXT_SYSCALL); } /* diff -urN linux-2.6.32-fan32d-r10202.old/security/commoncap.c linux-2.6.32-fan32d-r10202/security/commoncap.c --- linux-2.6.32-fan32d-r10202.old/security/commoncap.c 2014-12-17 18:31:29.000000000 +0300 +++ linux-2.6.32-fan32d-r10202/security/commoncap.c 2014-12-17 18:34:03.000000000 +0300 @@ -27,6 +27,7 @@ #include #include #include +#include #include #ifdef CONFIG_ANDROID_PARANOID_NETWORK @@ -985,16 +986,20 @@ /** * cap_syslog - Determine whether syslog function is permitted * @type: Function requested + * @context: What context this request came from (/proc or syscall) * * Determine whether the current process is permitted to use a particular * syslog function, returning 0 if permission is granted, -ve if not. */ -int cap_syslog(int type) +int cap_syslog(int type, int context) { if (dmesg_restrict && !capable(CAP_SYS_ADMIN) && ve_is_super(get_exec_env())) return -EPERM; + /* /proc/kmsg can open be opened by CAP_SYS_ADMIN */ + if (type != 1 && context == SYSLOG_CONTEXT_PROC) + return 0; if ((type != 3 && type != 10) && !capable(CAP_VE_SYS_ADMIN) && !capable(CAP_SYS_ADMIN)) return -EPERM; diff -urN linux-2.6.32-fan32d-r10202.old/security/security.c linux-2.6.32-fan32d-r10202/security/security.c --- linux-2.6.32-fan32d-r10202.old/security/security.c 2014-12-17 18:31:29.000000000 +0300 +++ linux-2.6.32-fan32d-r10202/security/security.c 2014-12-17 18:33:16.000000000 +0300 @@ -199,9 +199,9 @@ return security_ops->quota_on(dentry); } -int security_syslog(int type) +int security_syslog(int type, int context) { - return security_ops->syslog(type); + return security_ops->syslog(type, context); } int security_settime(const struct timespec *ts, const struct timezone *tz) diff -urN linux-2.6.32-fan32d-r10202.old/security/selinux/hooks.c linux-2.6.32-fan32d-r10202/security/selinux/hooks.c --- linux-2.6.32-fan32d-r10202.old/security/selinux/hooks.c 2014-12-17 18:31:29.000000000 +0300 +++ linux-2.6.32-fan32d-r10202/security/selinux/hooks.c 2014-12-17 18:33:16.000000000 +0300 @@ -2089,11 +2089,11 @@ return dentry_has_perm(cred, NULL, dentry, FILE__QUOTAON); } -static int selinux_syslog(int type) +static int selinux_syslog(int type, int context) { int rc; - rc = cap_syslog(type); + rc = cap_syslog(type, context); if (rc) return rc; diff -urN linux-2.6.32-fan32d-r10202.old/security/smack/smack_lsm.c linux-2.6.32-fan32d-r10202/security/smack/smack_lsm.c --- linux-2.6.32-fan32d-r10202.old/security/smack/smack_lsm.c 2014-12-17 18:31:29.000000000 +0300 +++ linux-2.6.32-fan32d-r10202/security/smack/smack_lsm.c 2014-12-17 18:33:16.000000000 +0300 @@ -157,12 +157,12 @@ * * Returns 0 on success, error code otherwise. */ -static int smack_syslog(int type) +static int smack_syslog(int type, int context) { int rc; char *sp = current_security(); - rc = cap_syslog(type); + rc = cap_syslog(type, context); if (rc != 0) return rc;