Commit 9c937dcc71021f2dbf78f904f03d962dd9bcc130

Authored by Amy Griffis
Committed by Al Viro
1 parent 6a2bceec0e

[PATCH] log more info for directory entry change events

When an audit event involves changes to a directory entry, include
a PATH record for the directory itself.  A few other notable changes:

    - fixed audit_inode_child() hooks in fsnotify_move()
    - removed unused flags arg from audit_inode()
    - added audit log routines for logging a portion of a string

Here's some sample output.

before patch:
type=SYSCALL msg=audit(1149821605.320:26): arch=40000003 syscall=39 success=yes exit=0 a0=bf8d3c7c a1=1ff a2=804e1b8 a3=bf8d3c7c items=1 ppid=739 pid=800 auid=0 uid=0 gid=0 euid=0 suid=0 fsuid=0 egid=0 sgid=0 fsgid=0 tty=ttyS0 comm="mkdir" exe="/bin/mkdir" subj=root:system_r:unconfined_t:s0-s0:c0.c255
type=CWD msg=audit(1149821605.320:26):  cwd="/root"
type=PATH msg=audit(1149821605.320:26): item=0 name="foo" parent=164068 inode=164010 dev=03:00 mode=040755 ouid=0 ogid=0 rdev=00:00 obj=root:object_r:user_home_t:s0

after patch:
type=SYSCALL msg=audit(1149822032.332:24): arch=40000003 syscall=39 success=yes exit=0 a0=bfdd9c7c a1=1ff a2=804e1b8 a3=bfdd9c7c items=2 ppid=714 pid=777 auid=0 uid=0 gid=0 euid=0 suid=0 fsuid=0 egid=0 sgid=0 fsgid=0 tty=ttyS0 comm="mkdir" exe="/bin/mkdir" subj=root:system_r:unconfined_t:s0-s0:c0.c255
type=CWD msg=audit(1149822032.332:24):  cwd="/root"
type=PATH msg=audit(1149822032.332:24): item=0 name="/root" inode=164068 dev=03:00 mode=040750 ouid=0 ogid=0 rdev=00:00 obj=root:object_r:user_home_dir_t:s0
type=PATH msg=audit(1149822032.332:24): item=1 name="foo" inode=164010 dev=03:00 mode=040755 ouid=0 ogid=0 rdev=00:00 obj=root:object_r:user_home_t:s0

Signed-off-by: Amy Griffis <amy.griffis@hp.com>
Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>

Showing 9 changed files with 142 additions and 74 deletions Side-by-side Diff

... ... @@ -1127,7 +1127,7 @@
1127 1127 if (likely(retval == 0)) {
1128 1128 if (unlikely(current->audit_context && nd && nd->dentry &&
1129 1129 nd->dentry->d_inode))
1130   - audit_inode(name, nd->dentry->d_inode, flags);
  1130 + audit_inode(name, nd->dentry->d_inode);
1131 1131 }
1132 1132 out_fail:
1133 1133 return retval;
... ... @@ -633,7 +633,7 @@
633 633 dentry = file->f_dentry;
634 634 inode = dentry->d_inode;
635 635  
636   - audit_inode(NULL, inode, 0);
  636 + audit_inode(NULL, inode);
637 637  
638 638 err = -EROFS;
639 639 if (IS_RDONLY(inode))
... ... @@ -786,7 +786,7 @@
786 786 if (file) {
787 787 struct dentry * dentry;
788 788 dentry = file->f_dentry;
789   - audit_inode(NULL, dentry->d_inode, 0);
  789 + audit_inode(NULL, dentry->d_inode);
790 790 error = chown_common(dentry, user, group);
791 791 fput(file);
792 792 }
... ... @@ -242,7 +242,7 @@
242 242 if (!f)
243 243 return error;
244 244 dentry = f->f_dentry;
245   - audit_inode(NULL, dentry->d_inode, 0);
  245 + audit_inode(NULL, dentry->d_inode);
