From c2f0c7c356dc9ae15419f00c725a2fcc58eeff58 Mon Sep 17 00:00:00 2001 From: Steve Grubb Date: Fri, 6 May 2005 12:38:39 +0100 Subject: The attached patch addresses the problem with getting the audit daemon shutdown credential information. It creates a new message type AUDIT_TERM_INFO, which is used by the audit daemon to query who issued the shutdown. It requires the placement of a hook function that gathers the information. The hook is after the DAC & MAC checks and before the function returns. Racing threads could overwrite the uid & pid - but they would have to be root and have policy that allows signalling the audit daemon. That should be a manageable risk. The userspace component will be released later in audit 0.7.2. When it receives the TERM signal, it queries the kernel for shutdown information. When it receives it, it writes the message and exits. The message looks like this: type=DAEMON msg=auditd(1114551182.000) auditd normal halt, sending pid=2650 uid=525, auditd pid=1685 Signed-off-by: Steve Grubb Signed-off-by: David Woodhouse --- include/linux/audit.h | 25 +++++++++++++++++-------- kernel/audit.c | 14 +++++++++++++- kernel/auditsc.c | 19 +++++++++++++++++++ kernel/signal.c | 7 ++++++- security/selinux/nlmsgtab.c | 1 + 5 files changed, 56 insertions(+), 10 deletions(-) diff --git a/include/linux/audit.h b/include/linux/audit.h index 19f04b04979..baa80760824 100644 --- a/include/linux/audit.h +++ b/include/linux/audit.h @@ -28,14 +28,16 @@ #include /* Request and reply types */ -#define AUDIT_GET 1000 /* Get status */ -#define AUDIT_SET 1001 /* Set status (enable/disable/auditd) */ -#define AUDIT_LIST 1002 /* List filtering rules */ -#define AUDIT_ADD 1003 /* Add filtering rule */ -#define AUDIT_DEL 1004 /* Delete filtering rule */ -#define AUDIT_USER 1005 /* Send a message from user-space */ -#define AUDIT_LOGIN 1006 /* Define the login id and informaiton */ -#define AUDIT_KERNEL 2000 /* Asynchronous audit record. NOT A REQUEST. */ +#define AUDIT_GET 1000 /* Get status */ +#define AUDIT_SET 1001 /* Set status (enable/disable/auditd) */ +#define AUDIT_LIST 1002 /* List filtering rules */ +#define AUDIT_ADD 1003 /* Add filtering rule */ +#define AUDIT_DEL 1004 /* Delete filtering rule */ +#define AUDIT_USER 1005 /* Send a message from user-space */ +#define AUDIT_LOGIN 1006 /* Define the login id and information */ +#define AUDIT_SIGNAL_INFO 1010 /* Get information about sender of signal*/ + +#define AUDIT_KERNEL 2000 /* Asynchronous audit record. NOT A REQUEST. */ /* Rule flags */ #define AUDIT_PER_TASK 0x01 /* Apply rule at task creation (not syscall) */ @@ -161,6 +163,11 @@ struct audit_rule { /* for AUDIT_LIST, AUDIT_ADD, and AUDIT_DEL */ #ifdef __KERNEL__ +struct audit_sig_info { + uid_t uid; + pid_t pid; +}; + struct audit_buffer; struct audit_context; struct inode; @@ -190,6 +197,7 @@ extern void audit_get_stamp(struct audit_context *ctx, extern int audit_set_loginuid(struct task_struct *task, uid_t loginuid); extern uid_t audit_get_loginuid(struct audit_context *ctx); extern int audit_ipc_perms(unsigned long qbytes, uid_t uid, gid_t gid, mode_t mode); +extern void audit_signal_info(int sig, struct task_struct *t); #else #define audit_alloc(t) ({ 0; }) #define audit_free(t) do { ; } while (0) @@ -200,6 +208,7 @@ extern int audit_ipc_perms(unsigned long qbytes, uid_t uid, gid_t gid, mode_t mo #define audit_inode(n,i) do { ; } while (0) #define audit_get_loginuid(c) ({ -1; }) #define audit_ipc_perms(q,u,g,m) ({ 0; }) +#define audit_signal_info(s,t) do { ; } while (0) #endif #ifdef CONFIG_AUDIT diff --git a/kernel/audit.c b/kernel/audit.c index 9c4f1af0c79..6f344b44d3d 100644 --- a/kernel/audit.c +++ b/kernel/audit.c @@ -68,7 +68,7 @@ static int audit_failure = AUDIT_FAIL_PRINTK; /* If audit records are to be written to the netlink socket, audit_pid * contains the (non-zero) pid. */ -static int audit_pid; +int audit_pid; /* If audit_limit is non-zero, limit the rate of sending audit records * to that number per second. This prevents DoS attacks, but results in @@ -79,6 +79,10 @@ static int audit_rate_limit; static int audit_backlog_limit = 64; static atomic_t audit_backlog = ATOMIC_INIT(0); +/* The identity of the user shutting down the audit system. */ +uid_t audit_sig_uid = -1; +pid_t audit_sig_pid = -1; + /* Records can be lost in several ways: 0) [suppressed in audit_alloc] 1) out of memory in audit_log_start [kmalloc of struct audit_buffer] @@ -321,6 +325,7 @@ static int audit_netlink_ok(kernel_cap_t eff_cap, u16 msg_type) case AUDIT_SET: case AUDIT_ADD: case AUDIT_DEL: + case AUDIT_SIGNAL_INFO: if (!cap_raised(eff_cap, CAP_AUDIT_CONTROL)) err = -EPERM; break; @@ -344,6 +349,7 @@ static int audit_receive_msg(struct sk_buff *skb, struct nlmsghdr *nlh) struct audit_buffer *ab; u16 msg_type = nlh->nlmsg_type; uid_t loginuid; /* loginuid of sender */ + struct audit_sig_info sig_data; err = audit_netlink_ok(NETLINK_CB(skb).eff_cap, msg_type); if (err) @@ -419,6 +425,12 @@ static int audit_receive_msg(struct sk_buff *skb, struct nlmsghdr *nlh) err = -EOPNOTSUPP; #endif break; + case AUDIT_SIGNAL_INFO: + sig_data.uid = audit_sig_uid; + sig_data.pid = audit_sig_pid; + audit_send_reply(NETLINK_CB(skb).pid, seq, AUDIT_SIGNAL_INFO, + 0, 0, &sig_data, sizeof(sig_data)); + break; default: err = -EINVAL; break; diff --git a/kernel/auditsc.c b/kernel/auditsc.c index 37b3ac94bc4..f1bf66510cd 100644 --- a/kernel/auditsc.c +++ b/kernel/auditsc.c @@ -1056,3 +1056,22 @@ int audit_ipc_perms(unsigned long qbytes, uid_t uid, gid_t gid, mode_t mode) context->aux = (void *)ax; return 0; } + +void audit_signal_info(int sig, struct task_struct *t) +{ + extern pid_t audit_sig_pid; + extern uid_t audit_sig_uid; + extern int audit_pid; + + if (unlikely(audit_pid && t->pid == audit_pid)) { + if (sig == SIGTERM || sig == SIGHUP) { + struct audit_context *ctx = current->audit_context; + audit_sig_pid = current->pid; + if (ctx) + audit_sig_uid = ctx->loginuid; + else + audit_sig_uid = current->uid; + } + } +} + diff --git a/kernel/signal.c b/kernel/signal.c index 8f3debc77c5..293e189d8bc 100644 --- a/kernel/signal.c +++ b/kernel/signal.c @@ -24,6 +24,7 @@ #include #include #include +#include #include #include #include @@ -658,7 +659,11 @@ static int check_kill_permission(int sig, struct siginfo *info, && (current->uid ^ t->suid) && (current->uid ^ t->uid) && !capable(CAP_KILL)) return error; - return security_task_kill(t, info, sig); + + error = security_task_kill(t, info, sig); + if (!error) + audit_signal_info(sig, t); /* Let audit system see the signal */ + return error; } /* forward decl */ diff --git a/security/selinux/nlmsgtab.c b/security/selinux/nlmsgtab.c index b3adb481bc2..deac14367d4 100644 --- a/security/selinux/nlmsgtab.c +++ b/security/selinux/nlmsgtab.c @@ -97,6 +97,7 @@ static struct nlmsg_perm nlmsg_audit_perms[] = { AUDIT_ADD, NETLINK_AUDIT_SOCKET__NLMSG_WRITE }, { AUDIT_DEL, NETLINK_AUDIT_SOCKET__NLMSG_WRITE }, { AUDIT_USER, NETLINK_AUDIT_SOCKET__NLMSG_RELAY }, + { AUDIT_SIGNAL_INFO, NETLINK_AUDIT_SOCKET__NLMSG_READ }, }; -- cgit v1.2.3 From 16e1904e694d459ec2ca9b33c22b818eaaa4c63f Mon Sep 17 00:00:00 2001 From: Chris Wright Date: Fri, 6 May 2005 15:53:34 +0100 Subject: AUDIT: Add helper functions to allocate and free audit_buffers. Signed-off-by: Chris Wright Signed-off-by: David Woodhouse --- kernel/audit.c | 61 ++++++++++++++++++++++++++++++++++++---------------------- 1 file changed, 38 insertions(+), 23 deletions(-) diff --git a/kernel/audit.c b/kernel/audit.c index 6f344b44d3d..e5bdba3e3ae 100644 --- a/kernel/audit.c +++ b/kernel/audit.c @@ -620,6 +620,42 @@ static int __init audit_enable(char *str) __setup("audit=", audit_enable); +static void audit_buffer_free(struct audit_buffer *ab) +{ + unsigned long flags; + + atomic_dec(&audit_backlog); + spin_lock_irqsave(&audit_freelist_lock, flags); + if (++audit_freelist_count > AUDIT_MAXFREE) + kfree(ab); + else + list_add(&ab->list, &audit_freelist); + spin_unlock_irqrestore(&audit_freelist_lock, flags); +} + +static struct audit_buffer * audit_buffer_alloc(int gfp_mask) +{ + unsigned long flags; + struct audit_buffer *ab = NULL; + + spin_lock_irqsave(&audit_freelist_lock, flags); + if (!list_empty(&audit_freelist)) { + ab = list_entry(audit_freelist.next, + struct audit_buffer, list); + list_del(&ab->list); + --audit_freelist_count; + } + spin_unlock_irqrestore(&audit_freelist_lock, flags); + + if (!ab) { + ab = kmalloc(sizeof(*ab), GFP_ATOMIC); + if (!ab) + goto out; + } + atomic_inc(&audit_backlog); +out: + return ab; +} /* Obtain an audit buffer. This routine does locking to obtain the * audit buffer, but then no locking is required for calls to @@ -630,7 +666,6 @@ __setup("audit=", audit_enable); struct audit_buffer *audit_log_start(struct audit_context *ctx) { struct audit_buffer *ab = NULL; - unsigned long flags; struct timespec t; unsigned int serial; @@ -649,23 +684,12 @@ struct audit_buffer *audit_log_start(struct audit_context *ctx) return NULL; } - spin_lock_irqsave(&audit_freelist_lock, flags); - if (!list_empty(&audit_freelist)) { - ab = list_entry(audit_freelist.next, - struct audit_buffer, list); - list_del(&ab->list); - --audit_freelist_count; - } - spin_unlock_irqrestore(&audit_freelist_lock, flags); - - if (!ab) - ab = kmalloc(sizeof(*ab), GFP_ATOMIC); + ab = audit_buffer_alloc(GFP_ATOMIC); if (!ab) { audit_log_lost("out of memory in audit_log_start"); return NULL; } - atomic_inc(&audit_backlog); skb_queue_head_init(&ab->sklist); ab->ctx = ctx; @@ -824,8 +848,6 @@ static void audit_log_end_irq(struct audit_buffer *ab) * be called in an irq context. */ static void audit_log_end_fast(struct audit_buffer *ab) { - unsigned long flags; - BUG_ON(in_irq()); if (!ab) return; @@ -836,14 +858,7 @@ static void audit_log_end_fast(struct audit_buffer *ab) if (audit_log_drain(ab)) return; } - - atomic_dec(&audit_backlog); - spin_lock_irqsave(&audit_freelist_lock, flags); - if (++audit_freelist_count > AUDIT_MAXFREE) - kfree(ab); - else - list_add(&ab->list, &audit_freelist); - spin_unlock_irqrestore(&audit_freelist_lock, flags); + audit_buffer_free(ab); } /* Send or queue the message in the audit buffer, depending on the -- cgit v1.2.3 From 8fc6115c2a04099a6e846dc0b2d85cba43821b54 Mon Sep 17 00:00:00 2001 From: Chris Wright Date: Fri, 6 May 2005 15:54:17 +0100 Subject: AUDIT: expand audit tmp buffer as needed Introduce audit_expand and make the audit_buffer use a dynamic buffer which can be resized. When audit buffer is moved to skb it will not be fragmented across skb's, so we can eliminate the sklist in the audit_buffer. During audit_log_move, we simply copy the full buffer into a single skb, and then audit_log_drain sends it on. Signed-off-by: Chris Wright Signed-off-by: David Woodhouse --- kernel/audit.c | 139 ++++++++++++++++++++++++++++++++------------------------- 1 file changed, 79 insertions(+), 60 deletions(-) diff --git a/kernel/audit.c b/kernel/audit.c index e5bdba3e3ae..c6e31d209c4 100644 --- a/kernel/audit.c +++ b/kernel/audit.c @@ -136,14 +136,11 @@ static DECLARE_MUTEX(audit_netlink_sem); * use simultaneously. */ struct audit_buffer { struct list_head list; - struct sk_buff_head sklist; /* formatted skbs ready to send */ + struct sk_buff *skb; /* formatted skb ready to send */ struct audit_context *ctx; /* NULL or associated context */ int len; /* used area of tmp */ - char tmp[AUDIT_BUFSIZ]; - - /* Pointer to header and contents */ - struct nlmsghdr *nlh; - int total; + int size; /* size of tmp */ + char *tmp; int type; int pid; }; @@ -488,55 +485,47 @@ static void audit_receive(struct sock *sk, int length) static void audit_log_move(struct audit_buffer *ab) { struct sk_buff *skb; + struct nlmsghdr *nlh; char *start; - int extra = ab->nlh ? 0 : NLMSG_SPACE(0); + int len = NLMSG_SPACE(0) + ab->len + 1; /* possible resubmission */ - if (ab->len == 0) + if (ab->skb) return; - skb = skb_peek_tail(&ab->sklist); - if (!skb || skb_tailroom(skb) <= ab->len + extra) { - skb = alloc_skb(2 * ab->len + extra, GFP_ATOMIC); - if (!skb) { - ab->len = 0; /* Lose information in ab->tmp */ - audit_log_lost("out of memory in audit_log_move"); - return; - } - __skb_queue_tail(&ab->sklist, skb); - if (!ab->nlh) - ab->nlh = (struct nlmsghdr *)skb_put(skb, - NLMSG_SPACE(0)); + skb = alloc_skb(len, GFP_ATOMIC); + if (!skb) { + /* Lose information in ab->tmp */ + audit_log_lost("out of memory in audit_log_move"); + return; } + ab->skb = skb; + nlh = (struct nlmsghdr *)skb_put(skb, NLMSG_SPACE(0)); + nlh->nlmsg_type = ab->type; + nlh->nlmsg_len = ab->len; + nlh->nlmsg_flags = 0; + nlh->nlmsg_pid = ab->pid; + nlh->nlmsg_seq = 0; start = skb_put(skb, ab->len); memcpy(start, ab->tmp, ab->len); - ab->len = 0; } /* Iterate over the skbuff in the audit_buffer, sending their contents * to user space. */ static inline int audit_log_drain(struct audit_buffer *ab) { - struct sk_buff *skb; + struct sk_buff *skb = ab->skb; - while ((skb = skb_dequeue(&ab->sklist))) { + if (skb) { int retval = 0; if (audit_pid) { - if (ab->nlh) { - ab->nlh->nlmsg_len = ab->total; - ab->nlh->nlmsg_type = ab->type; - ab->nlh->nlmsg_flags = 0; - ab->nlh->nlmsg_seq = 0; - ab->nlh->nlmsg_pid = ab->pid; - } skb_get(skb); /* because netlink_* frees */ retval = netlink_unicast(audit_sock, skb, audit_pid, MSG_DONTWAIT); } if (retval == -EAGAIN && (atomic_read(&audit_backlog)) < audit_backlog_limit) { - skb_queue_head(&ab->sklist, skb); audit_log_end_irq(ab); return 1; } @@ -550,13 +539,12 @@ static inline int audit_log_drain(struct audit_buffer *ab) audit_log_lost("netlink socket too busy"); } if (!audit_pid) { /* No daemon */ - int offset = ab->nlh ? NLMSG_SPACE(0) : 0; + int offset = NLMSG_SPACE(0); int len = skb->len - offset; skb->data[offset + len] = '\0'; printk(KERN_ERR "%s\n", skb->data + offset); } kfree_skb(skb); - ab->nlh = NULL; } return 0; } @@ -624,6 +612,10 @@ static void audit_buffer_free(struct audit_buffer *ab) { unsigned long flags; + if (!ab) + return; + + kfree(ab->tmp); atomic_dec(&audit_backlog); spin_lock_irqsave(&audit_freelist_lock, flags); if (++audit_freelist_count > AUDIT_MAXFREE) @@ -633,7 +625,8 @@ static void audit_buffer_free(struct audit_buffer *ab) spin_unlock_irqrestore(&audit_freelist_lock, flags); } -static struct audit_buffer * audit_buffer_alloc(int gfp_mask) +static struct audit_buffer * audit_buffer_alloc(struct audit_context *ctx, + int gfp_mask) { unsigned long flags; struct audit_buffer *ab = NULL; @@ -650,11 +643,24 @@ static struct audit_buffer * audit_buffer_alloc(int gfp_mask) if (!ab) { ab = kmalloc(sizeof(*ab), GFP_ATOMIC); if (!ab) - goto out; + goto err; } atomic_inc(&audit_backlog); -out: + + ab->tmp = kmalloc(AUDIT_BUFSIZ, GFP_ATOMIC); + if (!ab->tmp) + goto err; + + ab->skb = NULL; + ab->ctx = ctx; + ab->len = 0; + ab->size = AUDIT_BUFSIZ; + ab->type = AUDIT_KERNEL; + ab->pid = 0; return ab; +err: + audit_buffer_free(ab); + return NULL; } /* Obtain an audit buffer. This routine does locking to obtain the @@ -684,21 +690,12 @@ struct audit_buffer *audit_log_start(struct audit_context *ctx) return NULL; } - ab = audit_buffer_alloc(GFP_ATOMIC); + ab = audit_buffer_alloc(ctx, GFP_ATOMIC); if (!ab) { audit_log_lost("out of memory in audit_log_start"); return NULL; } - skb_queue_head_init(&ab->sklist); - - ab->ctx = ctx; - ab->len = 0; - ab->nlh = NULL; - ab->total = 0; - ab->type = AUDIT_KERNEL; - ab->pid = 0; - #ifdef CONFIG_AUDITSYSCALL if (ab->ctx) audit_get_stamp(ab->ctx, &t, &serial); @@ -713,6 +710,27 @@ struct audit_buffer *audit_log_start(struct audit_context *ctx) return ab; } +/** + * audit_expand - expand tmp buffer in the audit buffer + * @ab: audit_buffer + * + * Returns 0 (no space) on failed expansion, or available space if + * successful. + */ +static inline int audit_expand(struct audit_buffer *ab) +{ + char *tmp; + int len = ab->size + AUDIT_BUFSIZ; + + tmp = kmalloc(len, GFP_ATOMIC); + if (!tmp) + return 0; + memcpy(tmp, ab->tmp, ab->len); + kfree(ab->tmp); + ab->tmp = tmp; + ab->size = len; + return ab->size - ab->len; +} /* Format an audit message into the audit buffer. If there isn't enough * room in the audit buffer, more room will be allocated and vsnprint @@ -726,22 +744,25 @@ static void audit_log_vformat(struct audit_buffer *ab, const char *fmt, if (!ab) return; - avail = sizeof(ab->tmp) - ab->len; + avail = ab->size - ab->len; if (avail <= 0) { - audit_log_move(ab); - avail = sizeof(ab->tmp) - ab->len; + avail = audit_expand(ab); + if (!avail) + goto out; } - len = vsnprintf(ab->tmp + ab->len, avail, fmt, args); + len = vsnprintf(ab->tmp + ab->len, avail, fmt, args); if (len >= avail) { /* The printk buffer is 1024 bytes long, so if we get * here and AUDIT_BUFSIZ is at least 1024, then we can * log everything that printk could have logged. */ - audit_log_move(ab); - avail = sizeof(ab->tmp) - ab->len; - len = vsnprintf(ab->tmp + ab->len, avail, fmt, args); + avail = audit_expand(ab); + if (!avail) + goto out; + len = vsnprintf(ab->tmp + ab->len, avail, fmt, args); } ab->len += (len < avail) ? len : avail; - ab->total += (len < avail) ? len : avail; +out: + return; } /* Format a message into the audit buffer. All the work is done in @@ -789,21 +810,19 @@ void audit_log_d_path(struct audit_buffer *ab, const char *prefix, char *p; int len, avail; - if (prefix) audit_log_format(ab, " %s", prefix); + if (prefix) + audit_log_format(ab, " %s", prefix); - if (ab->len > 128) - audit_log_move(ab); - avail = sizeof(ab->tmp) - ab->len; + avail = ab->size - ab->len; p = d_path(dentry, vfsmnt, ab->tmp + ab->len, avail); if (IS_ERR(p)) { /* FIXME: can we save some information here? */ audit_log_format(ab, ""); } else { /* path isn't at start of buffer */ - len = (ab->tmp + sizeof(ab->tmp) - 1) - p; + len = (ab->tmp + ab->size - 1) - p; memmove(ab->tmp + ab->len, p, len); ab->len += len; - ab->total += len; } } -- cgit v1.2.3 From 5ac52f33b6f05fcb91a97124155183b779a4efdf Mon Sep 17 00:00:00 2001 From: Chris Wright Date: Fri, 6 May 2005 15:54:53 +0100 Subject: AUDIT: buffer audit msgs directly to skb Drop the use of a tmp buffer in the audit_buffer, and just buffer directly to the skb. All header data that was temporarily stored in the audit_buffer can now be stored directly in the netlink header in the skb. Resize skb as needed. This eliminates the extra copy (and the audit_log_move function which was responsible for copying). Signed-off-by: Chris Wright Signed-off-by: David Woodhouse --- kernel/audit.c | 122 ++++++++++++++++++++++----------------------------------- 1 file changed, 46 insertions(+), 76 deletions(-) diff --git a/kernel/audit.c b/kernel/audit.c index c6e31d209c4..993e445418a 100644 --- a/kernel/audit.c +++ b/kernel/audit.c @@ -138,16 +138,18 @@ struct audit_buffer { struct list_head list; struct sk_buff *skb; /* formatted skb ready to send */ struct audit_context *ctx; /* NULL or associated context */ - int len; /* used area of tmp */ - int size; /* size of tmp */ - char *tmp; - int type; - int pid; }; void audit_set_type(struct audit_buffer *ab, int type) { - ab->type = type; + struct nlmsghdr *nlh = (struct nlmsghdr *)ab->skb->data; + nlh->nlmsg_type = type; +} + +static void audit_set_pid(struct audit_buffer *ab, pid_t pid) +{ + struct nlmsghdr *nlh = (struct nlmsghdr *)ab->skb->data; + nlh->nlmsg_pid = pid; } struct audit_entry { @@ -405,8 +407,8 @@ static int audit_receive_msg(struct sk_buff *skb, struct nlmsghdr *nlh) (int)(nlh->nlmsg_len - ((char *)data - (char *)nlh)), loginuid, (char *)data); - ab->type = AUDIT_USER; - ab->pid = pid; + audit_set_type(ab, AUDIT_USER); + audit_set_pid(ab, pid); audit_log_end(ab); break; case AUDIT_ADD: @@ -476,42 +478,7 @@ static void audit_receive(struct sock *sk, int length) up(&audit_netlink_sem); } -/* Move data from tmp buffer into an skb. This is an extra copy, and - * that is unfortunate. However, the copy will only occur when a record - * is being written to user space, which is already a high-overhead - * operation. (Elimination of the copy is possible, for example, by - * writing directly into a pre-allocated skb, at the cost of wasting - * memory. */ -static void audit_log_move(struct audit_buffer *ab) -{ - struct sk_buff *skb; - struct nlmsghdr *nlh; - char *start; - int len = NLMSG_SPACE(0) + ab->len + 1; - - /* possible resubmission */ - if (ab->skb) - return; - - skb = alloc_skb(len, GFP_ATOMIC); - if (!skb) { - /* Lose information in ab->tmp */ - audit_log_lost("out of memory in audit_log_move"); - return; - } - ab->skb = skb; - nlh = (struct nlmsghdr *)skb_put(skb, NLMSG_SPACE(0)); - nlh->nlmsg_type = ab->type; - nlh->nlmsg_len = ab->len; - nlh->nlmsg_flags = 0; - nlh->nlmsg_pid = ab->pid; - nlh->nlmsg_seq = 0; - start = skb_put(skb, ab->len); - memcpy(start, ab->tmp, ab->len); -} - -/* Iterate over the skbuff in the audit_buffer, sending their contents - * to user space. */ +/* Grab skbuff from the audit_buffer and send to user space. */ static inline int audit_log_drain(struct audit_buffer *ab) { struct sk_buff *skb = ab->skb; @@ -520,6 +487,8 @@ static inline int audit_log_drain(struct audit_buffer *ab) int retval = 0; if (audit_pid) { + struct nlmsghdr *nlh = (struct nlmsghdr *)skb->data; + nlh->nlmsg_len = skb->len; skb_get(skb); /* because netlink_* frees */ retval = netlink_unicast(audit_sock, skb, audit_pid, MSG_DONTWAIT); @@ -544,7 +513,6 @@ static inline int audit_log_drain(struct audit_buffer *ab) skb->data[offset + len] = '\0'; printk(KERN_ERR "%s\n", skb->data + offset); } - kfree_skb(skb); } return 0; } @@ -615,7 +583,8 @@ static void audit_buffer_free(struct audit_buffer *ab) if (!ab) return; - kfree(ab->tmp); + if (ab->skb) + kfree_skb(ab->skb); atomic_dec(&audit_backlog); spin_lock_irqsave(&audit_freelist_lock, flags); if (++audit_freelist_count > AUDIT_MAXFREE) @@ -630,6 +599,7 @@ static struct audit_buffer * audit_buffer_alloc(struct audit_context *ctx, { unsigned long flags; struct audit_buffer *ab = NULL; + struct nlmsghdr *nlh; spin_lock_irqsave(&audit_freelist_lock, flags); if (!list_empty(&audit_freelist)) { @@ -647,16 +617,16 @@ static struct audit_buffer * audit_buffer_alloc(struct audit_context *ctx, } atomic_inc(&audit_backlog); - ab->tmp = kmalloc(AUDIT_BUFSIZ, GFP_ATOMIC); - if (!ab->tmp) + ab->skb = alloc_skb(AUDIT_BUFSIZ, GFP_ATOMIC); + if (!ab->skb) goto err; - ab->skb = NULL; ab->ctx = ctx; - ab->len = 0; - ab->size = AUDIT_BUFSIZ; - ab->type = AUDIT_KERNEL; - ab->pid = 0; + nlh = (struct nlmsghdr *)skb_put(ab->skb, NLMSG_SPACE(0)); + nlh->nlmsg_type = AUDIT_KERNEL; + nlh->nlmsg_flags = 0; + nlh->nlmsg_pid = 0; + nlh->nlmsg_seq = 0; return ab; err: audit_buffer_free(ab); @@ -711,7 +681,7 @@ struct audit_buffer *audit_log_start(struct audit_context *ctx) } /** - * audit_expand - expand tmp buffer in the audit buffer + * audit_expand - expand skb in the audit buffer * @ab: audit_buffer * * Returns 0 (no space) on failed expansion, or available space if @@ -719,17 +689,14 @@ struct audit_buffer *audit_log_start(struct audit_context *ctx) */ static inline int audit_expand(struct audit_buffer *ab) { - char *tmp; - int len = ab->size + AUDIT_BUFSIZ; - - tmp = kmalloc(len, GFP_ATOMIC); - if (!tmp) + struct sk_buff *skb = ab->skb; + int ret = pskb_expand_head(skb, skb_headroom(skb), AUDIT_BUFSIZ, + GFP_ATOMIC); + if (ret < 0) { + audit_log_lost("out of memory in audit_expand"); return 0; - memcpy(tmp, ab->tmp, ab->len); - kfree(ab->tmp); - ab->tmp = tmp; - ab->size = len; - return ab->size - ab->len; + } + return skb_tailroom(skb); } /* Format an audit message into the audit buffer. If there isn't enough @@ -740,17 +707,20 @@ static void audit_log_vformat(struct audit_buffer *ab, const char *fmt, va_list args) { int len, avail; + struct sk_buff *skb; if (!ab) return; - avail = ab->size - ab->len; - if (avail <= 0) { + BUG_ON(!ab->skb); + skb = ab->skb; + avail = skb_tailroom(skb); + if (avail == 0) { avail = audit_expand(ab); if (!avail) goto out; } - len = vsnprintf(ab->tmp + ab->len, avail, fmt, args); + len = vsnprintf(skb->tail, avail, fmt, args); if (len >= avail) { /* The printk buffer is 1024 bytes long, so if we get * here and AUDIT_BUFSIZ is at least 1024, then we can @@ -758,9 +728,9 @@ static void audit_log_vformat(struct audit_buffer *ab, const char *fmt, avail = audit_expand(ab); if (!avail) goto out; - len = vsnprintf(ab->tmp + ab->len, avail, fmt, args); + len = vsnprintf(skb->tail, avail, fmt, args); } - ab->len += (len < avail) ? len : avail; + skb_put(skb, (len < avail) ? len : avail); out: return; } @@ -808,21 +778,22 @@ void audit_log_d_path(struct audit_buffer *ab, const char *prefix, struct dentry *dentry, struct vfsmount *vfsmnt) { char *p; + struct sk_buff *skb = ab->skb; int len, avail; if (prefix) audit_log_format(ab, " %s", prefix); - avail = ab->size - ab->len; - p = d_path(dentry, vfsmnt, ab->tmp + ab->len, avail); + avail = skb_tailroom(skb); + p = d_path(dentry, vfsmnt, skb->tail, avail); if (IS_ERR(p)) { /* FIXME: can we save some information here? */ audit_log_format(ab, ""); } else { - /* path isn't at start of buffer */ - len = (ab->tmp + ab->size - 1) - p; - memmove(ab->tmp + ab->len, p, len); - ab->len += len; + /* path isn't at start of buffer */ + len = ((char *)skb->tail + avail - 1) - p; + memmove(skb->tail, p, len); + skb_put(skb, len); } } @@ -873,7 +844,6 @@ static void audit_log_end_fast(struct audit_buffer *ab) if (!audit_rate_check()) { audit_log_lost("rate limit exceeded"); } else { - audit_log_move(ab); if (audit_log_drain(ab)) return; } -- cgit v1.2.3 From 4332bdd332a2dca93dc3b1d017b2dd27d5c8cef3 Mon Sep 17 00:00:00 2001 From: David Woodhouse Date: Fri, 6 May 2005 15:59:57 +0100 Subject: AUDIT: Honour gfp_mask in audit_buffer_alloc() Signed-off-by: David Woodhouse --- kernel/audit.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/kernel/audit.c b/kernel/audit.c index 993e445418a..b86007da8a3 100644 --- a/kernel/audit.c +++ b/kernel/audit.c @@ -611,13 +611,13 @@ static struct audit_buffer * audit_buffer_alloc(struct audit_context *ctx, spin_unlock_irqrestore(&audit_freelist_lock, flags); if (!ab) { - ab = kmalloc(sizeof(*ab), GFP_ATOMIC); + ab = kmalloc(sizeof(*ab), gfp_mask); if (!ab) goto err; } atomic_inc(&audit_backlog); - ab->skb = alloc_skb(AUDIT_BUFSIZ, GFP_ATOMIC); + ab->skb = alloc_skb(AUDIT_BUFSIZ, gfp_mask); if (!ab->skb) goto err; -- cgit v1.2.3 From ea9c102cb0a7969df5733d34f26e0b12c8a3c889 Mon Sep 17 00:00:00 2001 From: David Woodhouse Date: Sun, 8 May 2005 15:56:09 +0100 Subject: Add CONFIG_AUDITSC and CONFIG_SECCOMP support for ppc32 Signed-off-by: David Woodhouse --- arch/ppc/Kconfig | 17 +++++++++++++++++ arch/ppc/kernel/entry.S | 16 +++++++++------- arch/ppc/kernel/ppc_ksyms.c | 2 -- arch/ppc/kernel/ptrace.c | 40 ++++++++++++++++++++++++++++++++++++---- include/asm-ppc/thread_info.h | 7 +++++++ init/Kconfig | 2 +- 6 files changed, 70 insertions(+), 14 deletions(-) diff --git a/arch/ppc/Kconfig b/arch/ppc/Kconfig index 600f23d7fd3..cd752a3cf3b 100644 --- a/arch/ppc/Kconfig +++ b/arch/ppc/Kconfig @@ -1083,6 +1083,23 @@ source "drivers/zorro/Kconfig" source kernel/power/Kconfig +config SECCOMP + bool "Enable seccomp to safely compute untrusted bytecode" + depends on PROC_FS + default y + help + This kernel feature is useful for number crunching applications + that may need to compute untrusted bytecode during their + execution. By using pipes or other transports made available to + the process as file descriptors supporting the read/write + syscalls, it's possible to isolate those applications in + their own address space using seccomp. Once seccomp is + enabled via /proc//seccomp, it cannot be disabled + and the task is only allowed to execute a few safe syscalls + defined by each seccomp mode. + + If unsure, say Y. Only embedded should say N here. + endmenu config ISA_DMA_API diff --git a/arch/ppc/kernel/entry.S b/arch/ppc/kernel/entry.S index 5f075dbc4ee..661523707e8 100644 --- a/arch/ppc/kernel/entry.S +++ b/arch/ppc/kernel/entry.S @@ -202,7 +202,7 @@ _GLOBAL(DoSyscall) rlwinm r11,r11,0,~_TIFL_FORCE_NOERROR stw r11,TI_LOCAL_FLAGS(r10) lwz r11,TI_FLAGS(r10) - andi. r11,r11,_TIF_SYSCALL_TRACE + andi. r11,r11,_TIF_SYSCALL_T_OR_A bne- syscall_dotrace syscall_dotrace_cont: cmplwi 0,r0,NR_syscalls @@ -237,7 +237,7 @@ ret_from_syscall: SYNC MTMSRD(r10) lwz r9,TI_FLAGS(r12) - andi. r0,r9,(_TIF_SYSCALL_TRACE|_TIF_SIGPENDING|_TIF_NEED_RESCHED) + andi. r0,r9,(_TIF_SYSCALL_T_OR_A|_TIF_SIGPENDING|_TIF_NEED_RESCHED) bne- syscall_exit_work syscall_exit_cont: #if defined(CONFIG_4xx) || defined(CONFIG_BOOKE) @@ -277,7 +277,8 @@ syscall_dotrace: SAVE_NVGPRS(r1) li r0,0xc00 stw r0,TRAP(r1) - bl do_syscall_trace + addi r3,r1,STACK_FRAME_OVERHEAD + bl do_syscall_trace_enter lwz r0,GPR0(r1) /* Restore original registers */ lwz r3,GPR3(r1) lwz r4,GPR4(r1) @@ -291,7 +292,7 @@ syscall_dotrace: syscall_exit_work: stw r6,RESULT(r1) /* Save result */ stw r3,GPR3(r1) /* Update return value */ - andi. r0,r9,_TIF_SYSCALL_TRACE + andi. r0,r9,_TIF_SYSCALL_T_OR_A beq 5f ori r10,r10,MSR_EE SYNC @@ -303,7 +304,8 @@ syscall_exit_work: li r4,0xc00 stw r4,TRAP(r1) 4: - bl do_syscall_trace + addi r3,r1,STACK_FRAME_OVERHEAD + bl do_syscall_trace_leave REST_NVGPRS(r1) 2: lwz r3,GPR3(r1) @@ -627,8 +629,8 @@ sigreturn_exit: subi r1,r3,STACK_FRAME_OVERHEAD rlwinm r12,r1,0,0,18 /* current_thread_info() */ lwz r9,TI_FLAGS(r12) - andi. r0,r9,_TIF_SYSCALL_TRACE - bnel- do_syscall_trace + andi. r0,r9,_TIF_SYSCALL_T_OR_A + bnel- do_syscall_trace_leave /* fall through */ .globl ret_from_except_full diff --git a/arch/ppc/kernel/ppc_ksyms.c b/arch/ppc/kernel/ppc_ksyms.c index 2ccb58fe4fc..d59ad07de8e 100644 --- a/arch/ppc/kernel/ppc_ksyms.c +++ b/arch/ppc/kernel/ppc_ksyms.c @@ -55,7 +55,6 @@ #define EXPORT_SYMTAB_STROPS extern void transfer_to_handler(void); -extern void do_syscall_trace(void); extern void do_IRQ(struct pt_regs *regs); extern void MachineCheckException(struct pt_regs *regs); extern void AlignmentException(struct pt_regs *regs); @@ -74,7 +73,6 @@ extern unsigned long mm_ptov (unsigned long paddr); EXPORT_SYMBOL(clear_pages); EXPORT_SYMBOL(clear_user_page); EXPORT_SYMBOL(do_signal); -EXPORT_SYMBOL(do_syscall_trace); EXPORT_SYMBOL(transfer_to_handler); EXPORT_SYMBOL(do_IRQ); EXPORT_SYMBOL(MachineCheckException); diff --git a/arch/ppc/kernel/ptrace.c b/arch/ppc/kernel/ptrace.c index 59d59a8dc24..e7aee4108de 100644 --- a/arch/ppc/kernel/ptrace.c +++ b/arch/ppc/kernel/ptrace.c @@ -27,6 +27,9 @@ #include #include #include +#include +#include +#include #include #include @@ -455,11 +458,10 @@ out: return ret; } -void do_syscall_trace(void) +static void do_syscall_trace(void) { - if (!test_thread_flag(TIF_SYSCALL_TRACE) - || !(current->ptrace & PT_PTRACED)) - return; + /* the 0x80 provides a way for the tracing parent to distinguish + between a syscall stop and SIGTRAP delivery */ ptrace_notify(SIGTRAP | ((current->ptrace & PT_TRACESYSGOOD) ? 0x80 : 0)); @@ -473,3 +475,33 @@ void do_syscall_trace(void) current->exit_code = 0; } } + +void do_syscall_trace_enter(struct pt_regs *regs) +{ + if (test_thread_flag(TIF_SYSCALL_TRACE) + && (current->ptrace & PT_PTRACED)) + do_syscall_trace(); + + if (unlikely(current->audit_context)) + audit_syscall_entry(current, AUDIT_ARCH_PPC, + regs->gpr[0], + regs->gpr[3], regs->gpr[4], + regs->gpr[5], regs->gpr[6]); +} + +void do_syscall_trace_leave(struct pt_regs *regs) +{ + secure_computing(regs->gpr[0]); + + if (unlikely(current->audit_context)) + audit_syscall_exit(current, + (regs->ccr&0x1000)?AUDITSC_FAILURE:AUDITSC_SUCCESS, + regs->result); + + if ((test_thread_flag(TIF_SYSCALL_TRACE)) + && (current->ptrace & PT_PTRACED)) + do_syscall_trace(); +} + +EXPORT_SYMBOL(do_syscall_trace_enter); +EXPORT_SYMBOL(do_syscall_trace_leave); diff --git a/include/asm-ppc/thread_info.h b/include/asm-ppc/thread_info.h index f7f01524e8a..e3b5284a6f9 100644 --- a/include/asm-ppc/thread_info.h +++ b/include/asm-ppc/thread_info.h @@ -77,12 +77,19 @@ static inline struct thread_info *current_thread_info(void) #define TIF_POLLING_NRFLAG 4 /* true if poll_idle() is polling TIF_NEED_RESCHED */ #define TIF_MEMDIE 5 +#define TIF_SYSCALL_AUDIT 6 /* syscall auditing active */ +#define TIF_SECCOMP 7 /* secure computing */ + /* as above, but as bit values */ #define _TIF_SYSCALL_TRACE (1< Date: Tue, 10 May 2005 18:53:07 +0100 Subject: AUDIT: Fix reported length of audit messages. We were setting nlmsg_len to skb->len, but we should be subtracting the size of the header. From: Steve Grubb Signed-off-by: David Woodhouse --- kernel/audit.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/kernel/audit.c b/kernel/audit.c index b86007da8a3..2ddd1a2b66d 100644 --- a/kernel/audit.c +++ b/kernel/audit.c @@ -488,7 +488,7 @@ static inline int audit_log_drain(struct audit_buffer *ab) if (audit_pid) { struct nlmsghdr *nlh = (struct nlmsghdr *)skb->data; - nlh->nlmsg_len = skb->len; + nlh->nlmsg_len = skb->len - sizeof(*nlh); skb_get(skb); /* because netlink_* frees */ retval = netlink_unicast(audit_sock, skb, audit_pid, MSG_DONTWAIT); -- cgit v1.2.3 From e3b926b4c1499ba7b1b9513aa6113944d572aba5 Mon Sep 17 00:00:00 2001 From: David Woodhouse Date: Tue, 10 May 2005 18:56:08 +0100 Subject: AUDIT: pass size argument to audit_expand(). Let audit_expand() know how much it's expected to grow the buffer, in the case that we have that information to hand. Signed-off-by: David Woodhouse --- kernel/audit.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/kernel/audit.c b/kernel/audit.c index 2ddd1a2b66d..1dd456c90ae 100644 --- a/kernel/audit.c +++ b/kernel/audit.c @@ -687,10 +687,10 @@ struct audit_buffer *audit_log_start(struct audit_context *ctx) * Returns 0 (no space) on failed expansion, or available space if * successful. */ -static inline int audit_expand(struct audit_buffer *ab) +static inline int audit_expand(struct audit_buffer *ab, int extra) { struct sk_buff *skb = ab->skb; - int ret = pskb_expand_head(skb, skb_headroom(skb), AUDIT_BUFSIZ, + int ret = pskb_expand_head(skb, skb_headroom(skb), extra, GFP_ATOMIC); if (ret < 0) { audit_log_lost("out of memory in audit_expand"); @@ -716,7 +716,7 @@ static void audit_log_vformat(struct audit_buffer *ab, const char *fmt, skb = ab->skb; avail = skb_tailroom(skb); if (avail == 0) { - avail = audit_expand(ab); + avail = audit_expand(ab, AUDIT_BUFSIZ); if (!avail) goto out; } @@ -725,7 +725,7 @@ static void audit_log_vformat(struct audit_buffer *ab, const char *fmt, /* The printk buffer is 1024 bytes long, so if we get * here and AUDIT_BUFSIZ is at least 1024, then we can * log everything that printk could have logged. */ - avail = audit_expand(ab); + avail = audit_expand(ab, 1+len-avail); if (!avail) goto out; len = vsnprintf(skb->tail, avail, fmt, args); -- cgit v1.2.3 From eecb0a7338ef6504aa49def4dde6429853025801 Mon Sep 17 00:00:00 2001 From: David Woodhouse Date: Tue, 10 May 2005 18:58:51 +0100 Subject: AUDIT: Fix abuse of va_args. We're not allowed to use args twice; we need to use va_copy. Signed-off-by: David Woodhouse --- kernel/audit.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/kernel/audit.c b/kernel/audit.c index 1dd456c90ae..ddb69a45820 100644 --- a/kernel/audit.c +++ b/kernel/audit.c @@ -708,6 +708,7 @@ static void audit_log_vformat(struct audit_buffer *ab, const char *fmt, { int len, avail; struct sk_buff *skb; + va_list args2; if (!ab) return; @@ -720,6 +721,7 @@ static void audit_log_vformat(struct audit_buffer *ab, const char *fmt, if (!avail) goto out; } + va_copy(args2, args); len = vsnprintf(skb->tail, avail, fmt, args); if (len >= avail) { /* The printk buffer is 1024 bytes long, so if we get @@ -728,7 +730,7 @@ static void audit_log_vformat(struct audit_buffer *ab, const char *fmt, avail = audit_expand(ab, 1+len-avail); if (!avail) goto out; - len = vsnprintf(skb->tail, avail, fmt, args); + len = vsnprintf(skb->tail, avail, fmt, args2); } skb_put(skb, (len < avail) ? len : avail); out: -- cgit v1.2.3 From 5a241d77039a2632e81070619d5733258728f8bd Mon Sep 17 00:00:00 2001 From: Chris Wright Date: Wed, 11 May 2005 10:43:07 +0100 Subject: AUDIT: Properly account for alignment difference in nlmsg_len. Signed-off-by: Chris Wright Signed-off-by: David Woodhouse --- kernel/audit.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/kernel/audit.c b/kernel/audit.c index ddb69a45820..a5f03cb2c0f 100644 --- a/kernel/audit.c +++ b/kernel/audit.c @@ -488,7 +488,7 @@ static inline int audit_log_drain(struct audit_buffer *ab) if (audit_pid) { struct nlmsghdr *nlh = (struct nlmsghdr *)skb->data; - nlh->nlmsg_len = skb->len - sizeof(*nlh); + nlh->nlmsg_len = skb->len - NLMSG_SPACE(0); skb_get(skb); /* because netlink_* frees */ retval = netlink_unicast(audit_sock, skb, audit_pid, MSG_DONTWAIT); -- cgit v1.2.3 From 804a6a49d874841a98ebea3247ad2e672812ad6a Mon Sep 17 00:00:00 2001 From: Chris Wright Date: Wed, 11 May 2005 10:52:45 +0100 Subject: Audit requires CONFIG_NET Audit now actually requires netlink. So make it depend on CONFIG_NET, and remove the inline dependencies on CONFIG_NET. Signed-off-by: Chris Wright Signed-off-by: David Woodhouse --- init/Kconfig | 1 + kernel/audit.c | 30 ------------------------------ kernel/auditsc.c | 2 -- 3 files changed, 1 insertion(+), 32 deletions(-) diff --git a/init/Kconfig b/init/Kconfig index 70549765346..448939d183d 100644 --- a/init/Kconfig +++ b/init/Kconfig @@ -164,6 +164,7 @@ config SYSCTL config AUDIT bool "Auditing support" + depends on NET default y if SECURITY_SELINUX help Enable auditing infrastructure that can be used with another diff --git a/kernel/audit.c b/kernel/audit.c index a5f03cb2c0f..dc4aba21f30 100644 --- a/kernel/audit.c +++ b/kernel/audit.c @@ -283,7 +283,6 @@ static int audit_set_failure(int state, uid_t loginuid) return old; } -#ifdef CONFIG_NET void audit_send_reply(int pid, int seq, int type, int done, int multi, void *payload, int size) { @@ -531,35 +530,6 @@ static int __init audit_init(void) audit_log(NULL, "initialized"); return 0; } - -#else -/* Without CONFIG_NET, we have no skbuffs. For now, print what we have - * in the buffer. */ -static void audit_log_move(struct audit_buffer *ab) -{ - printk(KERN_ERR "%*.*s\n", ab->len, ab->len, ab->tmp); - ab->len = 0; -} - -static inline int audit_log_drain(struct audit_buffer *ab) -{ - return 0; -} - -/* Initialize audit support at boot time. */ -int __init audit_init(void) -{ - printk(KERN_INFO "audit: initializing WITHOUT netlink support\n"); - audit_sock = NULL; - audit_pid = 0; - - audit_initialized = 1; - audit_enabled = audit_default; - audit_log(NULL, "initialized"); - return 0; -} -#endif - __initcall(audit_init); /* Process kernel command-line parameter at boot time. audit=0 or audit=1. */ diff --git a/kernel/auditsc.c b/kernel/auditsc.c index f1bf66510cd..680bb928343 100644 --- a/kernel/auditsc.c +++ b/kernel/auditsc.c @@ -226,7 +226,6 @@ static inline int audit_del_rule(struct audit_rule *rule, return -EFAULT; /* No matching rule */ } -#ifdef CONFIG_NET /* Copy rule from user-space to kernel-space. Called during * AUDIT_ADD. */ static int audit_copy_rule(struct audit_rule *d, struct audit_rule *s) @@ -305,7 +304,6 @@ int audit_receive_filter(int type, int pid, int uid, int seq, void *data, return err; } -#endif /* Compare a task_struct with an audit_rule. Return 1 on match, 0 * otherwise. */ -- cgit v1.2.3 From 197c69c6afd2deb7eec44040ff533d90d26c6161 Mon Sep 17 00:00:00 2001 From: Chris Wright Date: Wed, 11 May 2005 10:54:05 +0100 Subject: Move ifdef CONFIG_AUDITSYSCALL to header Remove code conditionally dependent on CONFIG_AUDITSYSCALL from audit.c. Move these dependencies to audit.h with the rest. Signed-off-by: Chris Wright Signed-off-by: David Woodhouse --- include/linux/audit.h | 4 +++- kernel/audit.c | 12 ++---------- kernel/auditsc.c | 7 +++---- 3 files changed, 8 insertions(+), 15 deletions(-) diff --git a/include/linux/audit.h b/include/linux/audit.h index baa80760824..58c5589b531 100644 --- a/include/linux/audit.h +++ b/include/linux/audit.h @@ -192,7 +192,7 @@ extern void audit_inode(const char *name, const struct inode *inode); /* Private API (for audit.c only) */ extern int audit_receive_filter(int type, int pid, int uid, int seq, void *data, uid_t loginuid); -extern void audit_get_stamp(struct audit_context *ctx, +extern int audit_get_stamp(struct audit_context *ctx, struct timespec *t, unsigned int *serial); extern int audit_set_loginuid(struct task_struct *task, uid_t loginuid); extern uid_t audit_get_loginuid(struct audit_context *ctx); @@ -206,6 +206,8 @@ extern void audit_signal_info(int sig, struct task_struct *t); #define audit_getname(n) do { ; } while (0) #define audit_putname(n) do { ; } while (0) #define audit_inode(n,i) do { ; } while (0) +#define audit_receive_filter(t,p,u,s,d,l) ({ -EOPNOTSUPP; }) +#define audit_get_stamp(c,t,s) ({ 0; }) #define audit_get_loginuid(c) ({ -1; }) #define audit_ipc_perms(q,u,g,m) ({ 0; }) #define audit_signal_info(s,t) do { ; } while (0) diff --git a/kernel/audit.c b/kernel/audit.c index dc4aba21f30..c18b769e23a 100644 --- a/kernel/audit.c +++ b/kernel/audit.c @@ -416,12 +416,8 @@ static int audit_receive_msg(struct sk_buff *skb, struct nlmsghdr *nlh) return -EINVAL; /* fallthrough */ case AUDIT_LIST: -#ifdef CONFIG_AUDITSYSCALL err = audit_receive_filter(nlh->nlmsg_type, NETLINK_CB(skb).pid, uid, seq, data, loginuid); -#else - err = -EOPNOTSUPP; -#endif break; case AUDIT_SIGNAL_INFO: sig_data.uid = audit_sig_uid; @@ -636,15 +632,11 @@ struct audit_buffer *audit_log_start(struct audit_context *ctx) return NULL; } -#ifdef CONFIG_AUDITSYSCALL - if (ab->ctx) - audit_get_stamp(ab->ctx, &t, &serial); - else -#endif - { + if (!audit_get_stamp(ab->ctx, &t, &serial)) { t = CURRENT_TIME; serial = 0; } + audit_log_format(ab, "audit(%lu.%03lu:%u): ", t.tv_sec, t.tv_nsec/1000000, serial); return ab; diff --git a/kernel/auditsc.c b/kernel/auditsc.c index 680bb928343..94338abf76f 100644 --- a/kernel/auditsc.c +++ b/kernel/auditsc.c @@ -992,7 +992,7 @@ void audit_inode(const char *name, const struct inode *inode) context->names[idx].rdev = inode->i_rdev; } -void audit_get_stamp(struct audit_context *ctx, +int audit_get_stamp(struct audit_context *ctx, struct timespec *t, unsigned int *serial) { if (ctx) { @@ -1000,10 +1000,9 @@ void audit_get_stamp(struct audit_context *ctx, t->tv_nsec = ctx->ctime.tv_nsec; *serial = ctx->serial; ctx->auditable = 1; - } else { - *t = CURRENT_TIME; - *serial = 0; + return 1; } + return 0; } extern int audit_set_type(struct audit_buffer *ab, int type); -- cgit v1.2.3 From c1b773d87eadc3972d697444127e89a7291769a2 Mon Sep 17 00:00:00 2001 From: Chris Wright Date: Wed, 11 May 2005 10:55:10 +0100 Subject: Add audit_log_type Add audit_log_type to allow callers to specify type and pid when logging. Convert audit_log to wrapper around audit_log_type. Could have converted all audit_log callers directly, but common case is default of type AUDIT_KERNEL and pid 0. Update audit_log_start to take type and pid values when creating a new audit_buffer. Move sequences that did audit_log_start, audit_log_format, audit_set_type, audit_log_end, to simply call audit_log_type directly. This obsoletes audit_set_type and audit_set_pid, so remove them. Signed-off-by: Chris Wright Signed-off-by: David Woodhouse --- include/linux/audit.h | 16 ++++++++++------ kernel/audit.c | 48 +++++++++++++++--------------------------------- kernel/auditsc.c | 23 +++++++---------------- security/selinux/avc.c | 2 +- 4 files changed, 33 insertions(+), 56 deletions(-) diff --git a/include/linux/audit.h b/include/linux/audit.h index 58c5589b531..405332ebf3c 100644 --- a/include/linux/audit.h +++ b/include/linux/audit.h @@ -216,11 +216,14 @@ extern void audit_signal_info(int sig, struct task_struct *t); #ifdef CONFIG_AUDIT /* These are defined in audit.c */ /* Public API */ -extern void audit_log(struct audit_context *ctx, - const char *fmt, ...) - __attribute__((format(printf,2,3))); +#define audit_log(ctx, fmt, args...) \ + audit_log_type(ctx, AUDIT_KERNEL, 0, fmt, ##args) +extern void audit_log_type(struct audit_context *ctx, int type, + int pid, const char *fmt, ...) + __attribute__((format(printf,4,5))); -extern struct audit_buffer *audit_log_start(struct audit_context *ctx); +extern struct audit_buffer *audit_log_start(struct audit_context *ctx, int type, + int pid); extern void audit_log_format(struct audit_buffer *ab, const char *fmt, ...) __attribute__((format(printf,2,3))); @@ -240,8 +243,9 @@ extern void audit_send_reply(int pid, int seq, int type, void *payload, int size); extern void audit_log_lost(const char *message); #else -#define audit_log(t,f,...) do { ; } while (0) -#define audit_log_start(t) ({ NULL; }) +#define audit_log(c,f,...) do { ; } while (0) +#define audit_log_type(c,t,p,f,...) do { ; } while (0) +#define audit_log_start(c,t,p) ({ NULL; }) #define audit_log_vformat(b,f,a) do { ; } while (0) #define audit_log_format(b,f,...) do { ; } while (0) #define audit_log_end(b) do { ; } while (0) diff --git a/kernel/audit.c b/kernel/audit.c index c18b769e23a..060b554f481 100644 --- a/kernel/audit.c +++ b/kernel/audit.c @@ -140,18 +140,6 @@ struct audit_buffer { struct audit_context *ctx; /* NULL or associated context */ }; -void audit_set_type(struct audit_buffer *ab, int type) -{ - struct nlmsghdr *nlh = (struct nlmsghdr *)ab->skb->data; - nlh->nlmsg_type = type; -} - -static void audit_set_pid(struct audit_buffer *ab, pid_t pid) -{ - struct nlmsghdr *nlh = (struct nlmsghdr *)ab->skb->data; - nlh->nlmsg_pid = pid; -} - struct audit_entry { struct list_head list; struct audit_rule rule; @@ -344,7 +332,6 @@ static int audit_receive_msg(struct sk_buff *skb, struct nlmsghdr *nlh) void *data; struct audit_status *status_get, status_set; int err; - struct audit_buffer *ab; u16 msg_type = nlh->nlmsg_type; uid_t loginuid; /* loginuid of sender */ struct audit_sig_info sig_data; @@ -396,19 +383,13 @@ static int audit_receive_msg(struct sk_buff *skb, struct nlmsghdr *nlh) loginuid); break; case AUDIT_USER: - ab = audit_log_start(NULL); - if (!ab) - break; /* audit_panic has been called */ - audit_log_format(ab, + audit_log_type(NULL, AUDIT_USER, pid, "user pid=%d uid=%d length=%d loginuid=%u" " msg='%.1024s'", pid, uid, (int)(nlh->nlmsg_len - ((char *)data - (char *)nlh)), loginuid, (char *)data); - audit_set_type(ab, AUDIT_USER); - audit_set_pid(ab, pid); - audit_log_end(ab); break; case AUDIT_ADD: case AUDIT_DEL: @@ -560,12 +541,10 @@ static void audit_buffer_free(struct audit_buffer *ab) spin_unlock_irqrestore(&audit_freelist_lock, flags); } -static struct audit_buffer * audit_buffer_alloc(struct audit_context *ctx, - int gfp_mask) +static struct audit_buffer * audit_buffer_alloc(int gfp_mask) { unsigned long flags; struct audit_buffer *ab = NULL; - struct nlmsghdr *nlh; spin_lock_irqsave(&audit_freelist_lock, flags); if (!list_empty(&audit_freelist)) { @@ -587,12 +566,6 @@ static struct audit_buffer * audit_buffer_alloc(struct audit_context *ctx, if (!ab->skb) goto err; - ab->ctx = ctx; - nlh = (struct nlmsghdr *)skb_put(ab->skb, NLMSG_SPACE(0)); - nlh->nlmsg_type = AUDIT_KERNEL; - nlh->nlmsg_flags = 0; - nlh->nlmsg_pid = 0; - nlh->nlmsg_seq = 0; return ab; err: audit_buffer_free(ab); @@ -605,11 +578,12 @@ err: * syscall, then the syscall is marked as auditable and an audit record * will be written at syscall exit. If there is no associated task, tsk * should be NULL. */ -struct audit_buffer *audit_log_start(struct audit_context *ctx) +struct audit_buffer *audit_log_start(struct audit_context *ctx, int type, int pid) { struct audit_buffer *ab = NULL; struct timespec t; unsigned int serial; + struct nlmsghdr *nlh; if (!audit_initialized) return NULL; @@ -626,12 +600,19 @@ struct audit_buffer *audit_log_start(struct audit_context *ctx) return NULL; } - ab = audit_buffer_alloc(ctx, GFP_ATOMIC); + ab = audit_buffer_alloc(GFP_ATOMIC); if (!ab) { audit_log_lost("out of memory in audit_log_start"); return NULL; } + ab->ctx = ctx; + nlh = (struct nlmsghdr *)skb_put(ab->skb, NLMSG_SPACE(0)); + nlh->nlmsg_type = type; + nlh->nlmsg_flags = 0; + nlh->nlmsg_pid = pid; + nlh->nlmsg_seq = 0; + if (!audit_get_stamp(ab->ctx, &t, &serial)) { t = CURRENT_TIME; serial = 0; @@ -828,12 +809,13 @@ void audit_log_end(struct audit_buffer *ab) /* Log an audit record. This is a convenience function that calls * audit_log_start, audit_log_vformat, and audit_log_end. It may be * called in any context. */ -void audit_log(struct audit_context *ctx, const char *fmt, ...) +void audit_log_type(struct audit_context *ctx, int type, int pid, + const char *fmt, ...) { struct audit_buffer *ab; va_list args; - ab = audit_log_start(ctx); + ab = audit_log_start(ctx, type, pid); if (ab) { va_start(args, fmt); audit_log_vformat(ab, fmt, args); diff --git a/kernel/auditsc.c b/kernel/auditsc.c index 94338abf76f..d089263253a 100644 --- a/kernel/auditsc.c +++ b/kernel/auditsc.c @@ -648,7 +648,7 @@ static void audit_log_exit(struct audit_context *context) int i; struct audit_buffer *ab; - ab = audit_log_start(context); + ab = audit_log_start(context, AUDIT_KERNEL, 0); if (!ab) return; /* audit_panic has been called */ audit_log_format(ab, "syscall=%d", context->major); @@ -680,7 +680,7 @@ static void audit_log_exit(struct audit_context *context) while (context->aux) { struct audit_aux_data *aux; - ab = audit_log_start(context); + ab = audit_log_start(context, AUDIT_KERNEL, 0); if (!ab) continue; /* audit_panic has been called */ @@ -701,7 +701,7 @@ static void audit_log_exit(struct audit_context *context) } for (i = 0; i < context->name_count; i++) { - ab = audit_log_start(context); + ab = audit_log_start(context, AUDIT_KERNEL, 0); if (!ab) continue; /* audit_panic has been called */ audit_log_format(ab, "item=%d", i); @@ -1005,22 +1005,13 @@ int audit_get_stamp(struct audit_context *ctx, return 0; } -extern int audit_set_type(struct audit_buffer *ab, int type); - int audit_set_loginuid(struct task_struct *task, uid_t loginuid) { if (task->audit_context) { - struct audit_buffer *ab; - - ab = audit_log_start(NULL); - if (ab) { - audit_log_format(ab, "login pid=%d uid=%u " - "old loginuid=%u new loginuid=%u", - task->pid, task->uid, - task->audit_context->loginuid, loginuid); - audit_set_type(ab, AUDIT_LOGIN); - audit_log_end(ab); - } + audit_log_type(NULL, AUDIT_LOGIN, 0, + "login pid=%d uid=%u old loginuid=%u new loginuid=%u", + task->pid, task->uid, task->audit_context->loginuid, + loginuid); task->audit_context->loginuid = loginuid; } return 0; diff --git a/security/selinux/avc.c b/security/selinux/avc.c index 85a6f66a873..9e71a1bbe01 100644 --- a/security/selinux/avc.c +++ b/security/selinux/avc.c @@ -549,7 +549,7 @@ void avc_audit(u32 ssid, u32 tsid, return; } - ab = audit_log_start(current->audit_context); + ab = audit_log_start(current->audit_context, AUDIT_KERNEL, 0); if (!ab) return; /* audit_panic has been called */ audit_log_format(ab, "avc: %s ", denied ? "denied" : "granted"); -- cgit v1.2.3 From add671412021b68c3b4f2882b0d10a56e2dcdabe Mon Sep 17 00:00:00 2001 From: David Woodhouse Date: Wed, 11 May 2005 11:36:21 +0100 Subject: Add missing asm-ppc/seccomp.h. Must learn to use git properly. Signed-off-by: David Woodhouse --- include/asm-ppc/seccomp.h | 10 ++++++++++ 1 file changed, 10 insertions(+) create mode 100644 include/asm-ppc/seccomp.h diff --git a/include/asm-ppc/seccomp.h b/include/asm-ppc/seccomp.h new file mode 100644 index 00000000000..666c4da96d8 --- /dev/null +++ b/include/asm-ppc/seccomp.h @@ -0,0 +1,10 @@ +#ifndef _ASM_SECCOMP_H + +#include + +#define __NR_seccomp_read __NR_read +#define __NR_seccomp_write __NR_write +#define __NR_seccomp_exit __NR_exit +#define __NR_seccomp_sigreturn __NR_rt_sigreturn + +#endif /* _ASM_SECCOMP_H */ -- cgit v1.2.3 From 9ea74f0655412d0fbd12bf9adb6c14c8fe707a42 Mon Sep 17 00:00:00 2001 From: David Woodhouse Date: Fri, 13 May 2005 16:35:19 +0100 Subject: AUDIT: Round up audit skb expansion to AUDIT_BUFSIZ. Otherwise, we will be repeatedly reallocating, even if we're only adding a few bytes at a time. Pointed out by Steve Grubb. Signed-off-by: David Woodhouse --- kernel/audit.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/kernel/audit.c b/kernel/audit.c index 060b554f481..187164572bd 100644 --- a/kernel/audit.c +++ b/kernel/audit.c @@ -670,7 +670,7 @@ static void audit_log_vformat(struct audit_buffer *ab, const char *fmt, /* The printk buffer is 1024 bytes long, so if we get * here and AUDIT_BUFSIZ is at least 1024, then we can * log everything that printk could have logged. */ - avail = audit_expand(ab, 1+len-avail); + avail = audit_expand(ab, max_t(AUDIT_BUFSIZ, 1+len-avail)); if (!avail) goto out; len = vsnprintf(skb->tail, avail, fmt, args2); -- cgit v1.2.3 From c04049939f88b29e235d2da217bce6e8ead44f32 Mon Sep 17 00:00:00 2001 From: Steve Grubb Date: Fri, 13 May 2005 18:17:42 +0100 Subject: AUDIT: Add message types to audit records This patch adds more messages types to the audit subsystem so that audit analysis is quicker, intuitive, and more useful. Signed-off-by: Steve Grubb --- I forgot one type in the big patch. I need to add one for user space originating SE Linux avc messages. This is used by dbus and nscd. -Steve --- Updated to 2.6.12-rc4-mm1. -dwmw2 Signed-off-by: David Woodhouse --- include/linux/audit.h | 66 ++++++++++++++++++++++++++--------- kernel/audit.c | 78 +++++++++++++++++++++++++++++------------- kernel/auditsc.c | 42 ++++++++++++++--------- security/selinux/avc.c | 4 +-- security/selinux/hooks.c | 2 +- security/selinux/nlmsgtab.c | 8 +++++ security/selinux/ss/services.c | 4 +-- 7 files changed, 143 insertions(+), 61 deletions(-) diff --git a/include/linux/audit.h b/include/linux/audit.h index 405332ebf3c..1a15ba38c66 100644 --- a/include/linux/audit.h +++ b/include/linux/audit.h @@ -27,15 +27,53 @@ #include #include -/* Request and reply types */ +/* The netlink messages for the audit system is divided into blocks: + * 1000 - 1099 are for commanding the audit system + * 1100 - 1199 user space trusted application messages + * 1200 - 1299 messages internal to the audit daemon + * 1300 - 1399 audit event messages + * 1400 - 1499 SE Linux use + * 1500 - 1999 future use + * 2000 is for otherwise unclassified kernel audit messages + * + * Messages from 1000-1199 are bi-directional. 1200-1299 are exclusively user + * space. Anything over that is kernel --> user space communication. + */ #define AUDIT_GET 1000 /* Get status */ #define AUDIT_SET 1001 /* Set status (enable/disable/auditd) */ -#define AUDIT_LIST 1002 /* List filtering rules */ -#define AUDIT_ADD 1003 /* Add filtering rule */ -#define AUDIT_DEL 1004 /* Delete filtering rule */ -#define AUDIT_USER 1005 /* Send a message from user-space */ +#define AUDIT_LIST 1002 /* List syscall filtering rules */ +#define AUDIT_ADD 1003 /* Add syscall filtering rule */ +#define AUDIT_DEL 1004 /* Delete syscall filtering rule */ +#define AUDIT_USER 1005 /* Message from userspace -- deprecated */ #define AUDIT_LOGIN 1006 /* Define the login id and information */ -#define AUDIT_SIGNAL_INFO 1010 /* Get information about sender of signal*/ +#define AUDIT_WATCH_INS 1007 /* Insert file/dir watch entry */ +#define AUDIT_WATCH_REM 1008 /* Remove file/dir watch entry */ +#define AUDIT_WATCH_LIST 1009 /* List all file/dir watches */ +#define AUDIT_SIGNAL_INFO 1010 /* Get info about sender of signal to auditd */ + +#define AUDIT_USER_AUTH 1100 /* User space authentication */ +#define AUDIT_USER_ACCT 1101 /* User space acct change */ +#define AUDIT_USER_MGMT 1102 /* User space acct management */ +#define AUDIT_CRED_ACQ 1103 /* User space credential acquired */ +#define AUDIT_CRED_DISP 1104 /* User space credential disposed */ +#define AUDIT_USER_START 1105 /* User space session start */ +#define AUDIT_USER_END 1106 /* User space session end */ +#define AUDIT_USER_AVC 1107 /* User space avc message */ + +#define AUDIT_DAEMON_START 1200 /* Daemon startup record */ +#define AUDIT_DAEMON_END 1201 /* Daemon normal stop record */ +#define AUDIT_DAEMON_ABORT 1202 /* Daemon error stop record */ +#define AUDIT_DAEMON_CONFIG 1203 /* Daemon config change */ + +#define AUDIT_SYSCALL 1300 /* Syscall event */ +#define AUDIT_FS_WATCH 1301 /* Filesystem watch event */ +#define AUDIT_PATH 1302 /* Filname path information */ +#define AUDIT_IPC 1303 /* IPC record */ +#define AUDIT_SOCKET 1304 /* Socket record */ +#define AUDIT_CONFIG_CHANGE 1305 /* Audit system configuration change */ + +#define AUDIT_AVC 1400 /* SE Linux avc denial or grant */ +#define AUDIT_SELINUX_ERR 1401 /* Internal SE Linux Errors */ #define AUDIT_KERNEL 2000 /* Asynchronous audit record. NOT A REQUEST. */ @@ -216,14 +254,11 @@ extern void audit_signal_info(int sig, struct task_struct *t); #ifdef CONFIG_AUDIT /* These are defined in audit.c */ /* Public API */ -#define audit_log(ctx, fmt, args...) \ - audit_log_type(ctx, AUDIT_KERNEL, 0, fmt, ##args) -extern void audit_log_type(struct audit_context *ctx, int type, - int pid, const char *fmt, ...) - __attribute__((format(printf,4,5))); +extern void audit_log(struct audit_context *ctx, int type, + const char *fmt, ...) + __attribute__((format(printf,3,4))); -extern struct audit_buffer *audit_log_start(struct audit_context *ctx, int type, - int pid); +extern struct audit_buffer *audit_log_start(struct audit_context *ctx,int type); extern void audit_log_format(struct audit_buffer *ab, const char *fmt, ...) __attribute__((format(printf,2,3))); @@ -243,9 +278,8 @@ extern void audit_send_reply(int pid, int seq, int type, void *payload, int size); extern void audit_log_lost(const char *message); #else -#define audit_log(c,f,...) do { ; } while (0) -#define audit_log_type(c,t,p,f,...) do { ; } while (0) -#define audit_log_start(c,t,p) ({ NULL; }) +#define audit_log(c,t,f,...) do { ; } while (0) +#define audit_log_start(c,t) ({ NULL; }) #define audit_log_vformat(b,f,a) do { ; } while (0) #define audit_log_format(b,f,...) do { ; } while (0) #define audit_log_end(b) do { ; } while (0) diff --git a/kernel/audit.c b/kernel/audit.c index 187164572bd..4e940c05ede 100644 --- a/kernel/audit.c +++ b/kernel/audit.c @@ -140,6 +140,12 @@ struct audit_buffer { struct audit_context *ctx; /* NULL or associated context */ }; +static void audit_set_pid(struct audit_buffer *ab, pid_t pid) +{ + struct nlmsghdr *nlh = (struct nlmsghdr *)ab->skb->data; + nlh->nlmsg_pid = pid; +} + struct audit_entry { struct list_head list; struct audit_rule rule; @@ -233,7 +239,8 @@ static int audit_set_rate_limit(int limit, uid_t loginuid) { int old = audit_rate_limit; audit_rate_limit = limit; - audit_log(NULL, "audit_rate_limit=%d old=%d by auid %u", + audit_log(NULL, AUDIT_CONFIG_CHANGE, + "audit_rate_limit=%d old=%d by auid %u", audit_rate_limit, old, loginuid); return old; } @@ -242,7 +249,8 @@ static int audit_set_backlog_limit(int limit, uid_t loginuid) { int old = audit_backlog_limit; audit_backlog_limit = limit; - audit_log(NULL, "audit_backlog_limit=%d old=%d by auid %u", + audit_log(NULL, AUDIT_CONFIG_CHANGE, + "audit_backlog_limit=%d old=%d by auid %u", audit_backlog_limit, old, loginuid); return old; } @@ -253,8 +261,9 @@ static int audit_set_enabled(int state, uid_t loginuid) if (state != 0 && state != 1) return -EINVAL; audit_enabled = state; - audit_log(NULL, "audit_enabled=%d old=%d by auid %u", - audit_enabled, old, loginuid); + audit_log(NULL, AUDIT_CONFIG_CHANGE, + "audit_enabled=%d old=%d by auid %u", + audit_enabled, old, loginuid); return old; } @@ -266,8 +275,9 @@ static int audit_set_failure(int state, uid_t loginuid) && state != AUDIT_FAIL_PANIC) return -EINVAL; audit_failure = state; - audit_log(NULL, "audit_failure=%d old=%d by auid %u", - audit_failure, old, loginuid); + audit_log(NULL, AUDIT_CONFIG_CHANGE, + "audit_failure=%d old=%d by auid %u", + audit_failure, old, loginuid); return old; } @@ -316,6 +326,14 @@ static int audit_netlink_ok(kernel_cap_t eff_cap, u16 msg_type) err = -EPERM; break; case AUDIT_USER: + case AUDIT_USER_AUTH: + case AUDIT_USER_ACCT: + case AUDIT_USER_MGMT: + case AUDIT_CRED_ACQ: + case AUDIT_CRED_DISP: + case AUDIT_USER_START: + case AUDIT_USER_END: + case AUDIT_USER_AVC: if (!cap_raised(eff_cap, CAP_AUDIT_WRITE)) err = -EPERM; break; @@ -332,6 +350,7 @@ static int audit_receive_msg(struct sk_buff *skb, struct nlmsghdr *nlh) void *data; struct audit_status *status_get, status_set; int err; + struct audit_buffer *ab; u16 msg_type = nlh->nlmsg_type; uid_t loginuid; /* loginuid of sender */ struct audit_sig_info sig_data; @@ -373,7 +392,8 @@ static int audit_receive_msg(struct sk_buff *skb, struct nlmsghdr *nlh) if (status_get->mask & AUDIT_STATUS_PID) { int old = audit_pid; audit_pid = status_get->pid; - audit_log(NULL, "audit_pid=%d old=%d by auid %u", + audit_log(NULL, AUDIT_CONFIG_CHANGE, + "audit_pid=%d old=%d by auid %u", audit_pid, old, loginuid); } if (status_get->mask & AUDIT_STATUS_RATE_LIMIT) @@ -383,13 +403,26 @@ static int audit_receive_msg(struct sk_buff *skb, struct nlmsghdr *nlh) loginuid); break; case AUDIT_USER: - audit_log_type(NULL, AUDIT_USER, pid, + case AUDIT_USER_AUTH: + case AUDIT_USER_ACCT: + case AUDIT_USER_MGMT: + case AUDIT_CRED_ACQ: + case AUDIT_CRED_DISP: + case AUDIT_USER_START: + case AUDIT_USER_END: + case AUDIT_USER_AVC: + ab = audit_log_start(NULL, msg_type); + if (!ab) + break; /* audit_panic has been called */ + audit_log_format(ab, "user pid=%d uid=%d length=%d loginuid=%u" " msg='%.1024s'", pid, uid, (int)(nlh->nlmsg_len - ((char *)data - (char *)nlh)), loginuid, (char *)data); + audit_set_pid(ab, pid); + audit_log_end(ab); break; case AUDIT_ADD: case AUDIT_DEL: @@ -504,7 +537,7 @@ static int __init audit_init(void) audit_initialized = 1; audit_enabled = audit_default; - audit_log(NULL, "initialized"); + audit_log(NULL, AUDIT_KERNEL, "initialized"); return 0; } __initcall(audit_init); @@ -541,10 +574,12 @@ static void audit_buffer_free(struct audit_buffer *ab) spin_unlock_irqrestore(&audit_freelist_lock, flags); } -static struct audit_buffer * audit_buffer_alloc(int gfp_mask) +static struct audit_buffer * audit_buffer_alloc(struct audit_context *ctx, + int gfp_mask, int type) { unsigned long flags; struct audit_buffer *ab = NULL; + struct nlmsghdr *nlh; spin_lock_irqsave(&audit_freelist_lock, flags); if (!list_empty(&audit_freelist)) { @@ -566,6 +601,12 @@ static struct audit_buffer * audit_buffer_alloc(int gfp_mask) if (!ab->skb) goto err; + ab->ctx = ctx; + nlh = (struct nlmsghdr *)skb_put(ab->skb, NLMSG_SPACE(0)); + nlh->nlmsg_type = type; + nlh->nlmsg_flags = 0; + nlh->nlmsg_pid = 0; + nlh->nlmsg_seq = 0; return ab; err: audit_buffer_free(ab); @@ -578,12 +619,11 @@ err: * syscall, then the syscall is marked as auditable and an audit record * will be written at syscall exit. If there is no associated task, tsk * should be NULL. */ -struct audit_buffer *audit_log_start(struct audit_context *ctx, int type, int pid) +struct audit_buffer *audit_log_start(struct audit_context *ctx, int type) { struct audit_buffer *ab = NULL; struct timespec t; unsigned int serial; - struct nlmsghdr *nlh; if (!audit_initialized) return NULL; @@ -600,19 +640,12 @@ struct audit_buffer *audit_log_start(struct audit_context *ctx, int type, int pi return NULL; } - ab = audit_buffer_alloc(GFP_ATOMIC); + ab = audit_buffer_alloc(ctx, GFP_ATOMIC, type); if (!ab) { audit_log_lost("out of memory in audit_log_start"); return NULL; } - ab->ctx = ctx; - nlh = (struct nlmsghdr *)skb_put(ab->skb, NLMSG_SPACE(0)); - nlh->nlmsg_type = type; - nlh->nlmsg_flags = 0; - nlh->nlmsg_pid = pid; - nlh->nlmsg_seq = 0; - if (!audit_get_stamp(ab->ctx, &t, &serial)) { t = CURRENT_TIME; serial = 0; @@ -809,13 +842,12 @@ void audit_log_end(struct audit_buffer *ab) /* Log an audit record. This is a convenience function that calls * audit_log_start, audit_log_vformat, and audit_log_end. It may be * called in any context. */ -void audit_log_type(struct audit_context *ctx, int type, int pid, - const char *fmt, ...) +void audit_log(struct audit_context *ctx, int type, const char *fmt, ...) { struct audit_buffer *ab; va_list args; - ab = audit_log_start(ctx, type, pid); + ab = audit_log_start(ctx, type); if (ab) { va_start(args, fmt); audit_log_vformat(ab, fmt, args); diff --git a/kernel/auditsc.c b/kernel/auditsc.c index d089263253a..1b7c91f9d5f 100644 --- a/kernel/auditsc.c +++ b/kernel/auditsc.c @@ -286,7 +286,8 @@ int audit_receive_filter(int type, int pid, int uid, int seq, void *data, err = audit_add_rule(entry, &audit_entlist); if (!err && (flags & AUDIT_AT_EXIT)) err = audit_add_rule(entry, &audit_extlist); - audit_log(NULL, "auid %u added an audit rule\n", loginuid); + audit_log(NULL, AUDIT_CONFIG_CHANGE, + "auid %u added an audit rule\n", loginuid); break; case AUDIT_DEL: flags =((struct audit_rule *)data)->flags; @@ -296,7 +297,8 @@ int audit_receive_filter(int type, int pid, int uid, int seq, void *data, err = audit_del_rule(data, &audit_entlist); if (!err && (flags & AUDIT_AT_EXIT)) err = audit_del_rule(data, &audit_extlist); - audit_log(NULL, "auid %u removed an audit rule\n", loginuid); + audit_log(NULL, AUDIT_CONFIG_CHANGE, + "auid %u removed an audit rule\n", loginuid); break; default: return -EINVAL; @@ -648,7 +650,7 @@ static void audit_log_exit(struct audit_context *context) int i; struct audit_buffer *ab; - ab = audit_log_start(context, AUDIT_KERNEL, 0); + ab = audit_log_start(context, AUDIT_SYSCALL); if (!ab) return; /* audit_panic has been called */ audit_log_format(ab, "syscall=%d", context->major); @@ -680,28 +682,28 @@ static void audit_log_exit(struct audit_context *context) while (context->aux) { struct audit_aux_data *aux; - ab = audit_log_start(context, AUDIT_KERNEL, 0); + aux = context->aux; + + ab = audit_log_start(context, aux->type); if (!ab) continue; /* audit_panic has been called */ - aux = context->aux; - context->aux = aux->next; - - audit_log_format(ab, "auxitem=%d", aux->type); switch (aux->type) { - case AUDIT_AUX_IPCPERM: { + case AUDIT_IPC: { struct audit_aux_data_ipcctl *axi = (void *)aux; audit_log_format(ab, - " qbytes=%lx uid=%d gid=%d mode=%x", + " qbytes=%lx iuid=%d igid=%d mode=%x", axi->qbytes, axi->uid, axi->gid, axi->mode); } } audit_log_end(ab); + + context->aux = aux->next; kfree(aux); } for (i = 0; i < context->name_count; i++) { - ab = audit_log_start(context, AUDIT_KERNEL, 0); + ab = audit_log_start(context, AUDIT_PATH); if (!ab) continue; /* audit_panic has been called */ audit_log_format(ab, "item=%d", i); @@ -711,7 +713,7 @@ static void audit_log_exit(struct audit_context *context) } if (context->names[i].ino != (unsigned long)-1) audit_log_format(ab, " inode=%lu dev=%02x:%02x mode=%#o" - " uid=%d gid=%d rdev=%02x:%02x", + " ouid=%d ogid=%d rdev=%02x:%02x", context->names[i].ino, MAJOR(context->names[i].dev), MINOR(context->names[i].dev), @@ -1008,10 +1010,16 @@ int audit_get_stamp(struct audit_context *ctx, int audit_set_loginuid(struct task_struct *task, uid_t loginuid) { if (task->audit_context) { - audit_log_type(NULL, AUDIT_LOGIN, 0, - "login pid=%d uid=%u old loginuid=%u new loginuid=%u", - task->pid, task->uid, task->audit_context->loginuid, - loginuid); + struct audit_buffer *ab; + + ab = audit_log_start(NULL, AUDIT_LOGIN); + if (ab) { + audit_log_format(ab, "login pid=%d uid=%u " + "old loginuid=%u new loginuid=%u", + task->pid, task->uid, + task->audit_context->loginuid, loginuid); + audit_log_end(ab); + } task->audit_context->loginuid = loginuid; } return 0; @@ -1039,7 +1047,7 @@ int audit_ipc_perms(unsigned long qbytes, uid_t uid, gid_t gid, mode_t mode) ax->gid = gid; ax->mode = mode; - ax->d.type = AUDIT_AUX_IPCPERM; + ax->d.type = AUDIT_IPC; ax->d.next = context->aux; context->aux = (void *)ax; return 0; diff --git a/security/selinux/avc.c b/security/selinux/avc.c index 9e71a1bbe01..042f91e9f9d 100644 --- a/security/selinux/avc.c +++ b/security/selinux/avc.c @@ -242,7 +242,7 @@ void __init avc_init(void) avc_node_cachep = kmem_cache_create("avc_node", sizeof(struct avc_node), 0, SLAB_PANIC, NULL, NULL); - audit_log(current->audit_context, "AVC INITIALIZED\n"); + audit_log(current->audit_context, AUDIT_KERNEL, "AVC INITIALIZED\n"); } int avc_get_hash_stats(char *page) @@ -549,7 +549,7 @@ void avc_audit(u32 ssid, u32 tsid, return; } - ab = audit_log_start(current->audit_context, AUDIT_KERNEL, 0); + ab = audit_log_start(current->audit_context, AUDIT_AVC); if (!ab) return; /* audit_panic has been called */ audit_log_format(ab, "avc: %s ", denied ? "denied" : "granted"); diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c index aae1e794fe4..db845cbd584 100644 --- a/security/selinux/hooks.c +++ b/security/selinux/hooks.c @@ -3419,7 +3419,7 @@ static int selinux_nlmsg_perm(struct sock *sk, struct sk_buff *skb) err = selinux_nlmsg_lookup(isec->sclass, nlh->nlmsg_type, &perm); if (err) { if (err == -EINVAL) { - audit_log(current->audit_context, + audit_log(current->audit_context, AUDIT_SELINUX_ERR, "SELinux: unrecognized netlink message" " type=%hu for sclass=%hu\n", nlh->nlmsg_type, isec->sclass); diff --git a/security/selinux/nlmsgtab.c b/security/selinux/nlmsgtab.c index deac14367d4..67e77acc479 100644 --- a/security/selinux/nlmsgtab.c +++ b/security/selinux/nlmsgtab.c @@ -98,6 +98,14 @@ static struct nlmsg_perm nlmsg_audit_perms[] = { AUDIT_DEL, NETLINK_AUDIT_SOCKET__NLMSG_WRITE }, { AUDIT_USER, NETLINK_AUDIT_SOCKET__NLMSG_RELAY }, { AUDIT_SIGNAL_INFO, NETLINK_AUDIT_SOCKET__NLMSG_READ }, + { AUDIT_USER_AUTH, NETLINK_AUDIT_SOCKET__NLMSG_RELAY }, + { AUDIT_USER_ACCT, NETLINK_AUDIT_SOCKET__NLMSG_RELAY }, + { AUDIT_USER_MGMT, NETLINK_AUDIT_SOCKET__NLMSG_RELAY }, + { AUDIT_CRED_ACQ, NETLINK_AUDIT_SOCKET__NLMSG_RELAY }, + { AUDIT_CRED_DISP, NETLINK_AUDIT_SOCKET__NLMSG_RELAY }, + { AUDIT_USER_START, NETLINK_AUDIT_SOCKET__NLMSG_RELAY }, + { AUDIT_USER_END, NETLINK_AUDIT_SOCKET__NLMSG_RELAY }, + { AUDIT_USER_AVC, NETLINK_AUDIT_SOCKET__NLMSG_RELAY }, }; diff --git a/security/selinux/ss/services.c b/security/selinux/ss/services.c index 5a820cf88c9..07fdf6ee614 100644 --- a/security/selinux/ss/services.c +++ b/security/selinux/ss/services.c @@ -365,7 +365,7 @@ static int security_validtrans_handle_fail(struct context *ocontext, goto out; if (context_struct_to_string(tcontext, &t, &tlen) < 0) goto out; - audit_log(current->audit_context, + audit_log(current->audit_context, AUDIT_SELINUX_ERR, "security_validate_transition: denied for" " oldcontext=%s newcontext=%s taskcontext=%s tclass=%s", o, n, t, policydb.p_class_val_to_name[tclass-1]); @@ -742,7 +742,7 @@ static int compute_sid_handle_invalid_context( goto out; if (context_struct_to_string(newcontext, &n, &nlen) < 0) goto out; - audit_log(current->audit_context, + audit_log(current->audit_context, AUDIT_SELINUX_ERR, "security_compute_sid: invalid context %s" " for scontext=%s" " tcontext=%s" -- cgit v1.2.3 From 23f32d18aa589e228c5a9e12e0d0c67c9b5bcdce Mon Sep 17 00:00:00 2001 From: Steve Grubb Date: Fri, 13 May 2005 18:35:15 +0100 Subject: AUDIT: Fix some spelling errors I'm going through the kernel code and have a patch that corrects several spelling errors in comments. From: Steve Grubb Signed-off-by: David Woodhouse --- include/linux/audit.h | 2 +- kernel/audit.c | 4 ++-- kernel/auditsc.c | 4 ++-- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/include/linux/audit.h b/include/linux/audit.h index 1a15ba38c66..51e5879af7f 100644 --- a/include/linux/audit.h +++ b/include/linux/audit.h @@ -181,7 +181,7 @@ struct audit_message { struct audit_status { __u32 mask; /* Bit mask for valid entries */ - __u32 enabled; /* 1 = enabled, 0 = disbaled */ + __u32 enabled; /* 1 = enabled, 0 = disabled */ __u32 failure; /* Failure-to-log action */ __u32 pid; /* pid of auditd process */ __u32 rate_limit; /* messages rate limit (per second) */ diff --git a/kernel/audit.c b/kernel/audit.c index 4e940c05ede..74779d3769f 100644 --- a/kernel/audit.c +++ b/kernel/audit.c @@ -116,7 +116,7 @@ static LIST_HEAD(audit_entlist); static LIST_HEAD(audit_extlist); /* The netlink socket is only to be read by 1 CPU, which lets us assume - * that list additions and deletions never happen simultaneiously in + * that list additions and deletions never happen simultaneously in * auditsc.c */ static DECLARE_MUTEX(audit_netlink_sem); @@ -775,7 +775,7 @@ void audit_log_d_path(struct audit_buffer *ab, const char *prefix, } } -/* Remove queued messages from the audit_txlist and send them to userspace. */ +/* Remove queued messages from the audit_txlist and send them to user space. */ static void audit_tasklet_handler(unsigned long arg) { LIST_HEAD(list); diff --git a/kernel/auditsc.c b/kernel/auditsc.c index 1b7c91f9d5f..773d28a3f70 100644 --- a/kernel/auditsc.c +++ b/kernel/auditsc.c @@ -444,7 +444,7 @@ static enum audit_state audit_filter_task(struct task_struct *tsk) /* At syscall entry and exit time, this filter is called if the * audit_state is not low enough that auditing cannot take place, but is - * also not high enough that we already know we have to write and audit + * also not high enough that we already know we have to write an audit * record (i.e., the state is AUDIT_SETUP_CONTEXT or AUDIT_BUILD_CONTEXT). */ static enum audit_state audit_filter_syscall(struct task_struct *tsk, @@ -750,7 +750,7 @@ void audit_free(struct task_struct *tsk) /* Compute a serial number for the audit record. Audit records are * written to user-space as soon as they are generated, so a complete * audit record may be written in several pieces. The timestamp of the - * record and this serial number are used by the user-space daemon to + * record and this serial number are used by the user-space tools to * determine which pieces belong to the same audit record. The * (timestamp,serial) tuple is unique for each syscall and is live from * syscall entry to syscall exit. -- cgit v1.2.3 From 5e014b10ef8477c32a939a48fa02aedcad35a226 Mon Sep 17 00:00:00 2001 From: David Woodhouse Date: Fri, 13 May 2005 18:50:33 +0100 Subject: AUDIT: fix max_t thinko. Der... if you use max_t it helps if you give it a type. Note to self: Always just apply the tested patches, don't try to port them by hand. You're not clever enough. Signed-off-by: David Woodhouse --- kernel/audit.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/kernel/audit.c b/kernel/audit.c index 74779d3769f..a0e33b6897d 100644 --- a/kernel/audit.c +++ b/kernel/audit.c @@ -703,7 +703,7 @@ static void audit_log_vformat(struct audit_buffer *ab, const char *fmt, /* The printk buffer is 1024 bytes long, so if we get * here and AUDIT_BUFSIZ is at least 1024, then we can * log everything that printk could have logged. */ - avail = audit_expand(ab, max_t(AUDIT_BUFSIZ, 1+len-avail)); + avail = audit_expand(ab, max_t(unsigned, AUDIT_BUFSIZ, 1+len-avail)); if (!avail) goto out; len = vsnprintf(skb->tail, avail, fmt, args2); -- cgit v1.2.3 From 3ec3b2fba526ead2fa3f3d7c91924f39a0733749 Mon Sep 17 00:00:00 2001 From: David Woodhouse Date: Tue, 17 May 2005 12:08:48 +0100 Subject: AUDIT: Capture sys_socketcall arguments and sockaddrs Signed-off-by: David Woodhouse --- include/linux/audit.h | 7 ++++- kernel/auditsc.c | 73 +++++++++++++++++++++++++++++++++++++++++++++++++-- net/socket.c | 9 +++++-- 3 files changed, 84 insertions(+), 5 deletions(-) diff --git a/include/linux/audit.h b/include/linux/audit.h index 51e5879af7f..2f5dc60f8bb 100644 --- a/include/linux/audit.h +++ b/include/linux/audit.h @@ -69,8 +69,9 @@ #define AUDIT_FS_WATCH 1301 /* Filesystem watch event */ #define AUDIT_PATH 1302 /* Filname path information */ #define AUDIT_IPC 1303 /* IPC record */ -#define AUDIT_SOCKET 1304 /* Socket record */ +#define AUDIT_SOCKETCALL 1304 /* sys_socketcall arguments */ #define AUDIT_CONFIG_CHANGE 1305 /* Audit system configuration change */ +#define AUDIT_SOCKADDR 1306 /* sockaddr copied as syscall arg */ #define AUDIT_AVC 1400 /* SE Linux avc denial or grant */ #define AUDIT_SELINUX_ERR 1401 /* Internal SE Linux Errors */ @@ -235,6 +236,8 @@ extern int audit_get_stamp(struct audit_context *ctx, extern int audit_set_loginuid(struct task_struct *task, uid_t loginuid); extern uid_t audit_get_loginuid(struct audit_context *ctx); extern int audit_ipc_perms(unsigned long qbytes, uid_t uid, gid_t gid, mode_t mode); +extern int audit_socketcall(int nargs, unsigned long *args); +extern int audit_sockaddr(int len, void *addr); extern void audit_signal_info(int sig, struct task_struct *t); #else #define audit_alloc(t) ({ 0; }) @@ -248,6 +251,8 @@ extern void audit_signal_info(int sig, struct task_struct *t); #define audit_get_stamp(c,t,s) ({ 0; }) #define audit_get_loginuid(c) ({ -1; }) #define audit_ipc_perms(q,u,g,m) ({ 0; }) +#define audit_socketcall(n,a) ({ 0; }) +#define audit_sockaddr(len, addr) ({ 0; }) #define audit_signal_info(s,t) do { ; } while (0) #endif diff --git a/kernel/auditsc.c b/kernel/auditsc.c index 773d28a3f70..818778d5b6a 100644 --- a/kernel/auditsc.c +++ b/kernel/auditsc.c @@ -34,7 +34,7 @@ #include #include #include - +#include #include #include #include @@ -112,6 +112,18 @@ struct audit_aux_data_ipcctl { mode_t mode; }; +struct audit_aux_data_socketcall { + struct audit_aux_data d; + int nargs; + unsigned long args[0]; +}; + +struct audit_aux_data_sockaddr { + struct audit_aux_data d; + int len; + char a[0]; +}; + /* The per-task audit context. */ struct audit_context { @@ -694,7 +706,22 @@ static void audit_log_exit(struct audit_context *context) audit_log_format(ab, " qbytes=%lx iuid=%d igid=%d mode=%x", axi->qbytes, axi->uid, axi->gid, axi->mode); - } + break; } + + case AUDIT_SOCKETCALL: { + int i; + struct audit_aux_data_socketcall *axs = (void *)aux; + audit_log_format(ab, "nargs=%d", axs->nargs); + for (i=0; inargs; i++) + audit_log_format(ab, " a%d=%lx", i, axs->args[i]); + break; } + + case AUDIT_SOCKADDR: { + struct audit_aux_data_sockaddr *axs = (void *)aux; + + audit_log_format(ab, "saddr="); + audit_log_hex(ab, axs->a, axs->len); + break; } } audit_log_end(ab); @@ -1053,6 +1080,48 @@ int audit_ipc_perms(unsigned long qbytes, uid_t uid, gid_t gid, mode_t mode) return 0; } +int audit_socketcall(int nargs, unsigned long *args) +{ + struct audit_aux_data_socketcall *ax; + struct audit_context *context = current->audit_context; + + if (likely(!context)) + return 0; + + ax = kmalloc(sizeof(*ax) + nargs * sizeof(unsigned long), GFP_KERNEL); + if (!ax) + return -ENOMEM; + + ax->nargs = nargs; + memcpy(ax->args, args, nargs * sizeof(unsigned long)); + + ax->d.type = AUDIT_SOCKETCALL; + ax->d.next = context->aux; + context->aux = (void *)ax; + return 0; +} + +int audit_sockaddr(int len, void *a) +{ + struct audit_aux_data_sockaddr *ax; + struct audit_context *context = current->audit_context; + + if (likely(!context)) + return 0; + + ax = kmalloc(sizeof(*ax) + len, GFP_KERNEL); + if (!ax) + return -ENOMEM; + + ax->len = len; + memcpy(ax->a, a, len); + + ax->d.type = AUDIT_SOCKADDR; + ax->d.next = context->aux; + context->aux = (void *)ax; + return 0; +} + void audit_signal_info(int sig, struct task_struct *t) { extern pid_t audit_sig_pid; diff --git a/net/socket.c b/net/socket.c index cec0cb38b9c..6b7c3b51a7c 100644 --- a/net/socket.c +++ b/net/socket.c @@ -81,6 +81,7 @@ #include #include #include +#include #ifdef CONFIG_NET_RADIO #include /* Note : will define WIRELESS_EXT */ @@ -226,7 +227,7 @@ int move_addr_to_kernel(void __user *uaddr, int ulen, void *kaddr) return 0; if(copy_from_user(kaddr,uaddr,ulen)) return -EFAULT; - return 0; + return audit_sockaddr(ulen, kaddr); } /** @@ -1906,7 +1907,11 @@ asmlinkage long sys_socketcall(int call, unsigned long __user *args) /* copy_from_user should be SMP safe. */ if (copy_from_user(a, args, nargs[call])) return -EFAULT; - + + err = audit_socketcall(nargs[call]/sizeof(unsigned long), args); + if (err) + return err; + a0=a[0]; a1=a[1]; -- cgit v1.2.3 From 209aba03243ee42a22f8df8d08aa9963f62aec64 Mon Sep 17 00:00:00 2001 From: David Woodhouse Date: Wed, 18 May 2005 10:21:07 +0100 Subject: AUDIT: Treat all user messages identically. It's silly to have to add explicit entries for new userspace messages as we invent them. Just treat all messages in the user range the same. Signed-off-by: David Woodhouse --- include/linux/audit.h | 17 ++--------------- kernel/audit.c | 20 ++------------------ security/selinux/nlmsgtab.c | 17 +++++++---------- 3 files changed, 11 insertions(+), 43 deletions(-) diff --git a/include/linux/audit.h b/include/linux/audit.h index 2f5dc60f8bb..17ea5d522d8 100644 --- a/include/linux/audit.h +++ b/include/linux/audit.h @@ -51,14 +51,8 @@ #define AUDIT_WATCH_LIST 1009 /* List all file/dir watches */ #define AUDIT_SIGNAL_INFO 1010 /* Get info about sender of signal to auditd */ -#define AUDIT_USER_AUTH 1100 /* User space authentication */ -#define AUDIT_USER_ACCT 1101 /* User space acct change */ -#define AUDIT_USER_MGMT 1102 /* User space acct management */ -#define AUDIT_CRED_ACQ 1103 /* User space credential acquired */ -#define AUDIT_CRED_DISP 1104 /* User space credential disposed */ -#define AUDIT_USER_START 1105 /* User space session start */ -#define AUDIT_USER_END 1106 /* User space session end */ -#define AUDIT_USER_AVC 1107 /* User space avc message */ +#define AUDIT_FIRST_USER_MSG 1100 /* Userspace messages uninteresting to kernel */ +#define AUDIT_LAST_USER_MSG 1199 #define AUDIT_DAEMON_START 1200 /* Daemon startup record */ #define AUDIT_DAEMON_END 1201 /* Daemon normal stop record */ @@ -173,13 +167,6 @@ #define AUDIT_ARCH_V850 (EM_V850|__AUDIT_ARCH_LE) #define AUDIT_ARCH_X86_64 (EM_X86_64|__AUDIT_ARCH_64BIT|__AUDIT_ARCH_LE) -#ifndef __KERNEL__ -struct audit_message { - struct nlmsghdr nlh; - char data[1200]; -}; -#endif - struct audit_status { __u32 mask; /* Bit mask for valid entries */ __u32 enabled; /* 1 = enabled, 0 = disabled */ diff --git a/kernel/audit.c b/kernel/audit.c index a0e33b6897d..e6d88635032 100644 --- a/kernel/audit.c +++ b/kernel/audit.c @@ -325,15 +325,7 @@ static int audit_netlink_ok(kernel_cap_t eff_cap, u16 msg_type) if (!cap_raised(eff_cap, CAP_AUDIT_CONTROL)) err = -EPERM; break; - case AUDIT_USER: - case AUDIT_USER_AUTH: - case AUDIT_USER_ACCT: - case AUDIT_USER_MGMT: - case AUDIT_CRED_ACQ: - case AUDIT_CRED_DISP: - case AUDIT_USER_START: - case AUDIT_USER_END: - case AUDIT_USER_AVC: + case AUDIT_FIRST_USER_MSG...AUDIT_LAST_USER_MSG: if (!cap_raised(eff_cap, CAP_AUDIT_WRITE)) err = -EPERM; break; @@ -402,15 +394,7 @@ static int audit_receive_msg(struct sk_buff *skb, struct nlmsghdr *nlh) audit_set_backlog_limit(status_get->backlog_limit, loginuid); break; - case AUDIT_USER: - case AUDIT_USER_AUTH: - case AUDIT_USER_ACCT: - case AUDIT_USER_MGMT: - case AUDIT_CRED_ACQ: - case AUDIT_CRED_DISP: - case AUDIT_USER_START: - case AUDIT_USER_END: - case AUDIT_USER_AVC: + case AUDIT_FIRST_USER_MSG...AUDIT_LAST_USER_MSG: ab = audit_log_start(NULL, msg_type); if (!ab) break; /* audit_panic has been called */ diff --git a/security/selinux/nlmsgtab.c b/security/selinux/nlmsgtab.c index 67e77acc479..f0fb6d76f7c 100644 --- a/security/selinux/nlmsgtab.c +++ b/security/selinux/nlmsgtab.c @@ -98,14 +98,6 @@ static struct nlmsg_perm nlmsg_audit_perms[] = { AUDIT_DEL, NETLINK_AUDIT_SOCKET__NLMSG_WRITE }, { AUDIT_USER, NETLINK_AUDIT_SOCKET__NLMSG_RELAY }, { AUDIT_SIGNAL_INFO, NETLINK_AUDIT_SOCKET__NLMSG_READ }, - { AUDIT_USER_AUTH, NETLINK_AUDIT_SOCKET__NLMSG_RELAY }, - { AUDIT_USER_ACCT, NETLINK_AUDIT_SOCKET__NLMSG_RELAY }, - { AUDIT_USER_MGMT, NETLINK_AUDIT_SOCKET__NLMSG_RELAY }, - { AUDIT_CRED_ACQ, NETLINK_AUDIT_SOCKET__NLMSG_RELAY }, - { AUDIT_CRED_DISP, NETLINK_AUDIT_SOCKET__NLMSG_RELAY }, - { AUDIT_USER_START, NETLINK_AUDIT_SOCKET__NLMSG_RELAY }, - { AUDIT_USER_END, NETLINK_AUDIT_SOCKET__NLMSG_RELAY }, - { AUDIT_USER_AVC, NETLINK_AUDIT_SOCKET__NLMSG_RELAY }, }; @@ -150,8 +142,13 @@ int selinux_nlmsg_lookup(u16 sclass, u16 nlmsg_type, u32 *perm) break; case SECCLASS_NETLINK_AUDIT_SOCKET: - err = nlmsg_perm(nlmsg_type, perm, nlmsg_audit_perms, - sizeof(nlmsg_audit_perms)); + if (nlmsg_type >= AUDIT_FIRST_USER_MSG && + nlmsg_type <= AUDIT_LAST_USER_MSG) { + *perm = NETLINK_AUDIT_SOCKET__NLMSG_RELAY; + } else { + err = nlmsg_perm(nlmsg_type, perm, nlmsg_audit_perms, + sizeof(nlmsg_audit_perms)); + } break; /* No messaging from userspace, or class unknown/unhandled */ -- cgit v1.2.3 From 168b7173959f80d20720dd1f7ec909a88ef2689d Mon Sep 17 00:00:00 2001 From: Steve Grubb Date: Thu, 19 May 2005 10:24:22 +0100 Subject: AUDIT: Clean up logging of untrusted strings * If vsnprintf returns -1, it will mess up the sk buffer space accounting. This is fixed by not calling skb_put with bogus len values. * audit_log_hex was a loop that called audit_log_vformat with %02X for each character. This is very inefficient since conversion from unsigned character to Ascii representation is essentially masking, shifting, and byte lookups. Also, the length of the converted string is well known - it's twice the original. Fixed by rewriting the function. * audit_log_untrustedstring had no comments. This makes it hard for someone to understand what the string format will be. * audit_log_d_path was never fixed to use untrustedstring. This could mess up user space parsers. This was fixed to make a temp buffer, call d_path, and log temp buffer using untrustedstring. From: Steve Grubb Signed-off-by: David Woodhouse --- kernel/audit.c | 71 +++++++++++++++++++++++++++++++++++++++------------------- 1 file changed, 48 insertions(+), 23 deletions(-) diff --git a/kernel/audit.c b/kernel/audit.c index e6d88635032..dae3570b3a3 100644 --- a/kernel/audit.c +++ b/kernel/audit.c @@ -692,7 +692,8 @@ static void audit_log_vformat(struct audit_buffer *ab, const char *fmt, goto out; len = vsnprintf(skb->tail, avail, fmt, args2); } - skb_put(skb, (len < avail) ? len : avail); + if (len > 0) + skb_put(skb, len); out: return; } @@ -710,20 +711,47 @@ void audit_log_format(struct audit_buffer *ab, const char *fmt, ...) va_end(args); } -void audit_log_hex(struct audit_buffer *ab, const unsigned char *buf, size_t len) +/* This function will take the passed buf and convert it into a string of + * ascii hex digits. The new string is placed onto the skb. */ +void audit_log_hex(struct audit_buffer *ab, const unsigned char *buf, + size_t len) { - int i; + int i, avail, new_len; + unsigned char *ptr; + struct sk_buff *skb; + static const unsigned char *hex = "0123456789ABCDEF"; + + BUG_ON(!ab->skb); + skb = ab->skb; + avail = skb_tailroom(skb); + new_len = len<<1; + if (new_len >= avail) { + /* Round the buffer request up to the next multiple */ + new_len = AUDIT_BUFSIZ*(((new_len-avail)/AUDIT_BUFSIZ) + 1); + avail = audit_expand(ab, new_len); + if (!avail) + return; + } - for (i=0; itail; + for (i=0; i>4]; /* Upper nibble */ + *ptr++ = hex[buf[i] & 0x0F]; /* Lower nibble */ + } + *ptr = 0; + skb_put(skb, len << 1); /* new string is twice the old string */ } +/* This code will escape a string that is passed to it if the string + * contains a control character, unprintable character, double quote mark, + * or a space. Unescaped strings will start and end with a double quote mark. + * Strings that are escaped are printed in hex (2 digits per char). */ void audit_log_untrustedstring(struct audit_buffer *ab, const char *string) { const unsigned char *p = string; while (*p) { - if (*p == '"' || *p == ' ' || *p < 0x20 || *p > 0x7f) { + if (*p == '"' || *p < 0x21 || *p > 0x7f) { audit_log_hex(ab, string, strlen(string)); return; } @@ -732,31 +760,28 @@ void audit_log_untrustedstring(struct audit_buffer *ab, const char *string) audit_log_format(ab, "\"%s\"", string); } - -/* This is a helper-function to print the d_path without using a static - * buffer or allocating another buffer in addition to the one in - * audit_buffer. */ +/* This is a helper-function to print the escaped d_path */ void audit_log_d_path(struct audit_buffer *ab, const char *prefix, struct dentry *dentry, struct vfsmount *vfsmnt) { - char *p; - struct sk_buff *skb = ab->skb; - int len, avail; + char *p, *path; if (prefix) audit_log_format(ab, " %s", prefix); - avail = skb_tailroom(skb); - p = d_path(dentry, vfsmnt, skb->tail, avail); - if (IS_ERR(p)) { - /* FIXME: can we save some information here? */ - audit_log_format(ab, ""); - } else { - /* path isn't at start of buffer */ - len = ((char *)skb->tail + avail - 1) - p; - memmove(skb->tail, p, len); - skb_put(skb, len); + /* We will allow 11 spaces for ' (deleted)' to be appended */ + path = kmalloc(PATH_MAX+11, GFP_KERNEL); + if (!path) { + audit_log_format(ab, ""); + return; } + p = d_path(dentry, vfsmnt, path, PATH_MAX+11); + if (IS_ERR(p)) { /* Should never happen since we send PATH_MAX */ + /* FIXME: can we save some information here? */ + audit_log_format(ab, ""); + } else + audit_log_untrustedstring(ab, p); + kfree(path); } /* Remove queued messages from the audit_txlist and send them to user space. */ -- cgit v1.2.3 From b7d1125817c9a46cc46f57db89d9c195e7af22f8 Mon Sep 17 00:00:00 2001 From: David Woodhouse Date: Thu, 19 May 2005 10:56:58 +0100 Subject: AUDIT: Send netlink messages from a separate kernel thread netlink_unicast() will attempt to reallocate and will free messages if the socket's rcvbuf limit is reached unless we give it an infinite timeout. So do that, from a kernel thread which is dedicated to spewing stuff up the netlink socket. Signed-off-by: David Woodhouse --- kernel/audit.c | 191 +++++++++++++++++++++------------------------------------ 1 file changed, 70 insertions(+), 121 deletions(-) diff --git a/kernel/audit.c b/kernel/audit.c index dae3570b3a3..bbc6f542c8f 100644 --- a/kernel/audit.c +++ b/kernel/audit.c @@ -46,6 +46,8 @@ #include #include #include +#include +#include #include @@ -77,7 +79,6 @@ static int audit_rate_limit; /* Number of outstanding audit_buffers allowed. */ static int audit_backlog_limit = 64; -static atomic_t audit_backlog = ATOMIC_INIT(0); /* The identity of the user shutting down the audit system. */ uid_t audit_sig_uid = -1; @@ -95,19 +96,17 @@ static atomic_t audit_lost = ATOMIC_INIT(0); /* The netlink socket. */ static struct sock *audit_sock; -/* There are two lists of audit buffers. The txlist contains audit - * buffers that cannot be sent immediately to the netlink device because - * we are in an irq context (these are sent later in a tasklet). - * - * The second list is a list of pre-allocated audit buffers (if more +/* The audit_freelist is a list of pre-allocated audit buffers (if more * than AUDIT_MAXFREE are in use, the audit buffer is freed instead of * being placed on the freelist). */ -static DEFINE_SPINLOCK(audit_txlist_lock); static DEFINE_SPINLOCK(audit_freelist_lock); static int audit_freelist_count = 0; -static LIST_HEAD(audit_txlist); static LIST_HEAD(audit_freelist); +static struct sk_buff_head audit_skb_queue; +static struct task_struct *kauditd_task; +static DECLARE_WAIT_QUEUE_HEAD(kauditd_wait); + /* There are three lists of rules -- one to search at task creation * time, one to search at syscall entry time, and another to search at * syscall exit time. */ @@ -151,9 +150,6 @@ struct audit_entry { struct audit_rule rule; }; -static void audit_log_end_irq(struct audit_buffer *ab); -static void audit_log_end_fast(struct audit_buffer *ab); - static void audit_panic(const char *message) { switch (audit_failure) @@ -224,10 +220,8 @@ void audit_log_lost(const char *message) if (print) { printk(KERN_WARNING - "audit: audit_lost=%d audit_backlog=%d" - " audit_rate_limit=%d audit_backlog_limit=%d\n", + "audit: audit_lost=%d audit_rate_limit=%d audit_backlog_limit=%d\n", atomic_read(&audit_lost), - atomic_read(&audit_backlog), audit_rate_limit, audit_backlog_limit); audit_panic(message); @@ -281,6 +275,38 @@ static int audit_set_failure(int state, uid_t loginuid) return old; } +int kauditd_thread(void *dummy) +{ + struct sk_buff *skb; + + while (1) { + skb = skb_dequeue(&audit_skb_queue); + if (skb) { + if (audit_pid) { + int err = netlink_unicast(audit_sock, skb, audit_pid, 0); + if (err < 0) { + BUG_ON(err != -ECONNREFUSED); /* Shoudn't happen */ + printk(KERN_ERR "audit: *NO* daemon at audit_pid=%d\n", audit_pid); + audit_pid = 0; + } + } else { + printk(KERN_ERR "%s\n", skb->data + NLMSG_SPACE(0)); + kfree_skb(skb); + } + } else { + DECLARE_WAITQUEUE(wait, current); + set_current_state(TASK_INTERRUPTIBLE); + add_wait_queue(&kauditd_wait, &wait); + + if (!skb_queue_len(&audit_skb_queue)) + schedule(); + + __set_current_state(TASK_RUNNING); + remove_wait_queue(&kauditd_wait, &wait); + } + } +} + void audit_send_reply(int pid, int seq, int type, int done, int multi, void *payload, int size) { @@ -293,13 +319,16 @@ void audit_send_reply(int pid, int seq, int type, int done, int multi, skb = alloc_skb(len, GFP_KERNEL); if (!skb) - goto nlmsg_failure; + return; - nlh = NLMSG_PUT(skb, pid, seq, t, len - sizeof(*nlh)); + nlh = NLMSG_PUT(skb, pid, seq, t, size); nlh->nlmsg_flags = flags; data = NLMSG_DATA(nlh); memcpy(data, payload, size); - netlink_unicast(audit_sock, skb, pid, MSG_DONTWAIT); + + /* Ignore failure. It'll only happen if the sender goes away, + because our timeout is set to infinite. */ + netlink_unicast(audit_sock, skb, pid, 0); return; nlmsg_failure: /* Used by NLMSG_PUT */ @@ -351,6 +380,15 @@ static int audit_receive_msg(struct sk_buff *skb, struct nlmsghdr *nlh) if (err) return err; + /* As soon as there's any sign of userspace auditd, start kauditd to talk to it */ + if (!kauditd_task) + kauditd_task = kthread_run(kauditd_thread, NULL, "kauditd"); + if (IS_ERR(kauditd_task)) { + err = PTR_ERR(kauditd_task); + kauditd_task = NULL; + return err; + } + pid = NETLINK_CREDS(skb)->pid; uid = NETLINK_CREDS(skb)->uid; loginuid = NETLINK_CB(skb).loginuid; @@ -365,7 +403,7 @@ static int audit_receive_msg(struct sk_buff *skb, struct nlmsghdr *nlh) status_set.rate_limit = audit_rate_limit; status_set.backlog_limit = audit_backlog_limit; status_set.lost = atomic_read(&audit_lost); - status_set.backlog = atomic_read(&audit_backlog); + status_set.backlog = skb_queue_len(&audit_skb_queue); audit_send_reply(NETLINK_CB(skb).pid, seq, AUDIT_GET, 0, 0, &status_set, sizeof(status_set)); break; @@ -471,44 +509,6 @@ static void audit_receive(struct sock *sk, int length) up(&audit_netlink_sem); } -/* Grab skbuff from the audit_buffer and send to user space. */ -static inline int audit_log_drain(struct audit_buffer *ab) -{ - struct sk_buff *skb = ab->skb; - - if (skb) { - int retval = 0; - - if (audit_pid) { - struct nlmsghdr *nlh = (struct nlmsghdr *)skb->data; - nlh->nlmsg_len = skb->len - NLMSG_SPACE(0); - skb_get(skb); /* because netlink_* frees */ - retval = netlink_unicast(audit_sock, skb, audit_pid, - MSG_DONTWAIT); - } - if (retval == -EAGAIN && - (atomic_read(&audit_backlog)) < audit_backlog_limit) { - audit_log_end_irq(ab); - return 1; - } - if (retval < 0) { - if (retval == -ECONNREFUSED) { - printk(KERN_ERR - "audit: *NO* daemon at audit_pid=%d\n", - audit_pid); - audit_pid = 0; - } else - audit_log_lost("netlink socket too busy"); - } - if (!audit_pid) { /* No daemon */ - int offset = NLMSG_SPACE(0); - int len = skb->len - offset; - skb->data[offset + len] = '\0'; - printk(KERN_ERR "%s\n", skb->data + offset); - } - } - return 0; -} /* Initialize audit support at boot time. */ static int __init audit_init(void) @@ -519,6 +519,8 @@ static int __init audit_init(void) if (!audit_sock) audit_panic("cannot initialize netlink socket"); + audit_sock->sk_sndtimeo = MAX_SCHEDULE_TIMEOUT; + skb_queue_head_init(&audit_skb_queue); audit_initialized = 1; audit_enabled = audit_default; audit_log(NULL, AUDIT_KERNEL, "initialized"); @@ -549,7 +551,7 @@ static void audit_buffer_free(struct audit_buffer *ab) if (ab->skb) kfree_skb(ab->skb); - atomic_dec(&audit_backlog); + spin_lock_irqsave(&audit_freelist_lock, flags); if (++audit_freelist_count > AUDIT_MAXFREE) kfree(ab); @@ -579,13 +581,12 @@ static struct audit_buffer * audit_buffer_alloc(struct audit_context *ctx, if (!ab) goto err; } - atomic_inc(&audit_backlog); ab->skb = alloc_skb(AUDIT_BUFSIZ, gfp_mask); if (!ab->skb) goto err; - ab->ctx = ctx; + ab->ctx = ctx; nlh = (struct nlmsghdr *)skb_put(ab->skb, NLMSG_SPACE(0)); nlh->nlmsg_type = type; nlh->nlmsg_flags = 0; @@ -612,18 +613,6 @@ struct audit_buffer *audit_log_start(struct audit_context *ctx, int type) if (!audit_initialized) return NULL; - if (audit_backlog_limit - && atomic_read(&audit_backlog) > audit_backlog_limit) { - if (audit_rate_check()) - printk(KERN_WARNING - "audit: audit_backlog=%d > " - "audit_backlog_limit=%d\n", - atomic_read(&audit_backlog), - audit_backlog_limit); - audit_log_lost("backlog limit exceeded"); - return NULL; - } - ab = audit_buffer_alloc(ctx, GFP_ATOMIC, type); if (!ab) { audit_log_lost("out of memory in audit_log_start"); @@ -784,70 +773,30 @@ void audit_log_d_path(struct audit_buffer *ab, const char *prefix, kfree(path); } -/* Remove queued messages from the audit_txlist and send them to user space. */ -static void audit_tasklet_handler(unsigned long arg) -{ - LIST_HEAD(list); - struct audit_buffer *ab; - unsigned long flags; - - spin_lock_irqsave(&audit_txlist_lock, flags); - list_splice_init(&audit_txlist, &list); - spin_unlock_irqrestore(&audit_txlist_lock, flags); - - while (!list_empty(&list)) { - ab = list_entry(list.next, struct audit_buffer, list); - list_del(&ab->list); - audit_log_end_fast(ab); - } -} - -static DECLARE_TASKLET(audit_tasklet, audit_tasklet_handler, 0); - /* The netlink_* functions cannot be called inside an irq context, so * the audit buffer is places on a queue and a tasklet is scheduled to * remove them from the queue outside the irq context. May be called in * any context. */ -static void audit_log_end_irq(struct audit_buffer *ab) -{ - unsigned long flags; - - if (!ab) - return; - spin_lock_irqsave(&audit_txlist_lock, flags); - list_add_tail(&ab->list, &audit_txlist); - spin_unlock_irqrestore(&audit_txlist_lock, flags); - - tasklet_schedule(&audit_tasklet); -} - -/* Send the message in the audit buffer directly to user space. May not - * be called in an irq context. */ -static void audit_log_end_fast(struct audit_buffer *ab) +void audit_log_end(struct audit_buffer *ab) { - BUG_ON(in_irq()); if (!ab) return; if (!audit_rate_check()) { audit_log_lost("rate limit exceeded"); } else { - if (audit_log_drain(ab)) - return; + if (audit_pid) { + struct nlmsghdr *nlh = (struct nlmsghdr *)ab->skb->data; + nlh->nlmsg_len = ab->skb->len - NLMSG_SPACE(0); + skb_queue_tail(&audit_skb_queue, ab->skb); + ab->skb = NULL; + wake_up_interruptible(&kauditd_wait); + } else { + printk("%s\n", ab->skb->data + NLMSG_SPACE(0)); + } } audit_buffer_free(ab); } -/* Send or queue the message in the audit buffer, depending on the - * current context. (A convenience function that may be called in any - * context.) */ -void audit_log_end(struct audit_buffer *ab) -{ - if (in_irq()) - audit_log_end_irq(ab); - else - audit_log_end_fast(ab); -} - /* Log an audit record. This is a convenience function that calls * audit_log_start, audit_log_vformat, and audit_log_end. It may be * called in any context. */ -- cgit v1.2.3 From cd77b8212d5473b800ac865364981d334ff564ea Mon Sep 17 00:00:00 2001 From: David Woodhouse Date: Thu, 19 May 2005 11:18:24 +0100 Subject: Restore logging of pid= and comm= in AVC audit messages We turned this all off because the 'exe=' was causing deadlocks on dcache_lock. There's no need to leave the pid and comm out though. They'll all be logged correctly if full auditing is enabled, but we should still print them in case auditing _isn't_ enabled. Signed-off-by: David Woodhouse --- security/selinux/avc.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/security/selinux/avc.c b/security/selinux/avc.c index 042f91e9f9d..62b963aca27 100644 --- a/security/selinux/avc.c +++ b/security/selinux/avc.c @@ -532,6 +532,7 @@ void avc_audit(u32 ssid, u32 tsid, u16 tclass, u32 requested, struct av_decision *avd, int result, struct avc_audit_data *a) { + struct task_struct *tsk = current; struct inode *inode = NULL; u32 denied, audited; struct audit_buffer *ab; @@ -555,6 +556,12 @@ void avc_audit(u32 ssid, u32 tsid, audit_log_format(ab, "avc: %s ", denied ? "denied" : "granted"); avc_dump_av(ab, tclass,audited); audit_log_format(ab, " for "); + if (a && a->tsk) + tsk = a->tsk; + if (a->tsk && a->tsk->pid) { + audit_log_format(ab, " pid=%d comm=", tsk->pid); + audit_log_untrustedstring(ab, tsk->comm); + } if (a) { switch (a->type) { case AVC_AUDIT_DATA_IPC: -- cgit v1.2.3 From 7ca0026495dbb644b4e32ede76be44072cb2bc7a Mon Sep 17 00:00:00 2001 From: David Woodhouse Date: Thu, 19 May 2005 11:23:13 +0100 Subject: AUDIT: Quis Custodiet Ipsos Custodes? Nobody does. Really, it gets very silly if auditd is recording its own actions. Signed-off-by: David Woodhouse --- kernel/auditsc.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/kernel/auditsc.c b/kernel/auditsc.c index 818778d5b6a..78d7a13fc86 100644 --- a/kernel/auditsc.c +++ b/kernel/auditsc.c @@ -169,6 +169,8 @@ struct audit_entry { struct audit_rule rule; }; +extern int audit_pid; + /* Check to see if two rules are identical. It is called from * audit_del_rule during AUDIT_DEL. */ static int audit_compare_rule(struct audit_rule *a, struct audit_rule *b) @@ -768,7 +770,7 @@ void audit_free(struct task_struct *tsk) /* Check for system calls that do not go through the exit * function (e.g., exit_group), then free context block. */ - if (context->in_syscall && context->auditable) + if (context->in_syscall && context->auditable && context->pid != audit_pid) audit_log_exit(context); audit_free_context(context); @@ -903,7 +905,7 @@ void audit_syscall_exit(struct task_struct *tsk, int valid, long return_code) if (likely(!context)) return; - if (context->in_syscall && context->auditable) + if (context->in_syscall && context->auditable && context->pid != audit_pid) audit_log_exit(context); context->in_syscall = 0; @@ -1126,7 +1128,6 @@ void audit_signal_info(int sig, struct task_struct *t) { extern pid_t audit_sig_pid; extern uid_t audit_sig_uid; - extern int audit_pid; if (unlikely(audit_pid && t->pid == audit_pid)) { if (sig == SIGTERM || sig == SIGHUP) { -- cgit v1.2.3 From fb19b4c6aa024837a0071f07baa07dbf49d07151 Mon Sep 17 00:00:00 2001 From: David Woodhouse Date: Thu, 19 May 2005 14:55:56 +0100 Subject: AUDIT: Honour audit_backlog_limit again. The limit on the number of outstanding audit messages was inadvertently removed with the switch to queuing skbs directly for sending by a kernel thread. Put it back again. Signed-off-by: David Woodhouse --- kernel/audit.c | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/kernel/audit.c b/kernel/audit.c index bbc6f542c8f..41581413529 100644 --- a/kernel/audit.c +++ b/kernel/audit.c @@ -613,6 +613,18 @@ struct audit_buffer *audit_log_start(struct audit_context *ctx, int type) if (!audit_initialized) return NULL; + if (audit_backlog_limit + && skb_queue_len(&audit_skb_queue) > audit_backlog_limit) { + if (audit_rate_check()) + printk(KERN_WARNING + "audit: audit_backlog=%d > " + "audit_backlog_limit=%d\n", + skb_queue_len(&audit_skb_queue), + audit_backlog_limit); + audit_log_lost("backlog limit exceeded"); + return NULL; + } + ab = audit_buffer_alloc(ctx, GFP_ATOMIC, type); if (!ab) { audit_log_lost("out of memory in audit_log_start"); -- cgit v1.2.3 From 011161051bbc25f7f8b7df059dbd934c534443f0 Mon Sep 17 00:00:00 2001 From: Stephen Smalley Date: Sat, 21 May 2005 00:15:52 +0100 Subject: AUDIT: Avoid sleeping function in SElinux AVC audit. This patch changes the SELinux AVC to defer logging of paths to the audit framework upon syscall exit, by saving a reference to the (dentry,vfsmount) pair in an auxiliary audit item on the current audit context for processing by audit_log_exit. Signed-off-by: Stephen Smalley Signed-off-by: David Woodhouse --- include/linux/audit.h | 3 +++ kernel/auditsc.c | 40 ++++++++++++++++++++++++++++++++++++++++ security/selinux/avc.c | 17 ++++++++--------- 3 files changed, 51 insertions(+), 9 deletions(-) diff --git a/include/linux/audit.h b/include/linux/audit.h index 17ea5d522d8..4b7caf0c6e1 100644 --- a/include/linux/audit.h +++ b/include/linux/audit.h @@ -69,6 +69,7 @@ #define AUDIT_AVC 1400 /* SE Linux avc denial or grant */ #define AUDIT_SELINUX_ERR 1401 /* Internal SE Linux Errors */ +#define AUDIT_AVC_PATH 1402 /* dentry, vfsmount pair from avc */ #define AUDIT_KERNEL 2000 /* Asynchronous audit record. NOT A REQUEST. */ @@ -225,6 +226,7 @@ extern uid_t audit_get_loginuid(struct audit_context *ctx); extern int audit_ipc_perms(unsigned long qbytes, uid_t uid, gid_t gid, mode_t mode); extern int audit_socketcall(int nargs, unsigned long *args); extern int audit_sockaddr(int len, void *addr); +extern int audit_avc_path(struct dentry *dentry, struct vfsmount *mnt); extern void audit_signal_info(int sig, struct task_struct *t); #else #define audit_alloc(t) ({ 0; }) @@ -240,6 +242,7 @@ extern void audit_signal_info(int sig, struct task_struct *t); #define audit_ipc_perms(q,u,g,m) ({ 0; }) #define audit_socketcall(n,a) ({ 0; }) #define audit_sockaddr(len, addr) ({ 0; }) +#define audit_avc_path(dentry, mnt) ({ 0; }) #define audit_signal_info(s,t) do { ; } while (0) #endif diff --git a/kernel/auditsc.c b/kernel/auditsc.c index 78d7a13fc86..8dc5b276714 100644 --- a/kernel/auditsc.c +++ b/kernel/auditsc.c @@ -34,6 +34,7 @@ #include #include #include +#include #include #include #include @@ -124,6 +125,11 @@ struct audit_aux_data_sockaddr { char a[0]; }; +struct audit_aux_data_path { + struct audit_aux_data d; + struct dentry *dentry; + struct vfsmount *mnt; +}; /* The per-task audit context. */ struct audit_context { @@ -553,6 +559,11 @@ static inline void audit_free_aux(struct audit_context *context) struct audit_aux_data *aux; while ((aux = context->aux)) { + if (aux->type == AUDIT_AVC_PATH) { + struct audit_aux_data_path *axi = (void *)aux; + dput(axi->dentry); + mntput(axi->mnt); + } context->aux = aux->next; kfree(aux); } @@ -724,6 +735,14 @@ static void audit_log_exit(struct audit_context *context) audit_log_format(ab, "saddr="); audit_log_hex(ab, axs->a, axs->len); break; } + + case AUDIT_AVC_PATH: { + struct audit_aux_data_path *axi = (void *)aux; + audit_log_d_path(ab, "path=", axi->dentry, axi->mnt); + dput(axi->dentry); + mntput(axi->mnt); + break; } + } audit_log_end(ab); @@ -1124,6 +1143,27 @@ int audit_sockaddr(int len, void *a) return 0; } +int audit_avc_path(struct dentry *dentry, struct vfsmount *mnt) +{ + struct audit_aux_data_path *ax; + struct audit_context *context = current->audit_context; + + if (likely(!context)) + return 0; + + ax = kmalloc(sizeof(*ax), GFP_ATOMIC); + if (!ax) + return -ENOMEM; + + ax->dentry = dget(dentry); + ax->mnt = mntget(mnt); + + ax->d.type = AUDIT_AVC_PATH; + ax->d.next = context->aux; + context->aux = (void *)ax; + return 0; +} + void audit_signal_info(int sig, struct task_struct *t) { extern pid_t audit_sig_pid; diff --git a/security/selinux/avc.c b/security/selinux/avc.c index 62b963aca27..0fbc3e98c5e 100644 --- a/security/selinux/avc.c +++ b/security/selinux/avc.c @@ -573,13 +573,10 @@ void avc_audit(u32 ssid, u32 tsid, case AVC_AUDIT_DATA_FS: if (a->u.fs.dentry) { struct dentry *dentry = a->u.fs.dentry; - if (a->u.fs.mnt) { - audit_log_d_path(ab, "path=", dentry, - a->u.fs.mnt); - } else { - audit_log_format(ab, " name=%s", - dentry->d_name.name); - } + if (a->u.fs.mnt) + audit_avc_path(dentry, a->u.fs.mnt); + audit_log_format(ab, " name=%s", + dentry->d_name.name); inode = dentry->d_inode; } else if (a->u.fs.inode) { struct dentry *dentry; @@ -630,8 +627,10 @@ void avc_audit(u32 ssid, u32 tsid, case AF_UNIX: u = unix_sk(sk); if (u->dentry) { - audit_log_d_path(ab, "path=", - u->dentry, u->mnt); + audit_avc_path(u->dentry, u->mnt); + audit_log_format(ab, " name=%s", + u->dentry->d_name.name); + break; } if (!u->addr) -- cgit v1.2.3 From 05474106a41f44d16d649bc8c7687fc24ce4370a Mon Sep 17 00:00:00 2001 From: Steve Grubb Date: Sat, 21 May 2005 00:18:37 +0100 Subject: AUDIT: Fix AVC_USER message passing. The original AVC_USER message wasn't consolidated with the new range of user messages. The attached patch fixes the kernel so the old messages work again. Signed-off-by: Steve Grubb Signed-off-by: David Woodhouse --- kernel/audit.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/kernel/audit.c b/kernel/audit.c index 41581413529..5e72895f482 100644 --- a/kernel/audit.c +++ b/kernel/audit.c @@ -354,6 +354,7 @@ static int audit_netlink_ok(kernel_cap_t eff_cap, u16 msg_type) if (!cap_raised(eff_cap, CAP_AUDIT_CONTROL)) err = -EPERM; break; + case AUDIT_USER: case AUDIT_FIRST_USER_MSG...AUDIT_LAST_USER_MSG: if (!cap_raised(eff_cap, CAP_AUDIT_WRITE)) err = -EPERM; @@ -432,6 +433,7 @@ static int audit_receive_msg(struct sk_buff *skb, struct nlmsghdr *nlh) audit_set_backlog_limit(status_get->backlog_limit, loginuid); break; + case AUDIT_USER: case AUDIT_FIRST_USER_MSG...AUDIT_LAST_USER_MSG: ab = audit_log_start(NULL, msg_type); if (!ab) -- cgit v1.2.3 From 326e9c8ba6a149f47e020719b23b24a14ba740d6 Mon Sep 17 00:00:00 2001 From: Steve Grubb Date: Sat, 21 May 2005 00:22:31 +0100 Subject: AUDIT: Fix inconsistent use of loginuid vs. auid, signed vs. unsigned The attached patch changes all occurrences of loginuid to auid. It also changes everything to %u that is an unsigned type. Signed-off-by: Steve Grubb Signed-off-by: David Woodhouse --- kernel/audit.c | 7 ++----- kernel/auditsc.c | 12 ++++++------ 2 files changed, 8 insertions(+), 11 deletions(-) diff --git a/kernel/audit.c b/kernel/audit.c index 5e72895f482..f0a003acf62 100644 --- a/kernel/audit.c +++ b/kernel/audit.c @@ -439,12 +439,9 @@ static int audit_receive_msg(struct sk_buff *skb, struct nlmsghdr *nlh) if (!ab) break; /* audit_panic has been called */ audit_log_format(ab, - "user pid=%d uid=%d length=%d loginuid=%u" + "user pid=%d uid=%u auid=%u" " msg='%.1024s'", - pid, uid, - (int)(nlh->nlmsg_len - - ((char *)data - (char *)nlh)), - loginuid, (char *)data); + pid, uid, loginuid, (char *)data); audit_set_pid(ab, pid); audit_log_end(ab); break; diff --git a/kernel/auditsc.c b/kernel/auditsc.c index 8dc5b276714..4193811d4fe 100644 --- a/kernel/auditsc.c +++ b/kernel/auditsc.c @@ -688,9 +688,9 @@ static void audit_log_exit(struct audit_context *context) context->return_code); audit_log_format(ab, " a0=%lx a1=%lx a2=%lx a3=%lx items=%d" - " pid=%d loginuid=%d uid=%d gid=%d" - " euid=%d suid=%d fsuid=%d" - " egid=%d sgid=%d fsgid=%d", + " pid=%d auid=%u uid=%u gid=%u" + " euid=%u suid=%u fsuid=%u" + " egid=%u sgid=%u fsgid=%u", context->argv[0], context->argv[1], context->argv[2], @@ -717,7 +717,7 @@ static void audit_log_exit(struct audit_context *context) case AUDIT_IPC: { struct audit_aux_data_ipcctl *axi = (void *)aux; audit_log_format(ab, - " qbytes=%lx iuid=%d igid=%d mode=%x", + " qbytes=%lx iuid=%u igid=%u mode=%x", axi->qbytes, axi->uid, axi->gid, axi->mode); break; } @@ -761,7 +761,7 @@ static void audit_log_exit(struct audit_context *context) } if (context->names[i].ino != (unsigned long)-1) audit_log_format(ab, " inode=%lu dev=%02x:%02x mode=%#o" - " ouid=%d ogid=%d rdev=%02x:%02x", + " ouid=%u ogid=%u rdev=%02x:%02x", context->names[i].ino, MAJOR(context->names[i].dev), MINOR(context->names[i].dev), @@ -1063,7 +1063,7 @@ int audit_set_loginuid(struct task_struct *task, uid_t loginuid) ab = audit_log_start(NULL, AUDIT_LOGIN); if (ab) { audit_log_format(ab, "login pid=%d uid=%u " - "old loginuid=%u new loginuid=%u", + "old auid=%u new auid=%u", task->pid, task->uid, task->audit_context->loginuid, loginuid); audit_log_end(ab); -- cgit v1.2.3 From 7b5d781ce1f19fb7382d3d3fb7af48e429bed12d Mon Sep 17 00:00:00 2001 From: David Woodhouse Date: Sat, 21 May 2005 16:52:57 +0100 Subject: Fix oops due to thinko in avc_audit() When I added the logging of pid= and comm= back to avc_audit() I screwed it up. Put it back how it should be. Signed-off-by: David Woodhouse --- security/selinux/avc.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/security/selinux/avc.c b/security/selinux/avc.c index 0fbc3e98c5e..914d0d294ff 100644 --- a/security/selinux/avc.c +++ b/security/selinux/avc.c @@ -558,7 +558,7 @@ void avc_audit(u32 ssid, u32 tsid, audit_log_format(ab, " for "); if (a && a->tsk) tsk = a->tsk; - if (a->tsk && a->tsk->pid) { + if (tsk && tsk->pid) { audit_log_format(ab, " pid=%d comm=", tsk->pid); audit_log_untrustedstring(ab, tsk->comm); } -- cgit v1.2.3 From bfb4496e7239c9132d732a65cdcf3d6a7431ad1a Mon Sep 17 00:00:00 2001 From: David Woodhouse Date: Sat, 21 May 2005 21:08:09 +0100 Subject: AUDIT: Assign serial number to non-syscall messages Move audit_serial() into audit.c and use it to generate serial numbers on messages even when there is no audit context from syscall auditing. This allows us to disambiguate audit records when more than one is generated in the same millisecond. Based on a patch by Steve Grubb after he observed the problem. Signed-off-by: David Woodhouse --- include/linux/audit.h | 7 ++++--- kernel/audit.c | 46 ++++++++++++++++++++++++++++++++++++++++++---- kernel/auditsc.c | 46 ++++++---------------------------------------- 3 files changed, 52 insertions(+), 47 deletions(-) diff --git a/include/linux/audit.h b/include/linux/audit.h index 4b7caf0c6e1..3278ddf41ce 100644 --- a/include/linux/audit.h +++ b/include/linux/audit.h @@ -219,8 +219,9 @@ extern void audit_inode(const char *name, const struct inode *inode); /* Private API (for audit.c only) */ extern int audit_receive_filter(int type, int pid, int uid, int seq, void *data, uid_t loginuid); -extern int audit_get_stamp(struct audit_context *ctx, - struct timespec *t, unsigned int *serial); +extern unsigned int audit_serial(void); +extern void auditsc_get_stamp(struct audit_context *ctx, + struct timespec *t, unsigned int *serial); extern int audit_set_loginuid(struct task_struct *task, uid_t loginuid); extern uid_t audit_get_loginuid(struct audit_context *ctx); extern int audit_ipc_perms(unsigned long qbytes, uid_t uid, gid_t gid, mode_t mode); @@ -237,7 +238,7 @@ extern void audit_signal_info(int sig, struct task_struct *t); #define audit_putname(n) do { ; } while (0) #define audit_inode(n,i) do { ; } while (0) #define audit_receive_filter(t,p,u,s,d,l) ({ -EOPNOTSUPP; }) -#define audit_get_stamp(c,t,s) ({ 0; }) +#define auditsc_get_stamp(c,t,s) do { BUG(); } while (0) #define audit_get_loginuid(c) ({ -1; }) #define audit_ipc_perms(q,u,g,m) ({ 0; }) #define audit_socketcall(n,a) ({ 0; }) diff --git a/kernel/audit.c b/kernel/audit.c index f0a003acf62..35306f4369e 100644 --- a/kernel/audit.c +++ b/kernel/audit.c @@ -597,6 +597,47 @@ err: return NULL; } +/* Compute a serial number for the audit record. Audit records are + * written to user-space as soon as they are generated, so a complete + * audit record may be written in several pieces. The timestamp of the + * record and this serial number are used by the user-space tools to + * determine which pieces belong to the same audit record. The + * (timestamp,serial) tuple is unique for each syscall and is live from + * syscall entry to syscall exit. + * + * Atomic values are only guaranteed to be 24-bit, so we count down. + * + * NOTE: Another possibility is to store the formatted records off the + * audit context (for those records that have a context), and emit them + * all at syscall exit. However, this could delay the reporting of + * significant errors until syscall exit (or never, if the system + * halts). */ +unsigned int audit_serial(void) +{ + static atomic_t serial = ATOMIC_INIT(0xffffff); + unsigned int a, b; + + do { + a = atomic_read(&serial); + if (atomic_dec_and_test(&serial)) + atomic_set(&serial, 0xffffff); + b = atomic_read(&serial); + } while (b != a - 1); + + return 0xffffff - b; +} + +static inline void audit_get_stamp(struct audit_context *ctx, + struct timespec *t, unsigned int *serial) +{ + if (ctx) + auditsc_get_stamp(ctx, t, serial); + else { + *t = CURRENT_TIME; + *serial = audit_serial(); + } +} + /* Obtain an audit buffer. This routine does locking to obtain the * audit buffer, but then no locking is required for calls to * audit_log_*format. If the tsk is a task that is currently in a @@ -630,10 +671,7 @@ struct audit_buffer *audit_log_start(struct audit_context *ctx, int type) return NULL; } - if (!audit_get_stamp(ab->ctx, &t, &serial)) { - t = CURRENT_TIME; - serial = 0; - } + audit_get_stamp(ab->ctx, &t, &serial); audit_log_format(ab, "audit(%lu.%03lu:%u): ", t.tv_sec, t.tv_nsec/1000000, serial); diff --git a/kernel/auditsc.c b/kernel/auditsc.c index 4193811d4fe..74c2ae804ca 100644 --- a/kernel/auditsc.c +++ b/kernel/auditsc.c @@ -795,36 +795,6 @@ void audit_free(struct task_struct *tsk) audit_free_context(context); } -/* Compute a serial number for the audit record. Audit records are - * written to user-space as soon as they are generated, so a complete - * audit record may be written in several pieces. The timestamp of the - * record and this serial number are used by the user-space tools to - * determine which pieces belong to the same audit record. The - * (timestamp,serial) tuple is unique for each syscall and is live from - * syscall entry to syscall exit. - * - * Atomic values are only guaranteed to be 24-bit, so we count down. - * - * NOTE: Another possibility is to store the formatted records off the - * audit context (for those records that have a context), and emit them - * all at syscall exit. However, this could delay the reporting of - * significant errors until syscall exit (or never, if the system - * halts). */ -static inline unsigned int audit_serial(void) -{ - static atomic_t serial = ATOMIC_INIT(0xffffff); - unsigned int a, b; - - do { - a = atomic_read(&serial); - if (atomic_dec_and_test(&serial)) - atomic_set(&serial, 0xffffff); - b = atomic_read(&serial); - } while (b != a - 1); - - return 0xffffff - b; -} - /* Fill in audit context at syscall entry. This only happens if the * audit context was created when the task was created and the state or * filters demand the audit context be built. If the state from the @@ -1042,17 +1012,13 @@ void audit_inode(const char *name, const struct inode *inode) context->names[idx].rdev = inode->i_rdev; } -int audit_get_stamp(struct audit_context *ctx, - struct timespec *t, unsigned int *serial) +void auditsc_get_stamp(struct audit_context *ctx, + struct timespec *t, unsigned int *serial) { - if (ctx) { - t->tv_sec = ctx->ctime.tv_sec; - t->tv_nsec = ctx->ctime.tv_nsec; - *serial = ctx->serial; - ctx->auditable = 1; - return 1; - } - return 0; + t->tv_sec = ctx->ctime.tv_sec; + t->tv_nsec = ctx->ctime.tv_nsec; + *serial = ctx->serial; + ctx->auditable = 1; } int audit_set_loginuid(struct task_struct *task, uid_t loginuid) -- cgit v1.2.3 From bccf6ae083318ea08094d6ab185fdf7c49906b3a Mon Sep 17 00:00:00 2001 From: David Woodhouse Date: Mon, 23 May 2005 21:35:28 +0100 Subject: AUDIT: Unify auid reporting, put arch before syscall number These changes make processing of audit logs easier. Based on a patch from Steve Grubb Signed-off-by: David Woodhouse --- kernel/audit.c | 10 +++++----- kernel/auditsc.c | 8 ++++---- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/kernel/audit.c b/kernel/audit.c index 35306f4369e..ef35166fdc2 100644 --- a/kernel/audit.c +++ b/kernel/audit.c @@ -234,7 +234,7 @@ static int audit_set_rate_limit(int limit, uid_t loginuid) int old = audit_rate_limit; audit_rate_limit = limit; audit_log(NULL, AUDIT_CONFIG_CHANGE, - "audit_rate_limit=%d old=%d by auid %u", + "audit_rate_limit=%d old=%d by auid=%u", audit_rate_limit, old, loginuid); return old; } @@ -244,7 +244,7 @@ static int audit_set_backlog_limit(int limit, uid_t loginuid) int old = audit_backlog_limit; audit_backlog_limit = limit; audit_log(NULL, AUDIT_CONFIG_CHANGE, - "audit_backlog_limit=%d old=%d by auid %u", + "audit_backlog_limit=%d old=%d by auid=%u", audit_backlog_limit, old, loginuid); return old; } @@ -256,7 +256,7 @@ static int audit_set_enabled(int state, uid_t loginuid) return -EINVAL; audit_enabled = state; audit_log(NULL, AUDIT_CONFIG_CHANGE, - "audit_enabled=%d old=%d by auid %u", + "audit_enabled=%d old=%d by auid=%u", audit_enabled, old, loginuid); return old; } @@ -270,7 +270,7 @@ static int audit_set_failure(int state, uid_t loginuid) return -EINVAL; audit_failure = state; audit_log(NULL, AUDIT_CONFIG_CHANGE, - "audit_failure=%d old=%d by auid %u", + "audit_failure=%d old=%d by auid=%u", audit_failure, old, loginuid); return old; } @@ -424,7 +424,7 @@ static int audit_receive_msg(struct sk_buff *skb, struct nlmsghdr *nlh) int old = audit_pid; audit_pid = status_get->pid; audit_log(NULL, AUDIT_CONFIG_CHANGE, - "audit_pid=%d old=%d by auid %u", + "audit_pid=%d old=%d by auid=%u", audit_pid, old, loginuid); } if (status_get->mask & AUDIT_STATUS_RATE_LIMIT) diff --git a/kernel/auditsc.c b/kernel/auditsc.c index 74c2ae804ca..5fc4f52d218 100644 --- a/kernel/auditsc.c +++ b/kernel/auditsc.c @@ -307,7 +307,7 @@ int audit_receive_filter(int type, int pid, int uid, int seq, void *data, if (!err && (flags & AUDIT_AT_EXIT)) err = audit_add_rule(entry, &audit_extlist); audit_log(NULL, AUDIT_CONFIG_CHANGE, - "auid %u added an audit rule\n", loginuid); + "auid=%u added an audit rule\n", loginuid); break; case AUDIT_DEL: flags =((struct audit_rule *)data)->flags; @@ -318,7 +318,7 @@ int audit_receive_filter(int type, int pid, int uid, int seq, void *data, if (!err && (flags & AUDIT_AT_EXIT)) err = audit_del_rule(data, &audit_extlist); audit_log(NULL, AUDIT_CONFIG_CHANGE, - "auid %u removed an audit rule\n", loginuid); + "auid=%u removed an audit rule\n", loginuid); break; default: return -EINVAL; @@ -678,10 +678,10 @@ static void audit_log_exit(struct audit_context *context) ab = audit_log_start(context, AUDIT_SYSCALL); if (!ab) return; /* audit_panic has been called */ - audit_log_format(ab, "syscall=%d", context->major); + audit_log_format(ab, "arch=%x syscall=%d", + context->arch, context->major); if (context->personality != PER_LINUX) audit_log_format(ab, " per=%lx", context->personality); - audit_log_format(ab, " arch=%x", context->arch); if (context->return_valid) audit_log_format(ab, " success=%s exit=%ld", (context->return_valid==AUDITSC_SUCCESS)?"yes":"no", -- cgit v1.2.3 From 99e45eeac867d51ff3395dcf3d7aedf5ac2812c8 Mon Sep 17 00:00:00 2001 From: David Woodhouse Date: Mon, 23 May 2005 21:57:41 +0100 Subject: AUDIT: Escape comm when logging task info It comes from the user; it needs to be escaped. Signed-off-by: David Woodhouse --- kernel/auditsc.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/kernel/auditsc.c b/kernel/auditsc.c index 5fc4f52d218..b45677eba78 100644 --- a/kernel/auditsc.c +++ b/kernel/auditsc.c @@ -650,7 +650,8 @@ static void audit_log_task_info(struct audit_buffer *ab) struct vm_area_struct *vma; get_task_comm(name, current); - audit_log_format(ab, " comm=%s", name); + audit_log_format(ab, " comm="); + audit_log_untrustedstring(ab, name); if (!mm) return; -- cgit v1.2.3 From 37ca5389b863e5ffba6fb7c22331bf57dbf7764a Mon Sep 17 00:00:00 2001 From: Stephen Smalley Date: Tue, 24 May 2005 21:28:28 +0100 Subject: AUDIT: Fix remaining cases of direct logging of untrusted strings by avc_audit Per Steve Grubb's observation that there are some remaining cases where avc_audit() directly logs untrusted strings without escaping them, here is a patch that changes avc_audit() to use audit_log_untrustedstring() or audit_log_hex() as appropriate. Note that d_name.name is nul- terminated by d_alloc(), and that sun_path is nul-terminated by unix_mkname(), so it is not necessary for the AVC to create nul- terminated copies or to alter audit_log_untrustedstring to take a length argument. In the case of an abstract name, we use audit_log_hex() with an explicit length. Signed-off-by: Stephen Smalley Signed-off-by: David Woodhouse --- security/selinux/avc.c | 22 +++++++++------------- 1 file changed, 9 insertions(+), 13 deletions(-) diff --git a/security/selinux/avc.c b/security/selinux/avc.c index 914d0d294ff..451502467a9 100644 --- a/security/selinux/avc.c +++ b/security/selinux/avc.c @@ -575,16 +575,16 @@ void avc_audit(u32 ssid, u32 tsid, struct dentry *dentry = a->u.fs.dentry; if (a->u.fs.mnt) audit_avc_path(dentry, a->u.fs.mnt); - audit_log_format(ab, " name=%s", - dentry->d_name.name); + audit_log_format(ab, " name="); + audit_log_untrustedstring(ab, dentry->d_name.name); inode = dentry->d_inode; } else if (a->u.fs.inode) { struct dentry *dentry; inode = a->u.fs.inode; dentry = d_find_alias(inode); if (dentry) { - audit_log_format(ab, " name=%s", - dentry->d_name.name); + audit_log_format(ab, " name="); + audit_log_untrustedstring(ab, dentry->d_name.name); dput(dentry); } } @@ -628,23 +628,19 @@ void avc_audit(u32 ssid, u32 tsid, u = unix_sk(sk); if (u->dentry) { audit_avc_path(u->dentry, u->mnt); - audit_log_format(ab, " name=%s", - u->dentry->d_name.name); - + audit_log_format(ab, " name="); + audit_log_untrustedstring(ab, u->dentry->d_name.name); break; } if (!u->addr) break; len = u->addr->len-sizeof(short); p = &u->addr->name->sun_path[0]; + audit_log_format(ab, " path="); if (*p) - audit_log_format(ab, - "path=%*.*s", len, - len, p); + audit_log_untrustedstring(ab, p); else - audit_log_format(ab, - "path=@%*.*s", len-1, - len-1, p+1); + audit_log_hex(ab, p, len); break; } } -- cgit v1.2.3 From 7551ced334ce6eb2a7a765309871e619f645add1 Mon Sep 17 00:00:00 2001 From: David Woodhouse Date: Thu, 26 May 2005 12:04:57 +0100 Subject: AUDIT: Defer freeing aux items until audit_free_context() While they were all just simple blobs it made sense to just free them as we walked through and logged them. Now that there are pointers to other objects which need refcounting, we might as well revert to _only_ logging them in audit_log_exit(), and put the code to free them properly in only one place -- in audit_free_aux(). Signed-off-by: David Woodhouse ---------------------------------------------------------- --- kernel/auditsc.c | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/kernel/auditsc.c b/kernel/auditsc.c index b45677eba78..7556c479d5a 100644 --- a/kernel/auditsc.c +++ b/kernel/auditsc.c @@ -675,6 +675,7 @@ static void audit_log_exit(struct audit_context *context) { int i; struct audit_buffer *ab; + struct audit_aux_data *aux; ab = audit_log_start(context, AUDIT_SYSCALL); if (!ab) @@ -705,10 +706,8 @@ static void audit_log_exit(struct audit_context *context) context->egid, context->sgid, context->fsgid); audit_log_task_info(ab); audit_log_end(ab); - while (context->aux) { - struct audit_aux_data *aux; - aux = context->aux; + for (aux = context->aux; aux; aux = aux->next) { ab = audit_log_start(context, aux->type); if (!ab) @@ -740,15 +739,10 @@ static void audit_log_exit(struct audit_context *context) case AUDIT_AVC_PATH: { struct audit_aux_data_path *axi = (void *)aux; audit_log_d_path(ab, "path=", axi->dentry, axi->mnt); - dput(axi->dentry); - mntput(axi->mnt); break; } } audit_log_end(ab); - - context->aux = aux->next; - kfree(aux); } for (i = 0; i < context->name_count; i++) { -- cgit v1.2.3 From 8f37d47c9bf74cb48692691086b482e315d07f40 Mon Sep 17 00:00:00 2001 From: David Woodhouse Date: Fri, 27 May 2005 12:17:28 +0100 Subject: AUDIT: Record working directory when syscall arguments are pathnames Signed-off-by: David Woodhouse --- include/linux/audit.h | 3 ++- kernel/auditsc.c | 23 +++++++++++++++++++++++ 2 files changed, 25 insertions(+), 1 deletion(-) diff --git a/include/linux/audit.h b/include/linux/audit.h index 3278ddf41ce..bf2ad3ba72e 100644 --- a/include/linux/audit.h +++ b/include/linux/audit.h @@ -61,11 +61,12 @@ #define AUDIT_SYSCALL 1300 /* Syscall event */ #define AUDIT_FS_WATCH 1301 /* Filesystem watch event */ -#define AUDIT_PATH 1302 /* Filname path information */ +#define AUDIT_PATH 1302 /* Filename path information */ #define AUDIT_IPC 1303 /* IPC record */ #define AUDIT_SOCKETCALL 1304 /* sys_socketcall arguments */ #define AUDIT_CONFIG_CHANGE 1305 /* Audit system configuration change */ #define AUDIT_SOCKADDR 1306 /* sockaddr copied as syscall arg */ +#define AUDIT_CWD 1307 /* Current working directory */ #define AUDIT_AVC 1400 /* SE Linux avc denial or grant */ #define AUDIT_SELINUX_ERR 1401 /* Internal SE Linux Errors */ diff --git a/kernel/auditsc.c b/kernel/auditsc.c index 7556c479d5a..e75f84e1a1a 100644 --- a/kernel/auditsc.c +++ b/kernel/auditsc.c @@ -145,6 +145,8 @@ struct audit_context { int auditable; /* 1 if record should be written */ int name_count; struct audit_names names[AUDIT_NAMES]; + struct dentry * pwd; + struct vfsmount * pwdmnt; struct audit_context *previous; /* For nested syscalls */ struct audit_aux_data *aux; @@ -552,6 +554,12 @@ static inline void audit_free_names(struct audit_context *context) if (context->names[i].name) __putname(context->names[i].name); context->name_count = 0; + if (context->pwd) + dput(context->pwd); + if (context->pwdmnt) + mntput(context->pwdmnt); + context->pwd = NULL; + context->pwdmnt = NULL; } static inline void audit_free_aux(struct audit_context *context) @@ -745,10 +753,18 @@ static void audit_log_exit(struct audit_context *context) audit_log_end(ab); } + if (context->pwd && context->pwdmnt) { + ab = audit_log_start(context, AUDIT_CWD); + if (ab) { + audit_log_d_path(ab, "cwd=", context->pwd, context->pwdmnt); + audit_log_end(ab); + } + } for (i = 0; i < context->name_count; i++) { ab = audit_log_start(context, AUDIT_PATH); if (!ab) continue; /* audit_panic has been called */ + audit_log_format(ab, "item=%d", i); if (context->names[i].name) { audit_log_format(ab, " name="); @@ -929,6 +945,13 @@ void audit_getname(const char *name) context->names[context->name_count].name = name; context->names[context->name_count].ino = (unsigned long)-1; ++context->name_count; + if (!context->pwd) { + read_lock(¤t->fs->lock); + context->pwd = dget(current->fs->pwd); + context->pwdmnt = mntget(current->fs->pwdmnt); + read_unlock(¤t->fs->lock); + } + } /* Intercept a putname request. Called from -- cgit v1.2.3 From 4bcff1b37e7c3aed914d1ce5b45994adc7dbf455 Mon Sep 17 00:00:00 2001 From: David Woodhouse Date: Thu, 2 Jun 2005 12:13:21 +0100 Subject: AUDIT: Fix user pointer deref thinko in sys_socketcall(). I cunningly put the audit call immediately after the copy_from_user().... but used the _userspace_ copy of the args still. Let's not do that. Signed-off-by: David Woodhouse --- net/socket.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/net/socket.c b/net/socket.c index 6b7c3b51a7c..38729af0946 100644 --- a/net/socket.c +++ b/net/socket.c @@ -1908,7 +1908,7 @@ asmlinkage long sys_socketcall(int call, unsigned long __user *args) if (copy_from_user(a, args, nargs[call])) return -EFAULT; - err = audit_socketcall(nargs[call]/sizeof(unsigned long), args); + err = audit_socketcall(nargs[call]/sizeof(unsigned long), a); if (err) return err; -- cgit v1.2.3