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 --- kernel/audit.c | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) (limited to 'kernel/audit.c') diff --git a/kernel/audit.c b/kernel/audit.c index 9c4f1af0c794..6f344b44d3d3 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; -- cgit v1.2.2 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(-) (limited to 'kernel/audit.c') diff --git a/kernel/audit.c b/kernel/audit.c index 6f344b44d3d3..e5bdba3e3ae1 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.2 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(-) (limited to 'kernel/audit.c') diff --git a/kernel/audit.c b/kernel/audit.c index e5bdba3e3ae1..c6e31d209c41 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.2 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(-) (limited to 'kernel/audit.c') diff --git a/kernel/audit.c b/kernel/audit.c index c6e31d209c41..993e445418a7 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.2 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(-) (limited to 'kernel/audit.c') diff --git a/kernel/audit.c b/kernel/audit.c index 993e445418a7..b86007da8a3f 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.2 From 8c5aa40c94ef8bb7f7da95ecd5942e2d20fc3c9d Mon Sep 17 00:00:00 2001 From: Steve Grubb 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(-) (limited to 'kernel/audit.c') diff --git a/kernel/audit.c b/kernel/audit.c index b86007da8a3f..2ddd1a2b66d0 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.2 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(-) (limited to 'kernel/audit.c') diff --git a/kernel/audit.c b/kernel/audit.c index 2ddd1a2b66d0..1dd456c90ae5 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.2 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(-) (limited to 'kernel/audit.c') diff --git a/kernel/audit.c b/kernel/audit.c index 1dd456c90ae5..ddb69a458203 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.2 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(-) (limited to 'kernel/audit.c') diff --git a/kernel/audit.c b/kernel/audit.c index ddb69a458203..a5f03cb2c0f5 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.2 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 --- kernel/audit.c | 30 ------------------------------ 1 file changed, 30 deletions(-) (limited to 'kernel/audit.c') diff --git a/kernel/audit.c b/kernel/audit.c index a5f03cb2c0f5..dc4aba21f30a 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. */ -- cgit v1.2.2 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 --- kernel/audit.c | 12 ++---------- 1 file changed, 2 insertions(+), 10 deletions(-) (limited to 'kernel/audit.c') diff --git a/kernel/audit.c b/kernel/audit.c index dc4aba21f30a..c18b769e23a2 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; -- cgit v1.2.2 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 --- kernel/audit.c | 48 +++++++++++++++--------------------------------- 1 file changed, 15 insertions(+), 33 deletions(-) (limited to 'kernel/audit.c') diff --git a/kernel/audit.c b/kernel/audit.c index c18b769e23a2..060b554f481e 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); -- cgit v1.2.2 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(-) (limited to 'kernel/audit.c') diff --git a/kernel/audit.c b/kernel/audit.c index 060b554f481e..187164572bd0 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.2 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 --- kernel/audit.c | 78 +++++++++++++++++++++++++++++++++++++++++----------------- 1 file changed, 55 insertions(+), 23 deletions(-) (limited to 'kernel/audit.c') diff --git a/kernel/audit.c b/kernel/audit.c index 187164572bd0..4e940c05ede8 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); -- cgit v1.2.2 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 --- kernel/audit.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'kernel/audit.c') diff --git a/kernel/audit.c b/kernel/audit.c index 4e940c05ede8..74779d3769fa 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); -- cgit v1.2.2 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(-) (limited to 'kernel/audit.c') diff --git a/kernel/audit.c b/kernel/audit.c index 74779d3769fa..a0e33b6897d7 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.2 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 --- kernel/audit.c | 20 ++------------------ 1 file changed, 2 insertions(+), 18 deletions(-) (limited to 'kernel/audit.c') diff --git a/kernel/audit.c b/kernel/audit.c index a0e33b6897d7..e6d88635032c 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 */ -- cgit v1.2.2 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(-) (limited to 'kernel/audit.c') diff --git a/kernel/audit.c b/kernel/audit.c index e6d88635032c..dae3570b3a3b 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.2 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(-) (limited to 'kernel/audit.c') diff --git a/kernel/audit.c b/kernel/audit.c index dae3570b3a3b..bbc6f542c8f7 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.2 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(+) (limited to 'kernel/audit.c') diff --git a/kernel/audit.c b/kernel/audit.c index bbc6f542c8f7..41581413529c 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.2 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(+) (limited to 'kernel/audit.c') diff --git a/kernel/audit.c b/kernel/audit.c index 41581413529c..5e72895f4826 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.2 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 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) (limited to 'kernel/audit.c') diff --git a/kernel/audit.c b/kernel/audit.c index 5e72895f4826..f0a003acf621 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; -- cgit v1.2.2 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 --- kernel/audit.c | 46 ++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 42 insertions(+), 4 deletions(-) (limited to 'kernel/audit.c') diff --git a/kernel/audit.c b/kernel/audit.c index f0a003acf621..35306f4369e7 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); -- cgit v1.2.2 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 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) (limited to 'kernel/audit.c') diff --git a/kernel/audit.c b/kernel/audit.c index 35306f4369e7..ef35166fdc29 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) -- cgit v1.2.2