246 246 error = setxattr(dentry, name, value, size, flags);
247 247 fput(f);
248 248 return error;
... ... @@ -469,7 +469,7 @@
469 469 if (!f)
470 470 return error;
471 471 dentry = f->f_dentry;
472   - audit_inode(NULL, dentry->d_inode, 0);
  472 + audit_inode(NULL, dentry->d_inode);
473 473 error = removexattr(dentry, name);
474 474 fput(f);
475 475 return error;
include/linux/audit.h
... ... @@ -310,7 +310,7 @@
310 310 extern void audit_syscall_exit(int failed, long return_code);
311 311 extern void __audit_getname(const char *name);
312 312 extern void audit_putname(const char *name);
313   -extern void __audit_inode(const char *name, const struct inode *inode, unsigned flags);
  313 +extern void __audit_inode(const char *name, const struct inode *inode);
314 314 extern void __audit_inode_child(const char *dname, const struct inode *inode,
315 315 unsigned long pino);
316 316 static inline void audit_getname(const char *name)
317 317  
... ... @@ -318,10 +318,9 @@
318 318 if (unlikely(current->audit_context))
319 319 __audit_getname(name);
320 320 }
321   -static inline void audit_inode(const char *name, const struct inode *inode,
322   - unsigned flags) {
  321 +static inline void audit_inode(const char *name, const struct inode *inode) {
323 322 if (unlikely(current->audit_context))
324   - __audit_inode(name, inode, flags);
  323 + __audit_inode(name, inode);
325 324 }
326 325 static inline void audit_inode_child(const char *dname,
327 326 const struct inode *inode,
328 327  
... ... @@ -398,9 +397,9 @@
398 397 #define audit_syscall_exit(f,r) do { ; } while (0)
399 398 #define audit_getname(n) do { ; } while (0)
400 399 #define audit_putname(n) do { ; } while (0)
401   -#define __audit_inode(n,i,f) do { ; } while (0)
  400 +#define __audit_inode(n,i) do { ; } while (0)
402 401 #define __audit_inode_child(d,i,p) do { ; } while (0)
403   -#define audit_inode(n,i,f) do { ; } while (0)
  402 +#define audit_inode(n,i) do { ; } while (0)
404 403 #define audit_inode_child(d,i,p) do { ; } while (0)
405 404 #define auditsc_get_stamp(c,t,s) do { BUG(); } while (0)
406 405 #define audit_get_loginuid(c) ({ -1; })
... ... @@ -435,6 +434,9 @@
435 434 size_t len);
436 435 extern const char * audit_log_untrustedstring(struct audit_buffer *ab,
437 436 const char *string);
  437 +extern const char * audit_log_n_untrustedstring(struct audit_buffer *ab,
  438 + size_t n,
  439 + const char *string);
438 440 extern void audit_log_d_path(struct audit_buffer *ab,
439 441 const char *prefix,
440 442 struct dentry *dentry,
... ... @@ -452,6 +454,7 @@
452 454 #define audit_log_end(b) do { ; } while (0)
453 455 #define audit_log_hex(a,b,l) do { ; } while (0)
454 456 #define audit_log_untrustedstring(a,s) do { ; } while (0)
  457 +#define audit_log_n_untrustedstring(a,n,s) do { ; } while (0)
455 458 #define audit_log_d_path(b,p,d,v) do { ; } while (0)
456 459 #endif
457 460 #endif
include/linux/fsnotify.h
... ... @@ -67,8 +67,7 @@
67 67 if (source) {
68 68 inotify_inode_queue_event(source, IN_MOVE_SELF, 0, NULL, NULL);
69 69 }
70   - audit_inode_child(old_name, source, old_dir->i_ino);
71   - audit_inode_child(new_name, target, new_dir->i_ino);
  70 + audit_inode_child(new_name, source, new_dir->i_ino);
72 71 }
73 72  
74 73 /*
... ... @@ -1051,20 +1051,53 @@
1051 1051 skb_put(skb, len << 1); /* new string is twice the old string */
1052 1052 }
1053 1053  
  1054 +/*
  1055 + * Format a string of no more than slen characters into the audit buffer,
  1056 + * enclosed in quote marks.
  1057 + */
  1058 +static void audit_log_n_string(struct audit_buffer *ab, size_t slen,
  1059 + const char *string)
  1060 +{
  1061 + int avail, new_len;
  1062 + unsigned char *ptr;
  1063 + struct sk_buff *skb;
  1064 +
  1065 + BUG_ON(!ab->skb);
  1066 + skb = ab->skb;
  1067 + avail = skb_tailroom(skb);
  1068 + new_len = slen + 3; /* enclosing quotes + null terminator */
  1069 + if (new_len > avail) {
  1070 + avail = audit_expand(ab, new_len);
  1071 + if (!avail)
  1072 + return;
  1073 + }
  1074 + ptr = skb->tail;
  1075 + *ptr++ = '"';
  1076 + memcpy(ptr, string, slen);
  1077 + ptr += slen;
  1078 + *ptr++ = '"';
  1079 + *ptr = 0;
  1080 + skb_put(skb, slen + 2); /* don't include null terminator */
  1081 +}
  1082 +
