diff options
Diffstat (limited to 'kernel/audit.c')
-rw-r--r-- | kernel/audit.c | 232 |
1 files changed, 165 insertions, 67 deletions
diff --git a/kernel/audit.c b/kernel/audit.c index d9b690ac684b..4e9d20829681 100644 --- a/kernel/audit.c +++ b/kernel/audit.c | |||
@@ -2,7 +2,7 @@ | |||
2 | * Gateway between the kernel (e.g., selinux) and the user-space audit daemon. | 2 | * Gateway between the kernel (e.g., selinux) and the user-space audit daemon. |
3 | * System-call specific features have moved to auditsc.c | 3 | * System-call specific features have moved to auditsc.c |
4 | * | 4 | * |
5 | * Copyright 2003-2004 Red Hat Inc., Durham, North Carolina. | 5 | * Copyright 2003-2007 Red Hat Inc., Durham, North Carolina. |
6 | * All Rights Reserved. | 6 | * All Rights Reserved. |
7 | * | 7 | * |
8 | * This program is free software; you can redistribute it and/or modify | 8 | * This program is free software; you can redistribute it and/or modify |
@@ -65,7 +65,9 @@ | |||
65 | * (Initialization happens after skb_init is called.) */ | 65 | * (Initialization happens after skb_init is called.) */ |
66 | static int audit_initialized; | 66 | static int audit_initialized; |
67 | 67 | ||
68 | /* No syscall auditing will take place unless audit_enabled != 0. */ | 68 | /* 0 - no auditing |
69 | * 1 - auditing enabled | ||
70 | * 2 - auditing enabled and configuration is locked/unchangeable. */ | ||
69 | int audit_enabled; | 71 | int audit_enabled; |
70 | 72 | ||
71 | /* Default state when kernel boots without any parameters. */ | 73 | /* Default state when kernel boots without any parameters. */ |
@@ -149,7 +151,7 @@ struct audit_buffer { | |||
149 | 151 | ||
150 | static void audit_set_pid(struct audit_buffer *ab, pid_t pid) | 152 | static void audit_set_pid(struct audit_buffer *ab, pid_t pid) |
151 | { | 153 | { |
152 | struct nlmsghdr *nlh = (struct nlmsghdr *)ab->skb->data; | 154 | struct nlmsghdr *nlh = nlmsg_hdr(ab->skb); |
153 | nlh->nlmsg_pid = pid; | 155 | nlh->nlmsg_pid = pid; |
154 | } | 156 | } |
155 | 157 | ||
@@ -239,102 +241,150 @@ void audit_log_lost(const char *message) | |||
239 | 241 | ||
240 | static int audit_set_rate_limit(int limit, uid_t loginuid, u32 sid) | 242 | static int audit_set_rate_limit(int limit, uid_t loginuid, u32 sid) |
241 | { | 243 | { |
242 | int old = audit_rate_limit; | 244 | int res, rc = 0, old = audit_rate_limit; |
245 | |||
246 | /* check if we are locked */ | ||
247 | if (audit_enabled == 2) | ||
248 | res = 0; | ||
249 | else | ||
250 | res = 1; | ||
243 | 251 | ||
244 | if (sid) { | 252 | if (sid) { |
245 | char *ctx = NULL; | 253 | char *ctx = NULL; |
246 | u32 len; | 254 | u32 len; |
247 | int rc; | 255 | if ((rc = selinux_sid_to_string(sid, &ctx, &len)) == 0) { |
248 | if ((rc = selinux_sid_to_string(sid, &ctx, &len))) | ||
249 | return rc; | ||
250 | else | ||
251 | audit_log(NULL, GFP_KERNEL, AUDIT_CONFIG_CHANGE, | 256 | audit_log(NULL, GFP_KERNEL, AUDIT_CONFIG_CHANGE, |
252 | "audit_rate_limit=%d old=%d by auid=%u subj=%s", | 257 | "audit_rate_limit=%d old=%d by auid=%u" |
253 | limit, old, loginuid, ctx); | 258 | " subj=%s res=%d", |
254 | kfree(ctx); | 259 | limit, old, loginuid, ctx, res); |
255 | } else | 260 | kfree(ctx); |
256 | audit_log(NULL, GFP_KERNEL, AUDIT_CONFIG_CHANGE, | 261 | } else |
257 | "audit_rate_limit=%d old=%d by auid=%u", | 262 | res = 0; /* Something weird, deny request */ |
258 | limit, old, loginuid); | 263 | } |
259 | audit_rate_limit = limit; | 264 | audit_log(NULL, GFP_KERNEL, AUDIT_CONFIG_CHANGE, |
260 | return 0; | 265 | "audit_rate_limit=%d old=%d by auid=%u res=%d", |
266 | limit, old, loginuid, res); | ||
267 | |||
268 | /* If we are allowed, make the change */ | ||
269 | if (res == 1) | ||
270 | audit_rate_limit = limit; | ||
271 | /* Not allowed, update reason */ | ||
272 | else if (rc == 0) | ||
273 | rc = -EPERM; | ||
274 | return rc; | ||
261 | } | 275 | } |
262 | 276 | ||
263 | static int audit_set_backlog_limit(int limit, uid_t loginuid, u32 sid) | 277 | static int audit_set_backlog_limit(int limit, uid_t loginuid, u32 sid) |
264 | { | 278 | { |
265 | int old = audit_backlog_limit; | 279 | int res, rc = 0, old = audit_backlog_limit; |
280 | |||
281 | /* check if we are locked */ | ||
282 | if (audit_enabled == 2) | ||
283 | res = 0; | ||
284 | else | ||
285 | res = 1; | ||
266 | 286 | ||
267 | if (sid) { | 287 | if (sid) { |
268 | char *ctx = NULL; | 288 | char *ctx = NULL; |
269 | u32 len; | 289 | u32 len; |
270 | int rc; | 290 | if ((rc = selinux_sid_to_string(sid, &ctx, &len)) == 0) { |
271 | if ((rc = selinux_sid_to_string(sid, &ctx, &len))) | ||
272 | return rc; | ||
273 | else | ||
274 | audit_log(NULL, GFP_KERNEL, AUDIT_CONFIG_CHANGE, | 291 | audit_log(NULL, GFP_KERNEL, AUDIT_CONFIG_CHANGE, |
275 | "audit_backlog_limit=%d old=%d by auid=%u subj=%s", | 292 | "audit_backlog_limit=%d old=%d by auid=%u" |
276 | limit, old, loginuid, ctx); | 293 | " subj=%s res=%d", |
277 | kfree(ctx); | 294 | limit, old, loginuid, ctx, res); |
278 | } else | 295 | kfree(ctx); |
279 | audit_log(NULL, GFP_KERNEL, AUDIT_CONFIG_CHANGE, | 296 | } else |
280 | "audit_backlog_limit=%d old=%d by auid=%u", | 297 | res = 0; /* Something weird, deny request */ |
281 | limit, old, loginuid); | 298 | } |
282 | audit_backlog_limit = limit; | 299 | audit_log(NULL, GFP_KERNEL, AUDIT_CONFIG_CHANGE, |
283 | return 0; | 300 | "audit_backlog_limit=%d old=%d by auid=%u res=%d", |
301 | limit, old, loginuid, res); | ||
302 | |||
303 | /* If we are allowed, make the change */ | ||
304 | if (res == 1) | ||
305 | audit_backlog_limit = limit; | ||
306 | /* Not allowed, update reason */ | ||
307 | else if (rc == 0) | ||
308 | rc = -EPERM; | ||
309 | return rc; | ||
284 | } | 310 | } |
285 | 311 | ||
286 | static int audit_set_enabled(int state, uid_t loginuid, u32 sid) | 312 | static int audit_set_enabled(int state, uid_t loginuid, u32 sid) |
287 | { | 313 | { |
288 | int old = audit_enabled; | 314 | int res, rc = 0, old = audit_enabled; |
289 | 315 | ||
290 | if (state != 0 && state != 1) | 316 | if (state < 0 || state > 2) |
291 | return -EINVAL; | 317 | return -EINVAL; |
292 | 318 | ||
319 | /* check if we are locked */ | ||
320 | if (audit_enabled == 2) | ||
321 | res = 0; | ||
322 | else | ||
323 | res = 1; | ||
324 | |||
293 | if (sid) { | 325 | if (sid) { |
294 | char *ctx = NULL; | 326 | char *ctx = NULL; |
295 | u32 len; | 327 | u32 len; |
296 | int rc; | 328 | if ((rc = selinux_sid_to_string(sid, &ctx, &len)) == 0) { |
297 | if ((rc = selinux_sid_to_string(sid, &ctx, &len))) | ||
298 | return rc; | ||
299 | else | ||
300 | audit_log(NULL, GFP_KERNEL, AUDIT_CONFIG_CHANGE, | 329 | audit_log(NULL, GFP_KERNEL, AUDIT_CONFIG_CHANGE, |
301 | "audit_enabled=%d old=%d by auid=%u subj=%s", | 330 | "audit_enabled=%d old=%d by auid=%u" |
302 | state, old, loginuid, ctx); | 331 | " subj=%s res=%d", |
303 | kfree(ctx); | 332 | state, old, loginuid, ctx, res); |
304 | } else | 333 | kfree(ctx); |
305 | audit_log(NULL, GFP_KERNEL, AUDIT_CONFIG_CHANGE, | 334 | } else |
306 | "audit_enabled=%d old=%d by auid=%u", | 335 | res = 0; /* Something weird, deny request */ |
307 | state, old, loginuid); | 336 | } |
308 | audit_enabled = state; | 337 | audit_log(NULL, GFP_KERNEL, AUDIT_CONFIG_CHANGE, |
309 | return 0; | 338 | "audit_enabled=%d old=%d by auid=%u res=%d", |
339 | state, old, loginuid, res); | ||
340 | |||
341 | /* If we are allowed, make the change */ | ||
342 | if (res == 1) | ||
343 | audit_enabled = state; | ||
344 | /* Not allowed, update reason */ | ||
345 | else if (rc == 0) | ||
346 | rc = -EPERM; | ||
347 | return rc; | ||
310 | } | 348 | } |
311 | 349 | ||
312 | static int audit_set_failure(int state, uid_t loginuid, u32 sid) | 350 | static int audit_set_failure(int state, uid_t loginuid, u32 sid) |
313 | { | 351 | { |
314 | int old = audit_failure; | 352 | int res, rc = 0, old = audit_failure; |
315 | 353 | ||
316 | if (state != AUDIT_FAIL_SILENT | 354 | if (state != AUDIT_FAIL_SILENT |
317 | && state != AUDIT_FAIL_PRINTK | 355 | && state != AUDIT_FAIL_PRINTK |
318 | && state != AUDIT_FAIL_PANIC) | 356 | && state != AUDIT_FAIL_PANIC) |
319 | return -EINVAL; | 357 | return -EINVAL; |
320 | 358 | ||
359 | /* check if we are locked */ | ||
360 | if (audit_enabled == 2) | ||
361 | res = 0; | ||
362 | else | ||
363 | res = 1; | ||
364 | |||
321 | if (sid) { | 365 | if (sid) { |
322 | char *ctx = NULL; | 366 | char *ctx = NULL; |
323 | u32 len; | 367 | u32 len; |
324 | int rc; | 368 | if ((rc = selinux_sid_to_string(sid, &ctx, &len)) == 0) { |
325 | if ((rc = selinux_sid_to_string(sid, &ctx, &len))) | ||
326 | return rc; | ||
327 | else | ||
328 | audit_log(NULL, GFP_KERNEL, AUDIT_CONFIG_CHANGE, | 369 | audit_log(NULL, GFP_KERNEL, AUDIT_CONFIG_CHANGE, |
329 | "audit_failure=%d old=%d by auid=%u subj=%s", | 370 | "audit_failure=%d old=%d by auid=%u" |
330 | state, old, loginuid, ctx); | 371 | " subj=%s res=%d", |
331 | kfree(ctx); | 372 | state, old, loginuid, ctx, res); |
332 | } else | 373 | kfree(ctx); |
333 | audit_log(NULL, GFP_KERNEL, AUDIT_CONFIG_CHANGE, | 374 | } else |
334 | "audit_failure=%d old=%d by auid=%u", | 375 | res = 0; /* Something weird, deny request */ |
335 | state, old, loginuid); | 376 | } |
336 | audit_failure = state; | 377 | audit_log(NULL, GFP_KERNEL, AUDIT_CONFIG_CHANGE, |
337 | return 0; | 378 | "audit_failure=%d old=%d by auid=%u res=%d", |
379 | state, old, loginuid, res); | ||
380 | |||
381 | /* If we are allowed, make the change */ | ||
382 | if (res == 1) | ||
383 | audit_failure = state; | ||
384 | /* Not allowed, update reason */ | ||
385 | else if (rc == 0) | ||
386 | rc = -EPERM; | ||
387 | return rc; | ||
338 | } | 388 | } |
339 | 389 | ||
340 | static int kauditd_thread(void *dummy) | 390 | static int kauditd_thread(void *dummy) |
@@ -599,6 +649,30 @@ static int audit_receive_msg(struct sk_buff *skb, struct nlmsghdr *nlh) | |||
599 | case AUDIT_DEL: | 649 | case AUDIT_DEL: |
600 | if (nlmsg_len(nlh) < sizeof(struct audit_rule)) | 650 | if (nlmsg_len(nlh) < sizeof(struct audit_rule)) |
601 | return -EINVAL; | 651 | return -EINVAL; |
652 | if (audit_enabled == 2) { | ||
653 | ab = audit_log_start(NULL, GFP_KERNEL, | ||
654 | AUDIT_CONFIG_CHANGE); | ||
655 | if (ab) { | ||
656 | audit_log_format(ab, | ||
657 | "pid=%d uid=%u auid=%u", | ||
658 | pid, uid, loginuid); | ||
659 | if (sid) { | ||
660 | if (selinux_sid_to_string( | ||
661 | sid, &ctx, &len)) { | ||
662 | audit_log_format(ab, | ||
663 | " ssid=%u", sid); | ||
664 | /* Maybe call audit_panic? */ | ||
665 | } else | ||
666 | audit_log_format(ab, | ||
667 | " subj=%s", ctx); | ||
668 | kfree(ctx); | ||
669 | } | ||
670 | audit_log_format(ab, " audit_enabled=%d res=0", | ||
671 | audit_enabled); | ||
672 | audit_log_end(ab); | ||
673 | } | ||
674 | return -EPERM; | ||
675 | } | ||
602 | /* fallthrough */ | 676 | /* fallthrough */ |
603 | case AUDIT_LIST: | 677 | case AUDIT_LIST: |
604 | err = audit_receive_filter(nlh->nlmsg_type, NETLINK_CB(skb).pid, | 678 | err = audit_receive_filter(nlh->nlmsg_type, NETLINK_CB(skb).pid, |
@@ -609,6 +683,30 @@ static int audit_receive_msg(struct sk_buff *skb, struct nlmsghdr *nlh) | |||
609 | case AUDIT_DEL_RULE: | 683 | case AUDIT_DEL_RULE: |
610 | if (nlmsg_len(nlh) < sizeof(struct audit_rule_data)) | 684 | if (nlmsg_len(nlh) < sizeof(struct audit_rule_data)) |
611 | return -EINVAL; | 685 | return -EINVAL; |
686 | if (audit_enabled == 2) { | ||
687 | ab = audit_log_start(NULL, GFP_KERNEL, | ||
688 | AUDIT_CONFIG_CHANGE); | ||
689 | if (ab) { | ||
690 | audit_log_format(ab, | ||
691 | "pid=%d uid=%u auid=%u", | ||
692 | pid, uid, loginuid); | ||
693 | if (sid) { | ||
694 | if (selinux_sid_to_string( | ||
695 | sid, &ctx, &len)) { | ||
696 | audit_log_format(ab, | ||
697 | " ssid=%u", sid); | ||
698 | /* Maybe call audit_panic? */ | ||
699 | } else | ||
700 | audit_log_format(ab, | ||
701 | " subj=%s", ctx); | ||
702 | kfree(ctx); | ||
703 | } | ||
704 | audit_log_format(ab, " audit_enabled=%d res=0", | ||
705 | audit_enabled); | ||
706 | audit_log_end(ab); | ||
707 | } | ||
708 | return -EPERM; | ||
709 | } | ||
612 | /* fallthrough */ | 710 | /* fallthrough */ |
613 | case AUDIT_LIST_RULES: | 711 | case AUDIT_LIST_RULES: |
614 | err = audit_receive_filter(nlh->nlmsg_type, NETLINK_CB(skb).pid, | 712 | err = audit_receive_filter(nlh->nlmsg_type, NETLINK_CB(skb).pid, |
@@ -652,7 +750,7 @@ static void audit_receive_skb(struct sk_buff *skb) | |||
652 | u32 rlen; | 750 | u32 rlen; |
653 | 751 | ||
654 | while (skb->len >= NLMSG_SPACE(0)) { | 752 | while (skb->len >= NLMSG_SPACE(0)) { |
655 | nlh = (struct nlmsghdr *)skb->data; | 753 | nlh = nlmsg_hdr(skb); |
656 | if (nlh->nlmsg_len < sizeof(*nlh) || skb->len < nlh->nlmsg_len) | 754 | if (nlh->nlmsg_len < sizeof(*nlh) || skb->len < nlh->nlmsg_len) |
657 | return; | 755 | return; |
658 | rlen = NLMSG_ALIGN(nlh->nlmsg_len); | 756 | rlen = NLMSG_ALIGN(nlh->nlmsg_len); |
@@ -697,7 +795,7 @@ static int __init audit_init(void) | |||
697 | printk(KERN_INFO "audit: initializing netlink socket (%s)\n", | 795 | printk(KERN_INFO "audit: initializing netlink socket (%s)\n", |
698 | audit_default ? "enabled" : "disabled"); | 796 | audit_default ? "enabled" : "disabled"); |
699 | audit_sock = netlink_kernel_create(NETLINK_AUDIT, 0, audit_receive, | 797 | audit_sock = netlink_kernel_create(NETLINK_AUDIT, 0, audit_receive, |
700 | THIS_MODULE); | 798 | NULL, THIS_MODULE); |
701 | if (!audit_sock) | 799 | if (!audit_sock) |
702 | audit_panic("cannot initialize netlink socket"); | 800 | audit_panic("cannot initialize netlink socket"); |
703 | else | 801 | else |
@@ -975,7 +1073,7 @@ static void audit_log_vformat(struct audit_buffer *ab, const char *fmt, | |||
975 | goto out; | 1073 | goto out; |
976 | } | 1074 | } |
977 | va_copy(args2, args); | 1075 | va_copy(args2, args); |
978 | len = vsnprintf(skb->tail, avail, fmt, args); | 1076 | len = vsnprintf(skb_tail_pointer(skb), avail, fmt, args); |
979 | if (len >= avail) { | 1077 | if (len >= avail) { |
980 | /* The printk buffer is 1024 bytes long, so if we get | 1078 | /* The printk buffer is 1024 bytes long, so if we get |
981 | * here and AUDIT_BUFSIZ is at least 1024, then we can | 1079 | * here and AUDIT_BUFSIZ is at least 1024, then we can |
@@ -984,7 +1082,7 @@ static void audit_log_vformat(struct audit_buffer *ab, const char *fmt, | |||
984 | max_t(unsigned, AUDIT_BUFSIZ, 1+len-avail)); | 1082 | max_t(unsigned, AUDIT_BUFSIZ, 1+len-avail)); |
985 | if (!avail) | 1083 | if (!avail) |
986 | goto out; | 1084 | goto out; |
987 | len = vsnprintf(skb->tail, avail, fmt, args2); | 1085 | len = vsnprintf(skb_tail_pointer(skb), avail, fmt, args2); |
988 | } | 1086 | } |
989 | if (len > 0) | 1087 | if (len > 0) |
990 | skb_put(skb, len); | 1088 | skb_put(skb, len); |
@@ -1045,7 +1143,7 @@ void audit_log_hex(struct audit_buffer *ab, const unsigned char *buf, | |||
1045 | return; | 1143 | return; |
1046 | } | 1144 | } |
1047 | 1145 | ||
1048 | ptr = skb->tail; | 1146 | ptr = skb_tail_pointer(skb); |
1049 | for (i=0; i<len; i++) { | 1147 | for (i=0; i<len; i++) { |
1050 | *ptr++ = hex[(buf[i] & 0xF0)>>4]; /* Upper nibble */ | 1148 | *ptr++ = hex[(buf[i] & 0xF0)>>4]; /* Upper nibble */ |
1051 | *ptr++ = hex[buf[i] & 0x0F]; /* Lower nibble */ | 1149 | *ptr++ = hex[buf[i] & 0x0F]; /* Lower nibble */ |
@@ -1077,7 +1175,7 @@ static void audit_log_n_string(struct audit_buffer *ab, size_t slen, | |||
1077 | if (!avail) | 1175 | if (!avail) |
1078 | return; | 1176 | return; |
1079 | } | 1177 | } |
1080 | ptr = skb->tail; | 1178 | ptr = skb_tail_pointer(skb); |
1081 | *ptr++ = '"'; | 1179 | *ptr++ = '"'; |
1082 | memcpy(ptr, string, slen); | 1180 | memcpy(ptr, string, slen); |
1083 | ptr += slen; | 1181 | ptr += slen; |
@@ -1170,7 +1268,7 @@ void audit_log_end(struct audit_buffer *ab) | |||
1170 | audit_log_lost("rate limit exceeded"); | 1268 | audit_log_lost("rate limit exceeded"); |
1171 | } else { | 1269 | } else { |
1172 | if (audit_pid) { | 1270 | if (audit_pid) { |
1173 | struct nlmsghdr *nlh = (struct nlmsghdr *)ab->skb->data; | 1271 | struct nlmsghdr *nlh = nlmsg_hdr(ab->skb); |
1174 | nlh->nlmsg_len = ab->skb->len - NLMSG_SPACE(0); | 1272 | nlh->nlmsg_len = ab->skb->len - NLMSG_SPACE(0); |
1175 | skb_queue_tail(&audit_skb_queue, ab->skb); | 1273 | skb_queue_tail(&audit_skb_queue, ab->skb); |
1176 | ab->skb = NULL; | 1274 | ab->skb = NULL; |