diff options
| author | Amy Griffis <amy.griffis@hp.com> | 2006-02-07 12:05:27 -0500 |
|---|---|---|
| committer | Al Viro <viro@zeniv.linux.org.uk> | 2006-03-20 14:08:54 -0500 |
| commit | 93315ed6dd12dacfc941f9eb8ca0293aadf99793 (patch) | |
| tree | 4fc070c92a1de21d3befe4ce48c733c65d044bb3 /kernel | |
| parent | af601e4623d0303bfafa54ec728b7ae8493a8e1b (diff) | |
[PATCH] audit string fields interface + consumer
Updated patch to dynamically allocate audit rule fields in kernel's
internal representation. Added unlikely() calls for testing memory
allocation result.
Amy Griffis wrote: [Wed Jan 11 2006, 02:02:31PM EST]
> Modify audit's kernel-userspace interface to allow the specification
> of string fields in audit rules.
>
> Signed-off-by: Amy Griffis <amy.griffis@hp.com>
Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>
(cherry picked from 5ffc4a863f92351b720fe3e9c5cd647accff9e03 commit)
Diffstat (limited to 'kernel')
| -rw-r--r-- | kernel/audit.c | 19 | ||||
| -rw-r--r-- | kernel/audit.h | 23 | ||||
| -rw-r--r-- | kernel/auditfilter.c | 467 | ||||
| -rw-r--r-- | kernel/auditsc.c | 50 |
4 files changed, 418 insertions, 141 deletions
diff --git a/kernel/audit.c b/kernel/audit.c index 07c5d2bdd38c..4eb97b62d7fa 100644 --- a/kernel/audit.c +++ b/kernel/audit.c | |||
| @@ -52,6 +52,7 @@ | |||
| 52 | #include <linux/audit.h> | 52 | #include <linux/audit.h> |
| 53 | 53 | ||
| 54 | #include <net/sock.h> | 54 | #include <net/sock.h> |
| 55 | #include <net/netlink.h> | ||
| 55 | #include <linux/skbuff.h> | 56 | #include <linux/skbuff.h> |
| 56 | #include <linux/netlink.h> | 57 | #include <linux/netlink.h> |
| 57 | 58 | ||
| @@ -361,9 +362,12 @@ static int audit_netlink_ok(kernel_cap_t eff_cap, u16 msg_type) | |||
| 361 | switch (msg_type) { | 362 | switch (msg_type) { |
| 362 | case AUDIT_GET: | 363 | case AUDIT_GET: |
| 363 | case AUDIT_LIST: | 364 | case AUDIT_LIST: |
| 365 | case AUDIT_LIST_RULES: | ||
| 364 | case AUDIT_SET: | 366 | case AUDIT_SET: |
| 365 | case AUDIT_ADD: | 367 | case AUDIT_ADD: |
| 368 | case AUDIT_ADD_RULE: | ||
| 366 | case AUDIT_DEL: | 369 | case AUDIT_DEL: |
| 370 | case AUDIT_DEL_RULE: | ||
| 367 | case AUDIT_SIGNAL_INFO: | 371 | case AUDIT_SIGNAL_INFO: |
| 368 | if (!cap_raised(eff_cap, CAP_AUDIT_CONTROL)) | 372 | if (!cap_raised(eff_cap, CAP_AUDIT_CONTROL)) |
| 369 | err = -EPERM; | 373 | err = -EPERM; |
| @@ -470,12 +474,23 @@ static int audit_receive_msg(struct sk_buff *skb, struct nlmsghdr *nlh) | |||
| 470 | break; | 474 | break; |
| 471 | case AUDIT_ADD: | 475 | case AUDIT_ADD: |
| 472 | case AUDIT_DEL: | 476 | case AUDIT_DEL: |
| 473 | if (nlh->nlmsg_len < sizeof(struct audit_rule)) | 477 | if (nlmsg_len(nlh) < sizeof(struct audit_rule)) |
| 474 | return -EINVAL; | 478 | return -EINVAL; |
| 475 | /* fallthrough */ | 479 | /* fallthrough */ |
| 476 | case AUDIT_LIST: | 480 | case AUDIT_LIST: |
| 477 | err = audit_receive_filter(nlh->nlmsg_type, NETLINK_CB(skb).pid, | 481 | err = audit_receive_filter(nlh->nlmsg_type, NETLINK_CB(skb).pid, |
| 478 | uid, seq, data, loginuid); | 482 | uid, seq, data, nlmsg_len(nlh), |
| 483 | loginuid); | ||
| 484 | break; | ||
| 485 | case AUDIT_ADD_RULE: | ||
| 486 | case AUDIT_DEL_RULE: | ||
| 487 | if (nlmsg_len(nlh) < sizeof(struct audit_rule_data)) | ||
| 488 | return -EINVAL; | ||
| 489 | /* fallthrough */ | ||
| 490 | case AUDIT_LIST_RULES: | ||
| 491 | err = audit_receive_filter(nlh->nlmsg_type, NETLINK_CB(skb).pid, | ||
| 492 | uid, seq, data, nlmsg_len(nlh), | ||
| 493 | loginuid); | ||
| 479 | break; | 494 | break; |
| 480 | case AUDIT_SIGNAL_INFO: | 495 | case AUDIT_SIGNAL_INFO: |
| 481 | sig_data.uid = audit_sig_uid; | 496 | sig_data.uid = audit_sig_uid; |
diff --git a/kernel/audit.h b/kernel/audit.h index 7643e46daeb2..4b602cdcabef 100644 --- a/kernel/audit.h +++ b/kernel/audit.h | |||
| @@ -52,10 +52,27 @@ enum audit_state { | |||
| 52 | }; | 52 | }; |
| 53 | 53 | ||
| 54 | /* Rule lists */ | 54 | /* Rule lists */ |
| 55 | struct audit_field { | ||
| 56 | u32 type; | ||
| 57 | u32 val; | ||
| 58 | u32 op; | ||
| 59 | }; | ||
| 60 | |||
| 61 | struct audit_krule { | ||
| 62 | int vers_ops; | ||
| 63 | u32 flags; | ||
| 64 | u32 listnr; | ||
| 65 | u32 action; | ||
| 66 | u32 mask[AUDIT_BITMASK_SIZE]; | ||
| 67 | u32 buflen; /* for data alloc on list rules */ | ||
| 68 | u32 field_count; | ||
| 69 | struct audit_field *fields; | ||
| 70 | }; | ||
| 71 | |||
| 55 | struct audit_entry { | 72 | struct audit_entry { |
| 56 | struct list_head list; | 73 | struct list_head list; |
| 57 | struct rcu_head rcu; | 74 | struct rcu_head rcu; |
| 58 | struct audit_rule rule; | 75 | struct audit_krule rule; |
| 59 | }; | 76 | }; |
| 60 | 77 | ||
| 61 | 78 | ||
diff --git a/kernel/auditfilter.c b/kernel/auditfilter.c index a3a32752f973..686d514a3518 100644 --- a/kernel/auditfilter.c +++ b/kernel/auditfilter.c | |||
| @@ -40,52 +40,279 @@ struct list_head audit_filter_list[AUDIT_NR_FILTERS] = { | |||
| 40 | #endif | 40 | #endif |
| 41 | }; | 41 | }; |
| 42 | 42 | ||
| 43 | /* Copy rule from user-space to kernel-space. Called from | 43 | static inline void audit_free_rule(struct audit_entry *e) |
| 44 | * audit_add_rule during AUDIT_ADD. */ | ||
| 45 | static inline int audit_copy_rule(struct audit_rule *d, struct audit_rule *s) | ||
| 46 | { | 44 | { |
| 45 | kfree(e->rule.fields); | ||
| 46 | kfree(e); | ||
| 47 | } | ||
| 48 | |||
| 49 | static inline void audit_free_rule_rcu(struct rcu_head *head) | ||
| 50 | { | ||
| 51 | struct audit_entry *e = container_of(head, struct audit_entry, rcu); | ||
| 52 | audit_free_rule(e); | ||
| 53 | } | ||
| 54 | |||
| 55 | /* Unpack a filter field's string representation from user-space | ||
| 56 | * buffer. */ | ||
| 57 | static __attribute__((unused)) char *audit_unpack_string(void **bufp, size_t *remain, size_t len) | ||
| 58 | { | ||
| 59 | char *str; | ||
| 60 | |||
| 61 | if (!*bufp || (len == 0) || (len > *remain)) | ||
| 62 | return ERR_PTR(-EINVAL); | ||
| 63 | |||
| 64 | /* Of the currently implemented string fields, PATH_MAX | ||
| 65 | * defines the longest valid length. | ||
| 66 | */ | ||
| 67 | if (len > PATH_MAX) | ||
| 68 | return ERR_PTR(-ENAMETOOLONG); | ||
| 69 | |||
| 70 | str = kmalloc(len + 1, GFP_KERNEL); | ||
| 71 | if (unlikely(!str)) | ||
| 72 | return ERR_PTR(-ENOMEM); | ||
| 73 | |||
| 74 | memcpy(str, *bufp, len); | ||
| 75 | str[len] = 0; | ||
| 76 | *bufp += len; | ||
| 77 | *remain -= len; | ||
| 78 | |||
| 79 | return str; | ||
| 80 | } | ||
| 81 | |||
| 82 | /* Common user-space to kernel rule translation. */ | ||
| 83 | static inline struct audit_entry *audit_to_entry_common(struct audit_rule *rule) | ||
| 84 | { | ||
| 85 | unsigned listnr; | ||
| 86 | struct audit_entry *entry; | ||
| 87 | struct audit_field *fields; | ||
| 88 | int i, err; | ||
| 89 | |||
| 90 | err = -EINVAL; | ||
| 91 | listnr = rule->flags & ~AUDIT_FILTER_PREPEND; | ||
| 92 | switch(listnr) { | ||
| 93 | default: | ||
| 94 | goto exit_err; | ||
| 95 | case AUDIT_FILTER_USER: | ||
| 96 | case AUDIT_FILTER_TYPE: | ||
| 97 | #ifdef CONFIG_AUDITSYSCALL | ||
| 98 | case AUDIT_FILTER_ENTRY: | ||
| 99 | case AUDIT_FILTER_EXIT: | ||
| 100 | case AUDIT_FILTER_TASK: | ||
| 101 | #endif | ||
| 102 | ; | ||
| 103 | } | ||
| 104 | if (rule->action != AUDIT_NEVER && rule->action != AUDIT_POSSIBLE && | ||
| 105 | rule->action != AUDIT_ALWAYS) | ||
| 106 | goto exit_err; | ||
| 107 | if (rule->field_count > AUDIT_MAX_FIELDS) | ||
| 108 | goto exit_err; | ||
| 109 | |||
| 110 | err = -ENOMEM; | ||
| 111 | entry = kmalloc(sizeof(*entry), GFP_KERNEL); | ||
| 112 | if (unlikely(!entry)) | ||
| 113 | goto exit_err; | ||
| 114 | fields = kmalloc(sizeof(*fields) * rule->field_count, GFP_KERNEL); | ||
| 115 | if (unlikely(!fields)) { | ||
| 116 | kfree(entry); | ||
| 117 | goto exit_err; | ||
| 118 | } | ||
| 119 | |||
| 120 | memset(&entry->rule, 0, sizeof(struct audit_krule)); | ||
| 121 | memset(fields, 0, sizeof(struct audit_field)); | ||
| 122 | |||
| 123 | entry->rule.flags = rule->flags & AUDIT_FILTER_PREPEND; | ||
| 124 | entry->rule.listnr = listnr; | ||
| 125 | entry->rule.action = rule->action; | ||
| 126 | entry->rule.field_count = rule->field_count; | ||
| 127 | entry->rule.fields = fields; | ||
| 128 | |||
| 129 | for (i = 0; i < AUDIT_BITMASK_SIZE; i++) | ||
| 130 | entry->rule.mask[i] = rule->mask[i]; | ||
| 131 | |||
| 132 | return entry; | ||
| 133 | |||
| 134 | exit_err: | ||
| 135 | return ERR_PTR(err); | ||
| 136 | } | ||
| 137 | |||
| 138 | /* Translate struct audit_rule to kernel's rule respresentation. | ||
| 139 | * Exists for backward compatibility with userspace. */ | ||
| 140 | static struct audit_entry *audit_rule_to_entry(struct audit_rule *rule) | ||
| 141 | { | ||
| 142 | struct audit_entry *entry; | ||
| 143 | int err = 0; | ||
| 47 | int i; | 144 | int i; |
| 48 | 145 | ||
| 49 | if (s->action != AUDIT_NEVER | 146 | entry = audit_to_entry_common(rule); |
| 50 | && s->action != AUDIT_POSSIBLE | 147 | if (IS_ERR(entry)) |
| 51 | && s->action != AUDIT_ALWAYS) | 148 | goto exit_nofree; |
| 52 | return -1; | 149 | |
| 53 | if (s->field_count < 0 || s->field_count > AUDIT_MAX_FIELDS) | 150 | for (i = 0; i < rule->field_count; i++) { |
| 54 | return -1; | 151 | struct audit_field *f = &entry->rule.fields[i]; |
| 55 | if ((s->flags & ~AUDIT_FILTER_PREPEND) >= AUDIT_NR_FILTERS) | 152 | |
| 56 | return -1; | 153 | if (rule->fields[i] & AUDIT_UNUSED_BITS) { |
| 57 | 154 | err = -EINVAL; | |
| 58 | d->flags = s->flags; | 155 | goto exit_free; |
| 59 | d->action = s->action; | 156 | } |
| 60 | d->field_count = s->field_count; | 157 | |
| 61 | for (i = 0; i < d->field_count; i++) { | 158 | f->op = rule->fields[i] & (AUDIT_NEGATE|AUDIT_OPERATORS); |
| 62 | d->fields[i] = s->fields[i]; | 159 | f->type = rule->fields[i] & ~(AUDIT_NEGATE|AUDIT_OPERATORS); |
| 63 | d->values[i] = s->values[i]; | 160 | f->val = rule->values[i]; |
| 161 | |||
| 162 | entry->rule.vers_ops = (f->op & AUDIT_OPERATORS) ? 2 : 1; | ||
| 163 | if (f->op & AUDIT_NEGATE) | ||
| 164 | f->op |= AUDIT_NOT_EQUAL; | ||
| 165 | else if (!(f->op & AUDIT_OPERATORS)) | ||
| 166 | f->op |= AUDIT_EQUAL; | ||
| 167 | f->op &= ~AUDIT_NEGATE; | ||
| 64 | } | 168 | } |
| 65 | for (i = 0; i < AUDIT_BITMASK_SIZE; i++) d->mask[i] = s->mask[i]; | 169 | |
| 66 | return 0; | 170 | exit_nofree: |
| 171 | return entry; | ||
| 172 | |||
| 173 | exit_free: | ||
| 174 | audit_free_rule(entry); | ||
| 175 | return ERR_PTR(err); | ||
| 67 | } | 176 | } |
| 68 | 177 | ||
| 69 | /* Check to see if two rules are identical. It is called from | 178 | /* Translate struct audit_rule_data to kernel's rule respresentation. */ |
| 70 | * audit_add_rule during AUDIT_ADD and | 179 | static struct audit_entry *audit_data_to_entry(struct audit_rule_data *data, |
| 71 | * audit_del_rule during AUDIT_DEL. */ | 180 | size_t datasz) |
| 72 | static int audit_compare_rule(struct audit_rule *a, struct audit_rule *b) | ||
| 73 | { | 181 | { |
| 182 | int err = 0; | ||
| 183 | struct audit_entry *entry; | ||
| 184 | void *bufp; | ||
| 185 | /* size_t remain = datasz - sizeof(struct audit_rule_data); */ | ||
| 74 | int i; | 186 | int i; |
| 75 | 187 | ||
| 76 | if (a->flags != b->flags) | 188 | entry = audit_to_entry_common((struct audit_rule *)data); |
| 77 | return 1; | 189 | if (IS_ERR(entry)) |
| 190 | goto exit_nofree; | ||
| 78 | 191 | ||
| 79 | if (a->action != b->action) | 192 | bufp = data->buf; |
| 80 | return 1; | 193 | entry->rule.vers_ops = 2; |
| 194 | for (i = 0; i < data->field_count; i++) { | ||
| 195 | struct audit_field *f = &entry->rule.fields[i]; | ||
| 196 | |||
| 197 | err = -EINVAL; | ||
| 198 | if (!(data->fieldflags[i] & AUDIT_OPERATORS) || | ||
| 199 | data->fieldflags[i] & ~AUDIT_OPERATORS) | ||
| 200 | goto exit_free; | ||
| 201 | |||
| 202 | f->op = data->fieldflags[i] & AUDIT_OPERATORS; | ||
| 203 | f->type = data->fields[i]; | ||
| 204 | switch(f->type) { | ||
| 205 | /* call type-specific conversion routines here */ | ||
| 206 | default: | ||
| 207 | f->val = data->values[i]; | ||
| 208 | } | ||
| 209 | } | ||
| 210 | |||
| 211 | exit_nofree: | ||
| 212 | return entry; | ||
| 213 | |||
| 214 | exit_free: | ||
| 215 | audit_free_rule(entry); | ||
| 216 | return ERR_PTR(err); | ||
| 217 | } | ||
| 218 | |||
| 219 | /* Pack a filter field's string representation into data block. */ | ||
| 220 | static inline size_t audit_pack_string(void **bufp, char *str) | ||
| 221 | { | ||
| 222 | size_t len = strlen(str); | ||
| 223 | |||
| 224 | memcpy(*bufp, str, len); | ||
| 225 | *bufp += len; | ||
| 226 | |||
| 227 | return len; | ||
| 228 | } | ||
| 229 | |||
| 230 | /* Translate kernel rule respresentation to struct audit_rule. | ||
| 231 | * Exists for backward compatibility with userspace. */ | ||
| 232 | static struct audit_rule *audit_krule_to_rule(struct audit_krule *krule) | ||
| 233 | { | ||
| 234 | struct audit_rule *rule; | ||
| 235 | int i; | ||
| 236 | |||
| 237 | rule = kmalloc(sizeof(*rule), GFP_KERNEL); | ||
| 238 | if (unlikely(!rule)) | ||
| 239 | return ERR_PTR(-ENOMEM); | ||
| 240 | memset(rule, 0, sizeof(*rule)); | ||
| 241 | |||
| 242 | rule->flags = krule->flags | krule->listnr; | ||
| 243 | rule->action = krule->action; | ||
| 244 | rule->field_count = krule->field_count; | ||
| 245 | for (i = 0; i < rule->field_count; i++) { | ||
| 246 | rule->values[i] = krule->fields[i].val; | ||
| 247 | rule->fields[i] = krule->fields[i].type; | ||
| 248 | |||
| 249 | if (krule->vers_ops == 1) { | ||
| 250 | if (krule->fields[i].op & AUDIT_NOT_EQUAL) | ||
| 251 | rule->fields[i] |= AUDIT_NEGATE; | ||
| 252 | } else { | ||
| 253 | rule->fields[i] |= krule->fields[i].op; | ||
| 254 | } | ||
| 255 | } | ||
| 256 | for (i = 0; i < AUDIT_BITMASK_SIZE; i++) rule->mask[i] = krule->mask[i]; | ||
| 257 | |||
| 258 | return rule; | ||
| 259 | } | ||
| 81 | 260 | ||
| 82 | if (a->field_count != b->field_count) | 261 | /* Translate kernel rule respresentation to struct audit_rule_data. */ |
| 262 | static struct audit_rule_data *audit_krule_to_data(struct audit_krule *krule) | ||
| 263 | { | ||
| 264 | struct audit_rule_data *data; | ||
| 265 | void *bufp; | ||
| 266 | int i; | ||
| 267 | |||
| 268 | data = kmalloc(sizeof(*data) + krule->buflen, GFP_KERNEL); | ||
| 269 | if (unlikely(!data)) | ||
| 270 | return ERR_PTR(-ENOMEM); | ||
| 271 | memset(data, 0, sizeof(*data)); | ||
| 272 | |||
| 273 | data->flags = krule->flags | krule->listnr; | ||
| 274 | data->action = krule->action; | ||
| 275 | data->field_count = krule->field_count; | ||
| 276 | bufp = data->buf; | ||
| 277 | for (i = 0; i < data->field_count; i++) { | ||
| 278 | struct audit_field *f = &krule->fields[i]; | ||
| 279 | |||
| 280 | data->fields[i] = f->type; | ||
| 281 | data->fieldflags[i] = f->op; | ||
| 282 | switch(f->type) { | ||
| 283 | /* call type-specific conversion routines here */ | ||
| 284 | default: | ||
| 285 | data->values[i] = f->val; | ||
| 286 | } | ||
| 287 | } | ||
| 288 | for (i = 0; i < AUDIT_BITMASK_SIZE; i++) data->mask[i] = krule->mask[i]; | ||
| 289 | |||
| 290 | return data; | ||
| 291 | } | ||
| 292 | |||
| 293 | /* Compare two rules in kernel format. Considered success if rules | ||
| 294 | * don't match. */ | ||
| 295 | static int audit_compare_rule(struct audit_krule *a, struct audit_krule *b) | ||
| 296 | { | ||
| 297 | int i; | ||
| 298 | |||
| 299 | if (a->flags != b->flags || | ||
| 300 | a->listnr != b->listnr || | ||
| 301 | a->action != b->action || | ||
| 302 | a->field_count != b->field_count) | ||
| 83 | return 1; | 303 | return 1; |
| 84 | 304 | ||
| 85 | for (i = 0; i < a->field_count; i++) { | 305 | for (i = 0; i < a->field_count; i++) { |
| 86 | if (a->fields[i] != b->fields[i] | 306 | if (a->fields[i].type != b->fields[i].type || |
| 87 | || a->values[i] != b->values[i]) | 307 | a->fields[i].op != b->fields[i].op) |
| 88 | return 1; | 308 | return 1; |
| 309 | |||
| 310 | switch(a->fields[i].type) { | ||
| 311 | /* call type-specific comparison routines here */ | ||
| 312 | default: | ||
| 313 | if (a->fields[i].val != b->fields[i].val) | ||
| 314 | return 1; | ||
| 315 | } | ||
| 89 | } | 316 | } |
| 90 | 317 | ||
| 91 | for (i = 0; i < AUDIT_BITMASK_SIZE; i++) | 318 | for (i = 0; i < AUDIT_BITMASK_SIZE; i++) |
| @@ -95,41 +322,21 @@ static int audit_compare_rule(struct audit_rule *a, struct audit_rule *b) | |||
| 95 | return 0; | 322 | return 0; |
| 96 | } | 323 | } |
| 97 | 324 | ||
| 98 | /* Note that audit_add_rule and audit_del_rule are called via | 325 | /* Add rule to given filterlist if not a duplicate. Protected by |
| 99 | * audit_receive() in audit.c, and are protected by | ||
| 100 | * audit_netlink_sem. */ | 326 | * audit_netlink_sem. */ |
| 101 | static inline int audit_add_rule(struct audit_rule *rule, | 327 | static inline int audit_add_rule(struct audit_entry *entry, |
| 102 | struct list_head *list) | 328 | struct list_head *list) |
| 103 | { | 329 | { |
| 104 | struct audit_entry *entry; | 330 | struct audit_entry *e; |
| 105 | int i; | ||
| 106 | 331 | ||
| 107 | /* Do not use the _rcu iterator here, since this is the only | 332 | /* Do not use the _rcu iterator here, since this is the only |
| 108 | * addition routine. */ | 333 | * addition routine. */ |
| 109 | list_for_each_entry(entry, list, list) { | 334 | list_for_each_entry(e, list, list) { |
| 110 | if (!audit_compare_rule(rule, &entry->rule)) | 335 | if (!audit_compare_rule(&entry->rule, &e->rule)) |
| 111 | return -EEXIST; | 336 | return -EEXIST; |
| 112 | } | 337 | } |
| 113 | 338 | ||
| 114 | for (i = 0; i < rule->field_count; i++) { | ||
| 115 | if (rule->fields[i] & AUDIT_UNUSED_BITS) | ||
| 116 | return -EINVAL; | ||
| 117 | if ( rule->fields[i] & AUDIT_NEGATE) | ||
| 118 | rule->fields[i] |= AUDIT_NOT_EQUAL; | ||
| 119 | else if ( (rule->fields[i] & AUDIT_OPERATORS) == 0 ) | ||
| 120 | rule->fields[i] |= AUDIT_EQUAL; | ||
| 121 | rule->fields[i] &= ~AUDIT_NEGATE; | ||
| 122 | } | ||
| 123 | |||
| 124 | if (!(entry = kmalloc(sizeof(*entry), GFP_KERNEL))) | ||
| 125 | return -ENOMEM; | ||
| 126 | if (audit_copy_rule(&entry->rule, rule)) { | ||
| 127 | kfree(entry); | ||
| 128 | return -EINVAL; | ||
| 129 | } | ||
| 130 | |||
| 131 | if (entry->rule.flags & AUDIT_FILTER_PREPEND) { | 339 | if (entry->rule.flags & AUDIT_FILTER_PREPEND) { |
| 132 | entry->rule.flags &= ~AUDIT_FILTER_PREPEND; | ||
| 133 | list_add_rcu(&entry->list, list); | 340 | list_add_rcu(&entry->list, list); |
| 134 | } else { | 341 | } else { |
| 135 | list_add_tail_rcu(&entry->list, list); | 342 | list_add_tail_rcu(&entry->list, list); |
| @@ -138,16 +345,9 @@ static inline int audit_add_rule(struct audit_rule *rule, | |||
| 138 | return 0; | 345 | return 0; |
| 139 | } | 346 | } |
| 140 | 347 | ||
| 141 | static inline void audit_free_rule(struct rcu_head *head) | 348 | /* Remove an existing rule from filterlist. Protected by |
| 142 | { | ||
| 143 | struct audit_entry *e = container_of(head, struct audit_entry, rcu); | ||
| 144 | kfree(e); | ||
| 145 | } | ||
| 146 | |||
| 147 | /* Note that audit_add_rule and audit_del_rule are called via | ||
| 148 | * audit_receive() in audit.c, and are protected by | ||
| 149 | * audit_netlink_sem. */ | 349 | * audit_netlink_sem. */ |
| 150 | static inline int audit_del_rule(struct audit_rule *rule, | 350 | static inline int audit_del_rule(struct audit_entry *entry, |
| 151 | struct list_head *list) | 351 | struct list_head *list) |
| 152 | { | 352 | { |
| 153 | struct audit_entry *e; | 353 | struct audit_entry *e; |
| @@ -155,16 +355,18 @@ static inline int audit_del_rule(struct audit_rule *rule, | |||
| 155 | /* Do not use the _rcu iterator here, since this is the only | 355 | /* Do not use the _rcu iterator here, since this is the only |
| 156 | * deletion routine. */ | 356 | * deletion routine. */ |
| 157 | list_for_each_entry(e, list, list) { | 357 | list_for_each_entry(e, list, list) { |
| 158 | if (!audit_compare_rule(rule, &e->rule)) { | 358 | if (!audit_compare_rule(&entry->rule, &e->rule)) { |
| 159 | list_del_rcu(&e->list); | 359 | list_del_rcu(&e->list); |
| 160 | call_rcu(&e->rcu, audit_free_rule); | 360 | call_rcu(&e->rcu, audit_free_rule_rcu); |
| 161 | return 0; | 361 | return 0; |
| 162 | } | 362 | } |
| 163 | } | 363 | } |
| 164 | return -ENOENT; /* No matching rule */ | 364 | return -ENOENT; /* No matching rule */ |
| 165 | } | 365 | } |
| 166 | 366 | ||
| 167 | static int audit_list_rules(void *_dest) | 367 | /* List rules using struct audit_rule. Exists for backward |
| 368 | * compatibility with userspace. */ | ||
| 369 | static int audit_list(void *_dest) | ||
| 168 | { | 370 | { |
| 169 | int pid, seq; | 371 | int pid, seq; |
| 170 | int *dest = _dest; | 372 | int *dest = _dest; |
| @@ -180,9 +382,16 @@ static int audit_list_rules(void *_dest) | |||
| 180 | /* The *_rcu iterators not needed here because we are | 382 | /* The *_rcu iterators not needed here because we are |
| 181 | always called with audit_netlink_sem held. */ | 383 | always called with audit_netlink_sem held. */ |
| 182 | for (i=0; i<AUDIT_NR_FILTERS; i++) { | 384 | for (i=0; i<AUDIT_NR_FILTERS; i++) { |
| 183 | list_for_each_entry(entry, &audit_filter_list[i], list) | 385 | list_for_each_entry(entry, &audit_filter_list[i], list) { |
| 386 | struct audit_rule *rule; | ||
| 387 | |||
| 388 | rule = audit_krule_to_rule(&entry->rule); | ||
| 389 | if (unlikely(!rule)) | ||
| 390 | break; | ||
| 184 | audit_send_reply(pid, seq, AUDIT_LIST, 0, 1, | 391 | audit_send_reply(pid, seq, AUDIT_LIST, 0, 1, |
| 185 | &entry->rule, sizeof(entry->rule)); | 392 | rule, sizeof(*rule)); |
| 393 | kfree(rule); | ||
| 394 | } | ||
| 186 | } | 395 | } |
| 187 | audit_send_reply(pid, seq, AUDIT_LIST, 1, 1, NULL, 0); | 396 | audit_send_reply(pid, seq, AUDIT_LIST, 1, 1, NULL, 0); |
| 188 | 397 | ||
| @@ -190,6 +399,40 @@ static int audit_list_rules(void *_dest) | |||
| 190 | return 0; | 399 | return 0; |
| 191 | } | 400 | } |
| 192 | 401 | ||
| 402 | /* List rules using struct audit_rule_data. */ | ||
| 403 | static int audit_list_rules(void *_dest) | ||
| 404 | { | ||
| 405 | int pid, seq; | ||
| 406 | int *dest = _dest; | ||
| 407 | struct audit_entry *e; | ||
| 408 | int i; | ||
| 409 | |||
| 410 | pid = dest[0]; | ||
| 411 | seq = dest[1]; | ||
| 412 | kfree(dest); | ||
| 413 | |||
| 414 | down(&audit_netlink_sem); | ||
| 415 | |||
| 416 | /* The *_rcu iterators not needed here because we are | ||
| 417 | always called with audit_netlink_sem held. */ | ||
| 418 | for (i=0; i<AUDIT_NR_FILTERS; i++) { | ||
| 419 | list_for_each_entry(e, &audit_filter_list[i], list) { | ||
| 420 | struct audit_rule_data *data; | ||
| 421 | |||
| 422 | data = audit_krule_to_data(&e->rule); | ||
| 423 | if (unlikely(!data)) | ||
| 424 | break; | ||
| 425 | audit_send_reply(pid, seq, AUDIT_LIST_RULES, 0, 1, | ||
| 426 | data, sizeof(*data)); | ||
| 427 | kfree(data); | ||
| 428 | } | ||
| 429 | } | ||
| 430 | audit_send_reply(pid, seq, AUDIT_LIST_RULES, 1, 1, NULL, 0); | ||
| 431 | |||
| 432 | up(&audit_netlink_sem); | ||
| 433 | return 0; | ||
| 434 | } | ||
| 435 | |||
| 193 | /** | 436 | /** |
| 194 | * audit_receive_filter - apply all rules to the specified message type | 437 | * audit_receive_filter - apply all rules to the specified message type |
| 195 | * @type: audit message type | 438 | * @type: audit message type |
| @@ -197,18 +440,20 @@ static int audit_list_rules(void *_dest) | |||
| 197 | * @uid: target uid for netlink audit messages | 440 | * @uid: target uid for netlink audit messages |
| 198 | * @seq: netlink audit message sequence (serial) number | 441 | * @seq: netlink audit message sequence (serial) number |
| 199 | * @data: payload data | 442 | * @data: payload data |
| 443 | * @datasz: size of payload data | ||
| 200 | * @loginuid: loginuid of sender | 444 | * @loginuid: loginuid of sender |
| 201 | */ | 445 | */ |
| 202 | int audit_receive_filter(int type, int pid, int uid, int seq, void *data, | 446 | int audit_receive_filter(int type, int pid, int uid, int seq, void *data, |
| 203 | uid_t loginuid) | 447 | size_t datasz, uid_t loginuid) |
| 204 | { | 448 | { |
| 205 | struct task_struct *tsk; | 449 | struct task_struct *tsk; |
| 206 | int *dest; | 450 | int *dest; |
| 207 | int err = 0; | 451 | int err = 0; |
| 208 | unsigned listnr; | 452 | struct audit_entry *entry; |
| 209 | 453 | ||
| 210 | switch (type) { | 454 | switch (type) { |
| 211 | case AUDIT_LIST: | 455 | case AUDIT_LIST: |
| 456 | case AUDIT_LIST_RULES: | ||
| 212 | /* We can't just spew out the rules here because we might fill | 457 | /* We can't just spew out the rules here because we might fill |
| 213 | * the available socket buffer space and deadlock waiting for | 458 | * the available socket buffer space and deadlock waiting for |
| 214 | * auditctl to read from it... which isn't ever going to | 459 | * auditctl to read from it... which isn't ever going to |
| @@ -221,41 +466,48 @@ int audit_receive_filter(int type, int pid, int uid, int seq, void *data, | |||
| 221 | dest[0] = pid; | 466 | dest[0] = pid; |
| 222 | dest[1] = seq; | 467 | dest[1] = seq; |
| 223 | 468 | ||
| 224 | tsk = kthread_run(audit_list_rules, dest, "audit_list_rules"); | 469 | if (type == AUDIT_LIST) |
| 470 | tsk = kthread_run(audit_list, dest, "audit_list"); | ||
| 471 | else | ||
| 472 | tsk = kthread_run(audit_list_rules, dest, | ||
| 473 | "audit_list_rules"); | ||
| 225 | if (IS_ERR(tsk)) { | 474 | if (IS_ERR(tsk)) { |
| 226 | kfree(dest); | 475 | kfree(dest); |
| 227 | err = PTR_ERR(tsk); | 476 | err = PTR_ERR(tsk); |
| 228 | } | 477 | } |
| 229 | break; | 478 | break; |
| 230 | case AUDIT_ADD: | 479 | case AUDIT_ADD: |
| 231 | listnr = ((struct audit_rule *)data)->flags & ~AUDIT_FILTER_PREPEND; | 480 | case AUDIT_ADD_RULE: |
| 232 | switch(listnr) { | 481 | if (type == AUDIT_ADD) |
| 233 | default: | 482 | entry = audit_rule_to_entry(data); |
| 234 | return -EINVAL; | 483 | else |
| 235 | 484 | entry = audit_data_to_entry(data, datasz); | |
| 236 | case AUDIT_FILTER_USER: | 485 | if (IS_ERR(entry)) |
| 237 | case AUDIT_FILTER_TYPE: | 486 | return PTR_ERR(entry); |
| 238 | #ifdef CONFIG_AUDITSYSCALL | 487 | |
| 239 | case AUDIT_FILTER_ENTRY: | 488 | err = audit_add_rule(entry, |
| 240 | case AUDIT_FILTER_EXIT: | 489 | &audit_filter_list[entry->rule.listnr]); |
| 241 | case AUDIT_FILTER_TASK: | ||
| 242 | #endif | ||
| 243 | ; | ||
| 244 | } | ||
| 245 | err = audit_add_rule(data, &audit_filter_list[listnr]); | ||
| 246 | if (!err) | 490 | if (!err) |
| 247 | audit_log(NULL, GFP_KERNEL, AUDIT_CONFIG_CHANGE, | 491 | audit_log(NULL, GFP_KERNEL, AUDIT_CONFIG_CHANGE, |
| 248 | "auid=%u added an audit rule\n", loginuid); | 492 | "auid=%u added an audit rule\n", loginuid); |
| 493 | else | ||
| 494 | audit_free_rule(entry); | ||
| 249 | break; | 495 | break; |
| 250 | case AUDIT_DEL: | 496 | case AUDIT_DEL: |
| 251 | listnr =((struct audit_rule *)data)->flags & ~AUDIT_FILTER_PREPEND; | 497 | case AUDIT_DEL_RULE: |
| 252 | if (listnr >= AUDIT_NR_FILTERS) | 498 | if (type == AUDIT_DEL) |
| 253 | return -EINVAL; | 499 | entry = audit_rule_to_entry(data); |
| 254 | 500 | else | |
| 255 | err = audit_del_rule(data, &audit_filter_list[listnr]); | 501 | entry = audit_data_to_entry(data, datasz); |
| 502 | if (IS_ERR(entry)) | ||
| 503 | return PTR_ERR(entry); | ||
| 504 | |||
| 505 | err = audit_del_rule(entry, | ||
| 506 | &audit_filter_list[entry->rule.listnr]); | ||
| 256 | if (!err) | 507 | if (!err) |
| 257 | audit_log(NULL, GFP_KERNEL, AUDIT_CONFIG_CHANGE, | 508 | audit_log(NULL, GFP_KERNEL, AUDIT_CONFIG_CHANGE, |
| 258 | "auid=%u removed an audit rule\n", loginuid); | 509 | "auid=%u removed an audit rule\n", loginuid); |
| 510 | audit_free_rule(entry); | ||
| 259 | break; | 511 | break; |
| 260 | default: | 512 | default: |
| 261 | return -EINVAL; | 513 | return -EINVAL; |
| @@ -287,29 +539,27 @@ int audit_comparator(const u32 left, const u32 op, const u32 right) | |||
| 287 | 539 | ||
| 288 | 540 | ||
| 289 | static int audit_filter_user_rules(struct netlink_skb_parms *cb, | 541 | static int audit_filter_user_rules(struct netlink_skb_parms *cb, |
| 290 | struct audit_rule *rule, | 542 | struct audit_krule *rule, |
| 291 | enum audit_state *state) | 543 | enum audit_state *state) |
| 292 | { | 544 | { |
| 293 | int i; | 545 | int i; |
| 294 | 546 | ||
| 295 | for (i = 0; i < rule->field_count; i++) { | 547 | for (i = 0; i < rule->field_count; i++) { |
| 296 | u32 field = rule->fields[i] & ~AUDIT_OPERATORS; | 548 | struct audit_field *f = &rule->fields[i]; |
| 297 | u32 op = rule->fields[i] & AUDIT_OPERATORS; | ||
| 298 | u32 value = rule->values[i]; | ||
| 299 | int result = 0; | 549 | int result = 0; |
| 300 | 550 | ||
| 301 | switch (field) { | 551 | switch (f->type) { |
| 302 | case AUDIT_PID: | 552 | case AUDIT_PID: |
| 303 | result = audit_comparator(cb->creds.pid, op, value); | 553 | result = audit_comparator(cb->creds.pid, f->op, f->val); |
| 304 | break; | 554 | break; |
| 305 | case AUDIT_UID: | 555 | case AUDIT_UID: |
| 306 | result = audit_comparator(cb->creds.uid, op, value); | 556 | result = audit_comparator(cb->creds.uid, f->op, f->val); |
| 307 | break; | 557 | break; |
| 308 | case AUDIT_GID: | 558 | case AUDIT_GID: |
| 309 | result = audit_comparator(cb->creds.gid, op, value); | 559 | result = audit_comparator(cb->creds.gid, f->op, f->val); |
| 310 | break; | 560 | break; |
| 311 | case AUDIT_LOGINUID: | 561 | case AUDIT_LOGINUID: |
| 312 | result = audit_comparator(cb->loginuid, op, value); | 562 | result = audit_comparator(cb->loginuid, f->op, f->val); |
| 313 | break; | 563 | break; |
| 314 | } | 564 | } |
| 315 | 565 | ||
| @@ -354,14 +604,11 @@ int audit_filter_type(int type) | |||
| 354 | 604 | ||
| 355 | list_for_each_entry_rcu(e, &audit_filter_list[AUDIT_FILTER_TYPE], | 605 | list_for_each_entry_rcu(e, &audit_filter_list[AUDIT_FILTER_TYPE], |
| 356 | list) { | 606 | list) { |
| 357 | struct audit_rule *rule = &e->rule; | ||
| 358 | int i; | 607 | int i; |
| 359 | for (i = 0; i < rule->field_count; i++) { | 608 | for (i = 0; i < e->rule.field_count; i++) { |
| 360 | u32 field = rule->fields[i] & ~AUDIT_OPERATORS; | 609 | struct audit_field *f = &e->rule.fields[i]; |
| 361 | u32 op = rule->fields[i] & AUDIT_OPERATORS; | 610 | if (f->type == AUDIT_MSGTYPE) { |
| 362 | u32 value = rule->values[i]; | 611 | result = audit_comparator(type, f->op, f->val); |
| 363 | if ( field == AUDIT_MSGTYPE ) { | ||
| 364 | result = audit_comparator(type, op, value); | ||
| 365 | if (!result) | 612 | if (!result) |
| 366 | break; | 613 | break; |
| 367 | } | 614 | } |
diff --git a/kernel/auditsc.c b/kernel/auditsc.c index 17719b303638..ba0878854777 100644 --- a/kernel/auditsc.c +++ b/kernel/auditsc.c | |||
| @@ -162,70 +162,68 @@ struct audit_context { | |||
| 162 | /* Compare a task_struct with an audit_rule. Return 1 on match, 0 | 162 | /* Compare a task_struct with an audit_rule. Return 1 on match, 0 |
| 163 | * otherwise. */ | 163 | * otherwise. */ |
| 164 | static int audit_filter_rules(struct task_struct *tsk, | 164 | static int audit_filter_rules(struct task_struct *tsk, |
| 165 | struct audit_rule *rule, | 165 | struct audit_krule *rule, |
| 166 | struct audit_context *ctx, | 166 | struct audit_context *ctx, |
| 167 | enum audit_state *state) | 167 | enum audit_state *state) |
| 168 | { | 168 | { |
| 169 | int i, j; | 169 | int i, j; |
| 170 | 170 | ||
| 171 | for (i = 0; i < rule->field_count; i++) { | 171 | for (i = 0; i < rule->field_count; i++) { |
| 172 | u32 field = rule->fields[i] & ~AUDIT_OPERATORS; | 172 | struct audit_field *f = &rule->fields[i]; |
| 173 | u32 op = rule->fields[i] & AUDIT_OPERATORS; | ||
| 174 | u32 value = rule->values[i]; | ||
| 175 | int result = 0; | 173 | int result = 0; |
| 176 | 174 | ||
| 177 | switch (field) { | 175 | switch (f->type) { |
| 178 | case AUDIT_PID: | 176 | case AUDIT_PID: |
| 179 | result = audit_comparator(tsk->pid, op, value); | 177 | result = audit_comparator(tsk->pid, f->op, f->val); |
| 180 | break; | 178 | break; |
| 181 | case AUDIT_UID: | 179 | case AUDIT_UID: |
| 182 | result = audit_comparator(tsk->uid, op, value); | 180 | result = audit_comparator(tsk->uid, f->op, f->val); |
| 183 | break; | 181 | break; |
| 184 | case AUDIT_EUID: | 182 | case AUDIT_EUID: |
| 185 | result = audit_comparator(tsk->euid, op, value); | 183 | result = audit_comparator(tsk->euid, f->op, f->val); |
| 186 | break; | 184 | break; |
| 187 | case AUDIT_SUID: | 185 | case AUDIT_SUID: |
| 188 | result = audit_comparator(tsk->suid, op, value); | 186 | result = audit_comparator(tsk->suid, f->op, f->val); |
| 189 | break; | 187 | break; |
| 190 | case AUDIT_FSUID: | 188 | case AUDIT_FSUID: |
| 191 | result = audit_comparator(tsk->fsuid, op, value); | 189 | result = audit_comparator(tsk->fsuid, f->op, f->val); |
| 192 | break; | 190 | break; |
| 193 | case AUDIT_GID: | 191 | case AUDIT_GID: |
| 194 | result = audit_comparator(tsk->gid, op, value); | 192 | result = audit_comparator(tsk->gid, f->op, f->val); |
| 195 | break; | 193 | break; |
| 196 | case AUDIT_EGID: | 194 | case AUDIT_EGID: |
| 197 | result = audit_comparator(tsk->egid, op, value); | 195 | result = audit_comparator(tsk->egid, f->op, f->val); |
| 198 | break; | 196 | break; |
| 199 | case AUDIT_SGID: | 197 | case AUDIT_SGID: |
| 200 | result = audit_comparator(tsk->sgid, op, value); | 198 | result = audit_comparator(tsk->sgid, f->op, f->val); |
| 201 | break; | 199 | break; |
| 202 | case AUDIT_FSGID: | 200 | case AUDIT_FSGID: |
| 203 | result = audit_comparator(tsk->fsgid, op, value); | 201 | result = audit_comparator(tsk->fsgid, f->op, f->val); |
| 204 | break; | 202 | break; |
| 205 | case AUDIT_PERS: | 203 | case AUDIT_PERS: |
| 206 | result = audit_comparator(tsk->personality, op, value); | 204 | result = audit_comparator(tsk->personality, f->op, f->val); |
| 207 | break; | 205 | break; |
| 208 | case AUDIT_ARCH: | 206 | case AUDIT_ARCH: |
| 209 | if (ctx) | 207 | if (ctx) |
| 210 | result = audit_comparator(ctx->arch, op, value); | 208 | result = audit_comparator(ctx->arch, f->op, f->val); |
| 211 | break; | 209 | break; |
| 212 | 210 | ||
| 213 | case AUDIT_EXIT: | 211 | case AUDIT_EXIT: |
| 214 | if (ctx && ctx->return_valid) | 212 | if (ctx && ctx->return_valid) |
| 215 | result = audit_comparator(ctx->return_code, op, value); | 213 | result = audit_comparator(ctx->return_code, f->op, f->val); |
| 216 | break; | 214 | break; |
| 217 | case AUDIT_SUCCESS: | 215 | case AUDIT_SUCCESS: |
| 218 | if (ctx && ctx->return_valid) { | 216 | if (ctx && ctx->return_valid) { |
| 219 | if (value) | 217 | if (f->val) |
| 220 | result = audit_comparator(ctx->return_valid, op, AUDITSC_SUCCESS); | 218 | result = audit_comparator(ctx->return_valid, f->op, AUDITSC_SUCCESS); |
| 221 | else | 219 | else |
| 222 | result = audit_comparator(ctx->return_valid, op, AUDITSC_FAILURE); | 220 | result = audit_comparator(ctx->return_valid, f->op, AUDITSC_FAILURE); |
| 223 | } | 221 | } |
| 224 | break; | 222 | break; |
| 225 | case AUDIT_DEVMAJOR: | 223 | case AUDIT_DEVMAJOR: |
| 226 | if (ctx) { | 224 | if (ctx) { |
| 227 | for (j = 0; j < ctx->name_count; j++) { | 225 | for (j = 0; j < ctx->name_count; j++) { |
| 228 | if (audit_comparator(MAJOR(ctx->names[j].dev), op, value)) { | 226 | if (audit_comparator(MAJOR(ctx->names[j].dev), f->op, f->val)) { |
| 229 | ++result; | 227 | ++result; |
| 230 | break; | 228 | break; |
| 231 | } | 229 | } |
| @@ -235,7 +233,7 @@ static int audit_filter_rules(struct task_struct *tsk, | |||
| 235 | case AUDIT_DEVMINOR: | 233 | case AUDIT_DEVMINOR: |
| 236 | if (ctx) { | 234 | if (ctx) { |
| 237 | for (j = 0; j < ctx->name_count; j++) { | 235 | for (j = 0; j < ctx->name_count; j++) { |
| 238 | if (audit_comparator(MINOR(ctx->names[j].dev), op, value)) { | 236 | if (audit_comparator(MINOR(ctx->names[j].dev), f->op, f->val)) { |
| 239 | ++result; | 237 | ++result; |
| 240 | break; | 238 | break; |
| 241 | } | 239 | } |
| @@ -245,8 +243,8 @@ static int audit_filter_rules(struct task_struct *tsk, | |||
| 245 | case AUDIT_INODE: | 243 | case AUDIT_INODE: |
| 246 | if (ctx) { | 244 | if (ctx) { |
| 247 | for (j = 0; j < ctx->name_count; j++) { | 245 | for (j = 0; j < ctx->name_count; j++) { |
| 248 | if (audit_comparator(ctx->names[j].ino, op, value) || | 246 | if (audit_comparator(ctx->names[j].ino, f->op, f->val) || |
| 249 | audit_comparator(ctx->names[j].pino, op, value)) { | 247 | audit_comparator(ctx->names[j].pino, f->op, f->val)) { |
| 250 | ++result; | 248 | ++result; |
| 251 | break; | 249 | break; |
| 252 | } | 250 | } |
| @@ -256,14 +254,14 @@ static int audit_filter_rules(struct task_struct *tsk, | |||
| 256 | case AUDIT_LOGINUID: | 254 | case AUDIT_LOGINUID: |
| 257 | result = 0; | 255 | result = 0; |
| 258 | if (ctx) | 256 | if (ctx) |
| 259 | result = audit_comparator(ctx->loginuid, op, value); | 257 | result = audit_comparator(ctx->loginuid, f->op, f->val); |
| 260 | break; | 258 | break; |
| 261 | case AUDIT_ARG0: | 259 | case AUDIT_ARG0: |
| 262 | case AUDIT_ARG1: | 260 | case AUDIT_ARG1: |
| 263 | case AUDIT_ARG2: | 261 | case AUDIT_ARG2: |
| 264 | case AUDIT_ARG3: | 262 | case AUDIT_ARG3: |
| 265 | if (ctx) | 263 | if (ctx) |
| 266 | result = audit_comparator(ctx->argv[field-AUDIT_ARG0], op, value); | 264 | result = audit_comparator(ctx->argv[f->type-AUDIT_ARG0], f->op, f->val); |
| 267 | break; | 265 | break; |
| 268 | } | 266 | } |
| 269 | 267 | ||