1054 1083 /**
1055   - * audit_log_unstrustedstring - log a string that may contain random characters
  1084 + * audit_log_n_unstrustedstring - log a string that may contain random characters
1056 1085 * @ab: audit_buffer
  1086 + * @len: lenth of string (not including trailing null)
1057 1087 * @string: string to be logged
1058 1088 *
1059 1089 * This code will escape a string that is passed to it if the string
1060 1090 * contains a control character, unprintable character, double quote mark,
1061 1091 * or a space. Unescaped strings will start and end with a double quote mark.
1062 1092 * Strings that are escaped are printed in hex (2 digits per char).
  1093 + *
  1094 + * The caller specifies the number of characters in the string to log, which may
  1095 + * or may not be the entire string.
1063 1096 */
1064   -const char *audit_log_untrustedstring(struct audit_buffer *ab, const char *string)
  1097 +const char *audit_log_n_untrustedstring(struct audit_buffer *ab, size_t len,
  1098 + const char *string)
1065 1099 {
1066 1100 const unsigned char *p = string;
1067   - size_t len = strlen(string);
1068 1101  
1069 1102 while (*p) {
1070 1103 if (*p == '"' || *p < 0x21 || *p > 0x7f) {
1071 1104  
... ... @@ -1073,8 +1106,21 @@
1073 1106 }
1074 1107 p++;
1075 1108 }
1076   - audit_log_format(ab, "\"%s\"", string);
  1109 + audit_log_n_string(ab, len, string);
1077 1110 return p + 1;
  1111 +}
  1112 +
  1113 +/**
  1114 + * audit_log_unstrustedstring - log a string that may contain random characters
  1115 + * @ab: audit_buffer
  1116 + * @string: string to be logged
  1117 + *
  1118 + * Same as audit_log_n_unstrustedstring(), except that strlen is used to
  1119 + * determine string length.
  1120 + */
  1121 +const char *audit_log_untrustedstring(struct audit_buffer *ab, const char *string)
  1122 +{
  1123 + return audit_log_n_untrustedstring(ab, strlen(string), string);
1078 1124 }
1079 1125  
1080 1126 /* This is a helper-function to print the escaped d_path */
... ... @@ -104,7 +104,8 @@
104 104 }
105 105  
106 106 extern int audit_comparator(const u32 left, const u32 op, const u32 right);
107   -extern int audit_compare_dname_path(const char *dname, const char *path);
  107 +extern int audit_compare_dname_path(const char *dname, const char *path,
  108 + int *dirlen);
