This patch extends Eric's test patch from 11/17 ( http://www.redhat.com/archives/linux-audit/2011-November/msg00045.html). This turns -C into a long opt with similar syntax to -F.
This allows uid/euid and gid/egid to be compared, like auditctl -a exit,always -F arch=b64 -C 'euid!=uid' -S execve -F 'euid!=0' -F 'success=1' which would audit on someone executing a setuid binary if the binary isn't setuid root. You can also check for writes to overly permissive files like auditctl -a exit,always -F arch=b64 -C 'obj_uid!=uid' -F 'uid!=0' -F 'dir=/home/' -F 'success=1' -S open -F 'a2&=2' This functionality is helpful in detecting user compromises across a shared fleet; eg, attacker finds a world-writable shell script (/home/victim/.bashrc, it's happened...) and inserts "cp /bin/bash /tmp/; chmod 7777 /tmp/bash". After victim executes this, attacker executes /tmp/bash -p and becomes victim. One strange thing related to this patch: auditd seems to be reporting success for a normal user process (gklrellm) opening /proc/meminfo (mode 444) O_RDWR, and I don't see how this is possible. eg: type=SYSCALL msg=audit(1323540255.146:97): arch=c000003e syscall=2 success=yes exit=13 a0=4b1972 a1=0 a2=1b6 a3=0 items=1 ppid=1704 pid=1797 auid=11532 uid=11532 gid=5000 euid=11532 suid=11532 fsuid=11532 egid=5000 sgid=5000 fsgid=5000 tty=(none) ses=1 comm="gkrellm" exe="/usr/bin/gkrellm" key="permissive" type=CWD msg=audit(1323540255.146:97): cwd="/home/pmoody" type=PATH msg=audit(1323540255.146:97): item=0 name="/proc/meminfo" inode= 4026532008 dev=00:03 mode=0100444 ouid=0 ogid=0 rdev=00:00 hopefully someone with more auditd internal knowledge can explain what's going on. auditctl -l doesn't know how to report this yet; if this patch is generally acceptable, I can try to fix that and update the manpage, etc. Signed-off-by: Peter Moody <[email protected]> --- trunk/auparse/typetab.h | 1 + trunk/lib/fieldtab.h | 1 + trunk/lib/libaudit.c | 144 +++++++++++++++++++++++++++++++++++++++++++ trunk/lib/libaudit.h | 2 + trunk/src/auditctl.c | 19 +++++- trunk/src/ausearch-report.c | 1 + 6 files changed, 166 insertions(+), 2 deletions(-) diff --git a/trunk/auparse/typetab.h b/trunk/auparse/typetab.h index 746573c..3e6c6d1 100644 --- a/trunk/auparse/typetab.h +++ b/trunk/auparse/typetab.h @@ -32,6 +32,7 @@ _S(AUPARSE_TYPE_UID, "iuid" ) _S(AUPARSE_TYPE_UID, "id" ) _S(AUPARSE_TYPE_UID, "inode_uid" ) _S(AUPARSE_TYPE_UID, "sauid" ) +_S(AUPARSE_TYPE_UID, "obj_uid" ) _S(AUPARSE_TYPE_GID, "gid" ) _S(AUPARSE_TYPE_GID, "egid" ) _S(AUPARSE_TYPE_GID, "sgid" ) diff --git a/trunk/lib/fieldtab.h b/trunk/lib/fieldtab.h index ad95814..e053df6 100644 --- a/trunk/lib/fieldtab.h +++ b/trunk/lib/fieldtab.h @@ -55,6 +55,7 @@ _S(AUDIT_WATCH, "path" ) _S(AUDIT_PERM, "perm" ) _S(AUDIT_DIR, "dir" ) _S(AUDIT_FILETYPE, "filetype" ) +_S(AUDIT_OBJ_UID, "obj_uid" ) _S(AUDIT_ARG0, "a0" ) _S(AUDIT_ARG1, "a1" ) diff --git a/trunk/lib/libaudit.c b/trunk/lib/libaudit.c index 9a5070c..b10f984 100644 --- a/trunk/lib/libaudit.c +++ b/trunk/lib/libaudit.c @@ -783,6 +783,148 @@ int audit_rule_syscallbyname_data(struct audit_rule_data *rule, } hidden_def(audit_rule_syscallbyname_data) +int audit_rule_interfield_fieldpair_data(struct audit_rule_data **rulep, + const char *pair, + int flags) { + const char *f = pair; + char *v; + int op; + int field1, field2; + int vlen; + int offset; + struct audit_rule_data *rule = *rulep; + + if (f == NULL) + return -1; + + /* look for 2-char operators first + then look for 1-char operators afterwards + when found, null out the bytes under the operators to split + and set value pointer just past operator bytes + */ + if ( (v = strstr(pair, "!=")) ) { + *v++ = '\0'; + *v++ = '\0'; + op = AUDIT_NOT_EQUAL; + } else if ( (v = strstr(pair, "=")) ) { + *v++ = '\0'; + op = AUDIT_EQUAL; + } else { + fprintf(stderr, "only =, != comparisons are allowed in interfield\n"); + return -1; + } + + if (v == NULL) + return -1; + + if (*f == 0) + return -22; + + if (*v == 0) + return -20; + + if ((field1 = audit_name_to_field(f)) < 0) + return -2; + + if ((field2 = audit_name_to_field(v)) < 0) + return -2; + + /* Exclude filter can be used only with MSGTYPE field */ + if (flags == AUDIT_FILTER_EXCLUDE && field1 != AUDIT_MSGTYPE) + return -12; + + // It should always be AUDIT_FIELD_COMPARE + rule->fields[rule->field_count] = AUDIT_FIELD_COMPARE; + rule->fieldflags[rule->field_count] = op; + switch (field1) + { + case AUDIT_UID: + switch(field2) { + case AUDIT_EUID: + rule->values[rule->field_count] = AUDIT_COMPARE_UID_TO_EUID; + break; + case AUDIT_OBJ_UID: + rule->values[rule->field_count] = AUDIT_COMPARE_UID_TO_OBJ_UID; + break; + default: + return -1; + } + break; + case AUDIT_EUID: + switch(field2) { + case AUDIT_UID: + rule->values[rule->field_count] = AUDIT_COMPARE_UID_TO_EUID; + break; + default: + return -1; + } + break; + case AUDIT_OBJ_UID: + switch(field2) { + case AUDIT_UID: + rule->values[rule->field_count] = AUDIT_COMPARE_UID_TO_OBJ_UID; + break; + default: + return -1; + } + break; + case AUDIT_OBJ_GID: + switch(field2) { + case AUDIT_GID: + rule->values[rule->field_count] = AUDIT_COMPARE_GID_TO_OBJ_GID; + break; + default: + return -1; + } + break; + case AUDIT_GID: + switch(field2) { + case AUDIT_EGID: + rule->values[rule->field_count] = AUDIT_COMPARE_GID_TO_EGID; + break; + case AUDIT_OBJ_GID: + rule->values[rule->field_count] = AUDIT_COMPARE_GID_TO_OBJ_GID; + break; + default: + return -1; + } + break; + case AUDIT_EGID: + switch(field2) { + case AUDIT_OBJ_GID: + rule->values[rule->field_count] = AUDIT_COMPARE_GID_TO_EGID; + break; + default: + return -1; + } + break; + /* fallthrough */ + default: + if (field1 == AUDIT_INODE) { + if (!(op == AUDIT_NOT_EQUAL || + op == AUDIT_EQUAL)) + return -13; + } + + if (field1 == AUDIT_PPID && !(flags == AUDIT_FILTER_EXIT + || flags == AUDIT_FILTER_ENTRY)) + return -17; + + if (!isdigit((char)*(v))) + return -21; + + if (field1 == AUDIT_INODE) + rule->values[rule->field_count] = + strtoul(v, NULL, 0); + else + rule->values[rule->field_count] = + strtol(v, NULL, 0); + break; + } + rule->field_count++; + return 0; +} + int audit_rule_fieldpair_data(struct audit_rule_data **rulep, const char *pair, int flags) { @@ -857,6 +999,8 @@ int audit_rule_fieldpair_data(struct audit_rule_data **rulep, const char *pair, case AUDIT_SUID: case AUDIT_FSUID: case AUDIT_LOGINUID: + case AUDIT_OBJ_UID: + case AUDIT_OBJ_GID: // Do positive & negative separate for 32 bit systems vlen = strlen(v); if (isdigit((char)*(v))) diff --git a/trunk/lib/libaudit.h b/trunk/lib/libaudit.h index 8feaa39..911bce4 100644 --- a/trunk/lib/libaudit.h +++ b/trunk/lib/libaudit.h @@ -428,6 +428,8 @@ extern int audit_rule_syscallbyname_data(struct audit_rule_data *rule, * adding new fields */ extern int audit_rule_fieldpair_data(struct audit_rule_data **rulep, const char *pair, int flags); +extern int audit_rule_interfield_fieldpair_data(struct audit_rule_data **rulep, + const char *pair, int flags); extern void audit_rule_free_data(struct audit_rule_data *rule); #ifdef __cplusplus diff --git a/trunk/src/auditctl.c b/trunk/src/auditctl.c index 34b7935..d7ec998 100644 --- a/trunk/src/auditctl.c +++ b/trunk/src/auditctl.c @@ -482,7 +482,7 @@ static int setopt(int count, int lineno, char *vars[]) keylen = AUDIT_MAX_KEY_LEN; while ((retval >= 0) && (c = getopt(count, vars, - "hislDvte:f:r:b:a:A:d:S:F:m:R:w:W:k:p:q:")) != EOF) { + "hislDvtC:e:f:r:b:a:A:d:S:F:m:R:w:W:k:p:q:")) != EOF) { int flags = AUDIT_FILTER_UNSET; rc = 10; // Init to something impossible to see if unused. switch (c) { @@ -731,7 +731,6 @@ static int setopt(int count, int lineno, char *vars[]) retval = -1; break; } - rc = audit_rule_fieldpair_data(&rule_new,optarg,flags); if (rc != 0) { audit_number_to_errmsg(rc, optarg); @@ -743,6 +742,22 @@ static int setopt(int count, int lineno, char *vars[]) } break; + case 'C': + if (add != AUDIT_FILTER_UNSET) + flags = add & AUDIT_FILTER_MASK; + else if (del != AUDIT_FILTER_UNSET) + flags = del & AUDIT_FILTER_MASK; + + rc = audit_rule_interfield_fieldpair_data(&rule_new, optarg, flags); + if (rc != 0) { + audit_number_to_errmsg(rc, optarg); + retval = -1; + } else { + if (rule_new->fields[rule_new->field_count - 1] == + AUDIT_PERM) + audit_permadded = 1; + } + break; case 'm': if (count > 3) { fprintf(stderr, diff --git a/trunk/src/ausearch-report.c b/trunk/src/ausearch-report.c index d50c732..62e1ae0 100644 --- a/trunk/src/ausearch-report.c +++ b/trunk/src/ausearch-report.c @@ -333,6 +333,7 @@ static struct nv_pair typetab[] = { {T_UID, "id"}, {T_UID, "inode_uid"}, {T_UID, "sauid"}, + {T_UID, "obj_uid"}, {T_GID, "gid"}, {T_GID, "egid"}, {T_GID, "sgid"}, -- 1.7.3.1 -- Peter Moody Google 1.650.253.7306 Security Engineer pgp:0xC3410038
-- Linux-audit mailing list [email protected] https://www.redhat.com/mailman/listinfo/linux-audit
