diff --git a/fs/pipe.c b/fs/pipe.c index 68090e84f589..ebafde7d6aba 100644 --- a/fs/pipe.c +++ b/fs/pipe.c @@ -16,6 +16,7 @@ #include #include #include +#include #include #include @@ -985,6 +986,10 @@ int do_pipe(int *fd) goto err_fdr; fdw = error; + error = audit_fd_pair(fdr, fdw); + if (error < 0) + goto err_fdw; + fd_install(fdr, fr); fd_install(fdw, fw); fd[0] = fdr; @@ -992,6 +997,8 @@ int do_pipe(int *fd) return 0; + err_fdw: + put_unused_fd(fdw); err_fdr: put_unused_fd(fdr); err_read_pipe: diff --git a/include/linux/audit.h b/include/linux/audit.h index 0e07db6cc0d0..229fa012c893 100644 --- a/include/linux/audit.h +++ b/include/linux/audit.h @@ -89,6 +89,7 @@ #define AUDIT_MQ_NOTIFY 1314 /* POSIX MQ notify record type */ #define AUDIT_MQ_GETSETATTR 1315 /* POSIX MQ get/set attribute record type */ #define AUDIT_KERNEL_OTHER 1316 /* For use by 3rd party modules */ +#define AUDIT_FD_PAIR 1317 /* audit record for pipe/socketpair */ #define AUDIT_AVC 1400 /* SE Linux avc denial or grant */ #define AUDIT_SELINUX_ERR 1401 /* Internal SE Linux Errors */ @@ -387,6 +388,7 @@ extern int __audit_ipc_set_perm(unsigned long qbytes, uid_t uid, gid_t gid, mode extern int audit_bprm(struct linux_binprm *bprm); extern int audit_socketcall(int nargs, unsigned long *args); extern int audit_sockaddr(int len, void *addr); +extern int __audit_fd_pair(int fd1, int fd2); extern int audit_avc_path(struct dentry *dentry, struct vfsmount *mnt); extern int audit_set_macxattr(const char *name); extern int __audit_mq_open(int oflag, mode_t mode, struct mq_attr __user *u_attr); @@ -401,6 +403,12 @@ static inline int audit_ipc_obj(struct kern_ipc_perm *ipcp) return __audit_ipc_obj(ipcp); return 0; } +static inline int audit_fd_pair(int fd1, int fd2) +{ + if (unlikely(!audit_dummy_context())) + return __audit_fd_pair(fd1, fd2); + return 0; +} static inline int audit_ipc_set_perm(unsigned long qbytes, uid_t uid, gid_t gid, mode_t mode) { if (unlikely(!audit_dummy_context())) @@ -459,6 +467,7 @@ extern int audit_n_rules; #define audit_ipc_set_perm(q,u,g,m) ({ 0; }) #define audit_bprm(p) ({ 0; }) #define audit_socketcall(n,a) ({ 0; }) +#define audit_fd_pair(n,a) ({ 0; }) #define audit_sockaddr(len, addr) ({ 0; }) #define audit_avc_path(dentry, mnt) ({ 0; }) #define audit_set_macxattr(n) do { ; } while (0) diff --git a/kernel/audit.c b/kernel/audit.c index d9b690ac684b..76c9a11b72d6 100644 --- a/kernel/audit.c +++ b/kernel/audit.c @@ -2,7 +2,7 @@ * Gateway between the kernel (e.g., selinux) and the user-space audit daemon. * System-call specific features have moved to auditsc.c * - * Copyright 2003-2004 Red Hat Inc., Durham, North Carolina. + * Copyright 2003-2007 Red Hat Inc., Durham, North Carolina. * All Rights Reserved. * * This program is free software; you can redistribute it and/or modify @@ -65,7 +65,9 @@ * (Initialization happens after skb_init is called.) */ static int audit_initialized; -/* No syscall auditing will take place unless audit_enabled != 0. */ +/* 0 - no auditing + * 1 - auditing enabled + * 2 - auditing enabled and configuration is locked/unchangeable. */ int audit_enabled; /* Default state when kernel boots without any parameters. */ @@ -239,102 +241,150 @@ void audit_log_lost(const char *message) static int audit_set_rate_limit(int limit, uid_t loginuid, u32 sid) { - int old = audit_rate_limit; + int res, rc = 0, old = audit_rate_limit; + + /* check if we are locked */ + if (audit_enabled == 2) + res = 0; + else + res = 1; if (sid) { char *ctx = NULL; u32 len; - int rc; - if ((rc = selinux_sid_to_string(sid, &ctx, &len))) - return rc; - else + if ((rc = selinux_sid_to_string(sid, &ctx, &len)) == 0) { audit_log(NULL, GFP_KERNEL, AUDIT_CONFIG_CHANGE, - "audit_rate_limit=%d old=%d by auid=%u subj=%s", - limit, old, loginuid, ctx); - kfree(ctx); - } else - audit_log(NULL, GFP_KERNEL, AUDIT_CONFIG_CHANGE, - "audit_rate_limit=%d old=%d by auid=%u", - limit, old, loginuid); - audit_rate_limit = limit; - return 0; + "audit_rate_limit=%d old=%d by auid=%u" + " subj=%s res=%d", + limit, old, loginuid, ctx, res); + kfree(ctx); + } else + res = 0; /* Something weird, deny request */ + } + audit_log(NULL, GFP_KERNEL, AUDIT_CONFIG_CHANGE, + "audit_rate_limit=%d old=%d by auid=%u res=%d", + limit, old, loginuid, res); + + /* If we are allowed, make the change */ + if (res == 1) + audit_rate_limit = limit; + /* Not allowed, update reason */ + else if (rc == 0) + rc = -EPERM; + return rc; } static int audit_set_backlog_limit(int limit, uid_t loginuid, u32 sid) { - int old = audit_backlog_limit; + int res, rc = 0, old = audit_backlog_limit; + + /* check if we are locked */ + if (audit_enabled == 2) + res = 0; + else + res = 1; if (sid) { char *ctx = NULL; u32 len; - int rc; - if ((rc = selinux_sid_to_string(sid, &ctx, &len))) - return rc; - else + if ((rc = selinux_sid_to_string(sid, &ctx, &len)) == 0) { audit_log(NULL, GFP_KERNEL, AUDIT_CONFIG_CHANGE, - "audit_backlog_limit=%d old=%d by auid=%u subj=%s", - limit, old, loginuid, ctx); - kfree(ctx); - } else - audit_log(NULL, GFP_KERNEL, AUDIT_CONFIG_CHANGE, - "audit_backlog_limit=%d old=%d by auid=%u", - limit, old, loginuid); - audit_backlog_limit = limit; - return 0; + "audit_backlog_limit=%d old=%d by auid=%u" + " subj=%s res=%d", + limit, old, loginuid, ctx, res); + kfree(ctx); + } else + res = 0; /* Something weird, deny request */ + } + audit_log(NULL, GFP_KERNEL, AUDIT_CONFIG_CHANGE, + "audit_backlog_limit=%d old=%d by auid=%u res=%d", + limit, old, loginuid, res); + + /* If we are allowed, make the change */ + if (res == 1) + audit_backlog_limit = limit; + /* Not allowed, update reason */ + else if (rc == 0) + rc = -EPERM; + return rc; } static int audit_set_enabled(int state, uid_t loginuid, u32 sid) { - int old = audit_enabled; + int res, rc = 0, old = audit_enabled; - if (state != 0 && state != 1) + if (state < 0 || state > 2) return -EINVAL; + /* check if we are locked */ + if (audit_enabled == 2) + res = 0; + else + res = 1; + if (sid) { char *ctx = NULL; u32 len; - int rc; - if ((rc = selinux_sid_to_string(sid, &ctx, &len))) - return rc; - else + if ((rc = selinux_sid_to_string(sid, &ctx, &len)) == 0) { audit_log(NULL, GFP_KERNEL, AUDIT_CONFIG_CHANGE, - "audit_enabled=%d old=%d by auid=%u subj=%s", - state, old, loginuid, ctx); - kfree(ctx); - } else - audit_log(NULL, GFP_KERNEL, AUDIT_CONFIG_CHANGE, - "audit_enabled=%d old=%d by auid=%u", - state, old, loginuid); - audit_enabled = state; - return 0; + "audit_enabled=%d old=%d by auid=%u" + " subj=%s res=%d", + state, old, loginuid, ctx, res); + kfree(ctx); + } else + res = 0; /* Something weird, deny request */ + } + audit_log(NULL, GFP_KERNEL, AUDIT_CONFIG_CHANGE, + "audit_enabled=%d old=%d by auid=%u res=%d", + state, old, loginuid, res); + + /* If we are allowed, make the change */ + if (res == 1) + audit_enabled = state; + /* Not allowed, update reason */ + else if (rc == 0) + rc = -EPERM; + return rc; } static int audit_set_failure(int state, uid_t loginuid, u32 sid) { - int old = audit_failure; + int res, rc = 0, old = audit_failure; if (state != AUDIT_FAIL_SILENT && state != AUDIT_FAIL_PRINTK && state != AUDIT_FAIL_PANIC) return -EINVAL; + /* check if we are locked */ + if (audit_enabled == 2) + res = 0; + else + res = 1; + if (sid) { char *ctx = NULL; u32 len; - int rc; - if ((rc = selinux_sid_to_string(sid, &ctx, &len))) - return rc; - else + if ((rc = selinux_sid_to_string(sid, &ctx, &len)) == 0) { audit_log(NULL, GFP_KERNEL, AUDIT_CONFIG_CHANGE, - "audit_failure=%d old=%d by auid=%u subj=%s", - state, old, loginuid, ctx); - kfree(ctx); - } else - audit_log(NULL, GFP_KERNEL, AUDIT_CONFIG_CHANGE, - "audit_failure=%d old=%d by auid=%u", - state, old, loginuid); - audit_failure = state; - return 0; + "audit_failure=%d old=%d by auid=%u" + " subj=%s res=%d", + state, old, loginuid, ctx, res); + kfree(ctx); + } else + res = 0; /* Something weird, deny request */ + } + audit_log(NULL, GFP_KERNEL, AUDIT_CONFIG_CHANGE, + "audit_failure=%d old=%d by auid=%u res=%d", + state, old, loginuid, res); + + /* If we are allowed, make the change */ + if (res == 1) + audit_failure = state; + /* Not allowed, update reason */ + else if (rc == 0) + rc = -EPERM; + return rc; } static int kauditd_thread(void *dummy) @@ -599,6 +649,30 @@ static int audit_receive_msg(struct sk_buff *skb, struct nlmsghdr *nlh) case AUDIT_DEL: if (nlmsg_len(nlh) < sizeof(struct audit_rule)) return -EINVAL; + if (audit_enabled == 2) { + ab = audit_log_start(NULL, GFP_KERNEL, + AUDIT_CONFIG_CHANGE); + if (ab) { + audit_log_format(ab, + "pid=%d uid=%u auid=%u", + pid, uid, loginuid); + if (sid) { + if (selinux_sid_to_string( + sid, &ctx, &len)) { + audit_log_format(ab, + " ssid=%u", sid); + /* Maybe call audit_panic? */ + } else + audit_log_format(ab, + " subj=%s", ctx); + kfree(ctx); + } + audit_log_format(ab, " audit_enabled=%d res=0", + audit_enabled); + audit_log_end(ab); + } + return -EPERM; + } /* fallthrough */ case AUDIT_LIST: err = audit_receive_filter(nlh->nlmsg_type, NETLINK_CB(skb).pid, @@ -609,6 +683,30 @@ static int audit_receive_msg(struct sk_buff *skb, struct nlmsghdr *nlh) case AUDIT_DEL_RULE: if (nlmsg_len(nlh) < sizeof(struct audit_rule_data)) return -EINVAL; + if (audit_enabled == 2) { + ab = audit_log_start(NULL, GFP_KERNEL, + AUDIT_CONFIG_CHANGE); + if (ab) { + audit_log_format(ab, + "pid=%d uid=%u auid=%u", + pid, uid, loginuid); + if (sid) { + if (selinux_sid_to_string( + sid, &ctx, &len)) { + audit_log_format(ab, + " ssid=%u", sid); + /* Maybe call audit_panic? */ + } else + audit_log_format(ab, + " subj=%s", ctx); + kfree(ctx); + } + audit_log_format(ab, " audit_enabled=%d res=0", + audit_enabled); + audit_log_end(ab); + } + return -EPERM; + } /* fallthrough */ case AUDIT_LIST_RULES: err = audit_receive_filter(nlh->nlmsg_type, NETLINK_CB(skb).pid, diff --git a/kernel/auditfilter.c b/kernel/auditfilter.c index 87865f8b4ce3..3749193aed8c 100644 --- a/kernel/auditfilter.c +++ b/kernel/auditfilter.c @@ -937,9 +937,10 @@ static void audit_update_watch(struct audit_parent *parent, } ab = audit_log_start(NULL, GFP_KERNEL, AUDIT_CONFIG_CHANGE); - audit_log_format(ab, "audit updated rules specifying path="); + audit_log_format(ab, "op=updated rules specifying path="); audit_log_untrustedstring(ab, owatch->path); audit_log_format(ab, " with dev=%u ino=%lu\n", dev, ino); + audit_log_format(ab, " list=%d res=1", r->listnr); audit_log_end(ab); audit_remove_watch(owatch); @@ -969,14 +970,14 @@ static void audit_remove_parent_watches(struct audit_parent *parent) e = container_of(r, struct audit_entry, rule); ab = audit_log_start(NULL, GFP_KERNEL, AUDIT_CONFIG_CHANGE); - audit_log_format(ab, "audit implicitly removed rule path="); + audit_log_format(ab, "op=remove rule path="); audit_log_untrustedstring(ab, w->path); if (r->filterkey) { audit_log_format(ab, " key="); audit_log_untrustedstring(ab, r->filterkey); } else audit_log_format(ab, " key=(null)"); - audit_log_format(ab, " list=%d", r->listnr); + audit_log_format(ab, " list=%d res=1", r->listnr); audit_log_end(ab); list_del(&r->rlist); @@ -1410,7 +1411,7 @@ static void audit_log_rule_change(uid_t loginuid, u32 sid, char *action, audit_log_format(ab, " subj=%s", ctx); kfree(ctx); } - audit_log_format(ab, " %s rule key=", action); + audit_log_format(ab, " op=%s rule key=", action); if (rule->filterkey) audit_log_untrustedstring(ab, rule->filterkey); else diff --git a/kernel/auditsc.c b/kernel/auditsc.c index 298897559ca4..359955800dd2 100644 --- a/kernel/auditsc.c +++ b/kernel/auditsc.c @@ -170,6 +170,11 @@ struct audit_aux_data_sockaddr { char a[0]; }; +struct audit_aux_data_fd_pair { + struct audit_aux_data d; + int fd[2]; +}; + struct audit_aux_data_path { struct audit_aux_data d; struct dentry *dentry; @@ -961,6 +966,11 @@ static void audit_log_exit(struct audit_context *context, struct task_struct *ts audit_log_d_path(ab, "path=", axi->dentry, axi->mnt); break; } + case AUDIT_FD_PAIR: { + struct audit_aux_data_fd_pair *axs = (void *)aux; + audit_log_format(ab, "fd0=%d fd1=%d", axs->fd[0], axs->fd[1]); + break; } + } audit_log_end(ab); } @@ -1814,6 +1824,36 @@ int audit_socketcall(int nargs, unsigned long *args) return 0; } +/** + * __audit_fd_pair - record audit data for pipe and socketpair + * @fd1: the first file descriptor + * @fd2: the second file descriptor + * + * Returns 0 for success or NULL context or < 0 on error. + */ +int __audit_fd_pair(int fd1, int fd2) +{ + struct audit_context *context = current->audit_context; + struct audit_aux_data_fd_pair *ax; + + if (likely(!context)) { + return 0; + } + + ax = kmalloc(sizeof(*ax), GFP_KERNEL); + if (!ax) { + return -ENOMEM; + } + + ax->fd[0] = fd1; + ax->fd[1] = fd2; + + ax->d.type = AUDIT_FD_PAIR; + ax->d.next = context->aux; + context->aux = (void *)ax; + return 0; +} + /** * audit_sockaddr - record audit data for sys_bind, sys_connect, sys_sendto * @len: data length in user space diff --git a/net/socket.c b/net/socket.c index 0778c5442411..9566e57ac7f5 100644 --- a/net/socket.c +++ b/net/socket.c @@ -1194,6 +1194,7 @@ asmlinkage long sys_socketpair(int family, int type, int protocol, { struct socket *sock1, *sock2; int fd1, fd2, err; + struct file *newfile1, *newfile2; /* * Obtain the first socket and check if the underlying protocol @@ -1212,18 +1213,37 @@ asmlinkage long sys_socketpair(int family, int type, int protocol, if (err < 0) goto out_release_both; - fd1 = fd2 = -1; - - err = sock_map_fd(sock1); - if (err < 0) + fd1 = sock_alloc_fd(&newfile1); + if (unlikely(fd1 < 0)) goto out_release_both; - fd1 = err; - err = sock_map_fd(sock2); - if (err < 0) - goto out_close_1; - fd2 = err; + fd2 = sock_alloc_fd(&newfile2); + if (unlikely(fd2 < 0)) { + put_filp(newfile1); + put_unused_fd(fd1); + goto out_release_both; + } + err = sock_attach_fd(sock1, newfile1); + if (unlikely(err < 0)) { + goto out_fd2; + } + + err = sock_attach_fd(sock2, newfile2); + if (unlikely(err < 0)) { + fput(newfile1); + goto out_fd1; + } + + err = audit_fd_pair(fd1, fd2); + if (err < 0) { + fput(newfile1); + fput(newfile2); + goto out_fd; + } + + fd_install(fd1, newfile1); + fd_install(fd2, newfile2); /* fd1 and fd2 may be already another descriptors. * Not kernel problem. */ @@ -1238,17 +1258,23 @@ asmlinkage long sys_socketpair(int family, int type, int protocol, sys_close(fd1); return err; -out_close_1: - sock_release(sock2); - sys_close(fd1); - return err; - out_release_both: sock_release(sock2); out_release_1: sock_release(sock1); out: return err; + +out_fd2: + put_filp(newfile1); + sock_release(sock1); +out_fd1: + put_filp(newfile2); + sock_release(sock2); +out_fd: + put_unused_fd(fd1); + put_unused_fd(fd2); + goto out; } /*