Browse Source

A new attempt

audit-shell
Marc Schoechlin 8 years ago
parent
commit
750ed35980
6 changed files with 90 additions and 393 deletions
  1. +25
    -73
      README.md
  2. +24
    -6
      helpers/auditshell
  3. +0
    -57
      helpers/auditshell_create_sessionfiles
  4. +0
    -131
      helpers/auditshell_script-upstream.patch
  5. +0
    -126
      helpers/auditshell_script.patch
  6. +41
    -0
      helpers/usr.local.bin.auditshell

+ 25
- 73
README.md View File

@ -118,91 +118,43 @@ SEE ALSO
# Auditshell
Auditshell submits the typescript and the timings of a patched util-linux/script binary to syslog which prevents modification by regular terminal users.
The logged information can also be forwarded to secured logging servers using standard syslog logfile distribution.
Auditshell replaces bash by audited replacement which writes scriptreplay data to /var/log/auditshell.
The apparmor profile prevents audited users to read, change or delete the recorded sessions and to change thei shell.
## Installation of "auditshell"
The following instructions describe the procedure how to install a audit shell in combination with
the scriptreplay utility.
* Install tools
```bash
cp scriptreplay helpers/auditshell helpers/auditshell_create_sessionfiles /usr/local/bin/
chown root:root /usr/local/bin/{scriptreplay,auditshell,auditshell_create_sessionfiles}
chmod 755 /usr/local/bin/{scriptreplay,auditshell,auditshell_create_sessionfiles}
```
* Install Build dependencies
```bash
apt-get install libtoolize libtool autopoint pkg-config make gcc
zypper install libtool gettext-tools pkg-config make gcc autoconf automake
* Install the tools and the apparmor configuration
```
cp scriptreplay helpers/auditshell /usr/local/bin/
cp helpers/usr.local.bin.auditshell /etc/apparmor.d/usr.local.bin.auditshell
chmod 755 /usr/local/bin/auditshell /usr/local/bin/scriptreplay
chown root:root /usr/local/bin/auditshell /usr/local/bin/scriptreplay
```
* Create a directory for auditfiles
```
mkdir /var/log/auditshell/
chmod 1777 /var/log/auditshell/
```
* Patch and install custom "script" implementation
```bash
cd helpers/
wget https://www.kernel.org/pub/linux/utils/util-linux/v2.23/util-linux-2.23.tar.gz
tar zxvf util-linux-2.23.tar.gz
cd util-linux-2.23/
patch -p1 < ../auditshell_script.patch
./configure --without-ncurses --disable-nls
make
cp script /usr/local/bin/
chown root:root /usr/local/bin/script
chmod 755 /usr/local/bin/script
* Enable apparmor profle
```
aa-enforce /usr/local/bin/auditshell
```
* Change shell of user which should be audited
```
* Syslog configuration:
* Disable string escaping on system which are using rsyslogd (i.e. Ubuntu systems with rsyslogd)
* Redirect the auditshell logs to another logfile using syslog configuration
* Change shell of user
```bash
chsh -s /usr/local/bin/auditshell <user>
```
* Prevent regular users to change their shell
* Remove all shells from /etc/shells
* Remove all perrmissions from the the /usr/bin/chsh binary
* Use the FAKE_SHELL variable in /etc/login.defs
## Watch auditshell sessions
* Start session, and execute commands
* Extract session files
```bash
/usr/local/bin/auditshell_create_sessionfiles /var/log/messages /tmp/foo
* Identify a session
```
* Replay session
```bash
scriptreplay -t /tmp/foo/2013-09-11_18-47-45.user1.11931.timing \
/tmp/foo/2013-09-11_18-47-45.user1.11931.typescript
cd /var/log/auditshell
ls -1d *
```
* View a session
```
scriptreplay -t 2017-10-21_10-19-17.marc.2159/timing* -s 2017-10-21_10-19-17.marc.2159/typescript*
```
## Logging configuration
### Syslog-NG Configuration
* Edit /etc/syslog-ng/syslog-ng.conf
```
# define audit shell filter
filter f_auditshell { match('^auditshell'); };
# enhance existing messages filter by f_auditshell to ignore messages matched by f_auditshell
filter f_messages { not facility(news, mail) and not filter(f_iptables) and not filter(f_auditshell); };
# define a log-sink for auditshell
destination auditshell {
file ("/var/log/auditshell/$YEAR-$MONTH/$FACILITY-$YEAR-$MONTH-$DAY"
owner(root) group(root) perm(0600) dir_perm(0700) create_dirs(yes)
);
};
log { source(src); filter(f_auditshell); destination(auditshell); };
```
* Restart Syslogd
```
/etc/init.d/syslog restart
```

+ 24
- 6
helpers/auditshell View File

@ -2,6 +2,11 @@
IDENT="`date --date="today" "+%Y-%m-%d_%H-%M-%S"`.`whoami`.$$"
LOGDIR="/var/log/auditshell/${IDENT}"
TYPESCRIPT="${LOGDIR}/typescript.${IDENT}"
TIMING="${LOGDIR}/timing.${IDENT}"
# This is a file transfer, no audit shell neccessary
if (echo "$@"|egrep -q "^-c.*scp.*$");then
logger -t auditshell.filetransfer.${IDENT} <<< "/bin/sh $@"
@ -12,8 +17,18 @@ elif (echo "$@"|egrep -q "^-c.*$");then
exec /bin/bash "$@"
fi
TYPESCRIPT="auditshell.typescript.${IDENT}"
TIMING="auditshell.timing.${IDENT}"
if [ "$AUDITSHELL" ];then
echo "INFO: already in a auditshell session"
exit 1
fi
mkdir $LOGDIR
RET="$?"
if [ "$RET" != "0" ];then
echo "ERROR: Creation of dir '$LOGDIR' failed, exitcode $RET"
exit 1
fi
export SHELL=/bin/bash
@ -31,7 +46,10 @@ AUDIT KEY: $IDENT
EOF
/usr/local/bin/script -d -e -f -q -t 5 \
5> >(base64|logger -t $TYPESCRIPT) \
2> >(base64|logger -t $TIMING)
echo "Finish"
export AUDITSHELL="$IDENT"
umask 0077
logger -t auditshell.session.${IDENT} <<< "Starting auditshell session for user $USER"
script -f -e -q --timing=$TIMING $TYPESCRIPT -c "/bin/bash -l"
logger -t auditshell.session.${IDENT} <<< "Finished auditshell session for user $USER"
echo "AUDITSHELL FINISHED"

+ 0
- 57
helpers/auditshell_create_sessionfiles View File

@ -1,57 +0,0 @@
#!/usr/bin/env perl
use strict;
use warnings;
use FileHandle;
my $file = shift();
my $dir = shift();
if ( (!defined $file) || (!defined $file) ){
print "auditshell_create_sessionfiles <logfile> <dir>\n";
exit(1);
}
chdir($dir);
unless(chdir($dir))
{
die "Error: Can't change directory!: $!";
}
open( INFILE, "<$file" ) || die "input-file '$file' could not be opened";
my $fdcache = {};
while (my $zeile = <INFILE>) {
if ($zeile =~m /auditshell\.(typescript|timing)\.(.*?): (.*)$/){
chomp($zeile);
my $type = $1;
my $ident = $2;
my $line = $3;
if ( !exists $fdcache->{$ident}){
$fdcache->{$ident} = {};
print "Create $ident.typescript.base64\n";
$fdcache->{$ident}->{typescript} = FileHandle->new("> $ident.typescript.base64");
print "Create $ident.timing.base64\n";
$fdcache->{$ident}->{timing} = FileHandle->new("> $ident.timing.base64");
}
my $fd = $fdcache->{$ident}->{$type};
print $fd $line."\n";
}
}
close(INFILE);
foreach my $ident(keys %{$fdcache}){
close $fdcache->{$ident}->{typescript};
close $fdcache->{$ident}->{timing};
system("base64 -d $ident.typescript.base64 |gzip -c > $ident.typescript.gz");
system("base64 -d $ident.timing.base64 |gzip -c > $ident.timing.gz");
unlink("$ident.timing.base64");
unlink("$ident.typescript.base64");
print "removed $ident.typescript.base64, created $ident.typescript.gz\n";
print "removed $ident.timing.base64, created $ident.timing.gz\n";
}

+ 0
- 131
helpers/auditshell_script-upstream.patch View File

@ -1,131 +0,0 @@
diff --git a/term-utils/script.1 b/term-utils/script.1
index 5c366b129..33789a927 100644
--- a/term-utils/script.1
+++ b/term-utils/script.1
@@ -86,14 +86,21 @@ or symbolic link. The command will follow a symbolic link.
Be quiet (do not write start and done messages to either standard output
or the typescript file).
.TP
-\fB\-t\fR, \fB\-\-timing\fR[=\fIfile\fR]
-Output timing data to standard error, or to
+\fB\-t\fR, \fB\-\-timing\fR[=\fIfile|descriptor\fR]
+Output timing data to standard error, or to a
.I file
+or
+.I descriptor
when given. This data contains two fields, separated by a space. The first
field indicates how much time elapsed since the previous output. The second
field indicates how many characters were output this time. This information
can be used to replay typescripts with realistic typing and output delays.
.TP
+\fB\-d\fR, \fB\-\-descriptor\fR
+Use this option in combination with
+.I --timing
+for using the given resource as a file descriptor number as destination for timing data
+.TP
\fB\-V\fR, \fB\-\-version\fR
Display version information and exit.
.TP
diff --git a/term-utils/script.c b/term-utils/script.c
index d1ef07203..4b56b671d 100644
--- a/term-utils/script.c
+++ b/term-utils/script.c
@@ -116,7 +116,8 @@ struct script_control {
#endif
unsigned int
append:1, /* append output */
- rc_wanted:1, /* return child exit value */
+ tsdesc:1, /* output typescript to file descriptor */
+ rc_wanted:1, /* return child exit value */
flush:1, /* flush after each write */
quiet:1, /* suppress most output */
timing:1, /* include timing file */
@@ -169,6 +170,7 @@ static void __attribute__((__noreturn__)) usage(FILE *out)
" --force use output file even when it is a link\n"
" -q, --quiet be quiet\n"
" -t, --timing[=<file>] output timing data to stderr (or to FILE)\n"
+ " -d, --descriptor[=<n>] output timing data to a defined descriptor number\n"
" -V, --version output version information and exit\n"
" -h, --help display this help and exit\n\n"), out);
@@ -195,8 +197,14 @@ static void __attribute__((__noreturn__)) done(struct script_control *ctl)
if (ctl->isterm)
tcsetattr(STDIN_FILENO, TCSADRAIN, &ctl->attrs);
- if (!ctl->quiet && ctl->typescriptfp)
- printf(_("Script done, file is %s\n"), ctl->fname);
+ if (!ctl->quiet && ctl->typescriptfp) {
+ if (ctl->tsdesc == 1) {
+ printf(_("Script done, file descriptor number is %s\n"), ctl->fname);
+ }else{
+ printf(_("Script done, file is %s\n"), ctl->fname);
+ }
+ }
+
#ifdef HAVE_LIBUTEMPTER
if (ctl->master >= 0)
utempter_remove_record(ctl->master);
@@ -407,6 +415,7 @@ static void do_io(struct script_control *ctl)
int ret, ignore_stdin = 0, eof = 0;
time_t tvec = script_time((time_t *)NULL);
char buf[128];
+ int fdnum_typescript = 0;
enum {
POLLFD_SIGNAL = 0,
POLLFD_MASTER,
@@ -420,7 +429,18 @@ static void do_io(struct script_control *ctl)
};
- if ((ctl->typescriptfp =
+
+ if (ctl->tsdesc == 1){
+ fdnum_typescript = atoi(ctl->fname);
+ if (fdnum_typescript == 0){
+ warn(_("file descriptor is not a number"));
+ fail(ctl);
+ }
+ if ((ctl->typescriptfp = fdopen (fdnum_typescript, "w")) == NULL) {
+ warn(_("cannot open fd %s"), ctl->fname);
+ fail(ctl);
+ }
+ }else if ((ctl->typescriptfp =
fopen(ctl->fname, ctl->append ? "a" UL_CLOEXECSTR : "w" UL_CLOEXECSTR)) == NULL) {
warn(_("cannot open %s"), ctl->fname);
fail(ctl);
@@ -678,6 +698,7 @@ int main(int argc, char **argv)
{"force", no_argument, NULL, FORCE_OPTION,},
{"quiet", no_argument, NULL, 'q'},
{"timing", optional_argument, NULL, 't'},
+ {"descriptor", no_argument, NULL, 'd' },
{"version", no_argument, NULL, 'V'},
{"help", no_argument, NULL, 'h'},
{NULL, 0, NULL, 0}
@@ -698,8 +719,10 @@ int main(int argc, char **argv)
script_init_debug();
- while ((ch = getopt_long(argc, argv, "ac:efqt::Vh", longopts, NULL)) != -1)
+ while ((ch = getopt_long(argc, argv, "dac:efqt::Vh", longopts, NULL)) != -1)
switch (ch) {
+ case 'd':
+ ctl.tsdesc = 1;
case 'a':
ctl.append = 1;
break;
@@ -748,8 +771,13 @@ int main(int argc, char **argv)
ctl.shell = _PATH_BSHELL;
getmaster(&ctl);
- if (!ctl.quiet)
- printf(_("Script started, file is %s\n"), ctl.fname);
+ if (!ctl.quiet) {
+ if (ctl.tsdesc == 1) {
+ printf(_("Script started, file descriptor number is %s\n"), ctl.fname);
+ }else{
+ printf(_("Script started, file is %s\n"), ctl.fname);
+ }
+ }
fixtty(&ctl);
#ifdef HAVE_LIBUTEMPTER

+ 0
- 126
helpers/auditshell_script.patch View File

@ -1,126 +0,0 @@
diff --git a/term-utils/script.1 b/term-utils/script.1
index 1e430d8..1808a37 100644
--- a/term-utils/script.1
+++ b/term-utils/script.1
@@ -84,14 +84,21 @@ or symbolic link. The command will follow a symbolic link.
\fB\-q\fR, \fB\-\-quiet\fR
Be quiet.
.TP
-\fB\-t\fR, \fB\-\-timing\fR[=\fIfile\fR]
-Output timing data to standard error, or to
+\fB\-t\fR, \fB\-\-timing\fR[=\fIfile|descriptor\fR]
+Output timing data to standard error, or to a
.I file
+or
+.I descriptor
when given. This data contains two fields, separated by a space. The first
field indicates how much time elapsed since the previous output. The second
field indicates how many characters were output this time. This information
can be used to replay typescripts with realistic typing and output delays.
.TP
+\fB\-d\fR, \fB\-\-descriptor\fR
+Use this option in combination with
+.I --timing
+for using the given resource as a file descriptor number as destination for timing data
+.TP
\fB\-V\fR, \fB\-\-version\fR
Output version information and exit.
.TP
diff --git a/term-utils/script.c b/term-utils/script.c
index 242b815..778fbbe 100644
--- a/term-utils/script.c
+++ b/term-utils/script.c
@@ -101,12 +101,14 @@ int l;
char line[] = "/dev/ptyXX";
#endif
int aflg = 0;
+int dflg = 0;
char *cflg = NULL;
int eflg = 0;
int fflg = 0;
int qflg = 0;
int tflg = 0;
int forceflg = 0;
+int fdnum_typescript = 0;
int die;
int resized;
@@ -171,6 +173,7 @@ main(int argc, char **argv) {
{ "force", no_argument, NULL, FORCE_OPTION, },
{ "quiet", no_argument, NULL, 'q' },
{ "timing", optional_argument, NULL, 't' },
+ { "descriptor", no_argument, NULL, 'd' },
{ "version", no_argument, NULL, 'V' },
{ "help", no_argument, NULL, 'h' },
{ NULL, 0, NULL, 0 }
@@ -182,8 +185,10 @@ main(int argc, char **argv) {
textdomain(PACKAGE);
atexit(close_stdout);
- while ((ch = getopt_long(argc, argv, "ac:efqt::Vh", longopts, NULL)) != -1)
+ while ((ch = getopt_long(argc, argv, "dac:efqt::Vh", longopts, NULL)) != -1)
switch(ch) {
+ case 'd':
+ dflg = 1;
case 'a':
aflg = 1;
break;
@@ -229,7 +234,22 @@ main(int argc, char **argv) {
fname = DEFAULT_OUTPUT;
die_if_link(fname);
}
- if ((fscript = fopen(fname, aflg ? "a" : "w")) == NULL) {
+
+
+ if (dflg == 1){
+
+ fdnum_typescript = atoi(fname);
+
+ if (fdnum_typescript == 0){
+ warn(_("file descriptor is not a number"));
+ fail();
+ }
+
+ if ((fscript = fdopen (fdnum_typescript, "w")) == NULL) {
+ warn(_("cannot open fd %s"), fname);
+ fail();
+ }
+ }else if((fscript = fopen(fname, aflg ? "a" : "w")) == NULL) {
warn(_("cannot open %s"), fname);
fail();
}
@@ -239,8 +259,15 @@ main(int argc, char **argv) {
shell = _PATH_BSHELL;
getmaster();
- if (!qflg)
+
+ if (!qflg) {
+ if (dflg == 1){
+ printf(_("Script started, file descriptor number is %s\n"), fname);
+ }else{
printf(_("Script started, file is %s\n"), fname);
+ }
+ }
+
fixtty();
#ifdef HAVE_LIBUTEMPTER
@@ -495,8 +520,16 @@ done(void) {
master = -1;
} else {
tcsetattr(STDIN_FILENO, TCSADRAIN, &tt);
- if (!qflg)
+
+ if (!qflg) {
+ if (dflg == 1){
+ printf(_("Script done, file descriptor number is %s\n"), fname);
+ }else{
printf(_("Script done, file is %s\n"), fname);
+ }
+ }
+
+
#ifdef HAVE_LIBUTEMPTER
if (master >= 0)
utempter_remove_record(master);

+ 41
- 0
helpers/usr.local.bin.auditshell View File

@ -0,0 +1,41 @@
# Apparmor profile for the auditshell
#include<tunables/global>
/usr/local/bin/auditshell {
#include <abstractions/base>
/** lrwix,
/bin/bash cx,
profile /bin/bash {
#include <abstractions/base>
#include <abstractions/bash>
network inet tcp,
/** lrwix,
# TCP/UDP network access
network inet stream,
network inet6 stream,
network inet dgram,
network inet6 dgram,
network netlink raw,
deny /usr/bin/chsh lrwx,
deny /var/log/auditshell/ lrwx,
deny /var/log/auditshell/** lrwx,
#include <abstractions/dbus-strict>
# dbus send
# bus=system
# path="/org/freedesktop/resolve1"
# interface="org.freedesktop.resolve1.Manager"
# member="Resolve{Address,Hostname,Record,Service}"
# peer=(name="org.freedesktop.resolve1"),
}
}

Loading…
Cancel
Save