108 109 extern struct sk_buff * audit_make_reply(int pid, int seq, int type,
109 110 int done, int multi,
110 111 void *payload, int size);
kernel/auditfilter.c
... ... @@ -787,7 +787,7 @@
787 787  
788 788 mutex_lock(&audit_filter_mutex);
789 789 list_for_each_entry_safe(owatch, nextw, &parent->watches, wlist) {
790   - if (audit_compare_dname_path(dname, owatch->path))
  790 + if (audit_compare_dname_path(dname, owatch->path, NULL))
791 791 continue;
792 792  
793 793 /* If the update involves invalidating rules, do the inode-based
... ... @@ -1387,7 +1387,8 @@
1387 1387  
1388 1388 /* Compare given dentry name with last component in given path,
1389 1389 * return of 0 indicates a match. */
1390   -int audit_compare_dname_path(const char *dname, const char *path)
  1390 +int audit_compare_dname_path(const char *dname, const char *path,
  1391 + int *dirlen)
1391 1392 {
1392 1393 int dlen, plen;
1393 1394 const char *p;
... ... @@ -1416,6 +1417,9 @@
1416 1417 p++;
1417 1418 }
1418 1419  
  1420 + /* return length of path's directory component */
  1421 + if (dirlen)
  1422 + *dirlen = p - path;
1419 1423 return strncmp(p, dname, dlen);
1420 1424 }
1421 1425  
... ... @@ -82,6 +82,9 @@
82 82 * path_lookup. */
83 83 #define AUDIT_NAMES_RESERVED 7
84 84  
  85 +/* Indicates that audit should log the full pathname. */
  86 +#define AUDIT_NAME_FULL -1
  87 +
