diff -urN linux-2.0.31pre9-privs-clean/Documentation/Configure.help linux-2.0.31pre9-privs/Documentation/Configure.help --- linux-2.0.31pre9-privs-clean/Documentation/Configure.help Wed Sep 10 15:05:33 1997 +++ linux-2.0.31pre9-privs/Documentation/Configure.help Sun Sep 21 09:43:16 1997 @@ -830,6 +830,32 @@ than all being tied to UID zero. This completely changes the way a Unix system works; say N unless you really know what you are doing. +POSIX.6 auditing +CONFIG_POSIX6_AUDIT + This option turns on kernel level auditing. Every time a user process + makes a system call that performs some function that may impact on the + security of the system, the kernel may choose to log information about + the event. The kernel maintains an internal circular buffer for such + data which must be drained periodically or the kernel will grind to a + halt. The internal buffer is not very large so unless you are sure + that something is going to drain it, you should say N. + +Auditing optimized for speed not verifiability +CONFIG_POSIX6_AUDIT_MAXSPEED + This option makes the kernel check if an event is likely to be audited + before constructing an audit record for it. You should only disable + this option if you want to centralize control over which events get + logged. (Mainly for the purposes of verifiability). It is safe to + say Y here. + +Permit unprivileged user to flood audit trail +CONFIG_AUDIT_PERMIT_LUSER_FLOODING + This option opens the possibility for a(n unprivileged) process to + flood the audit buffer with failed write requests. It is intended + for sites where an audit monitor might like to have full investigative + control over such a rogue process. Unless you _really_ know what you + are doing it is strongly recommended that you say N here. + Default security level DEFAULT_SECURE_LEVEL This is a potentially dangerous number to change. The default is @@ -837,7 +863,8 @@ is actually a union of independent flag values (see ) that affect the way in which the system gives access to priviledged users. Unless you know what you are doing, it is probably safest to - leave this number alone -- a bad value could make a kernel unusable. + leave this number alone -- a different value could make this kernel + unusable! ARP daemon support (EXPERIMENTAL) CONFIG_ARPD diff -urN linux-2.0.31pre9-privs-clean/Documentation/POSIX.6_audit.txt linux-2.0.31pre9-privs/Documentation/POSIX.6_audit.txt --- linux-2.0.31pre9-privs-clean/Documentation/POSIX.6_audit.txt Wed Dec 31 16:00:00 1969 +++ linux-2.0.31pre9-privs/Documentation/POSIX.6_audit.txt Mon Sep 22 11:08:25 1997 @@ -0,0 +1,147 @@ +POSIX.1e (a.k.a. 6) Auditing System +=================================== + + +================================ WARNING =================================== + THIS IS CODE IN DEVELOPMENT AND IS VERY UNSTABLE AND NOT WELL TESTED. + +If you intend using this code or want to help out or just want to say +something about it then please join . + +Please don't send messages to Linux-Kernel. + +Announcements to Linux-Kernel when the maintainers of linux-privs deem +it appropriate, please keep all other discussion off that list. +============================================================================ + + +Rationale +========= + +The basic idea behind POSIX.1e auditing is to provide a facility for +logging every security relevant event to some securely maintained +audit log. At the time of writing (w.r.t Draft 15) POSIX have +developed an extremely open interpretation of a "standard" that gives +little/no indication of how the kernel is supposed to manage an audit +trail, and little idea of what an audit trail looks like. Instead +they have specified minimal records for POSIX-defined system calls. + +For this reason we have implemented a very general circular buffer +audit trail which is similar to that of printk, but which can be +written to (partially) in parallel. This audit trail can store +information of any sort with two calls: an internal kernel interface +kernel_auditwrite() and a user interface sys_auditwrite(). The +circular audit buffer is drained from user space with the function +sys_auditor(). Such draining _must_ be done from the earliest +opportunity to avoid the kernel grinding to a halt. + +A great effort has been made to make the code both readable and +transparent in the case that the kernel is compiled with auditing +turned off. Auditing has a VERY GREAT potential for slowing the +system down considerably. Every attempt has been made to keep things +speedy, with emphasis on the case that no auditing should leave the +kernel as fast as "normal". + +The System +========== + +The basic idea is that each security sensitive system call (generally +those that create/destroy or set something) is identified as an event +that should be audited. Accordingly, code is attatched to such system +calls which writes details of the event (an audit record) to the kernel +maintained auditing subsystem. + +Generic code for constructing an audit record is provided in + and takes the form of a number of macros with the +prefix: _Audit_ . This scheme has been adopted to make it simple to +turn auditing on and off at compile time by simple #define's for these +macros. This removes the need for many #ifdef's all over the kernel +sources (which are difficult to maintain and, equally significantly, +are known to annoy Linus). + +In addition to using macros in this way a per-process audit_mask is +provided to indicate which classes of event are to be audited (the +system call sys_auditswitch() can be used to modify/probe the +audit_mask of each process). This is an optimization feature that can +be used to fine-tune the auditing at as early a stage as is possible: +minimizing the CPU time and kernel resources that will be devoted to +each system event. + +As much as is possible the construction of an audit record, within the +kernel, should be protected by the following macro: + + _Audit_if_want_event(task,audit_mask) { + ... + } + +Normally, this macro will check that audit_mask contains bits that the +current task is ear-marked for auditing. [Note, when the kernel is +compiled with auditing turned off, _Audit_if_want_event is #define'd +in the following manner: + + #define _Audit_if_want_event(task,audit_mask) if (0) + +which indicates to the compiler that the auditing code (...) can be +completely ignored.] + +The process of writing an audit record is to first construct it and +then to send it to the auditing subsystem through a call to +kernel_auditwrite(). This function can be called in both blocking and +non-blocking modes (although the latter has yet to be fully tested). +To encourage acknowledgement of the returned status value, you are +strongly encouraged to call this function with the _Audit_if_not_ok() +macro. This macro has the syntax of an 'if (...)' and will evaluate +to true if the audit record was not written - for reason of error or +the fact that the auditing subsystem was busy. + +Here is some sample code from the sys_setuid() function: + + __u32 audit_mask = AUDIT_EVENT_CLASS_UID|AUDIT_EVENT_CLASS_PROC; + + ... retval is set to indicate success or failure ... + + audit_mask |= retval ? + AUDIT_EVENT_CLASS_DENIED:AUDIT_EVENT_CLASS_SUCCESS; + + _Audit_if_want_event(current,audit_mask) { + int result; + + _Audit_begin(2*sizeof(int)); + _Audit_append_simple(int, AUD_AET_SETUID); + _Audit_append_simple(int, uid); + _Audit_if_not_ok(_AUDIT_CAN_BLOCK,audit_mask,&result) { + panic("failed audit setuid (%d)",current->pid); + /* NB. 'result' contains return code from + kernel_auditwrite(). */ + } + } + +Note, that gcc -O2 should completely remove all trace of this code in +the case that auditing is turned off. + +Status +====== + +The auditing subsystem is fully implemented. + +Only a small number of system calls are currently capable of writing +an audit record. An ad hoc procedure has been adopted for classifying +audit events. When a large number of system calls are implemented, +this classification should be formalized. + +No effort has been made to provide hooks for an audit-monitor module. +Such a module could enhance the configuarbility/effectiveness of the +auditing subsystem by further regulating which events are actually +logged to the audit trail. Take a look at kernel/audit.c for the two +sets of comments on this. (Such a module would be called for both +kernel and user-space auditing). + +No serious effort has been made to provide a useful audit daemon. +(readaudit is available on request from Andrew, but it is not well +written). Ideally, some effort should be made to automate the +construction of an audit daemon from the kernel source. A start in +this direction is the perl script: scripts/audit_events.pl. + +There is a POSIX auditing library (libaudit), however, its system call +interface has not been implemented yet. + diff -urN linux-2.0.31pre9-privs-clean/Documentation/POSIX.6_cap.txt linux-2.0.31pre9-privs/Documentation/POSIX.6_cap.txt --- linux-2.0.31pre9-privs-clean/Documentation/POSIX.6_cap.txt Wed Sep 10 15:05:33 1997 +++ linux-2.0.31pre9-privs/Documentation/POSIX.6_cap.txt Mon Sep 22 08:13:04 1997 @@ -3,7 +3,7 @@ ================================ WARNING =================================== - THIS IS CODE IN DEVELOPMENT AND IS VERY UNSTABLE AND NOT WELL TESTED. + THIS IS CODE IN DEVELOPMENT AND IS THUS UNSTABLE AND NOT WELL TESTED. If you intend using this code or want to help out or just want to say something about it then please join . @@ -114,8 +114,8 @@ steaming straight ahead and mangle the filesystem. -Using capabilities (during development) -======================================= +Using capabilities (quick and dirty) +==================================== Enable Experimental options, enable CONFIG_POSIX6_CAP, build the kernel, and reboot. @@ -157,6 +157,14 @@ Status ====== +Capabilities are basically fully implemented. The current status is +one of testing. If you want to help, try to run a capability aware +system and see if it works as seems reasonable to you. For example, +you may consider a different capability than the one implemented is +appropriate for a given request. You may also consider some new +action should be restricted with a capability. Please email the +linux-privs list with such suggestions. + Processes have capability sets. These can be listed through the /proc fs. System calls _setproccap() and _getproccap() are implemented and tested. @@ -171,17 +179,26 @@ Capabilities are computed appropriately on exec, and the dumpable flag handled. Capabilities are also cleared when a file is written to, and -when the EUID is changed by syscall. These things are not fully tested -yet, but the code is sufficiently simple to leave little room for doubt. +when the EUID is changed by syscall (should we do this?). These things +are not fully tested yet, but the code is sufficiently simple to make +the code look right... The capable() function, for testing capabilities, is implemented. -(The securelevel mechanism has been improved as required.) All (as of -kernel version 2.0.30) existing {,f}suser() checks have been converted to -use it. Additional actions have been permitted based on the CAP_SIGMASK -and CAP_LINK_DIR capabilities; uid=0 does *not* confer these capabilities. +(The securelevel mechanism has been modified to become a bitmap.) All +(as of kernel version 2.0.30) existing {,f}suser() checks have been +converted to use it. Additional actions have been permitted based on +the CAP_SIGMASK and CAP_LINK_DIR capabilities; uid=0 does *not* confer +these capabilities. + +Libcap provides a POSIX interface to these kernel modifications. The +current version of libcap is 0.85 and is available from + + http://parc.power.net/morgan/Orange-Linux/linux-privs/sampler/index.html + +and/or -TODO: implement the proper user-space interface to the syscalls. -(Header and a library.) + ftp://ftp.linux.ucla.edu/pub/LINUS/security/linux-privs/sampler -TODO: implement user-space utilities for listing and setting file -capabilities. +libcap contains the two (anticipated POSIX) applications for reading +and writing file capabilities: setcap and getcap. Manual pages have +been provided for these applications and all of the library functions. diff -urN linux-2.0.31pre9-privs-clean/Makefile linux-2.0.31pre9-privs/Makefile --- linux-2.0.31pre9-privs-clean/Makefile Wed Sep 10 15:04:45 1997 +++ linux-2.0.31pre9-privs/Makefile Sat Sep 13 17:52:18 1997 @@ -164,7 +164,7 @@ endif -Version: dummy +Version: dummy @rm -f include/linux/compile.h boot: vmlinux @@ -202,6 +202,9 @@ $(TOPDIR)/include/linux/version.h: include/linux/version.h $(TOPDIR)/include/linux/compile.h: include/linux/compile.h +include/asm-$(ARCH)/audit_aet.h: include/asm-$(ARCH)/unistd.h + scripts/mkaudit_aet_h + newversion: @if [ ! -f .version ]; then \ echo 1 > .version; \ @@ -228,7 +231,7 @@ @echo \#define LINUX_COMPILER \"`$(CC) -v 2>&1 | tail -1`\" >> .ver @mv -f .ver $@ -include/linux/version.h: ./Makefile +include/linux/version.h: ./Makefile include/asm-$(ARCH)/audit_aet.h @echo \#define UTS_RELEASE \"$(VERSION).$(PATCHLEVEL).$(SUBLEVEL)\" > .ver @echo \#define LINUX_VERSION_CODE `expr $(VERSION) \\* 65536 + $(PATCHLEVEL) \\* 256 + $(SUBLEVEL)` >> .ver @mv -f .ver $@ @@ -317,6 +320,7 @@ rm -f submenu* mrproper: clean + rm -f include/asm-$(ARCH)/audit_aet.h rm -f include/linux/autoconf.h include/linux/version.h rm -f drivers/sound/local.h drivers/sound/.defines rm -f drivers/char/uni_hash.tbl drivers/char/conmakehash diff -urN linux-2.0.31pre9-privs-clean/arch/i386/config.in linux-2.0.31pre9-privs/arch/i386/config.in --- linux-2.0.31pre9-privs-clean/arch/i386/config.in Wed Sep 10 15:05:33 1997 +++ linux-2.0.31pre9-privs/arch/i386/config.in Sun Sep 21 09:24:35 1997 @@ -40,7 +40,12 @@ bool ' Autodetect GCC trampolines' CONFIG_STACKEXEC_AUTOENABLE bool ' Log buffer overflow exploit attempts' CONFIG_STACKEXEC_LOG fi - bool 'POSIX.6 (aka. .1e) capability system (experimental)' CONFIG_POSIX6_CAP + bool 'POSIX.6 (.1e v15) capability system (experimental)' CONFIG_POSIX6_CAP + bool 'POSIX.6 (.1e v15) auditing (experimental)' CONFIG_POSIX6_AUDIT + if [ "$CONFIG_POSIX6_AUDIT" = "y" ]; then + bool ' Optimize audit for speed not verifiability' CONFIG_POSIX6_AUDIT_MAXSPEED + bool ' Permit unprivileged user to flood audit trail' CONFIG_AUDIT_PERMIT_LUSER_FLOODING + fi fi int 'Default security level - see help' DEFAULT_SECURE_LEVEL 0 bool 'Compile kernel as ELF - if your GCC is ELF-GCC' CONFIG_KERNEL_ELF diff -urN linux-2.0.31pre9-privs-clean/arch/i386/defconfig linux-2.0.31pre9-privs/arch/i386/defconfig --- linux-2.0.31pre9-privs-clean/arch/i386/defconfig Wed Sep 10 15:05:33 1997 +++ linux-2.0.31pre9-privs/arch/i386/defconfig Sun Sep 14 15:05:58 1997 @@ -33,6 +33,8 @@ CONFIG_M586=y # CONFIG_M686 is not set # CONFIG_POSIX6_CAP is not set +# CONFIG_POSIX6_AUDIT is not set +CONFIG_POSIX6_AUDIT_MAXSPEED=y DEFAULT_SECURE_LEVEL=0 # diff -urN linux-2.0.31pre9-privs-clean/arch/i386/kernel/entry.S linux-2.0.31pre9-privs/arch/i386/kernel/entry.S --- linux-2.0.31pre9-privs-clean/arch/i386/kernel/entry.S Wed Sep 10 15:05:33 1997 +++ linux-2.0.31pre9-privs/arch/i386/kernel/entry.S Fri Sep 12 06:39:44 1997 @@ -710,4 +710,7 @@ .long SYMBOL_NAME(sys_getfilecap) .long SYMBOL_NAME(sys_fsetfilecap) .long SYMBOL_NAME(sys_fgetfilecap) /* 175 */ - .space (NR_syscalls-175)*4 + .long SYMBOL_NAME(sys_auditor) + .long SYMBOL_NAME(sys_auditswitch) + .long SYMBOL_NAME(sys_auditwrite) + .space (NR_syscalls-178)*4 diff -urN linux-2.0.31pre9-privs-clean/arch/i386/kernel/process.c linux-2.0.31pre9-privs/arch/i386/kernel/process.c --- linux-2.0.31pre9-privs-clean/arch/i386/kernel/process.c Tue Oct 29 17:42:40 1996 +++ linux-2.0.31pre9-privs/arch/i386/kernel/process.c Sat Sep 27 16:28:38 1997 @@ -11,6 +11,7 @@ #define __KERNEL_SYSCALLS__ #include +#include #include #include #include @@ -550,7 +551,26 @@ asmlinkage int sys_fork(struct pt_regs regs) { - return do_fork(SIGCHLD, regs.esp, ®s); + int retval; + int audit_mask = AUDIT_EVENT_CLASS_PROGRESS|AUDIT_EVENT_CLASS_PROC; + + retval = do_fork(SIGCHLD, regs.esp, ®s); + audit_mask |= retval <= 0 ? + AUDIT_EVENT_CLASS_DENIED:AUDIT_EVENT_CLASS_SUCCESS; + + /* audit this attempt */ + _Audit_if_want_event(current,audit_mask) { + int result; + + _Audit_begin(2*sizeof(int)); + _Audit_append_simple(int, AUD_AET_FORK); + _Audit_append_simple(int, retval); + _Audit_if_not_ok(_AUDIT_CAN_BLOCK, audit_mask, &result) { + panic("failed audit fork (%d)", current->pid); + } + } + + return retval; } asmlinkage int sys_clone(struct pt_regs regs) @@ -570,13 +590,66 @@ */ asmlinkage int sys_execve(struct pt_regs regs) { + __u32 audit_mask; int error; - char * filename; + char * filename=NULL; error = getname((char *) regs.ebx, &filename); - if (error) - return error; - error = do_execve(filename, (char **) regs.ecx, (char **) regs.edx, ®s); - putname(filename); + if (!error) { + error = do_execve(filename, (char **) regs.ecx, + (char **) regs.edx, ®s); + } + + /* prepare audit mask */ + audit_mask = AUDIT_EVENT_CLASS_PROC|AUDIT_EVENT_CLASS_FILE + | (error ? + AUDIT_EVENT_CLASS_DENIED:AUDIT_EVENT_CLASS_SUCCESS); + + /* will only dump capabilities if some are set. */ + if (!(cap_isclear(current->cap_effective) + && cap_isclear(current->cap_effective) + && cap_isclear(current->cap_effective) )) { + audit_mask |= AUDIT_EVENT_CLASS_CAP; + } + + _Audit_if_want_event(current,audit_mask) { + static int magic=0; + const char *c_tmp, **tcptr; + __u32 audit_mask; + int key, result, length, argc=0, envc=0; + + key = ++magic; /* use to uniquely link audit records */ + + c_tmp = filename ? filename:"(badname)"; + length = 1+strlen(c_tmp); + result = (audit_mask & AUDIT_EVENT_CLASS_CAP) ? + (3*sizeof(__cap_s)) : 0 ; + /* count number of argv's and env's*/ + for (tcptr = (char **) regs.ecx; @@userland@@ *tcptr; ++tcptr, ++argc); + for (tcptr = (char **) regs.edx; *tcptr; ++tcptr, ++argc); + /* exec header - CAP's key ARGC ENVC EUID EGID PATH */ + { + _Audit_begin(result + 4*sizeof(int) + + sizeof(uid_t) + sizeof(gid_t) + + length); + _Audit_append_simple(int, AUD_AET_EXEC); + if (result) { + /* NB. assumes capabilities are adjacent.. */ + _Audit_append_n_data(3*sizeof(__cap_s), + &(current->cap_effective)); + } + _Audit_append_simple(int, argc); + _Audit_append_simple(int, envc); + _Audit_append_simple(uid_t, current->euid); + _Audit_append_simple(gid_t, current->egid); + _Audit_append_n_string(length, c_tmp); + } +@@@ /* loop through arg's - key, len */ + /* loop through env's - key, len */ + } + + if (filename) + putname(filename); + return error; } diff -urN linux-2.0.31pre9-privs-clean/arch/i386/kernel/sys_i386.c linux-2.0.31pre9-privs/arch/i386/kernel/sys_i386.c --- linux-2.0.31pre9-privs-clean/arch/i386/kernel/sys_i386.c Thu Apr 11 23:49:30 1996 +++ linux-2.0.31pre9-privs/arch/i386/kernel/sys_i386.c Sun Sep 21 13:50:15 1997 @@ -6,6 +6,7 @@ * platform. */ +#include #include #include #include @@ -23,18 +24,32 @@ */ asmlinkage int sys_pipe(unsigned long * fildes) { + __u32 audit_mask = AUDIT_EVENT_CLASS_FILE|AUDIT_EVENT_CLASS_BEGIN; int fd[2]; int error; - error = verify_area(VERIFY_WRITE,fildes,8); - if (error) - return error; - error = do_pipe(fd); - if (error) - return error; - put_fs_long(fd[0],0+fildes); - put_fs_long(fd[1],1+fildes); - return 0; + error = verify_area(VERIFY_WRITE,fildes,2*sizeof(*fildes)); + if (!error && !(error = do_pipe(fd))) { + put_fs_long(fd[0],0+fildes); + put_fs_long(fd[1],1+fildes); + } else { + fd[0] = fd[1] = -1; + } + audit_mask |= error ? + AUDIT_EVENT_CLASS_DENIED:AUDIT_EVENT_CLASS_SUCCESS; + _Audit_if_want_event(current,audit_mask) { + int result; + + _Audit_begin(3*sizeof(int)); + _Audit_append_simple(int, AUD_AET_PIPE); + _Audit_append_simple(int, fd[0]); + _Audit_append_simple(int, fd[1]); + _Audit_if_not_ok(_AUDIT_CAN_BLOCK,audit_mask,&result) { + panic("failed audit pipe (%d)",current->pid); + } + } + + return error; } /* diff -urN linux-2.0.31pre9-privs-clean/fs/filesystems.c linux-2.0.31pre9-privs/fs/filesystems.c --- linux-2.0.31pre9-privs-clean/fs/filesystems.c Wed Sep 10 15:05:09 1997 +++ linux-2.0.31pre9-privs/fs/filesystems.c Sun Sep 21 15:47:38 1997 @@ -9,6 +9,7 @@ #include #include +#include #include #include #include @@ -35,8 +36,10 @@ { static int callable = 1; - if (!callable) - return -1; + if (!capable(CAP_SYS_BOOT) || !callable) { + return -EPERM; + } + callable = 0; device_setup(); @@ -115,5 +118,18 @@ init_autofs_fs(); #endif mount_root(); + + _Audit_if_want_event(current, + AUDIT_EVENT_CLASS_MANDATORY|AUDIT_EVENT_CLASS_BOOT + |AUDIT_EVENT_CLASS_BEGIN|AUDIT_EVENT_CLASS_PRIV) { + int result; + + _Audit_begin(sizeof(int)); + _Audit_append_simple(int, AUD_AET_SETUP); + _Audit_if_not_ok(_AUDIT_CAN_BLOCK,audit_mask,&result) { + panic("failed audit setup (%d)", current->pid); + } + } + return 0; } diff -urN linux-2.0.31pre9-privs-clean/fs/namei.c linux-2.0.31pre9-privs/fs/namei.c --- linux-2.0.31pre9-privs-clean/fs/namei.c Wed Sep 10 15:05:40 1997 +++ linux-2.0.31pre9-privs/fs/namei.c Sun Sep 21 14:02:49 1997 @@ -13,6 +13,7 @@ #include +#include #include #include #include @@ -641,15 +642,39 @@ return error; } -asmlinkage int sys_mkdir(const char * pathname, int mode) +asmlinkage int sys_mkdir(const char * pathname, mode_t mode) { - int error; - char * tmp; + __u32 audit_mask = AUDIT_EVENT_CLASS_DIR|AUDIT_EVENT_CLASS_BEGIN; + int error, length; + char * tmp=NULL; + const char *d_name; error = getname(pathname,&tmp); if (!error) { remove_trailing_slashes(tmp); + d_name = tmp; error = do_mkdir(tmp,mode); + } else { + d_name = "(nomem)"; + } + audit_mask |= error ? + AUDIT_EVENT_CLASS_DENIED:AUDIT_EVENT_CLASS_SUCCESS; + + /* audit this attempt */ + length = 1+strlen(d_name); + _Audit_if_want_event(current,audit_mask) { + int result; + + _Audit_begin(length + sizeof(int) + sizeof(mode_t)); + _Audit_append_simple(int, AUD_AET_MKDIR); + _Audit_append_simple(mode_t, mode); + _Audit_append_n_string(length, d_name); + _Audit_if_not_ok(_AUDIT_CAN_BLOCK,audit_mask,&result) { + panic("failed mkdir audit (%d)",current->pid); + } + } + + if (tmp) { putname(tmp); } return error; @@ -697,13 +722,36 @@ asmlinkage int sys_rmdir(const char * pathname) { - int error; - char * tmp; + __u32 audit_mask = AUDIT_EVENT_CLASS_DIR|AUDIT_EVENT_CLASS_END; + int error, length; + char * tmp=NULL; + const char *d_name; error = getname(pathname,&tmp); if (!error) { remove_trailing_slashes(tmp); + d_name = tmp; error = do_rmdir(tmp); + } else { + d_name = "(nomem)"; + } + audit_mask |= error ? + AUDIT_EVENT_CLASS_DENIED:AUDIT_EVENT_CLASS_SUCCESS; + + /* audit this attempt */ + length = 1+strlen(d_name); + _Audit_if_want_event(current,audit_mask) { + int result; + + _Audit_begin(length + sizeof(int)); + _Audit_append_simple(int, AUD_AET_RMDIR); + _Audit_append_n_string(length, d_name); + _Audit_if_not_ok(_AUDIT_CAN_BLOCK,audit_mask,&result) { + panic("failed rmdir audit (%d)",current->pid); + } + } + + if (tmp) { putname(tmp); } return error; @@ -751,12 +799,36 @@ asmlinkage int sys_unlink(const char * pathname) { - int error; - char * tmp; + __u32 audit_mask = AUDIT_EVENT_CLASS_FILE|AUDIT_EVENT_CLASS_DIR + |AUDIT_EVENT_CLASS_END; + int error, length; + char * tmp=NULL; + const char *d_name; error = getname(pathname,&tmp); if (!error) { + d_name = tmp; error = do_unlink(tmp); + } else { + d_name = "(nomem)"; + } + audit_mask |= error ? + AUDIT_EVENT_CLASS_DENIED:AUDIT_EVENT_CLASS_SUCCESS; + + /* audit this attempt */ + length = 1+strlen(d_name); + _Audit_if_want_event(current,audit_mask) { + int result; + + _Audit_begin(length) + sizeof(int); + _Audit_append_simple(int, AUD_AET_UNLINK); + _Audit_append_n_string(length, d_name); + _Audit_if_not_ok(_AUDIT_CAN_BLOCK,audit_mask,&result) { + panic("failed audit unlink (%d)",current->pid); + } + } + + if (tmp) { putname(tmp); } return error; @@ -886,20 +958,53 @@ asmlinkage int sys_link(const char * oldname, const char * newname) { - int error; - char * to; + __u32 audit_mask = AUDIT_EVENT_CLASS_FILE|AUDIT_EVENT_CLASS_DIR + |AUDIT_EVENT_CLASS_BEGIN; + int error, length1, length2; + char * to=NULL, *from=NULL; + const char *c_to, *c_from; struct inode * oldinode; error = lnamei(oldname, &oldinode); - if (error) - return error; - error = getname(newname,&to); - if (error) { - iput(oldinode); - return error; + if (!error) { + error = getname(newname,&to); + if (error) { + c_to = "(nomem)"; + iput(oldinode); + } else { + c_to = to; + error = do_link(oldinode,to); + } + } else { + c_to = "(nomem)"; } - error = do_link(oldinode,to); - putname(to); + + audit_mask |= error ? + AUDIT_EVENT_CLASS_DENIED:AUDIT_EVENT_CLASS_SUCCESS; + + if (getname(oldname, &from)) { + c_from = "(nomem)"; + } else { + c_from = from; + } + length1 = 1+strlen(c_from); + length2 = 1+strlen(c_to); + _Audit_if_want_event(current,audit_mask) { + int result; + + _Audit_begin(length1+length2+sizeof(int)); + _Audit_append_simple(int, AUD_AET_LINK); + _Audit_append_n_string(length1, c_from); + _Audit_append_n_string(length2, c_to); + _Audit_if_not_ok(_AUDIT_CAN_BLOCK,audit_mask,&result) { + panic("failed audit link (%d)",current->pid); + } + } + + if (from) + putname(from); + if (to) + putname(to); return error; } @@ -975,19 +1080,49 @@ asmlinkage int sys_rename(const char * oldname, const char * newname) { - int error; - char * from, * to; + __u32 audit_mask = AUDIT_EVENT_CLASS_FILE|AUDIT_EVENT_CLASS_DIR; + int error, length1, length2; + char * from=NULL, * to=NULL; + const char *c_from, *c_to; error = getname(oldname,&from); if (!error) { + c_from = from; error = getname(newname,&to); if (!error) { + c_to = to; error = do_rename(from,to, remove_trailing_slashes(from) | remove_trailing_slashes(to)); - putname(to); + } else { + c_to = "(nomem)"; } - putname(from); + } else { + c_from = c_to = "(nomem)"; } + + audit_mask |= error ? + AUDIT_EVENT_CLASS_DENIED:AUDIT_EVENT_CLASS_SUCCESS; + + /* audit this attempt */ + length1 = 1+strlen(c_from); + length2 = 1+strlen(c_to); + _Audit_if_want_event(current,audit_mask) { + int result; + + _Audit_begin(length1+length2+sizeof(int)); + _Audit_append_simple(int, AUD_AET_RENAME); + _Audit_append_n_string(length1, c_from); + _Audit_append_n_string(length2, c_to); + _Audit_if_not_ok(_AUDIT_CAN_BLOCK,audit_mask,&result) { + panic("failed audit rename (%d)",current->pid); + } + } + + if (to) + putname(to); + if (from) + putname(from); + return error; } diff -urN linux-2.0.31pre9-privs-clean/fs/open.c linux-2.0.31pre9-privs/fs/open.c --- linux-2.0.31pre9-privs-clean/fs/open.c Wed Sep 10 15:05:40 1997 +++ linux-2.0.31pre9-privs/fs/open.c Sun Sep 21 16:39:51 1997 @@ -4,6 +4,7 @@ * Copyright (C) 1991, 1992 Linus Torvalds */ +#include #include #include #include @@ -268,23 +269,52 @@ asmlinkage int sys_chdir(const char * filename) { + __u32 audit_mask = AUDIT_EVENT_CLASS_DIR; struct inode * inode; int error; error = namei(filename,&inode); if (error) - return error; + goto failed; + if (!S_ISDIR(inode->i_mode)) { iput(inode); - return -ENOTDIR; - } - if ((error = permission(inode,MAY_EXEC)) != 0) { + error = -ENOTDIR; + } else if ((error = permission(inode,MAY_EXEC)) != 0) { iput(inode); - return error; + } else { + iput(current->fs->pwd); + current->fs->pwd = inode; + audit_mask |= AUDIT_EVENT_CLASS_SUCCESS; + goto audit; + } +failed: + audit_mask |= AUDIT_EVENT_CLASS_DENIED; +audit: + /* audit this attempt */ + _Audit_if_want_event(current,audit_mask) { + int result; + char *tmp=NULL; + const char *c_tmp; + + result = getname(filename, &tmp); + c_tmp = result && tmp ? tmp:"(nomem)" ; + result = 1+strlen(c_tmp); + { + _Audit_begin(result + sizeof(int)); + _Audit_append_simple(int, AUD_AET_CHDIR); + _Audit_append_n_string(result, c_tmp); + _Audit_if_not_ok(_AUDIT_CAN_BLOCK,audit_mask,&result) { + panic("failed audit chdir (%d)", current->pid); + } + } + /* clean up */ + if (tmp) { + putname(tmp); + } } - iput(current->fs->pwd); - current->fs->pwd = inode; - return (0); + + return error; } asmlinkage int sys_fchdir(unsigned int fd) @@ -352,28 +382,57 @@ asmlinkage int sys_chmod(const char * filename, mode_t mode) { + __u32 audit_mask = AUDIT_EVENT_CLASS_DIR|AUDIT_EVENT_CLASS_FILE; struct inode * inode; int error; struct iattr newattrs; error = namei(filename,&inode); - if (error) - return error; - if (IS_RDONLY(inode)) { - iput(inode); - return -EROFS; - } - if (IS_IMMUTABLE(inode) || IS_APPEND(inode)) { - iput(inode); - return -EPERM; + if (error) { + goto audit; + } else if (IS_RDONLY(inode)) { + error = -EROFS; + } else if (IS_IMMUTABLE(inode) || IS_APPEND(inode)) { + error = -EPERM; + } else { + if (mode == (mode_t) -1) + mode = inode->i_mode; + newattrs.ia_mode = (mode & S_IALLUGO) | + (inode->i_mode & ~S_IALLUGO); + newattrs.ia_valid = ATTR_MODE | ATTR_CTIME; + inode->i_dirt = 1; + error = notify_change(inode, &newattrs); } - if (mode == (mode_t) -1) - mode = inode->i_mode; - newattrs.ia_mode = (mode & S_IALLUGO) | (inode->i_mode & ~S_IALLUGO); - newattrs.ia_valid = ATTR_MODE | ATTR_CTIME; - inode->i_dirt = 1; - error = notify_change(inode, &newattrs); + + /* drop link to inode */ iput(inode); +audit: + audit_mask |= error ? + AUDIT_EVENT_CLASS_DENIED:AUDIT_EVENT_CLASS_SUCCESS; + _Audit_if_want_event(current,audit_mask) { + int result; + char *tmp=NULL; + const char *c_tmp; + + result = getname(filename, &tmp); + c_tmp = result && tmp ? tmp:"(nomem)" ; + result = 1+strlen(c_tmp); + { + _Audit_begin(result + sizeof(int) + sizeof(mode_t)); + _Audit_append_simple(int, AUD_AET_CHMOD); + _Audit_append_simple(mode_t, mode); + _Audit_append_n_string(result, c_tmp); + _Audit_if_not_ok(_AUDIT_CAN_BLOCK, + audit_mask, &result) { + panic("failed audit chmod (%d)", current->pid); + } + } + /* clean up */ + if (tmp) { + putname(tmp); + } + } + return error; } @@ -435,20 +494,26 @@ asmlinkage int sys_chown(const char * filename, uid_t user, gid_t group) { + __u32 audit_mask = AUDIT_EVENT_CLASS_FILE; + char * tmp=NULL; + const char *c_tmp; struct inode * inode; - int error; + int error, length; struct iattr newattrs; error = lnamei(filename,&inode); - if (error) - return error; + if (error) { + goto audit_event; + } if (IS_RDONLY(inode)) { iput(inode); - return -EROFS; + error = -EROFS; + goto audit_event; } if (IS_IMMUTABLE(inode) || IS_APPEND(inode)) { iput(inode); - return -EPERM; + error = -EPERM; + goto audit_event; } if (user == (uid_t) -1) user = inode->i_uid; @@ -459,6 +524,10 @@ newattrs.ia_gid = group; newattrs.ia_valid = ATTR_UID | ATTR_GID | ATTR_CTIME; /* + * XXX.CAP - unless capable(CAP_SETFCAP) need to also remove + * any capabilities on file. + */ + /* * If the owner has been changed, remove the setuid bit */ if (user != inode->i_uid && @@ -489,6 +558,31 @@ } else error = notify_change(inode, &newattrs); iput(inode); + +audit_event: + audit_mask |= error ? + AUDIT_EVENT_CLASS_DENIED:AUDIT_EVENT_CLASS_SUCCESS; + + (void) getname(filename, &tmp); + c_tmp = tmp ? tmp:"(nomem)"; + length = strlen(c_tmp)+1; + + _Audit_if_want_event(current,audit_mask) { + int result; + + _Audit_begin(sizeof(int) + sizeof(uid_t) + + sizeof(gid_t) + length); + _Audit_append_simple(int, AUD_AET_CHOWN); + _Audit_append_simple(uid_t, user); + _Audit_append_simple(gid_t, group); + _Audit_append_n_string(length, c_tmp); + _Audit_if_not_ok(_AUDIT_CAN_BLOCK,audit_mask,&result) { + panic("failed audit chown (%d)",current->pid); + } + } + if (tmp) + putname(tmp); + return(error); } @@ -578,23 +672,50 @@ FD_CLR(fd, ¤t->files->open_fds); } -asmlinkage int sys_open(const char * filename,int flags,int mode) +asmlinkage int sys_open(const char * filename, int flags, mode_t mode) { - char * tmp; - int fd, error; + __u32 audit_mask = AUDIT_EVENT_CLASS_FILE|AUDIT_EVENT_CLASS_BEGIN; + char * tmp=NULL; + const char *c_tmp; + int fd, error, length; - fd = get_unused_fd(); - if (fd < 0) - return fd; error = getname(filename, &tmp); - if (!error) { - error = do_open(tmp,flags,mode, fd); - putname(tmp); - if (!error) - return fd; + if (error) { + c_tmp = "(nomem)"; + fd = error; + } else { + c_tmp = tmp; + fd = get_unused_fd(); + if (fd >= 0) { + error = do_open(tmp,flags,mode, fd); + if (error) { + put_unused_fd(fd); + fd = error; + } + } } - put_unused_fd(fd); - return error; + + /* audit event */ + audit_mask |= (fd < 0) ? + AUDIT_EVENT_CLASS_DENIED:AUDIT_EVENT_CLASS_SUCCESS; + length = 1+strlen(c_tmp); + _Audit_if_want_event(current,audit_mask) { + int result; + + _Audit_begin(3*sizeof(int) + sizeof(mode_t) + length); + _Audit_append_simple(int, AUD_AET_OPEN); + _Audit_append_simple(int, flags); + _Audit_append_simple(int, fd); + _Audit_append_simple(mode_t, mode); + _Audit_append_n_string(length, c_tmp); + _Audit_if_not_ok(_AUDIT_CAN_BLOCK,audit_mask,&result) { + panic("failed audit open (%d)",current->pid); + } + } + + if (tmp) + putname(tmp); + return fd; } #ifndef __alpha__ @@ -649,6 +770,22 @@ files->fd[fd] = NULL; error = close_fp(filp); } + + if (!error) { + __u32 audit_mask = AUDIT_EVENT_CLASS_FILE + |AUDIT_EVENT_CLASS_BEGIN|AUDIT_EVENT_CLASS_SUCCESS; + _Audit_if_want_event(current, audit_mask) { + int result; + + _Audit_begin(2*sizeof(int)); + _Audit_append_simple(int, AUD_AET_CLOSE); + _Audit_append_simple(int, fd); + _Audit_if_not_ok(_AUDIT_CAN_BLOCK,audit_mask,&result) { + panic("failed audit close (%d)", current->pid); + } + } + } + return error; } diff -urN linux-2.0.31pre9-privs-clean/include/asm-i386/unistd.h linux-2.0.31pre9-privs/include/asm-i386/unistd.h --- linux-2.0.31pre9-privs-clean/include/asm-i386/unistd.h Wed Sep 10 15:05:41 1997 +++ linux-2.0.31pre9-privs/include/asm-i386/unistd.h Sat Sep 13 17:11:59 1997 @@ -181,6 +181,9 @@ #define __NR__getfilecap 173 #define __NR__fsetfilecap 174 #define __NR__fgetfilecap 175 +#define __NR__auditor 176 +#define __NR__auditswitch 177 +#define __NR__auditwrite 178 /* XXX - _foo needs to be __foo, while __NR_bar could be _NR_bar. */ #define _syscall0(type,name) \ diff -urN linux-2.0.31pre9-privs-clean/include/linux/audit.h linux-2.0.31pre9-privs/include/linux/audit.h --- linux-2.0.31pre9-privs-clean/include/linux/audit.h Wed Dec 31 16:00:00 1969 +++ linux-2.0.31pre9-privs/include/linux/audit.h Mon Sep 22 11:08:28 1997 @@ -0,0 +1,240 @@ +#ifndef LINUX_AUDIT_H +#define LINUX_AUDIT_H + +#include +#include +#include + +/* the kernel audit structure, flags indicates if the rest of the header + * is in little or big endian format. */ + +struct __aud_header_s { + __u8 flags; /* For configuration use (includes byteordering) */ + __u8 generation; /* Indicator of generation of audit record */ + __u16 length; /* Number of __aud_s's that follow this */ + __u32 audit_id; /* Audit id - for tracing process trees */ + __u32 proc_id; /* PID of audited process */ + __u32 event_class; /* bits that triggered event recording: + * (bit 0)=0 -> user-level event + * (bit 0)=1 -> kernel-level event + * + * NB. AUDIT_EVENT_CLASS_PRIV -> E and P + * capability sets immediately after + * header. + */ + __u32 time_sec; /* when did event occur (seconds) */ + __u32 time_nsec; /* offset (nanosec) */ + __u64 unused; /* pad out to 256-bits = 32 bytes */ +} __attribute__ ((packed)); + +#ifdef __KERNEL__ + +/* some numbers that determine how many atoms of the internal audit + trail are to be buffered */ + +#define AUDIT_DATA_ATOM_SIZE sizeof(struct __aud_header_s) +#define MAX_AUDIT_ATOMS 1024 /* number of lines stored at + * any time in audit buffer: + * must be power of 2 */ +#define MAX_AUDIT_WRAP_MASK (MAX_AUDIT_ATOMS-1) +#define MAX_AUDIT_DATA (MAX_AUDIT_WRAP_MASK*AUDIT_DATA_ATOM) + +/* union for type of internal audit buffer */ + +union audit_atom { + struct __aud_header_s header; + __u8 data[AUDIT_DATA_ATOM_SIZE]; +}; + +/* The following are a set of tools for building an audit record. + * + * They can be conveniently #define'd to [empty] in the case that one + * wishes to compile code with auditing removed (gcc +02 will in all + * such cases investigated completely remove every trace of the + * auditing code). Note that the writing of an audit record macro is + * defined in the way it is to encourage the programmer to verify the + * exit status of the command. + * + * Usage is to place all commands between a single pair of { }'s: + * + * _Audit_if_want_event(current, event_mask) { + * int result; + * _Audit_begin(sizeof(int) + sizeof(gid_t)); + * _Audit_append_simple(int, AUD_AET_SETGID); + * _Audit_append_simple(gid_t, current->gid); + * _Audit_if_not_ok(_AUDIT_CAN_BLOCK, event_mask, &result) { + * panic("attempt to audit setgid event failed (%d)", result); + * } + * } + * + * In the case of non-blocking calls, the 'result' should be checked + * for being '+ERESTARTSYS' before panic()ing and the appropriate + * action is taken. + * + * It is anticipated that the list of audit events might most + * efficiently be stored in a single meta-data file which can be used + * to create both the auditing components within the kernel and the + * audit report generator application. It is also possible that one + * can 'parse' the kernel tree for events audited in the above fashion + * and generate the event profiles from that. (I currently favor this + * latter approach as it would offer some sort of verification process + * for the kernel's auditing code. My guess is that if someone doesn't + * beat me to it, I'll blow the dust of my lex/yacc books and put that + * together - AGM 1997/9/13) + */ + +/* this #define is used to mark a section for auditing. If + * CONFIG_POSIX6_AUDIT_MAXSPEED is in effect, an auditing decision is + * made here and now about whether this event will be audited or not. + * The alternative is that all such decisions will be made within the + * auditing function (kernel_audit) itself - where it is easier to + * "prove" for correctness. The advantage is one of speed, namely + * that we do not have to construct the audit record in advance of + * rejecting it because of information that was available in the first + * place... The idea is that the compiler of kernel makes this + * choice. XXX - add to config scripts. */ + +#ifdef CONFIG_POSIX6_AUDIT + +#ifdef CONFIG_POSIX6_AUDIT_MAXSPEED +# define _Audit_if_want_event(task, mask) \ + if ((mask) & (AUDIT_EVENT_CLASS_MANDATORY|(task)->audit_event_mask)) +#else +# define _Audit_if_want_event(task, mask) if (1) +#endif + +#define _Audit_begin(max_length) \ + __u8 _Audit_data[max_length] __attribute__ ((aligned)); \ + __u16 _Audit_length = 0 + +#define _Audit_append_simple(prototype, datum) \ + *((prototype *)(_Audit_length + _Audit_data)) = datum; \ + _Audit_length += sizeof(prototype) + +#define _Audit_append_by_ptr(prototype, data_p) \ + memcpy(_Audit_length+_Audit_data, data_p, sizeof(prototype)); \ + _Audit_length += sizeof(prototype) + +#define _Audit_append_n_string(length, data_p) \ + strncpy(_Audit_length+(char *)_Audit_data, \ + (const char *)data_p, length); \ + _Audit_length += length + +#define _Audit_append_n_data(length, data_p) \ + memcpy(_Audit_length+_Audit_data, data_p, length); \ + _Audit_length += length + +#define _Audit_if_not_ok(ok_to_block, event_mask, result_p) \ + if ((_Audit_length > sizeof(_Audit_data) && (*(result_p)=EINVAL)) || \ + (*(result_p) = kernel_auditwrite(ok_to_block,event_mask, \ + _Audit_length,_Audit_data))) + +#define _AUDIT_DONT_BLOCK 0 +#define _AUDIT_CAN_BLOCK 1 + +extern int kernel_auditwrite(int may_block, __u32 mask, __u16 length, + const void *data); + +#else /* ie. "not" CONFIG_POSIX6_AUDIT */ + +/* We don't want auditing: gcc -O2 should optimize away any trace of + the auditing code (the "if (0) ..." stuff is to silence gcc over + warnings about unused variables). This has been the case where I + looked at the assembly. */ + +#define _Audit_if_want_event(task, mask) if (0) +#define _Audit_begin(max_length) +#define _Audit_append_simple(prototype, datum) +#define _Audit_append_by_ptr(prototype, data_p) +#define _Audit_append_n_string(length, data_p) +#define _Audit_append_n_data(length, data_p) +#define _Audit_if_not_ok(ok_to_block, event_mask, result_p) \ + if (0) { *result_p = 0; } + +#endif /* CONFIG_POSIX6_AUDIT */ + +/* the current audit format generation number. Note, '0' is used to lock + record so it is not drained before it is written... */ + +#define AUDIT_RECORD_LOCKED 0 +#define AUDIT_CURRENT_GENERATION 1 + +/* flag-bits to indicate the nature of the audited event. These can be + combined to generate a profile of the event that can be more easily + vetoed as it is written to the audit log */ + +#define AUDIT_EVENT_CLASS_USER 0x00000000 /* user generated event */ +#define AUDIT_EVENT_CLASS_KERNEL 0x00000001 /* kernel generated event */ + +/* this event class is special - it cannot be turned off. It could be + defined as a union of other event classes, thus hard-wiring a + specified selection of events for mandated auditing. */ + +#define AUDIT_EVENT_CLASS_MANDATORY 0x80000000 /* "must audit" class */ + +#endif /* __KERNEL__ */ + +/* flags for interpreting audit header/record structure */ + +#define AUDIT_FLAG_BIG_ENDIAN 0x80 + +/* make a choice based on the endianness of the local chip */ + +#ifndef __LITTLE_ENDIAN +# define AUDIT_FLAG_PREFIX AUDIT_FLAG_BIG_ENDIAN +#else +# define AUDIT_FLAG_PREFIX 0x00 +#endif + +/* state bit pairs - mutually exclusive triads (0 = not applicable) */ +#define AUDIT_EVENT_CLASS_SUCCESS 0x00000002 /* event success notice */ +#define AUDIT_EVENT_CLASS_DENIED 0x00000004 /* event failure notice */ +#define AUDIT_EVENT_CLASS_PROGRESS 0x00000006 /* ambiguous state notice */ +#define AUDIT_EVENT_CLASS_RESULT_MASK 0x00000006 + +/* state bit pairs - mutually exclusive triads (0 = not applicable) */ +#define AUDIT_EVENT_CLASS_BEGIN 0x00000008 /* event starting notice */ +#define AUDIT_EVENT_CLASS_END 0x00000010 /* event ending notice */ +#define AUDIT_EVENT_CLASS_INTERIM 0x00000018 /* intermediate notice */ +#define AUDIT_EVENT_CLASS_EPOCH_MASK 0x00000018 + +#define AUDIT_EVENT_CLASS_FILE 0x00000100 /* related to file access */ +#define AUDIT_EVENT_CLASS_DRIVER 0x00000200 /* related to device driver */ +#define AUDIT_EVENT_CLASS_NET 0x00000400 /* related to a network */ +#define AUDIT_EVENT_CLASS_PROC 0x00000800 /* related to a process */ +#define AUDIT_EVENT_CLASS_CAP 0x00001000 /* setting capability(ies) */ +#define AUDIT_EVENT_CLASS_PRIV 0x00002000 /* success requires caps */ +#define AUDIT_EVENT_CLASS_UID 0x00004000 /* to do with uids */ +#define AUDIT_EVENT_CLASS_GID 0x00008000 /* to do with gids */ +#define AUDIT_EVENT_CLASS_DIR 0x00010000 /* related to directories */ +#define AUDIT_EVENT_CLASS_AUDIT 0x00020000 /* to do with audit control */ +#define AUDIT_EVENT_CLASS_FAWRITE 0x00040000 /* _failed_ audit write */ +#define AUDIT_EVENT_CLASS_BOOT 0x00080000 /* system boot and shutdown */ + +/* syscall control codes for sys_auditswitch */ + +#define LINUX_PROBE_AUDIT_MASK 1 +#define LINUX_PROBE_AUDIT_ID 2 + +/* POSIX event types - XXX. some of this belongs in */ + +#define AUD_STD_96_1 0xAD5D199601 /* draft 15, p296 par 3 */ +#define AUD_SYSTEM_LOG -1 /* file descriptor for system + audit log - cannot be 'opened'. */ +#define AUD_NATIVE 0 /* default format of records */ + +/* macros (types of numeric argument) - this is built from */ + +#include + +#define AUD_AET_AUD_SWITCH AUD_AET__AUDITSWITCH /* aud_switch() */ +#define AUD_AET_AUD_WRITE AUD_AET__AUDITWRITE /* aud_write() */ +#define AUD_AET_EXEC AUD_AET_EXECVE +#define AUD_AET_MKFIFO ?? /* mkfifo() */ + +/* Linux specific event types */ + +/* sys_auditswitch(LINUX_PROBE_AUDIT_ID) */ +#define AUD_AET_SYS_PROBE_ID 10001 + +#endif /* LINUX_AUDIT_H */ diff -urN linux-2.0.31pre9-privs-clean/include/linux/fs.h linux-2.0.31pre9-privs/include/linux/fs.h --- linux-2.0.31pre9-privs-clean/include/linux/fs.h Wed Sep 10 15:05:42 1997 +++ linux-2.0.31pre9-privs/include/linux/fs.h Sun Sep 21 09:49:09 1997 @@ -538,7 +538,7 @@ extern int register_filesystem(struct file_system_type *); extern int unregister_filesystem(struct file_system_type *); -asmlinkage int sys_open(const char *, int, int); +asmlinkage int sys_open(const char *, int, mode_t); asmlinkage int sys_close(unsigned int); /* yes, it's really unsigned */ extern void kill_fasync(struct fasync_struct *fa, int sig); diff -urN linux-2.0.31pre9-privs-clean/include/linux/sched.h linux-2.0.31pre9-privs/include/linux/sched.h --- linux-2.0.31pre9-privs-clean/include/linux/sched.h Wed Sep 10 15:05:42 1997 +++ linux-2.0.31pre9-privs/include/linux/sched.h Sun Sep 21 09:49:09 1997 @@ -232,6 +232,7 @@ #ifdef CONFIG_POSIX6_CAP __cap_s cap_effective, cap_inheritable, cap_permitted; #endif + __u32 aid, audit_event_mask; /* audit id & event mask */ char comm[16]; /* limits */ struct rlimit rlim[RLIM_NLIMITS]; @@ -295,6 +296,8 @@ # define INIT_CAPS #endif +#define INIT_AUDIT 0, ~0, + /* * INIT_TASK is used to set up the first task table, touch at * your own risk!. Base=0, limit=0x1fffff (=2MB) @@ -318,6 +321,7 @@ /* uid etc */ 0,0,0,0,0,0,0,0, \ /* suppl grps*/ {NOGROUP,}, \ /* cap's */ INIT_CAPS \ +/* aid, audit_event_mask */ INIT_AUDIT \ /* comm */ "swapper", \ /* rlimits */ INIT_RLIMITS, \ /* math */ 0, \ diff -urN linux-2.0.31pre9-privs-clean/kernel/Makefile linux-2.0.31pre9-privs/kernel/Makefile --- linux-2.0.31pre9-privs-clean/kernel/Makefile Wed Sep 10 15:05:45 1997 +++ linux-2.0.31pre9-privs/kernel/Makefile Thu Sep 11 07:57:51 1997 @@ -13,7 +13,7 @@ O_TARGET := kernel.o O_OBJS = sched.o dma.o fork.o exec_domain.o panic.o printk.o sys.o \ module.o exit.o signal.o itimer.o info.o time.o softirq.o \ - resource.o sysctl.o cap.o + resource.o sysctl.o cap.o audit.o ifeq ($(CONFIG_MODULES),y) OX_OBJS = ksyms.o diff -urN linux-2.0.31pre9-privs-clean/kernel/audit.c linux-2.0.31pre9-privs/kernel/audit.c --- linux-2.0.31pre9-privs-clean/kernel/audit.c Wed Dec 31 16:00:00 1969 +++ linux-2.0.31pre9-privs/kernel/audit.c Mon Sep 22 07:04:12 1997 @@ -0,0 +1,520 @@ +/* + * This file contains the kernel auditing interface. + * + * Copyright (c) 1997 Andrew G. Morgan + * PGP: 1024/2A398175 D7 B8 FB 8F 9C E5 43 A3 3D 41 17 8F D5 71 69 50 + * + * There are three user-level auditing functions, they are used to + * modify/query the auditing status of the current task + * (sys_auditswitch), write to the audit buffer (sys_auditwrite), and + * to drain the audit buffer (sys_auditor). + * + * There is also an internal audit function that can be used to write + * kernel-driven audit events to the audit trail: kernel_auditor(). + * + * The intentions of these functions is to provide a mechanism for + * both the kernel and userspace to write arbitrary audit events to a + * kernel maintained audit buffer. This buffer is then drained from a + * sufficiently capable userspace daemon. The daemon itself is + * responsible for ensuring that the audit trail is safely deposited + * on some permanent medium (through calls to other parts of the + * kernel). This daemon is likely to need to turn off/limit auditing for + * its own actions. + * + * XXX - we could control task specific auditing with a task specific + * mask perhaps? */ + +#include +#include +#include +#include +#include +#include + +#ifdef CONFIG_POSIX6_AUDIT + +/* this should probably be dynamically allocated */ + +static union audit_atom audit_buffer[MAX_AUDIT_ATOMS]; + +/* some global variables for blocking auditing calls */ + +struct wait_queue *audit_wait_queue = NULL; /* queued audit events */ +int audit_first_full_atom=0; /* where drain reads from */ +int audit_filled_data_atoms=0; /* how many filled atoms are there? */ +int audit_empty_atoms=MAX_AUDIT_ATOMS; /* how many empty atoms? */ +int audit_first_empty_atom=0; /* where new audit records are written to */ + +/* + * kernel level interface for writing to the audit trail + */ + +int kernel_auditwrite(int will_block, __u32 event_mask, __u16 length, + const void *data) +{ + int audit_atoms_needed, from, i; + + audit_atoms_needed = ((length+AUDIT_DATA_ATOM_SIZE-1) + /AUDIT_DATA_ATOM_SIZE) + 1; + + /* Note, kernel calls; we can be severe about failures. */ + if (audit_atoms_needed > MAX_AUDIT_ATOMS) { + panic("kernel_auditor choked on large request (%d/%d)", + audit_atoms_needed, MAX_AUDIT_ATOMS); + } + + /* + * this should be _after_ the above checks to avoid covert anlysis + * of the auditing state with the use of inappropriate arguments. + * Note, this is more of an issue for user level code, so for this + * kernel level function is can be reasonably placed earlier (for + * speed). + */ + + if (!((AUDIT_EVENT_CLASS_MANDATORY|current->audit_event_mask) + & event_mask)) { + return 0; + } + + /* + * XXX - this is where we would probably employ an audit filter. + * See the sys_auditor() function below for more comments. + */ + + /* now we wait(?) for an opportunity to write to the audit trail */ + cli(); + if (audit_atoms_needed > audit_empty_atoms) { + if (!will_block) { + sti(); + return ERESTARTSYS; /* no blocking so return */ + } + /* wait for chance to write to audit trail */ + do { + /* we do not acknowledge signals until later + * -- XXX.AUDIT is there an + * "noninterruptible_sleep_on() fn? */ + interruptible_sleep_on(&audit_wait_queue); + } while (audit_atoms_needed > audit_empty_atoms); + } + + from = audit_first_empty_atom & MAX_AUDIT_WRAP_MASK; + audit_empty_atoms -= audit_atoms_needed; + audit_first_empty_atom += audit_atoms_needed; + audit_filled_data_atoms += audit_atoms_needed--; /* NB '--' */ + audit_buffer[from].header.generation = AUDIT_RECORD_LOCKED; + sti(); + + if (length > (MAX_AUDIT_WRAP_MASK-(1+from))) { + /* will have to copy data in two blocks */ + int offset = (MAX_AUDIT_WRAP_MASK-(1+from)) + *AUDIT_DATA_ATOM_SIZE; + memcpy(&(audit_buffer[(from+1)]), data, offset); + memcpy(&(audit_buffer[0]), data+offset, length-offset); + } else { + /* data simply fits into the buffer as is */ + memcpy(&(audit_buffer[(from+1)]), data, length); + } + + /* XXX - should we scrub the unused part of the tail atom? */ + + /* write most of the header */ + audit_buffer[from].header.flags = AUDIT_FLAG_PREFIX; + audit_buffer[from].header.length = length + AUDIT_DATA_ATOM_SIZE * + (current->audit_event_mask & AUDIT_EVENT_CLASS_PRIV) ? 2:1 ; + audit_buffer[from].header.audit_id = current->aid; + audit_buffer[from].header.proc_id = current->pid; + audit_buffer[from].header.event_class = + event_mask | AUDIT_EVENT_CLASS_KERNEL; + audit_buffer[from].header.time_sec = xtime.tv_sec; + audit_buffer[from].header.time_nsec = 1000*xtime.tv_usec; + audit_buffer[from].header.unused = 0; + + /* header is prepared now: mark buffer as ready to go. */ + audit_buffer[from].header.generation = AUDIT_CURRENT_GENERATION; + + /* wake up next process waiting on this queue */ + wake_up_interruptible(&audit_wait_queue); + + return 0; /* success! */ +} + +/* user level interface for changing audit status of current task */ + +asmlinkage int sys_auditswitch(int control, + int pid, + __u32 audit_mask_flags, + __u32 audit_append_flags, + __u32 *old_value_p) +{ + struct task_struct * p; + int retval; + __u32 audit_mask = AUDIT_EVENT_CLASS_AUDIT|AUDIT_EVENT_CLASS_PROC + |AUDIT_EVENT_CLASS_MANDATORY; + __u32 tmp, old; + + if (!capable(CAP_AUDIT_CONTROL)) { + /* user is not permitted to interact with auditing control */ + return -EPERM; + } + if (verify_area(VERIFY_WRITE, old_value_p, sizeof(*old_value_p))) { + return -EINVAL; + } + + /* verify control parameter */ + switch (control) { + case LINUX_PROBE_AUDIT_MASK: + case LINUX_PROBE_AUDIT_ID: + break; + default: + return -EINVAL; + } + + /* locate process */ + if (current->pid == pid) { + p = current; + retval = 0; + } else { + retval = -ESRCH; + for_each_task(p) { + if (p->pid == pid) { + retval = 0; + break; + } + } + } + + if (!retval) { + /* may want to set some defaults */ + old = tmp = 0; /* new setting */ + audit_mask |= AUDIT_EVENT_CLASS_DENIED; + goto audit; + } + + audit_mask |= AUDIT_EVENT_CLASS_SUCCESS; + + /* the resetting actions need to be atomic -- note, there is a + * race here between the getting of the old value and the + * setting of the new one. However, assuming that the two + * assignments (**) are atomic, we cannot ever be in an + * undefined audit state. */ + + switch (control) { + case LINUX_PROBE_AUDIT_MASK: + /* this is used to get a copy of the previous audit + * mask and or modify it for given process. */ + +/* (**) */ old = tmp = p->audit_event_mask; + memcpy_tofs(old_value_p, &tmp, sizeof(*old_value_p)); + + /* user may be changing audit properties of 'pid' */ + tmp &= audit_mask_flags; + tmp |= audit_append_flags; + +/* (**) */ p->audit_event_mask = tmp; + + break; + case LINUX_PROBE_AUDIT_ID: + /* this is used to get a copy of the former audit id + * and to possibly modify it for given process */ +/* (**) */ old = tmp = p->aid; + memcpy_tofs(old_value_p, &tmp, sizeof(*old_value_p)); + + /* user is attempting to change audit id of 'pid' */ + tmp &= audit_mask_flags; + tmp |= audit_append_flags; + +/* (**) */ p->aid = tmp; + + break; + default: + /* how did we get here? */ + panic("audit control parameter unknown (%d)\n", control); + } + +audit: + /* audit this attempt */ + _Audit_if_want_event(current,audit_mask) { + int result; + + _Audit_begin(3*sizeof(int)+2*sizeof(__u32)); + _Audit_append_simple(int, AUD_AET_AUD_SWITCH); + _Audit_append_simple(int, control); + _Audit_append_simple(int, pid); + _Audit_append_simple(__u32, old); + _Audit_append_simple(__u32, tmp); + _Audit_if_not_ok(_AUDIT_CAN_BLOCK,audit_mask,&result) { + panic("failed audit auditswitch (%d)",current->pid); + } + } + + return retval; +} + +/* user level interface for writing audit data */ + +asmlinkage int sys_auditwrite(__u32 event_mask, int length, + const void *user_audit_data) +{ + int audit_atoms_needed, from, i, retval; + + /* verify that the request is valid */ + if (!capable(CAP_AUDIT_WRITE)) { + /* user is not permitted to write to the audit log */ + retval = -EPERM; + goto write_failed; + } + + audit_atoms_needed = ((length+AUDIT_DATA_ATOM_SIZE-1) + /AUDIT_DATA_ATOM_SIZE) + 1; + if (audit_atoms_needed > MAX_AUDIT_ATOMS) { + /* user dumping too much into the audit buffer */ + retval = -EMSGSIZE; + goto write_failed; + } + + if (verify_area(VERIFY_READ, user_audit_data, length)) { + retval = -EINVAL; + goto write_failed; + } + + /* + * this must be _after_ the above checks to avoid covert anlysis + */ + if (!((AUDIT_EVENT_CLASS_MANDATORY|current->audit_event_mask) + & event_mask)) { + return 0; + } + + /* + * XXX.AUDIT - this is likely where we would call an audit filter + * that would decide if we want events to be + * logged. A possible candidate for a module and/or + * an accept reject rule-set. + * + * It is also possible that such a facility could be + * (ab)used by a "big brother" module to regulate the + * behavior of users (cf. big blue) and offer the local + * sys-admin/auditor the option of simply terminating a + * process if it tries to do something inappropriate. + * + * Note, to avoid a user-level program probing the + * severity of the current audit selection rules, this + * check has to be _after_ verifying the user-provided + * arguments are valid. + */ + + /* + * block until we can get a section of the audit buffer to ourselves + */ + cli(); + while (audit_atoms_needed > audit_empty_atoms) { + /* return if interrupted */ + if (current->signal & ~current->blocked) { + sti(); + return -ERESTARTSYS; + } + /* sleep until we next get to the front of the queue */ + interruptible_sleep_on(&audit_wait_queue); + } + from = audit_first_empty_atom & MAX_AUDIT_WRAP_MASK; + audit_empty_atoms -= audit_atoms_needed; + audit_first_empty_atom += audit_atoms_needed; + audit_filled_data_atoms += audit_atoms_needed--; /* NB '--' */ + audit_buffer[from].header.generation = AUDIT_RECORD_LOCKED; + sti(); + + /* now we have a section of the buffer (possibly wrapped) that + is ours to keep. It will not be drained until the 'generation' + field of the first block in this reserved section is + non-zero... */ + + if (length > (MAX_AUDIT_WRAP_MASK-(1+from))) { + /* will have to copy data in two blocks */ + int offset = (MAX_AUDIT_WRAP_MASK-(1+from)) + *AUDIT_DATA_ATOM_SIZE; + memcpy_fromfs(&(audit_buffer[(from+1)]), user_audit_data, + offset); + memcpy_fromfs(&(audit_buffer[0]), user_audit_data+offset, + length-offset); + } else { + /* data simply fits into the buffer as is */ + memcpy_fromfs(&(audit_buffer[(from+1)]), data, length); + } + + /* XXX.AUDIT - should we scrub the unused part of last atom? */ + + /* write most of the header */ + audit_buffer[from].header.flags = AUDIT_FLAG_PREFIX; + audit_buffer[from].header.length = length + AUDIT_DATA_ATOM_SIZE; + audit_buffer[from].header.audit_id = current->aid; + audit_buffer[from].header.proc_id = current->pid; + audit_buffer[from].header.event_class = + event_mask & ~AUDIT_EVENT_CLASS_KERNEL; + audit_buffer[from].header.time_sec = xtime.tv_sec; + audit_buffer[from].header.time_nsec = 1000*xtime.tv_usec; + audit_buffer[from].header.unused = 0; + + /* header is prepared now: mark buffer as ready to go. */ + audit_buffer[from].header.generation = AUDIT_CURRENT_GENERATION; + + /* wake up next process waiting on this queue */ + wake_up_interruptible(&audit_wait_queue); + + return 0; /* success! */ + +write_failed: + + _Audit_if_want_event(current, AUDIT_EVENT_CLASS_FAWRITE) { + int result; + + _Audit_begin(2*sizeof(int)); + _Audit_append_simple(int, AUD_AET_AUD_WRITE); + _Audit_append_simple(int, retval); + _Audit_if_not_ok(_AUDIT_CAN_BLOCK, AUDIT_EVENT_CLASS_FAWRITE + ,&result) { + panic("failed audit auditwrite (%d)",current->pid); + } +#ifndef CONFIG_AUDIT_PERMIT_LUSER_FLOODING + /* + * Joe Luser can flood the audit buffer with this + * function - hence this little bit of code. + * Alternatively, if you prefer, the audit monitor (a + * module or daemon) could turn this process' + * AUDIT_EVENT_CLASS_FAWRITE bit off after one such + * event. In such a case you should + * + * #define CONFIG_AUDIT_PERMIT_LUSER_FLOODING + */ + current->audit_event_mask &= ~AUDIT_EVENT_CLASS_FAWRITE; +#endif /* CONFIG_AUDIT_PERMIT_LUSER_FLOODING */ + } + + return retval; +} + +/* userspace function to drain the audit buffer - not POSIX, but + needed if we are to avoid too much kernel bloat. */ + +asmlinkage int sys_auditor(void *sink_for_audit_data, int length, + int *number_p) +{ + int from, i, audit_atoms_needed; + + /* verify that we are permitted to drain the buffer */ + if (!capable(CAP_AUDIT_CONTROL)) { + return -EPERM; + } + + /* verify argument pointers */ + if (verify_area(VERIFY_WRITE, sink_for_audit_data, length) + || verify_area(VERIFY_WRITE, number_p, sizeof(int))) { + return -EINVAL; + } + + /* loop until we have read everything or filled the supplied buffer */ + cli(); + /* loop until something is available */ + while (audit_first_full_atom == audit_first_empty_atom) { + /* take advantage of this opportunity to shrink the + counters: avoid "249 day" effect.. */ + + audit_first_empty_atom = + audit_first_full_atom &= MAX_AUDIT_WRAP_MASK; + + /* return if interrupted */ + if (current->signal & ~current->blocked) { + sti(); + return -ERESTARTSYS; + } + /* sleep until we next get to the front of the queue - we + share this queue with the writing part of the audit + code. */ + interruptible_sleep_on(&audit_wait_queue); + } + + /* we have some audit data - verify that nothing has broken */ + if (audit_first_full_atom > audit_first_empty_atom) { + sti(); + panic("auditor has overrun audit trail (%d/%d)\n", + audit_first_full_atom, audit_first_empty_atom); + } + + /* now we take out as many individual audit entries as we can + given the space limitations imposed by the calling process. */ + + i = 0; + while (audit_first_full_atom < audit_first_empty_atom && + (from = (audit_first_full_atom & MAX_AUDIT_WRAP_MASK), + audit_buffer[from].header.generation)) { + /* check we have space left */ + if (length < i+audit_buffer[from].header.length) { + if (i) { + break; /* have something so return it */ + } else { + sti(); + return -EMSGSIZE; + } + } + + /* we have enough space to copy this audit record (too) */ + audit_atoms_needed = + (audit_buffer[from].header.length + + AUDIT_DATA_ATOM_SIZE-1)/ AUDIT_DATA_ATOM_SIZE; + + if ((from + audit_atoms_needed) > MAX_AUDIT_ATOMS) { + /* need to handle the wrapped entry case */ + int partial = AUDIT_DATA_ATOM_SIZE + *(MAX_AUDIT_ATOMS-from); + + memcpy_tofs(sink_for_audit_data+i, + &audit_buffer[from].header, partial); + memcpy_tofs(sink_for_audit_data+i+partial, + &audit_buffer[0].header, + audit_buffer[from].header.length-partial); + } else { + /* one simple chunk */ + memcpy_tofs(sink_for_audit_data+i, + &audit_buffer[from].header, + audit_buffer[from].header.length); + } + /* less space available for any more audit events */ + i += audit_buffer[from].header.length; + + /* and finally we move on to the next block */ + audit_filled_data_atoms -= audit_atoms_needed; + audit_empty_atoms += audit_atoms_needed; + audit_first_full_atom += audit_atoms_needed; + } + sti(); + memcpy_tofs(number_p, &i, sizeof(i)); + + /* wake up next process waiting on this queue */ + wake_up_interruptible(&audit_wait_queue); + + return 0; /* success! */ +} + +#else /* ie. "not" CONFIG_POSIX6_AUDIT */ + +asmlinkage int sys_auditswitch(int control, + int pid, + __u32 audit_mask_flags, + __u32 audit_append_flags, + __u32 *old_value_p) +{ + return -ENOSYS; +} + +asmlinkage int sys_auditwrite(__u32 event_mask, int length, + const void *user_audit_data) +{ + return -ENOSYS; +} + +asmlinkage int sys_auditor(void *sink_for_audit_data, int length, + int *number_p) +{ + return -ENOSYS; +} + +#endif /* CONFIG_POSIX6_AUDIT */ diff -urN linux-2.0.31pre9-privs-clean/kernel/exit.c linux-2.0.31pre9-privs/kernel/exit.c --- linux-2.0.31pre9-privs-clean/kernel/exit.c Wed Sep 10 15:05:45 1997 +++ linux-2.0.31pre9-privs/kernel/exit.c Sun Sep 21 15:14:14 1997 @@ -17,6 +17,7 @@ #include #include #include +#include #include #include @@ -132,7 +133,7 @@ kfree(p); return; } - panic("trying to release non-existent task"); + panic("trying to release non-existent task"); } #ifdef DEBUG_PROC_TREE @@ -318,9 +319,9 @@ { int err, retval = 0, count = 0; - if (!pid) - return(kill_pg(current->pgrp,sig,0)); - if (pid == -1) { + if (!pid) { + retval = kill_pg(current->pgrp,sig,0); + } else if (pid == -1) { struct task_struct * p; for_each_task(p) { if (p->pid > 1 && p != current) { @@ -329,12 +330,33 @@ retval = err; } } - return(count ? retval : -ESRCH); + retval = count ? retval : -ESRCH; + } else if (pid < 0) { + retval = kill_pg(-pid,sig,0); + } else { + /* Normal kill */ + retval = kill_proc(pid,sig,0); + } + + /* use err for audit event_mask */ + err = AUDIT_EVENT_CLASS_END|AUDIT_EVENT_CLASS_PROC| + (retval ? AUDIT_EVENT_CLASS_DENIED:AUDIT_EVENT_CLASS_SUCCESS); + + /* audit this event */ + _Audit_if_want_event(current,err) { + int result; + + _Audit_begin(4*sizeof(int)); + _Audit_append_simple(int, AUD_AET_KILL); + _Audit_append_simple(int, pid); + _Audit_append_simple(int, sig); + _Audit_append_simple(int, retval); + _Audit_if_not_ok(_AUDIT_CAN_BLOCK, err, &result) { + panic("failed audit kill (%d)", current->pid); + } } - if (pid < 0) - return(kill_pg(-pid,sig,0)); - /* Normal kill */ - return(kill_proc(pid,sig,0)); + + return retval; } /* @@ -604,6 +626,21 @@ asmlinkage int sys_exit(int error_code) { + _Audit_if_want_event(current, + AUDIT_EVENT_CLASS_END|AUDIT_EVENT_CLASS_PROC) { + int result; + + _Audit_begin(2*sizeof(int)); + _Audit_append_simple(int, AUD_AET_EXIT); + _Audit_append_simple(int, error_code); + _Audit_if_not_ok(_AUDIT_CAN_BLOCK, + AUDIT_EVENT_CLASS_END + |AUDIT_EVENT_CLASS_PROC + |AUDIT_EVENT_CLASS_SUCCESS, + &result) { + panic("failed audit exit (%d)", current->pid); + } + } do_exit((error_code&0xff)<<8); } diff -urN linux-2.0.31pre9-privs-clean/kernel/printk.c linux-2.0.31pre9-privs/kernel/printk.c --- linux-2.0.31pre9-privs-clean/kernel/printk.c Wed Sep 10 15:05:46 1997 +++ linux-2.0.31pre9-privs/kernel/printk.c Wed Sep 10 20:36:10 1997 @@ -66,7 +66,7 @@ char c; int error; - if ((type != 3) && !capable(CAP_AUDIT_CONTROL)) /* CAP.FIXME */ + if ((type != 3) && !capable(CAP_SYS_ADMIN)) /* CAP.FIXME */ return -EPERM; switch (type) { case 0: /* Close log */ diff -urN linux-2.0.31pre9-privs-clean/kernel/sys.c linux-2.0.31pre9-privs/kernel/sys.c --- linux-2.0.31pre9-privs-clean/kernel/sys.c Wed Sep 10 15:05:46 1997 +++ linux-2.0.31pre9-privs/kernel/sys.c Sun Sep 21 13:42:32 1997 @@ -4,6 +4,7 @@ * Copyright (C) 1991, 1992 Linus Torvalds */ +#include #include #include #include @@ -282,18 +283,36 @@ */ asmlinkage int sys_setgid(gid_t gid) { + __u32 audit_mask = AUDIT_EVENT_CLASS_GID|AUDIT_EVENT_CLASS_PROC; + int retval = 0; int old_egid = current->egid; - if (capable(CAP_SETGID)) + if ((gid == current->gid) || (gid == current->sgid)) + current->egid = current->fsgid = gid; + else if (capable(CAP_SETGID)) { current->gid = current->egid = current->sgid = current->fsgid = gid; - else if ((gid == current->gid) || (gid == current->sgid)) - current->egid = current->fsgid = gid; - else - return -EPERM; - if (current->egid != old_egid) + } else + retval = -EPERM; + + if (!retval && current->egid != old_egid) current->dumpable = 0; - return 0; + + audit_mask |= retval ? + AUDIT_EVENT_CLASS_DENIED:AUDIT_EVENT_CLASS_SUCCESS; + + _Audit_if_want_event(current,audit_mask) { + int result; + + _Audit_begin(2*sizeof(int)); + _Audit_append_simple(int, AUD_AET_SETGID); + _Audit_append_simple(int, gid); + _Audit_if_not_ok(_AUDIT_CAN_BLOCK,audit_mask,&result) { + panic("failed audit setgid (%d)", current->pid); + } + } + + return retval; } static char acct_active = 0; @@ -504,22 +523,41 @@ */ asmlinkage int sys_setuid(uid_t uid) { + __u32 audit_mask = AUDIT_EVENT_CLASS_UID|AUDIT_EVENT_CLASS_PROC; + int retval = 0; int old_euid = current->euid; - if (capable(CAP_SETUID)) + if ((uid == current->uid) || (uid == current->suid)) + current->fsuid = current->euid = uid; + else if (capable(CAP_SETUID)) { current->uid = current->euid = current->suid = current->fsuid = uid; - else if ((uid == current->uid) || (uid == current->suid)) - current->fsuid = current->euid = uid; - else - return -EPERM; - if (current->euid != old_euid) { + } else + retval = -EPERM; + + if (!retval && current->euid != old_euid) { #ifdef CONFIG_POSIX6_CAP + /* XXX.CAP - should we do this? clear permitted too? */ cap_clear(¤t->cap_effective); #endif current->dumpable = 0; } - return(0); + + audit_mask |= retval ? + AUDIT_EVENT_CLASS_DENIED:AUDIT_EVENT_CLASS_SUCCESS; + + _Audit_if_want_event(current,audit_mask) { + int result; + + _Audit_begin(2*sizeof(int)); + _Audit_append_simple(int, AUD_AET_SETUID); + _Audit_append_simple(int, uid); + _Audit_if_not_ok(_AUDIT_CAN_BLOCK,audit_mask,&result) { + panic("failed audit setuid (%d)", current->pid); + } + } + + return retval; } /* diff -urN linux-2.0.31pre9-privs-clean/scripts/audit_events.pl linux-2.0.31pre9-privs/scripts/audit_events.pl --- linux-2.0.31pre9-privs-clean/scripts/audit_events.pl Wed Dec 31 16:00:00 1969 +++ linux-2.0.31pre9-privs/scripts/audit_events.pl Sun Sep 21 13:38:35 1997 @@ -0,0 +1,50 @@ +#!/usr/bin/perl +# +# this file searches the standard input for audit events +# + +$bracecount = 0; +$inaline = 0; + +while ($file = shift @ARGV) { + + open INPUT_FILE, "<$file" || next "couldn't read $file\n"; + + while ($line = ) { + $showme = 0; + + chomp($line); + if ($inaline) { + $showme = 1; + if ($line =~ /\;$/) { + $inaline = 0; + } elsif ($line =~ /\{$/) { + $inaline = 0; + ++$bracecount; + } + } elsif ($line =~ /_Audit_/) { + $showme = 1; + if ($line =~ /\{$/) { + ++$bracecount; + } elsif (!($line =~ /\;$/)) { + $inaline = 1; + } + } elsif ($bracecount) { + $showme = 1; + if ($line =~ /\}$/) { + --$bracecount; + } elsif ($line =~ /\{$/) { + ++$bracecount; + } + } + + if ($showme) { + if ($file) { + print("$file:\n"); + $file = ""; + } + print("$line\n"); + } + } + +} diff -urN linux-2.0.31pre9-privs-clean/scripts/audit_events.pre linux-2.0.31pre9-privs/scripts/audit_events.pre --- linux-2.0.31pre9-privs-clean/scripts/audit_events.pre Wed Dec 31 16:00:00 1969 +++ linux-2.0.31pre9-privs/scripts/audit_events.pre Thu Sep 11 17:28:32 1997 @@ -0,0 +1,29 @@ +# +# It is intended that this file will contain all of the audit events. +# Before compiling the kernel, this file will be parsed into the form +# of an include file that can be used to include appropriate events +# with a custom macro. It should also be noted that the output file +# will contain appropriate provision for compiling without audit support +# by providing an alternative #define for each macro. +# +# Version 0 of the syntax is something like: +# +# macro_name(macro_args) AUD_AET_"EVENT_IDENTIFIER" { +# [ type , value ] +# < type , value_p > +# ... +# } +# +# Where the AUD_AET_"EVENT_IDENTIFIER" is the id for the specific audit +# event. [ ] indicates that the datum will expand to _Audit_append_simple() +# and <> to _Audit_append_by_ptr . +# +# The principal reason for thinking this is a good idea is that this will +# make the creation of an audit analysis tool much easier. Another +# alternative is to simply parse the 'C' of the kernel for events as they +# appear there.. +# +# Currently, this latter course is chosen as we have few audited events. +# Later this may become inappropriate and we can return to the suggestions +# above. +# diff -urN linux-2.0.31pre9-privs-clean/scripts/mkaudit_aet_h linux-2.0.31pre9-privs/scripts/mkaudit_aet_h --- linux-2.0.31pre9-privs-clean/scripts/mkaudit_aet_h Wed Dec 31 16:00:00 1969 +++ linux-2.0.31pre9-privs/scripts/mkaudit_aet_h Sat Sep 13 17:49:39 1997 @@ -0,0 +1,20 @@ +#!/bin/bash +# +# This script makes a file from the current +# file. +cat > include/asm/audit_aet.h < it should not + * be changed by hand. If you need to add anything, add it to + * which includes this file + */ +#ifndef ASM_AUDIT_AET_H +#define ASM_AUDIT_AET_H + +EOF +cat include/asm/unistd.h | sed -ne '/^#define[\t ]__NR_/{s/^#define[\t ]__NR_//;s/[ \t]+/\t/;y/abcdefghijklmnopqrstuvwxyz/ABCDEFGHIJKLMNOPQRSTUVWXYZ/;s/^/#define AUD_AET_/;s@[\t]*/\*[^\n]*\*/[\t]*$@@;p;}' | fgrep -v '_NR_' >> include/asm/audit_aet.h +cat >> include/asm/audit_aet.h <