diff options
author | Peter Zijlstra <a.p.zijlstra@chello.nl> | 2007-07-19 04:48:15 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@woody.linux-foundation.org> | 2007-07-19 13:04:45 -0400 |
commit | bdf4c48af20a3b0f01671799ace345e3d49576da (patch) | |
tree | 7c3b903d2de1cba6e212ad6f347bc8742b08035a | |
parent | b111757c50ee30dad162192df6168e270a90c252 (diff) |
audit: rework execve audit
The purpose of audit_bprm() is to log the argv array to a userspace daemon at
the end of the execve system call. Since user-space hasn't had time to run,
this array is still in pristine state on the process' stack; so no need to
copy it, we can just grab it from there.
In order to minimize the damage to audit_log_*() copy each string into a
temporary kernel buffer first.
Currently the audit code requires that the full argument vector fits in a
single packet. So currently it does clip the argv size to a (sysctl) limit,
but only when execve auditing is enabled.
If the audit protocol gets extended to allow for multiple packets this check
can be removed.
Signed-off-by: Peter Zijlstra <a.p.zijlstra@chello.nl>
Signed-off-by: Ollie Wild <aaw@google.com>
Cc: <linux-audit@redhat.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
-rw-r--r-- | Documentation/filesystems/proc.txt | 7 | ||||
-rw-r--r-- | fs/exec.c | 3 | ||||
-rw-r--r-- | include/linux/binfmts.h | 1 | ||||
-rw-r--r-- | kernel/auditsc.c | 84 | ||||
-rw-r--r-- | kernel/sysctl.c | 11 |
5 files changed, 85 insertions, 21 deletions
diff --git a/Documentation/filesystems/proc.txt b/Documentation/filesystems/proc.txt index ebffdffb3d99..72e247ef6fa2 100644 --- a/Documentation/filesystems/proc.txt +++ b/Documentation/filesystems/proc.txt | |||
@@ -1065,6 +1065,13 @@ check the amount of free space (value is in seconds). Default settings are: 4, | |||
1065 | resume it if we have a value of 3 or more percent; consider information about | 1065 | resume it if we have a value of 3 or more percent; consider information about |
1066 | the amount of free space valid for 30 seconds | 1066 | the amount of free space valid for 30 seconds |
1067 | 1067 | ||
1068 | audit_argv_kb | ||
1069 | ------------- | ||
1070 | |||
1071 | The file contains a single value denoting the limit on the argv array size | ||
1072 | for execve (in KiB). This limit is only applied when system call auditing for | ||
1073 | execve is enabled, otherwise the value is ignored. | ||
1074 | |||
1068 | ctrl-alt-del | 1075 | ctrl-alt-del |
1069 | ------------ | 1076 | ------------ |
1070 | 1077 | ||
@@ -1154,6 +1154,7 @@ int do_execve(char * filename, | |||
1154 | { | 1154 | { |
1155 | struct linux_binprm *bprm; | 1155 | struct linux_binprm *bprm; |
1156 | struct file *file; | 1156 | struct file *file; |
1157 | unsigned long env_p; | ||
1157 | int retval; | 1158 | int retval; |
1158 | int i; | 1159 | int i; |
1159 | 1160 | ||
@@ -1208,9 +1209,11 @@ int do_execve(char * filename, | |||
1208 | if (retval < 0) | 1209 | if (retval < 0) |
1209 | goto out; | 1210 | goto out; |
1210 | 1211 | ||
1212 | env_p = bprm->p; | ||
1211 | retval = copy_strings(bprm->argc, argv, bprm); | 1213 | retval = copy_strings(bprm->argc, argv, bprm); |
1212 | if (retval < 0) | 1214 | if (retval < 0) |
1213 | goto out; | 1215 | goto out; |
1216 | bprm->argv_len = env_p - bprm->p; | ||
1214 | 1217 | ||
1215 | retval = search_binary_handler(bprm,regs); | 1218 | retval = search_binary_handler(bprm,regs); |
1216 | if (retval >= 0) { | 1219 | if (retval >= 0) { |
diff --git a/include/linux/binfmts.h b/include/linux/binfmts.h index e1a708337be3..a0b209cd5761 100644 --- a/include/linux/binfmts.h +++ b/include/linux/binfmts.h | |||
@@ -40,6 +40,7 @@ struct linux_binprm{ | |||
40 | unsigned interp_flags; | 40 | unsigned interp_flags; |
41 | unsigned interp_data; | 41 | unsigned interp_data; |
42 | unsigned long loader, exec; | 42 | unsigned long loader, exec; |
43 | unsigned long argv_len; | ||
43 | }; | 44 | }; |
44 | 45 | ||
45 | #define BINPRM_FLAGS_ENFORCE_NONDUMP_BIT 0 | 46 | #define BINPRM_FLAGS_ENFORCE_NONDUMP_BIT 0 |
diff --git a/kernel/auditsc.c b/kernel/auditsc.c index b7640a5f382a..535586fc498b 100644 --- a/kernel/auditsc.c +++ b/kernel/auditsc.c | |||
@@ -153,7 +153,7 @@ struct audit_aux_data_execve { | |||
153 | struct audit_aux_data d; | 153 | struct audit_aux_data d; |
154 | int argc; | 154 | int argc; |
155 | int envc; | 155 | int envc; |
156 | char mem[0]; | 156 | struct mm_struct *mm; |
157 | }; | 157 | }; |
158 | 158 | ||
159 | struct audit_aux_data_socketcall { | 159 | struct audit_aux_data_socketcall { |
@@ -831,6 +831,55 @@ static int audit_log_pid_context(struct audit_context *context, pid_t pid, | |||
831 | return rc; | 831 | return rc; |
832 | } | 832 | } |
833 | 833 | ||
834 | static void audit_log_execve_info(struct audit_buffer *ab, | ||
835 | struct audit_aux_data_execve *axi) | ||
836 | { | ||
837 | int i; | ||
838 | long len, ret; | ||
839 | const char __user *p = (const char __user *)axi->mm->arg_start; | ||
840 | char *buf; | ||
841 | |||
842 | if (axi->mm != current->mm) | ||
843 | return; /* execve failed, no additional info */ | ||
844 | |||
845 | for (i = 0; i < axi->argc; i++, p += len) { | ||
846 | len = strnlen_user(p, MAX_ARG_PAGES*PAGE_SIZE); | ||
847 | /* | ||
848 | * We just created this mm, if we can't find the strings | ||
849 | * we just copied into it something is _very_ wrong. Similar | ||
850 | * for strings that are too long, we should not have created | ||
851 | * any. | ||
852 | */ | ||
853 | if (!len || len > MAX_ARG_STRLEN) { | ||
854 | WARN_ON(1); | ||
855 | send_sig(SIGKILL, current, 0); | ||
856 | } | ||
857 | |||
858 | buf = kmalloc(len, GFP_KERNEL); | ||
859 | if (!buf) { | ||
860 | audit_panic("out of memory for argv string\n"); | ||
861 | break; | ||
862 | } | ||
863 | |||
864 | ret = copy_from_user(buf, p, len); | ||
865 | /* | ||
866 | * There is no reason for this copy to be short. We just | ||
867 | * copied them here, and the mm hasn't been exposed to user- | ||
868 | * space yet. | ||
869 | */ | ||
870 | if (!ret) { | ||
871 | WARN_ON(1); | ||
872 | send_sig(SIGKILL, current, 0); | ||
873 | } | ||
874 | |||
875 | audit_log_format(ab, "a%d=", i); | ||
876 | audit_log_untrustedstring(ab, buf); | ||
877 | audit_log_format(ab, "\n"); | ||
878 | |||
879 | kfree(buf); | ||
880 | } | ||
881 | } | ||
882 | |||
834 | static void audit_log_exit(struct audit_context *context, struct task_struct *tsk) | 883 | static void audit_log_exit(struct audit_context *context, struct task_struct *tsk) |
835 | { | 884 | { |
836 | int i, call_panic = 0; | 885 | int i, call_panic = 0; |
@@ -971,13 +1020,7 @@ static void audit_log_exit(struct audit_context *context, struct task_struct *ts | |||
971 | 1020 | ||
972 | case AUDIT_EXECVE: { | 1021 | case AUDIT_EXECVE: { |
973 | struct audit_aux_data_execve *axi = (void *)aux; | 1022 | struct audit_aux_data_execve *axi = (void *)aux; |
974 | int i; | 1023 | audit_log_execve_info(ab, axi); |
975 | const char *p; | ||
976 | for (i = 0, p = axi->mem; i < axi->argc; i++) { | ||
977 | audit_log_format(ab, "a%d=", i); | ||
978 | p = audit_log_untrustedstring(ab, p); | ||
979 | audit_log_format(ab, "\n"); | ||
980 | } | ||
981 | break; } | 1024 | break; } |
982 | 1025 | ||
983 | case AUDIT_SOCKETCALL: { | 1026 | case AUDIT_SOCKETCALL: { |
@@ -1821,32 +1864,31 @@ int __audit_ipc_set_perm(unsigned long qbytes, uid_t uid, gid_t gid, mode_t mode | |||
1821 | return 0; | 1864 | return 0; |
1822 | } | 1865 | } |
1823 | 1866 | ||
1867 | int audit_argv_kb = 32; | ||
1868 | |||
1824 | int audit_bprm(struct linux_binprm *bprm) | 1869 | int audit_bprm(struct linux_binprm *bprm) |
1825 | { | 1870 | { |
1826 | struct audit_aux_data_execve *ax; | 1871 | struct audit_aux_data_execve *ax; |
1827 | struct audit_context *context = current->audit_context; | 1872 | struct audit_context *context = current->audit_context; |
1828 | unsigned long p, next; | ||
1829 | void *to; | ||
1830 | 1873 | ||
1831 | if (likely(!audit_enabled || !context || context->dummy)) | 1874 | if (likely(!audit_enabled || !context || context->dummy)) |
1832 | return 0; | 1875 | return 0; |
1833 | 1876 | ||
1834 | ax = kmalloc(sizeof(*ax) + PAGE_SIZE * MAX_ARG_PAGES - bprm->p, | 1877 | /* |
1835 | GFP_KERNEL); | 1878 | * Even though the stack code doesn't limit the arg+env size any more, |
1879 | * the audit code requires that _all_ arguments be logged in a single | ||
1880 | * netlink skb. Hence cap it :-( | ||
1881 | */ | ||
1882 | if (bprm->argv_len > (audit_argv_kb << 10)) | ||
1883 | return -E2BIG; | ||
1884 | |||
1885 | ax = kmalloc(sizeof(*ax), GFP_KERNEL); | ||
1836 | if (!ax) | 1886 | if (!ax) |
1837 | return -ENOMEM; | 1887 | return -ENOMEM; |
1838 | 1888 | ||
1839 | ax->argc = bprm->argc; | 1889 | ax->argc = bprm->argc; |
1840 | ax->envc = bprm->envc; | 1890 | ax->envc = bprm->envc; |
1841 | for (p = bprm->p, to = ax->mem; p < MAX_ARG_PAGES*PAGE_SIZE; p = next) { | 1891 | ax->mm = bprm->mm; |
1842 | struct page *page = bprm->page[p / PAGE_SIZE]; | ||
1843 | void *kaddr = kmap(page); | ||
1844 | next = (p + PAGE_SIZE) & ~(PAGE_SIZE - 1); | ||
1845 | memcpy(to, kaddr + (p & (PAGE_SIZE - 1)), next - p); | ||
1846 | to += next - p; | ||
1847 | kunmap(page); | ||
1848 | } | ||
1849 | |||
1850 | ax->d.type = AUDIT_EXECVE; | 1892 | ax->d.type = AUDIT_EXECVE; |
1851 | ax->d.next = context->aux; | 1893 | ax->d.next = context->aux; |
1852 | context->aux = (void *)ax; | 1894 | context->aux = (void *)ax; |
diff --git a/kernel/sysctl.c b/kernel/sysctl.c index 3ed4912bf183..8db41764e2a1 100644 --- a/kernel/sysctl.c +++ b/kernel/sysctl.c | |||
@@ -78,6 +78,7 @@ extern int percpu_pagelist_fraction; | |||
78 | extern int compat_log; | 78 | extern int compat_log; |
79 | extern int maps_protect; | 79 | extern int maps_protect; |
80 | extern int sysctl_stat_interval; | 80 | extern int sysctl_stat_interval; |
81 | extern int audit_argv_kb; | ||
81 | 82 | ||
82 | /* this is needed for the proc_dointvec_minmax for [fs_]overflow UID and GID */ | 83 | /* this is needed for the proc_dointvec_minmax for [fs_]overflow UID and GID */ |
83 | static int maxolduid = 65535; | 84 | static int maxolduid = 65535; |
@@ -306,6 +307,16 @@ static ctl_table kern_table[] = { | |||
306 | .mode = 0644, | 307 | .mode = 0644, |
307 | .proc_handler = &proc_dointvec, | 308 | .proc_handler = &proc_dointvec, |
308 | }, | 309 | }, |
310 | #ifdef CONFIG_AUDITSYSCALL | ||
311 | { | ||
312 | .ctl_name = CTL_UNNUMBERED, | ||
313 | .procname = "audit_argv_kb", | ||
314 | .data = &audit_argv_kb, | ||
315 | .maxlen = sizeof(int), | ||
316 | .mode = 0644, | ||
317 | .proc_handler = &proc_dointvec, | ||
318 | }, | ||
319 | #endif | ||
309 | { | 320 | { |
310 | .ctl_name = KERN_CORE_PATTERN, | 321 | .ctl_name = KERN_CORE_PATTERN, |
311 | .procname = "core_pattern", | 322 | .procname = "core_pattern", |