85 88 /* When fs/namei.c:getname() is called, we store the pointer in name and
86 89 * we don't let putname() free it (instead we free all of the saved
87 90 * pointers at syscall exit time).
88 91  
... ... @@ -89,8 +92,9 @@
89 92 * Further, in fs/namei.c:path_lookup() we store the inode and device. */
90 93 struct audit_names {
91 94 const char *name;
  95 + int name_len; /* number of name's characters to log */
  96 + unsigned name_put; /* call __putname() for this name */
92 97 unsigned long ino;
93   - unsigned long pino;
94 98 dev_t dev;
95 99 umode_t mode;
96 100 uid_t uid;
97 101  
... ... @@ -296,12 +300,10 @@
296 300 break;
297 301 case AUDIT_INODE:
298 302 if (name)
299   - result = (name->ino == f->val ||
300   - name->pino == f->val);
  303 + result = (name->ino == f->val);
301 304 else if (ctx) {
302 305 for (j = 0; j < ctx->name_count; j++) {
303   - if (audit_comparator(ctx->names[j].ino, f->op, f->val) ||
304   - audit_comparator(ctx->names[j].pino, f->op, f->val)) {
  306 + if (audit_comparator(ctx->names[j].ino, f->op, f->val)) {
305 307 ++result;
306 308 break;
307 309 }
... ... @@ -311,8 +313,7 @@
311 313 case AUDIT_WATCH:
312 314 if (name && rule->watch->ino != (unsigned long)-1)
313 315 result = (name->dev == rule->watch->dev &&
314   - (name->ino == rule->watch->ino ||
315   - name->pino == rule->watch->ino));
  316 + name->ino == rule->watch->ino);
316 317 break;
317 318 case AUDIT_LOGINUID:
318 319 result = 0;
... ... @@ -526,7 +527,7 @@
526 527 #endif
527 528  
528 529 for (i = 0; i < context->name_count; i++) {
529   - if (context->names[i].name)
  530 + if (context->names[i].name && context->names[i].name_put)
530 531 __putname(context->names[i].name);
531 532 }
532 533 context->name_count = 0;
... ... @@ -850,8 +851,7 @@
850 851 }
851 852 }
852 853 for (i = 0; i < context->name_count; i++) {
853   - unsigned long ino = context->names[i].ino;
854   - unsigned long pino = context->names[i].pino;
  854 + struct audit_names *n = &context->names[i];
855 855  
856 856 ab = audit_log_start(context, GFP_KERNEL, AUDIT_PATH);
857 857 if (!ab)
858 858  
859 859  
... ... @@ -859,33 +859,47 @@
859 859  
860 860 audit_log_format(ab, "item=%d", i);
861 861  
862   - audit_log_format(ab, " name=");
863   - if (context->names[i].name)
864   - audit_log_untrustedstring(ab, context->names[i].name);
865   - else
866   - audit_log_format(ab, "(null)");
  862 + if (n->name) {
  863 + switch(n->name_len) {
  864 + case AUDIT_NAME_FULL:
  865 + /* log the full path */
  866 + audit_log_format(ab, " name=");
  867 + audit_log_untrustedstring(ab, n->name);
  868 + break;
  869 + case 0:
  870 + /* name was specified as a relative path and the
  871 + * directory component is the cwd */
  872 + audit_log_d_path(ab, " name=", context->pwd,
  873 + context->pwdmnt);
  874 + break;
  875 + default:
  876 + /* log the name's directory component */
  877 + audit_log_format(ab, " name=");
  878 + audit_log_n_untrustedstring(ab, n->name_len,
  879 + n->name);
  880 + }
  881 + } else
  882 + audit_log_format(ab, " name=(null)");
867 883  
868   - if (pino != (unsigned long)-1)
869   - audit_log_format(ab, " parent=%lu", pino);
870   - if (ino != (unsigned long)-1)
871   - audit_log_format(ab, " inode=%lu", ino);
872   - if ((pino != (unsigned long)-1) || (ino != (unsigned long)-1))
873   - audit_log_format(ab, " dev=%02x:%02x mode=%#o"
874   - " ouid=%u ogid=%u rdev=%02x:%02x",
875   - MAJOR(context->names[i].dev),
876   - MINOR(context->names[i].dev),
877   - context->names[i].mode,
878   - context->names[i].uid,
879   - context->names[i].gid,
880   - MAJOR(context->names[i].rdev),
881   - MINOR(context->names[i].rdev));
882   - if (context->names[i].osid != 0) {
  884 + if (n->ino != (unsigned long)-1) {
  885 + audit_log_format(ab, " inode=%lu"
  886 + " dev=%02x:%02x mode=%#o"
  887 + " ouid=%u ogid=%u rdev=%02x:%02x",
  888 + n->ino,
  889 + MAJOR(n->dev),
  890 + MINOR(n->dev),
  891 + n->mode,
  892 + n->uid,
  893 + n->gid,
  894 + MAJOR(n->rdev),
  895 + MINOR(n->rdev));
  896 + }
  897 + if (n->osid != 0) {
883 898 char *ctx = NULL;
884 899 u32 len;
885 900 if (selinux_ctxid_to_string(
886   - context->names[i].osid, &ctx, &len)) {
887   - audit_log_format(ab, " osid=%u",
888   - context->names[i].osid);
  901 + n->osid, &ctx, &len)) {
  902 + audit_log_format(ab, " osid=%u", n->osid);
889 903 call_panic = 2;
890 904 } else
891 905 audit_log_format(ab, " obj=%s", ctx);
... ... @@ -1075,6 +1089,8 @@
1075 1089 }
1076 1090 BUG_ON(context->name_count >= AUDIT_NAMES);
1077 1091 context->names[context->name_count].name = name;
  1092 + context->names[context->name_count].name_len = AUDIT_NAME_FULL;
  1093 + context->names[context->name_count].name_put = 1;
1078 1094 context->names[context->name_count].ino = (unsigned long)-1;
1079 1095 ++context->name_count;
1080 1096 if (!context->pwd) {
1081 1097  
... ... @@ -1141,11 +1157,10 @@
1141 1157 * audit_inode - store the inode and device from a lookup
1142 1158 * @name: name being audited
1143 1159 * @inode: inode being audited
1144   - * @flags: lookup flags (as used in path_lookup())
1145 1160 *
1146 1161 * Called from fs/namei.c:path_lookup().
1147 1162 */
1148   -void __audit_inode(const char *name, const struct inode *inode, unsigned flags)
  1163 +void __audit_inode(const char *name, const struct inode *inode)
1149 1164 {
1150 1165 int idx;
1151 1166 struct audit_context *context = current->audit_context;
1152 1167  
... ... @@ -1171,20 +1186,13 @@
1171 1186 ++context->ino_count;
1172 1187 #endif
1173 1188 }
  1189 + context->names[idx].ino = inode->i_ino;
1174 1190 context->names[idx].dev = inode->i_sb->s_dev;
1175 1191 context->names[idx].mode = inode->i_mode;
1176 1192 context->names[idx].uid = inode->i_uid;
1177 1193 context->names[idx].gid = inode->i_gid;
1178 1194 context->names[idx].rdev = inode->i_rdev;
1179 1195 audit_inode_context(idx, inode);
1180   - if ((flags & LOOKUP_PARENT) && (strcmp(name, "/") != 0) &&
1181   - (strcmp(name, ".") != 0)) {
1182   - context->names[idx].ino = (unsigned long)-1;
1183   - context->names[idx].pino = inode->i_ino;
1184   - } else {
1185   - context->names[idx].ino = inode->i_ino;
1186   - context->names[idx].pino = (unsigned long)-1;
1187   - }
1188 1196 }
1189 1197  
1190 1198 /**
1191 1199  
1192 1200  
1193 1201  
1194 1202  
1195 1203  
1196 1204  
1197 1205  
... ... @@ -1206,34 +1214,40 @@
1206 1214 {
1207 1215 int idx;
1208 1216 struct audit_context *context = current->audit_context;
  1217 + const char *found_name = NULL;
  1218 + int dirlen = 0;
1209 1219  
1210 1220 if (!context->in_syscall)
1211 1221 return;
1212 1222  
1213 1223 /* determine matching parent */
1214 1224 if (!dname)
1215   - goto no_match;
  1225 + goto update_context;
1216 1226 for (idx = 0; idx < context->name_count; idx++)
1217   - if (context->names[idx].pino == pino) {
  1227 + if (context->names[idx].ino == pino) {
1218 1228 const char *name = context->names[idx].name;
1219 1229  
1220 1230 if (!name)
1221 1231 continue;
1222 1232  
1223   - if (audit_compare_dname_path(dname, name) == 0)
1224   - goto update_context;
  1233 + if (audit_compare_dname_path(dname, name, &dirlen) == 0) {
  1234 + context->names[idx].name_len = dirlen;
  1235 + found_name = name;
  1236 + break;
  1237 + }
1225 1238 }
1226 1239  
1227   -no_match:
1228   - /* catch-all in case match not found */
  1240 +update_context:
1229 1241 idx = context->name_count++;
1230   - context->names[idx].name = NULL;
1231   - context->names[idx].pino = pino;
1232 1242 #if AUDIT_DEBUG
1233 1243 context->ino_count++;
1234 1244 #endif
  1245 + /* Re-use the name belonging to the slot for a matching parent directory.
  1246 + * All names for this context are relinquished in audit_free_names() */
  1247 + context->names[idx].name = found_name;
  1248 + context->names[idx].name_len = AUDIT_NAME_FULL;
  1249 + context->names[idx].name_put = 0; /* don't call __putname() */
1235 1250  
1236   -update_context:
1237 1251 if (inode) {
1238 1252 context->names[idx].ino = inode->i_ino;
1239 1253 context->names[idx].dev = inode->i_sb->s_dev;
... ... @@ -1242,7 +1256,8 @@
1242 1256 context->names[idx].gid = inode->i_gid;
1243 1257 context->names[idx].rdev = inode->i_rdev;
1244 1258 audit_inode_context(idx, inode);
1245   - }
  1259 + } else
  1260 + context->names[idx].ino = (unsigned long)-1;
1246 1261 }
1247 1262  
1248 1263 /**