📄 audit.c
字号:
if (!ab) { ab = kmalloc(sizeof(*ab), gfp_mask); if (!ab) goto err; } ab->skb = alloc_skb(AUDIT_BUFSIZ, gfp_mask); if (!ab->skb) goto err; ab->ctx = ctx; ab->gfp_mask = gfp_mask; 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); return NULL;}/** * audit_serial - compute a serial number for the audit record * * 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. * * 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 DEFINE_SPINLOCK(serial_lock); static unsigned int serial = 0; unsigned long flags; unsigned int ret; spin_lock_irqsave(&serial_lock, flags); do { ret = ++serial; } while (unlikely(!ret)); spin_unlock_irqrestore(&serial_lock, flags); return ret;}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 * 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. *//** * audit_log_start - obtain an audit buffer * @ctx: audit_context (may be NULL) * @gfp_mask: type of allocation * @type: audit message type * * Returns audit_buffer pointer on success or NULL on error. * * 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 task (ctx) is a task that is currently in a * syscall, then the syscall is marked as auditable and an audit record * will be written at syscall exit. If there is no associated task, then * task context (ctx) should be NULL. */struct audit_buffer *audit_log_start(struct audit_context *ctx, gfp_t gfp_mask, int type){ struct audit_buffer *ab = NULL; struct timespec t; unsigned int serial; int reserve; unsigned long timeout_start = jiffies; if (!audit_initialized) return NULL; if (unlikely(audit_filter_type(type))) return NULL; if (gfp_mask & __GFP_WAIT) reserve = 0; else reserve = 5; /* Allow atomic callers to go up to five entries over the normal backlog limit */ while (audit_backlog_limit && skb_queue_len(&audit_skb_queue) > audit_backlog_limit + reserve) { if (gfp_mask & __GFP_WAIT && audit_backlog_wait_time && time_before(jiffies, timeout_start + audit_backlog_wait_time)) { /* Wait for auditd to drain the queue a little */ DECLARE_WAITQUEUE(wait, current); set_current_state(TASK_INTERRUPTIBLE); add_wait_queue(&audit_backlog_wait, &wait); if (audit_backlog_limit && skb_queue_len(&audit_skb_queue) > audit_backlog_limit) schedule_timeout(timeout_start + audit_backlog_wait_time - jiffies); __set_current_state(TASK_RUNNING); remove_wait_queue(&audit_backlog_wait, &wait); continue; } 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"); audit_backlog_wait_time = audit_backlog_wait_overflow; wake_up(&audit_backlog_wait); return NULL; } ab = audit_buffer_alloc(ctx, gfp_mask, type); if (!ab) { audit_log_lost("out of memory in audit_log_start"); return NULL; } audit_get_stamp(ab->ctx, &t, &serial); audit_log_format(ab, "audit(%lu.%03lu:%u): ", t.tv_sec, t.tv_nsec/1000000, serial); return ab;}/** * audit_expand - expand skb in the audit buffer * @ab: audit_buffer * @extra: space to add at tail of the skb * * Returns 0 (no space) on failed expansion, or available space if * successful. */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), extra, ab->gfp_mask); if (ret < 0) { audit_log_lost("out of memory in audit_expand"); return 0; } return skb_tailroom(skb);}/* * 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 * will be called a second time. Currently, we assume that a printk * can't format message larger than 1024 bytes, so we don't either. */static void audit_log_vformat(struct audit_buffer *ab, const char *fmt, va_list args){ int len, avail; struct sk_buff *skb; va_list args2; if (!ab) return; BUG_ON(!ab->skb); skb = ab->skb; avail = skb_tailroom(skb); if (avail == 0) { avail = audit_expand(ab, AUDIT_BUFSIZ); if (!avail) goto out; } va_copy(args2, args); len = vsnprintf(skb_tail_pointer(skb), 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. */ avail = audit_expand(ab, max_t(unsigned, AUDIT_BUFSIZ, 1+len-avail)); if (!avail) goto out; len = vsnprintf(skb_tail_pointer(skb), avail, fmt, args2); } if (len > 0) skb_put(skb, len);out: return;}/** * audit_log_format - format a message into the audit buffer. * @ab: audit_buffer * @fmt: format string * @...: optional parameters matching @fmt string * * All the work is done in audit_log_vformat. */void audit_log_format(struct audit_buffer *ab, const char *fmt, ...){ va_list args; if (!ab) return; va_start(args, fmt); audit_log_vformat(ab, fmt, args); va_end(args);}/** * audit_log_hex - convert a buffer to hex and append it to the audit skb * @ab: the audit_buffer * @buf: buffer to convert to hex * @len: length of @buf to be converted * * No return value; failure to expand is silently ignored. * * 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, avail, new_len; unsigned char *ptr; struct sk_buff *skb; static const unsigned char *hex = "0123456789ABCDEF"; if (!ab) return; 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; } ptr = skb_tail_pointer(skb); for (i=0; i<len; i++) { *ptr++ = hex[(buf[i] & 0xF0)>>4]; /* Upper nibble */ *ptr++ = hex[buf[i] & 0x0F]; /* Lower nibble */ } *ptr = 0; skb_put(skb, len << 1); /* new string is twice the old string */}/* * Format a string of no more than slen characters into the audit buffer, * enclosed in quote marks. */static void audit_log_n_string(struct audit_buffer *ab, size_t slen, const char *string){ int avail, new_len; unsigned char *ptr; struct sk_buff *skb; if (!ab) return; BUG_ON(!ab->skb); skb = ab->skb; avail = skb_tailroom(skb); new_len = slen + 3; /* enclosing quotes + null terminator */ if (new_len > avail) { avail = audit_expand(ab, new_len); if (!avail) return; } ptr = skb_tail_pointer(skb); *ptr++ = '"'; memcpy(ptr, string, slen); ptr += slen; *ptr++ = '"'; *ptr = 0; skb_put(skb, slen + 2); /* don't include null terminator */}/** * audit_log_n_unstrustedstring - log a string that may contain random characters * @ab: audit_buffer * @len: lenth of string (not including trailing null) * @string: string to be logged * * 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). * * The caller specifies the number of characters in the string to log, which may * or may not be the entire string. */const char *audit_log_n_untrustedstring(struct audit_buffer *ab, size_t len, const char *string){ const unsigned char *p = string; while (*p) { if (*p == '"' || *p < 0x21 || *p > 0x7f) { audit_log_hex(ab, string, len); return string + len + 1; } p++; } audit_log_n_string(ab, len, string); return p + 1;}/** * audit_log_unstrustedstring - log a string that may contain random characters * @ab: audit_buffer * @string: string to be logged * * Same as audit_log_n_unstrustedstring(), except that strlen is used to * determine string length. */const char *audit_log_untrustedstring(struct audit_buffer *ab, const char *string){ return audit_log_n_untrustedstring(ab, strlen(string), string);}/* 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, *path; if (prefix) audit_log_format(ab, " %s", prefix); /* We will allow 11 spaces for ' (deleted)' to be appended */ path = kmalloc(PATH_MAX+11, ab->gfp_mask); if (!path) { audit_log_format(ab, "<no memory>"); 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, "<too long>"); } else audit_log_untrustedstring(ab, p); kfree(path);}/** * audit_log_end - end one audit record * @ab: the audit_buffer * * The netlink_* functions cannot be called inside an irq context, so * the audit buffer is placed on a queue and a tasklet is scheduled to * remove them from the queue outside the irq context. May be called in * any context. */void audit_log_end(struct audit_buffer *ab){ if (!ab) return; if (!audit_rate_check()) { audit_log_lost("rate limit exceeded"); } else { if (audit_pid) { struct nlmsghdr *nlh = nlmsg_hdr(ab->skb); 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(KERN_NOTICE "%s\n", ab->skb->data + NLMSG_SPACE(0)); } } audit_buffer_free(ab);}/** * audit_log - Log an audit record * @ctx: audit context * @gfp_mask: type of allocation * @type: audit message type * @fmt: format string to use * @...: variable parameters matching the format string * * 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, gfp_t gfp_mask, int type, const char *fmt, ...){ struct audit_buffer *ab; va_list args; ab = audit_log_start(ctx, gfp_mask, type); if (ab) { va_start(args, fmt); audit_log_vformat(ab, fmt, args); va_end(args); audit_log_end(ab); }}EXPORT_SYMBOL(audit_log_start);EXPORT_SYMBOL(audit_log_end);EXPORT_SYMBOL(audit_log_format);EXPORT_SYMBOL(audit_log);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -