From e23eb920b0f3978687c497de2ac3eb9e281dab32 Mon Sep 17 00:00:00 2001 From: Peter Moody Date: Thu, 14 Jun 2012 10:04:35 -0700 Subject: audit: export audit_log_task_info At the suggestion of eparis@redhat.com, move this chunk of task logging from audit_log_exit to audit_log_task_info and export this function so it's usuable elsewhere in the kernel. This patch is against git://git.kernel.org/pub/scm/linux/kernel/git/zohar/linux-integrity#next-ima-appraisal Changelog v2: - add empty audit_log_task_info if CONFIG_AUDITSYSCALL isn't set. Changelog v1: - Initial post. Signed-off-by: Peter Moody Signed-off-by: Mimi Zohar --- kernel/auditsc.c | 74 ++++++++++++++++++++++++++------------------------------ 1 file changed, 34 insertions(+), 40 deletions(-) (limited to 'kernel/auditsc.c') diff --git a/kernel/auditsc.c b/kernel/auditsc.c index 4b96415527b8..37f52f27828d 100644 --- a/kernel/auditsc.c +++ b/kernel/auditsc.c @@ -1154,13 +1154,38 @@ error_path: EXPORT_SYMBOL(audit_log_task_context); -static void audit_log_task_info(struct audit_buffer *ab, struct task_struct *tsk) +void audit_log_task_info(struct audit_buffer *ab, struct task_struct *tsk) { + const struct cred *cred; char name[sizeof(tsk->comm)]; struct mm_struct *mm = tsk->mm; struct vm_area_struct *vma; + char *tty; + + if (!ab) + return; /* tsk == current */ + cred = current_cred(); + + spin_lock_irq(&tsk->sighand->siglock); + if (tsk->signal && tsk->signal->tty && tsk->signal->tty->name) + tty = tsk->signal->tty->name; + else + tty = "(none)"; + spin_unlock_irq(&tsk->sighand->siglock); + + + audit_log_format(ab, + " ppid=%ld pid=%d auid=%u uid=%u gid=%u" + " euid=%u suid=%u fsuid=%u" + " egid=%u sgid=%u fsgid=%u ses=%u tty=%s", + sys_getppid(), + tsk->pid, + tsk->loginuid, cred->uid, cred->gid, + cred->euid, cred->suid, cred->fsuid, + cred->egid, cred->sgid, cred->fsgid, + tsk->sessionid, tty); get_task_comm(name, tsk); audit_log_format(ab, " comm="); @@ -1183,6 +1208,8 @@ static void audit_log_task_info(struct audit_buffer *ab, struct task_struct *tsk audit_log_task_context(ab); } +EXPORT_SYMBOL(audit_log_task_info); + static int audit_log_pid_context(struct audit_context *context, pid_t pid, uid_t auid, uid_t uid, unsigned int sessionid, u32 sid, char *comm) @@ -1585,26 +1612,12 @@ static void audit_log_name(struct audit_context *context, struct audit_names *n, static void audit_log_exit(struct audit_context *context, struct task_struct *tsk) { - const struct cred *cred; int i, call_panic = 0; struct audit_buffer *ab; struct audit_aux_data *aux; - const char *tty; struct audit_names *n; /* tsk == current */ - context->pid = tsk->pid; - if (!context->ppid) - context->ppid = sys_getppid(); - cred = current_cred(); - context->uid = cred->uid; - context->gid = cred->gid; - context->euid = cred->euid; - context->suid = cred->suid; - context->fsuid = cred->fsuid; - context->egid = cred->egid; - context->sgid = cred->sgid; - context->fsgid = cred->fsgid; context->personality = tsk->personality; ab = audit_log_start(context, GFP_KERNEL, AUDIT_SYSCALL); @@ -1619,32 +1632,13 @@ static void audit_log_exit(struct audit_context *context, struct task_struct *ts (context->return_valid==AUDITSC_SUCCESS)?"yes":"no", context->return_code); - spin_lock_irq(&tsk->sighand->siglock); - if (tsk->signal && tsk->signal->tty && tsk->signal->tty->name) - tty = tsk->signal->tty->name; - else - tty = "(none)"; - spin_unlock_irq(&tsk->sighand->siglock); - audit_log_format(ab, - " a0=%lx a1=%lx a2=%lx a3=%lx items=%d" - " ppid=%d pid=%d auid=%u uid=%u gid=%u" - " euid=%u suid=%u fsuid=%u" - " egid=%u sgid=%u fsgid=%u tty=%s ses=%u", - context->argv[0], - context->argv[1], - context->argv[2], - context->argv[3], - context->name_count, - context->ppid, - context->pid, - tsk->loginuid, - context->uid, - context->gid, - context->euid, context->suid, context->fsuid, - context->egid, context->sgid, context->fsgid, tty, - tsk->sessionid); - + " a0=%lx a1=%lx a2=%lx a3=%lx items=%d", + context->argv[0], + context->argv[1], + context->argv[2], + context->argv[3], + context->name_count); audit_log_task_info(ab, tsk); audit_log_key(ab, context->filterkey); -- cgit v1.2.3 From ca57ec0f00c3f139c41bf6b0a5b9bcc95bbb2ad7 Mon Sep 17 00:00:00 2001 From: "Eric W. Biederman" Date: Tue, 11 Sep 2012 02:18:08 -0700 Subject: audit: Add typespecific uid and gid comparators The audit filter code guarantees that uid are always compared with uids and gids are always compared with gids, as the comparason operations are type specific. Take advantage of this proper to define audit_uid_comparator and audit_gid_comparator which use the type safe comparasons from uidgid.h. Build on audit_uid_comparator and audit_gid_comparator and replace audit_compare_id with audit_compare_uid and audit_compare_gid. This is one of those odd cases where being type safe and duplicating code leads to simpler shorter and more concise code. Don't allow bitmask operations in uid and gid comparisons in audit_data_to_entry. Bitmask operations are already denined in audit_rule_to_entry. Convert constants in audit_rule_to_entry and audit_data_to_entry into kuids and kgids when appropriate. Convert the uid and gid field in struct audit_names to be of type kuid_t and kgid_t respectively, so that the new uid and gid comparators can be applied in a type safe manner. Cc: Al Viro Cc: Eric Paris Signed-off-by: "Eric W. Biederman" --- include/linux/audit.h | 2 + kernel/audit.h | 2 + kernel/auditfilter.c | 119 +++++++++++++++++++++++++++++++++++---- kernel/auditsc.c | 150 ++++++++++++++++++++++++-------------------------- 4 files changed, 184 insertions(+), 89 deletions(-) (limited to 'kernel/auditsc.c') diff --git a/include/linux/audit.h b/include/linux/audit.h index b9c5b22e34a5..ca019bb74da3 100644 --- a/include/linux/audit.h +++ b/include/linux/audit.h @@ -442,6 +442,8 @@ struct audit_krule { struct audit_field { u32 type; u32 val; + kuid_t uid; + kgid_t gid; u32 op; char *lsm_str; void *lsm_rule; diff --git a/kernel/audit.h b/kernel/audit.h index 816766803371..4b428bb41ea3 100644 --- a/kernel/audit.h +++ b/kernel/audit.h @@ -76,6 +76,8 @@ static inline int audit_hash_ino(u32 ino) extern int audit_match_class(int class, unsigned syscall); extern int audit_comparator(const u32 left, const u32 op, const u32 right); +extern int audit_uid_comparator(kuid_t left, u32 op, kuid_t right); +extern int audit_gid_comparator(kgid_t left, u32 op, kgid_t right); extern int audit_compare_dname_path(const char *dname, const char *path, int *dirlen); extern struct sk_buff * audit_make_reply(int pid, int seq, int type, diff --git a/kernel/auditfilter.c b/kernel/auditfilter.c index e242dd9aa2d0..b30320cea26f 100644 --- a/kernel/auditfilter.c +++ b/kernel/auditfilter.c @@ -342,6 +342,8 @@ static struct audit_entry *audit_rule_to_entry(struct audit_rule *rule) f->type = rule->fields[i] & ~(AUDIT_NEGATE|AUDIT_OPERATORS); f->val = rule->values[i]; + f->uid = INVALID_UID; + f->gid = INVALID_GID; err = -EINVAL; if (f->op == Audit_bad) @@ -350,16 +352,32 @@ static struct audit_entry *audit_rule_to_entry(struct audit_rule *rule) switch(f->type) { default: goto exit_free; - case AUDIT_PID: case AUDIT_UID: case AUDIT_EUID: case AUDIT_SUID: case AUDIT_FSUID: + case AUDIT_LOGINUID: + /* bit ops not implemented for uid comparisons */ + if (f->op == Audit_bitmask || f->op == Audit_bittest) + goto exit_free; + + f->uid = make_kuid(current_user_ns(), f->val); + if (!uid_valid(f->uid)) + goto exit_free; + break; case AUDIT_GID: case AUDIT_EGID: case AUDIT_SGID: case AUDIT_FSGID: - case AUDIT_LOGINUID: + /* bit ops not implemented for gid comparisons */ + if (f->op == Audit_bitmask || f->op == Audit_bittest) + goto exit_free; + + f->gid = make_kgid(current_user_ns(), f->val); + if (!gid_valid(f->gid)) + goto exit_free; + break; + case AUDIT_PID: case AUDIT_PERS: case AUDIT_MSGTYPE: case AUDIT_PPID: @@ -437,19 +455,39 @@ static struct audit_entry *audit_data_to_entry(struct audit_rule_data *data, f->type = data->fields[i]; f->val = data->values[i]; + f->uid = INVALID_UID; + f->gid = INVALID_GID; f->lsm_str = NULL; f->lsm_rule = NULL; switch(f->type) { - case AUDIT_PID: case AUDIT_UID: case AUDIT_EUID: case AUDIT_SUID: case AUDIT_FSUID: + case AUDIT_LOGINUID: + case AUDIT_OBJ_UID: + /* bit ops not implemented for uid comparisons */ + if (f->op == Audit_bitmask || f->op == Audit_bittest) + goto exit_free; + + f->uid = make_kuid(current_user_ns(), f->val); + if (!uid_valid(f->uid)) + goto exit_free; + break; case AUDIT_GID: case AUDIT_EGID: case AUDIT_SGID: case AUDIT_FSGID: - case AUDIT_LOGINUID: + case AUDIT_OBJ_GID: + /* bit ops not implemented for gid comparisons */ + if (f->op == Audit_bitmask || f->op == Audit_bittest) + goto exit_free; + + f->gid = make_kgid(current_user_ns(), f->val); + if (!gid_valid(f->gid)) + goto exit_free; + break; + case AUDIT_PID: case AUDIT_PERS: case AUDIT_MSGTYPE: case AUDIT_PPID: @@ -461,8 +499,6 @@ static struct audit_entry *audit_data_to_entry(struct audit_rule_data *data, case AUDIT_ARG1: case AUDIT_ARG2: case AUDIT_ARG3: - case AUDIT_OBJ_UID: - case AUDIT_OBJ_GID: break; case AUDIT_ARCH: entry->rule.arch_f = f; @@ -707,6 +743,23 @@ static int audit_compare_rule(struct audit_krule *a, struct audit_krule *b) if (strcmp(a->filterkey, b->filterkey)) return 1; break; + case AUDIT_UID: + case AUDIT_EUID: + case AUDIT_SUID: + case AUDIT_FSUID: + case AUDIT_LOGINUID: + case AUDIT_OBJ_UID: + if (!uid_eq(a->fields[i].uid, b->fields[i].uid)) + return 1; + break; + case AUDIT_GID: + case AUDIT_EGID: + case AUDIT_SGID: + case AUDIT_FSGID: + case AUDIT_OBJ_GID: + if (!gid_eq(a->fields[i].gid, b->fields[i].gid)) + return 1; + break; default: if (a->fields[i].val != b->fields[i].val) return 1; @@ -1198,6 +1251,52 @@ int audit_comparator(u32 left, u32 op, u32 right) } } +int audit_uid_comparator(kuid_t left, u32 op, kuid_t right) +{ + switch (op) { + case Audit_equal: + return uid_eq(left, right); + case Audit_not_equal: + return !uid_eq(left, right); + case Audit_lt: + return uid_lt(left, right); + case Audit_le: + return uid_lte(left, right); + case Audit_gt: + return uid_gt(left, right); + case Audit_ge: + return uid_gte(left, right); + case Audit_bitmask: + case Audit_bittest: + default: + BUG(); + return 0; + } +} + +int audit_gid_comparator(kgid_t left, u32 op, kgid_t right) +{ + switch (op) { + case Audit_equal: + return gid_eq(left, right); + case Audit_not_equal: + return !gid_eq(left, right); + case Audit_lt: + return gid_lt(left, right); + case Audit_le: + return gid_lte(left, right); + case Audit_gt: + return gid_gt(left, right); + case Audit_ge: + return gid_gte(left, right); + case Audit_bitmask: + case Audit_bittest: + default: + BUG(); + return 0; + } +} + /* Compare given dentry name with last component in given path, * return of 0 indicates a match. */ int audit_compare_dname_path(const char *dname, const char *path, @@ -1251,14 +1350,14 @@ static int audit_filter_user_rules(struct audit_krule *rule, result = audit_comparator(task_pid_vnr(current), f->op, f->val); break; case AUDIT_UID: - result = audit_comparator(current_uid(), f->op, f->val); + result = audit_uid_comparator(current_uid(), f->op, f->uid); break; case AUDIT_GID: - result = audit_comparator(current_gid(), f->op, f->val); + result = audit_gid_comparator(current_gid(), f->op, f->gid); break; case AUDIT_LOGINUID: - result = audit_comparator(audit_get_loginuid(current), - f->op, f->val); + result = audit_uid_comparator(audit_get_loginuid(current), + f->op, f->uid); break; case AUDIT_SUBJ_USER: case AUDIT_SUBJ_ROLE: diff --git a/kernel/auditsc.c b/kernel/auditsc.c index 4b96415527b8..0b5b8a232b55 100644 --- a/kernel/auditsc.c +++ b/kernel/auditsc.c @@ -113,8 +113,8 @@ struct audit_names { unsigned long ino; dev_t dev; umode_t mode; - uid_t uid; - gid_t gid; + kuid_t uid; + kgid_t gid; dev_t rdev; u32 osid; struct audit_cap_data fcap; @@ -464,37 +464,47 @@ static int match_tree_refs(struct audit_context *ctx, struct audit_tree *tree) return 0; } -static int audit_compare_id(uid_t uid1, - struct audit_names *name, - unsigned long name_offset, - struct audit_field *f, - struct audit_context *ctx) +static int audit_compare_uid(kuid_t uid, + struct audit_names *name, + struct audit_field *f, + struct audit_context *ctx) { struct audit_names *n; - unsigned long addr; - uid_t uid2; int rc; - - BUILD_BUG_ON(sizeof(uid_t) != sizeof(gid_t)); - + if (name) { - addr = (unsigned long)name; - addr += name_offset; - - uid2 = *(uid_t *)addr; - rc = audit_comparator(uid1, f->op, uid2); + rc = audit_uid_comparator(uid, f->op, name->uid); if (rc) return rc; } - + if (ctx) { list_for_each_entry(n, &ctx->names_list, list) { - addr = (unsigned long)n; - addr += name_offset; - - uid2 = *(uid_t *)addr; + rc = audit_uid_comparator(uid, f->op, n->uid); + if (rc) + return rc; + } + } + return 0; +} - rc = audit_comparator(uid1, f->op, uid2); +static int audit_compare_gid(kgid_t gid, + struct audit_names *name, + struct audit_field *f, + struct audit_context *ctx) +{ + struct audit_names *n; + int rc; + + if (name) { + rc = audit_gid_comparator(gid, f->op, name->gid); + if (rc) + return rc; + } + + if (ctx) { + list_for_each_entry(n, &ctx->names_list, list) { + rc = audit_gid_comparator(gid, f->op, n->gid); if (rc) return rc; } @@ -511,80 +521,62 @@ static int audit_field_compare(struct task_struct *tsk, switch (f->val) { /* process to file object comparisons */ case AUDIT_COMPARE_UID_TO_OBJ_UID: - return audit_compare_id(cred->uid, - name, offsetof(struct audit_names, uid), - f, ctx); + return audit_compare_uid(cred->uid, name, f, ctx); case AUDIT_COMPARE_GID_TO_OBJ_GID: - return audit_compare_id(cred->gid, - name, offsetof(struct audit_names, gid), - f, ctx); + return audit_compare_gid(cred->gid, name, f, ctx); case AUDIT_COMPARE_EUID_TO_OBJ_UID: - return audit_compare_id(cred->euid, - name, offsetof(struct audit_names, uid), - f, ctx); + return audit_compare_uid(cred->euid, name, f, ctx); case AUDIT_COMPARE_EGID_TO_OBJ_GID: - return audit_compare_id(cred->egid, - name, offsetof(struct audit_names, gid), - f, ctx); + return audit_compare_gid(cred->egid, name, f, ctx); case AUDIT_COMPARE_AUID_TO_OBJ_UID: - return audit_compare_id(tsk->loginuid, - name, offsetof(struct audit_names, uid), - f, ctx); + return audit_compare_uid(tsk->loginuid, name, f, ctx); case AUDIT_COMPARE_SUID_TO_OBJ_UID: - return audit_compare_id(cred->suid, - name, offsetof(struct audit_names, uid), - f, ctx); + return audit_compare_uid(cred->suid, name, f, ctx); case AUDIT_COMPARE_SGID_TO_OBJ_GID: - return audit_compare_id(cred->sgid, - name, offsetof(struct audit_names, gid), - f, ctx); + return audit_compare_gid(cred->sgid, name, f, ctx); case AUDIT_COMPARE_FSUID_TO_OBJ_UID: - return audit_compare_id(cred->fsuid, - name, offsetof(struct audit_names, uid), - f, ctx); + return audit_compare_uid(cred->fsuid, name, f, ctx); case AUDIT_COMPARE_FSGID_TO_OBJ_GID: - return audit_compare_id(cred->fsgid, - name, offsetof(struct audit_names, gid), - f, ctx); + return audit_compare_gid(cred->fsgid, name, f, ctx); /* uid comparisons */ case AUDIT_COMPARE_UID_TO_AUID: - return audit_comparator(cred->uid, f->op, tsk->loginuid); + return audit_uid_comparator(cred->uid, f->op, tsk->loginuid); case AUDIT_COMPARE_UID_TO_EUID: - return audit_comparator(cred->uid, f->op, cred->euid); + return audit_uid_comparator(cred->uid, f->op, cred->euid); case AUDIT_COMPARE_UID_TO_SUID: - return audit_comparator(cred->uid, f->op, cred->suid); + return audit_uid_comparator(cred->uid, f->op, cred->suid); case AUDIT_COMPARE_UID_TO_FSUID: - return audit_comparator(cred->uid, f->op, cred->fsuid); + return audit_uid_comparator(cred->uid, f->op, cred->fsuid); /* auid comparisons */ case AUDIT_COMPARE_AUID_TO_EUID: - return audit_comparator(tsk->loginuid, f->op, cred->euid); + return audit_uid_comparator(tsk->loginuid, f->op, cred->euid); case AUDIT_COMPARE_AUID_TO_SUID: - return audit_comparator(tsk->loginuid, f->op, cred->suid); + return audit_uid_comparator(tsk->loginuid, f->op, cred->suid); case AUDIT_COMPARE_AUID_TO_FSUID: - return audit_comparator(tsk->loginuid, f->op, cred->fsuid); + return audit_uid_comparator(tsk->loginuid, f->op, cred->fsuid); /* euid comparisons */ case AUDIT_COMPARE_EUID_TO_SUID: - return audit_comparator(cred->euid, f->op, cred->suid); + return audit_uid_comparator(cred->euid, f->op, cred->suid); case AUDIT_COMPARE_EUID_TO_FSUID: - return audit_comparator(cred->euid, f->op, cred->fsuid); + return audit_uid_comparator(cred->euid, f->op, cred->fsuid); /* suid comparisons */ case AUDIT_COMPARE_SUID_TO_FSUID: - return audit_comparator(cred->suid, f->op, cred->fsuid); + return audit_uid_comparator(cred->suid, f->op, cred->fsuid); /* gid comparisons */ case AUDIT_COMPARE_GID_TO_EGID: - return audit_comparator(cred->gid, f->op, cred->egid); + return audit_gid_comparator(cred->gid, f->op, cred->egid); case AUDIT_COMPARE_GID_TO_SGID: - return audit_comparator(cred->gid, f->op, cred->sgid); + return audit_gid_comparator(cred->gid, f->op, cred->sgid); case AUDIT_COMPARE_GID_TO_FSGID: - return audit_comparator(cred->gid, f->op, cred->fsgid); + return audit_gid_comparator(cred->gid, f->op, cred->fsgid); /* egid comparisons */ case AUDIT_COMPARE_EGID_TO_SGID: - return audit_comparator(cred->egid, f->op, cred->sgid); + return audit_gid_comparator(cred->egid, f->op, cred->sgid); case AUDIT_COMPARE_EGID_TO_FSGID: - return audit_comparator(cred->egid, f->op, cred->fsgid); + return audit_gid_comparator(cred->egid, f->op, cred->fsgid); /* sgid comparison */ case AUDIT_COMPARE_SGID_TO_FSGID: - return audit_comparator(cred->sgid, f->op, cred->fsgid); + return audit_gid_comparator(cred->sgid, f->op, cred->fsgid); default: WARN(1, "Missing AUDIT_COMPARE define. Report as a bug\n"); return 0; @@ -630,28 +622,28 @@ static int audit_filter_rules(struct task_struct *tsk, } break; case AUDIT_UID: - result = audit_comparator(cred->uid, f->op, f->val); + result = audit_uid_comparator(cred->uid, f->op, f->uid); break; case AUDIT_EUID: - result = audit_comparator(cred->euid, f->op, f->val); + result = audit_uid_comparator(cred->euid, f->op, f->uid); break; case AUDIT_SUID: - result = audit_comparator(cred->suid, f->op, f->val); + result = audit_uid_comparator(cred->suid, f->op, f->uid); break; case AUDIT_FSUID: - result = audit_comparator(cred->fsuid, f->op, f->val); + result = audit_uid_comparator(cred->fsuid, f->op, f->uid); break; case AUDIT_GID: - result = audit_comparator(cred->gid, f->op, f->val); + result = audit_gid_comparator(cred->gid, f->op, f->gid); break; case AUDIT_EGID: - result = audit_comparator(cred->egid, f->op, f->val); + result = audit_gid_comparator(cred->egid, f->op, f->gid); break; case AUDIT_SGID: - result = audit_comparator(cred->sgid, f->op, f->val); + result = audit_gid_comparator(cred->sgid, f->op, f->gid); break; case AUDIT_FSGID: - result = audit_comparator(cred->fsgid, f->op, f->val); + result = audit_gid_comparator(cred->fsgid, f->op, f->gid); break; case AUDIT_PERS: result = audit_comparator(tsk->personality, f->op, f->val); @@ -717,10 +709,10 @@ static int audit_filter_rules(struct task_struct *tsk, break; case AUDIT_OBJ_UID: if (name) { - result = audit_comparator(name->uid, f->op, f->val); + result = audit_uid_comparator(name->uid, f->op, f->uid); } else if (ctx) { list_for_each_entry(n, &ctx->names_list, list) { - if (audit_comparator(n->uid, f->op, f->val)) { + if (audit_uid_comparator(n->uid, f->op, f->uid)) { ++result; break; } @@ -729,10 +721,10 @@ static int audit_filter_rules(struct task_struct *tsk, break; case AUDIT_OBJ_GID: if (name) { - result = audit_comparator(name->gid, f->op, f->val); + result = audit_gid_comparator(name->gid, f->op, f->gid); } else if (ctx) { list_for_each_entry(n, &ctx->names_list, list) { - if (audit_comparator(n->gid, f->op, f->val)) { + if (audit_gid_comparator(n->gid, f->op, f->gid)) { ++result; break; } @@ -750,7 +742,7 @@ static int audit_filter_rules(struct task_struct *tsk, case AUDIT_LOGINUID: result = 0; if (ctx) - result = audit_comparator(tsk->loginuid, f->op, f->val); + result = audit_uid_comparator(tsk->loginuid, f->op, f->uid); break; case AUDIT_SUBJ_USER: case AUDIT_SUBJ_ROLE: -- cgit v1.2.3 From e1760bd5ffae8cb98cffb030ee8e631eba28f3d8 Mon Sep 17 00:00:00 2001 From: "Eric W. Biederman" Date: Mon, 10 Sep 2012 22:39:43 -0700 Subject: userns: Convert the audit loginuid to be a kuid Always store audit loginuids in type kuid_t. Print loginuids by converting them into uids in the appropriate user namespace, and then printing the resulting uid. Modify audit_get_loginuid to return a kuid_t. Modify audit_set_loginuid to take a kuid_t. Modify /proc//loginuid on read to convert the loginuid into the user namespace of the opener of the file. Modify /proc//loginud on write to convert the loginuid rom the user namespace of the opener of the file. Cc: Al Viro Cc: Eric Paris Cc: Paul Moore ? Cc: David Miller Signed-off-by: Eric W. Biederman --- drivers/tty/tty_audit.c | 14 ++++++++------ fs/proc/base.c | 12 ++++++++++-- include/linux/audit.h | 6 +++--- include/linux/init_task.h | 2 +- include/linux/sched.h | 2 +- include/linux/tty.h | 4 ++-- include/net/netlabel.h | 2 +- include/net/xfrm.h | 23 ++++++++++++----------- kernel/audit.c | 20 ++++++++++---------- kernel/audit_watch.c | 2 +- kernel/auditfilter.c | 7 ++++--- kernel/auditsc.c | 20 +++++++++++--------- net/core/dev.c | 2 +- net/netlabel/netlabel_unlabeled.c | 2 +- net/netlabel/netlabel_user.c | 2 +- net/xfrm/xfrm_policy.c | 8 ++++---- net/xfrm/xfrm_state.c | 6 +++--- net/xfrm/xfrm_user.c | 12 ++++++------ 18 files changed, 80 insertions(+), 66 deletions(-) (limited to 'kernel/auditsc.c') diff --git a/drivers/tty/tty_audit.c b/drivers/tty/tty_audit.c index 7c5866920622..5b59bd7f4227 100644 --- a/drivers/tty/tty_audit.c +++ b/drivers/tty/tty_audit.c @@ -61,7 +61,7 @@ static void tty_audit_buf_put(struct tty_audit_buf *buf) } static void tty_audit_log(const char *description, struct task_struct *tsk, - uid_t loginuid, unsigned sessionid, int major, + kuid_t loginuid, unsigned sessionid, int major, int minor, unsigned char *data, size_t size) { struct audit_buffer *ab; @@ -73,7 +73,9 @@ static void tty_audit_log(const char *description, struct task_struct *tsk, audit_log_format(ab, "%s pid=%u uid=%u auid=%u ses=%u " "major=%d minor=%d comm=", description, - tsk->pid, uid, loginuid, sessionid, + tsk->pid, uid, + from_kuid(&init_user_ns, loginuid), + sessionid, major, minor); get_task_comm(name, tsk); audit_log_untrustedstring(ab, name); @@ -89,7 +91,7 @@ static void tty_audit_log(const char *description, struct task_struct *tsk, * Generate an audit message from the contents of @buf, which is owned by * @tsk with @loginuid. @buf->mutex must be locked. */ -static void tty_audit_buf_push(struct task_struct *tsk, uid_t loginuid, +static void tty_audit_buf_push(struct task_struct *tsk, kuid_t loginuid, unsigned int sessionid, struct tty_audit_buf *buf) { @@ -112,7 +114,7 @@ static void tty_audit_buf_push(struct task_struct *tsk, uid_t loginuid, */ static void tty_audit_buf_push_current(struct tty_audit_buf *buf) { - uid_t auid = audit_get_loginuid(current); + kuid_t auid = audit_get_loginuid(current); unsigned int sessionid = audit_get_sessionid(current); tty_audit_buf_push(current, auid, sessionid, buf); } @@ -179,7 +181,7 @@ void tty_audit_tiocsti(struct tty_struct *tty, char ch) } if (should_audit && audit_enabled) { - uid_t auid; + kuid_t auid; unsigned int sessionid; auid = audit_get_loginuid(current); @@ -199,7 +201,7 @@ void tty_audit_tiocsti(struct tty_struct *tty, char ch) * reference to the tty audit buffer if available. * Flush the buffer or return an appropriate error code. */ -int tty_audit_push_task(struct task_struct *tsk, uid_t loginuid, u32 sessionid) +int tty_audit_push_task(struct task_struct *tsk, kuid_t loginuid, u32 sessionid) { struct tty_audit_buf *buf = ERR_PTR(-EPERM); unsigned long flags; diff --git a/fs/proc/base.c b/fs/proc/base.c index 1b6c84cbdb73..138cff4b05dd 100644 --- a/fs/proc/base.c +++ b/fs/proc/base.c @@ -1089,7 +1089,8 @@ static ssize_t proc_loginuid_read(struct file * file, char __user * buf, if (!task) return -ESRCH; length = scnprintf(tmpbuf, TMPBUFLEN, "%u", - audit_get_loginuid(task)); + from_kuid(file->f_cred->user_ns, + audit_get_loginuid(task))); put_task_struct(task); return simple_read_from_buffer(buf, count, ppos, tmpbuf, length); } @@ -1101,6 +1102,7 @@ static ssize_t proc_loginuid_write(struct file * file, const char __user * buf, char *page, *tmp; ssize_t length; uid_t loginuid; + kuid_t kloginuid; rcu_read_lock(); if (current != pid_task(proc_pid(inode), PIDTYPE_PID)) { @@ -1130,7 +1132,13 @@ static ssize_t proc_loginuid_write(struct file * file, const char __user * buf, goto out_free_page; } - length = audit_set_loginuid(loginuid); + kloginuid = make_kuid(file->f_cred->user_ns, loginuid); + if (!uid_valid(kloginuid)) { + length = -EINVAL; + goto out_free_page; + } + + length = audit_set_loginuid(kloginuid); if (likely(length == 0)) length = count; diff --git a/include/linux/audit.h b/include/linux/audit.h index ca019bb74da3..12367cbadfe1 100644 --- a/include/linux/audit.h +++ b/include/linux/audit.h @@ -527,7 +527,7 @@ static inline void audit_ptrace(struct task_struct *t) extern unsigned int audit_serial(void); extern int auditsc_get_stamp(struct audit_context *ctx, struct timespec *t, unsigned int *serial); -extern int audit_set_loginuid(uid_t loginuid); +extern int audit_set_loginuid(kuid_t loginuid); #define audit_get_loginuid(t) ((t)->loginuid) #define audit_get_sessionid(t) ((t)->sessionid) extern void audit_log_task_context(struct audit_buffer *ab); @@ -639,7 +639,7 @@ extern int audit_signals; #define audit_core_dumps(i) do { ; } while (0) #define audit_seccomp(i,s,c) do { ; } while (0) #define auditsc_get_stamp(c,t,s) (0) -#define audit_get_loginuid(t) (-1) +#define audit_get_loginuid(t) (INVALID_UID) #define audit_get_sessionid(t) (-1) #define audit_log_task_context(b) do { ; } while (0) #define audit_ipc_obj(i) ((void)0) @@ -705,7 +705,7 @@ extern int audit_update_lsm_rules(void); extern int audit_filter_user(void); extern int audit_filter_type(int type); extern int audit_receive_filter(int type, int pid, int seq, - void *data, size_t datasz, uid_t loginuid, + void *data, size_t datasz, kuid_t loginuid, u32 sessionid, u32 sid); extern int audit_enabled; #else diff --git a/include/linux/init_task.h b/include/linux/init_task.h index 89f1cb1056f0..6d087c5f57f7 100644 --- a/include/linux/init_task.h +++ b/include/linux/init_task.h @@ -92,7 +92,7 @@ extern struct group_info init_groups; #ifdef CONFIG_AUDITSYSCALL #define INIT_IDS \ - .loginuid = -1, \ + .loginuid = INVALID_UID, \ .sessionid = -1, #else #define INIT_IDS diff --git a/include/linux/sched.h b/include/linux/sched.h index c147e7024f11..f64d092f2bed 100644 --- a/include/linux/sched.h +++ b/include/linux/sched.h @@ -1426,7 +1426,7 @@ struct task_struct { struct audit_context *audit_context; #ifdef CONFIG_AUDITSYSCALL - uid_t loginuid; + kuid_t loginuid; unsigned int sessionid; #endif struct seccomp seccomp; diff --git a/include/linux/tty.h b/include/linux/tty.h index 9f47ab540f65..7298385815e6 100644 --- a/include/linux/tty.h +++ b/include/linux/tty.h @@ -553,7 +553,7 @@ extern void tty_audit_fork(struct signal_struct *sig); extern void tty_audit_tiocsti(struct tty_struct *tty, char ch); extern void tty_audit_push(struct tty_struct *tty); extern int tty_audit_push_task(struct task_struct *tsk, - uid_t loginuid, u32 sessionid); + kuid_t loginuid, u32 sessionid); #else static inline void tty_audit_add_data(struct tty_struct *tty, unsigned char *data, size_t size) @@ -572,7 +572,7 @@ static inline void tty_audit_push(struct tty_struct *tty) { } static inline int tty_audit_push_task(struct task_struct *tsk, - uid_t loginuid, u32 sessionid) + kuid_t loginuid, u32 sessionid) { return 0; } diff --git a/include/net/netlabel.h b/include/net/netlabel.h index f67440970d7e..2c95d55f7914 100644 --- a/include/net/netlabel.h +++ b/include/net/netlabel.h @@ -110,7 +110,7 @@ struct cipso_v4_doi; /* NetLabel audit information */ struct netlbl_audit { u32 secid; - uid_t loginuid; + kuid_t loginuid; u32 sessionid; }; diff --git a/include/net/xfrm.h b/include/net/xfrm.h index d9509eb29b80..1f217e2c5d82 100644 --- a/include/net/xfrm.h +++ b/include/net/xfrm.h @@ -662,7 +662,7 @@ struct xfrm_spi_skb_cb { /* Audit Information */ struct xfrm_audit { u32 secid; - uid_t loginuid; + kuid_t loginuid; u32 sessionid; }; @@ -681,13 +681,14 @@ static inline struct audit_buffer *xfrm_audit_start(const char *op) return audit_buf; } -static inline void xfrm_audit_helper_usrinfo(uid_t auid, u32 ses, u32 secid, +static inline void xfrm_audit_helper_usrinfo(kuid_t auid, u32 ses, u32 secid, struct audit_buffer *audit_buf) { char *secctx; u32 secctx_len; - audit_log_format(audit_buf, " auid=%u ses=%u", auid, ses); + audit_log_format(audit_buf, " auid=%u ses=%u", + from_kuid(&init_user_ns, auid), ses); if (secid != 0 && security_secid_to_secctx(secid, &secctx, &secctx_len) == 0) { audit_log_format(audit_buf, " subj=%s", secctx); @@ -697,13 +698,13 @@ static inline void xfrm_audit_helper_usrinfo(uid_t auid, u32 ses, u32 secid, } extern void xfrm_audit_policy_add(struct xfrm_policy *xp, int result, - u32 auid, u32 ses, u32 secid); + kuid_t auid, u32 ses, u32 secid); extern void xfrm_audit_policy_delete(struct xfrm_policy *xp, int result, - u32 auid, u32 ses, u32 secid); + kuid_t auid, u32 ses, u32 secid); extern void xfrm_audit_state_add(struct xfrm_state *x, int result, - u32 auid, u32 ses, u32 secid); + kuid_t auid, u32 ses, u32 secid); extern void xfrm_audit_state_delete(struct xfrm_state *x, int result, - u32 auid, u32 ses, u32 secid); + kuid_t auid, u32 ses, u32 secid); extern void xfrm_audit_state_replay_overflow(struct xfrm_state *x, struct sk_buff *skb); extern void xfrm_audit_state_replay(struct xfrm_state *x, @@ -716,22 +717,22 @@ extern void xfrm_audit_state_icvfail(struct xfrm_state *x, #else static inline void xfrm_audit_policy_add(struct xfrm_policy *xp, int result, - u32 auid, u32 ses, u32 secid) + kuid_t auid, u32 ses, u32 secid) { } static inline void xfrm_audit_policy_delete(struct xfrm_policy *xp, int result, - u32 auid, u32 ses, u32 secid) + kuid_t auid, u32 ses, u32 secid) { } static inline void xfrm_audit_state_add(struct xfrm_state *x, int result, - u32 auid, u32 ses, u32 secid) + kuid_t auid, u32 ses, u32 secid) { } static inline void xfrm_audit_state_delete(struct xfrm_state *x, int result, - u32 auid, u32 ses, u32 secid) + kuid_t auid, u32 ses, u32 secid) { } diff --git a/kernel/audit.c b/kernel/audit.c index 2e0dd5edf69b..44a4b13c9f00 100644 --- a/kernel/audit.c +++ b/kernel/audit.c @@ -265,7 +265,7 @@ void audit_log_lost(const char *message) } static int audit_log_config_change(char *function_name, int new, int old, - uid_t loginuid, u32 sessionid, u32 sid, + kuid_t loginuid, u32 sessionid, u32 sid, int allow_changes) { struct audit_buffer *ab; @@ -273,7 +273,7 @@ static int audit_log_config_change(char *function_name, int new, int old, ab = audit_log_start(NULL, GFP_KERNEL, AUDIT_CONFIG_CHANGE); audit_log_format(ab, "%s=%d old=%d auid=%u ses=%u", function_name, new, - old, loginuid, sessionid); + old, from_kuid(&init_user_ns, loginuid), sessionid); if (sid) { char *ctx = NULL; u32 len; @@ -293,7 +293,7 @@ static int audit_log_config_change(char *function_name, int new, int old, } static int audit_do_config_change(char *function_name, int *to_change, - int new, uid_t loginuid, u32 sessionid, + int new, kuid_t loginuid, u32 sessionid, u32 sid) { int allow_changes, rc = 0, old = *to_change; @@ -320,21 +320,21 @@ static int audit_do_config_change(char *function_name, int *to_change, return rc; } -static int audit_set_rate_limit(int limit, uid_t loginuid, u32 sessionid, +static int audit_set_rate_limit(int limit, kuid_t loginuid, u32 sessionid, u32 sid) { return audit_do_config_change("audit_rate_limit", &audit_rate_limit, limit, loginuid, sessionid, sid); } -static int audit_set_backlog_limit(int limit, uid_t loginuid, u32 sessionid, +static int audit_set_backlog_limit(int limit, kuid_t loginuid, u32 sessionid, u32 sid) { return audit_do_config_change("audit_backlog_limit", &audit_backlog_limit, limit, loginuid, sessionid, sid); } -static int audit_set_enabled(int state, uid_t loginuid, u32 sessionid, u32 sid) +static int audit_set_enabled(int state, kuid_t loginuid, u32 sessionid, u32 sid) { int rc; if (state < AUDIT_OFF || state > AUDIT_LOCKED) @@ -349,7 +349,7 @@ static int audit_set_enabled(int state, uid_t loginuid, u32 sessionid, u32 sid) return rc; } -static int audit_set_failure(int state, uid_t loginuid, u32 sessionid, u32 sid) +static int audit_set_failure(int state, kuid_t loginuid, u32 sessionid, u32 sid) { if (state != AUDIT_FAIL_SILENT && state != AUDIT_FAIL_PRINTK @@ -607,7 +607,7 @@ static int audit_netlink_ok(struct sk_buff *skb, u16 msg_type) } static int audit_log_common_recv_msg(struct audit_buffer **ab, u16 msg_type, - uid_t auid, u32 ses, u32 sid) + kuid_t auid, u32 ses, u32 sid) { int rc = 0; char *ctx = NULL; @@ -622,7 +622,7 @@ static int audit_log_common_recv_msg(struct audit_buffer **ab, u16 msg_type, audit_log_format(*ab, "pid=%d uid=%u auid=%u ses=%u", task_tgid_vnr(current), from_kuid(&init_user_ns, current_uid()), - auid, ses); + from_kuid(&init_user_ns, auid), ses); if (sid) { rc = security_secid_to_secctx(sid, &ctx, &len); if (rc) @@ -644,7 +644,7 @@ static int audit_receive_msg(struct sk_buff *skb, struct nlmsghdr *nlh) int err; struct audit_buffer *ab; u16 msg_type = nlh->nlmsg_type; - uid_t loginuid; /* loginuid of sender */ + kuid_t loginuid; /* loginuid of sender */ u32 sessionid; struct audit_sig_info *sig_data; char *ctx = NULL; diff --git a/kernel/audit_watch.c b/kernel/audit_watch.c index 3823281401b5..1c22ec3d87bc 100644 --- a/kernel/audit_watch.c +++ b/kernel/audit_watch.c @@ -241,7 +241,7 @@ static void audit_watch_log_rule_change(struct audit_krule *r, struct audit_watc struct audit_buffer *ab; ab = audit_log_start(NULL, GFP_NOFS, AUDIT_CONFIG_CHANGE); audit_log_format(ab, "auid=%u ses=%u op=", - audit_get_loginuid(current), + from_kuid(&init_user_ns, audit_get_loginuid(current)), audit_get_sessionid(current)); audit_log_string(ab, op); audit_log_format(ab, " path="); diff --git a/kernel/auditfilter.c b/kernel/auditfilter.c index b30320cea26f..c4bcdbaf4d4d 100644 --- a/kernel/auditfilter.c +++ b/kernel/auditfilter.c @@ -1109,7 +1109,7 @@ static void audit_list_rules(int pid, int seq, struct sk_buff_head *q) } /* Log rule additions and removals */ -static void audit_log_rule_change(uid_t loginuid, u32 sessionid, u32 sid, +static void audit_log_rule_change(kuid_t loginuid, u32 sessionid, u32 sid, char *action, struct audit_krule *rule, int res) { @@ -1121,7 +1121,8 @@ static void audit_log_rule_change(uid_t loginuid, u32 sessionid, u32 sid, ab = audit_log_start(NULL, GFP_KERNEL, AUDIT_CONFIG_CHANGE); if (!ab) return; - audit_log_format(ab, "auid=%u ses=%u", loginuid, sessionid); + audit_log_format(ab, "auid=%u ses=%u", + from_kuid(&init_user_ns, loginuid), sessionid); if (sid) { char *ctx = NULL; u32 len; @@ -1152,7 +1153,7 @@ static void audit_log_rule_change(uid_t loginuid, u32 sessionid, u32 sid, * @sid: SE Linux Security ID of sender */ int audit_receive_filter(int type, int pid, int seq, void *data, - size_t datasz, uid_t loginuid, u32 sessionid, u32 sid) + size_t datasz, kuid_t loginuid, u32 sessionid, u32 sid) { struct task_struct *tsk; struct audit_netlink_list *dest; diff --git a/kernel/auditsc.c b/kernel/auditsc.c index 0b5b8a232b55..26fdfc092e35 100644 --- a/kernel/auditsc.c +++ b/kernel/auditsc.c @@ -149,7 +149,7 @@ struct audit_aux_data_execve { struct audit_aux_data_pids { struct audit_aux_data d; pid_t target_pid[AUDIT_AUX_PIDS]; - uid_t target_auid[AUDIT_AUX_PIDS]; + kuid_t target_auid[AUDIT_AUX_PIDS]; uid_t target_uid[AUDIT_AUX_PIDS]; unsigned int target_sessionid[AUDIT_AUX_PIDS]; u32 target_sid[AUDIT_AUX_PIDS]; @@ -214,7 +214,7 @@ struct audit_context { int arch; pid_t target_pid; - uid_t target_auid; + kuid_t target_auid; uid_t target_uid; unsigned int target_sessionid; u32 target_sid; @@ -1176,7 +1176,7 @@ static void audit_log_task_info(struct audit_buffer *ab, struct task_struct *tsk } static int audit_log_pid_context(struct audit_context *context, pid_t pid, - uid_t auid, uid_t uid, unsigned int sessionid, + kuid_t auid, uid_t uid, unsigned int sessionid, u32 sid, char *comm) { struct audit_buffer *ab; @@ -1188,7 +1188,8 @@ static int audit_log_pid_context(struct audit_context *context, pid_t pid, if (!ab) return rc; - audit_log_format(ab, "opid=%d oauid=%d ouid=%d oses=%d", pid, auid, + audit_log_format(ab, "opid=%d oauid=%d ouid=%d oses=%d", pid, + from_kuid(&init_user_ns, auid), uid, sessionid); if (security_secid_to_secctx(sid, &ctx, &len)) { audit_log_format(ab, " obj=(none)"); @@ -1630,7 +1631,7 @@ static void audit_log_exit(struct audit_context *context, struct task_struct *ts context->name_count, context->ppid, context->pid, - tsk->loginuid, + from_kuid(&init_user_ns, tsk->loginuid), context->uid, context->gid, context->euid, context->suid, context->fsuid, @@ -2291,14 +2292,14 @@ static atomic_t session_id = ATOMIC_INIT(0); * * Called (set) from fs/proc/base.c::proc_loginuid_write(). */ -int audit_set_loginuid(uid_t loginuid) +int audit_set_loginuid(kuid_t loginuid) { struct task_struct *task = current; struct audit_context *context = task->audit_context; unsigned int sessionid; #ifdef CONFIG_AUDIT_LOGINUID_IMMUTABLE - if (task->loginuid != -1) + if (uid_valid(task->loginuid)) return -EPERM; #else /* CONFIG_AUDIT_LOGINUID_IMMUTABLE */ if (!capable(CAP_AUDIT_CONTROL)) @@ -2315,7 +2316,8 @@ int audit_set_loginuid(uid_t loginuid) "old auid=%u new auid=%u" " old ses=%u new ses=%u", task->pid, task_uid(task), - task->loginuid, loginuid, + from_kuid(&init_user_ns, task->loginuid), + from_kuid(&init_user_ns, loginuid), task->sessionid, sessionid); audit_log_end(ab); } @@ -2543,7 +2545,7 @@ int __audit_signal_info(int sig, struct task_struct *t) if (audit_pid && t->tgid == audit_pid) { if (sig == SIGTERM || sig == SIGHUP || sig == SIGUSR1 || sig == SIGUSR2) { audit_sig_pid = tsk->pid; - if (tsk->loginuid != -1) + if (uid_valid(tsk->loginuid)) audit_sig_uid = tsk->loginuid; else audit_sig_uid = uid; diff --git a/net/core/dev.c b/net/core/dev.c index 026bb4a37665..1c0d0823a5a4 100644 --- a/net/core/dev.c +++ b/net/core/dev.c @@ -4524,7 +4524,7 @@ static int __dev_set_promiscuity(struct net_device *dev, int inc) "dev=%s prom=%d old_prom=%d auid=%u uid=%u gid=%u ses=%u", dev->name, (dev->flags & IFF_PROMISC), (old_flags & IFF_PROMISC), - audit_get_loginuid(current), + from_kuid(&init_user_ns, audit_get_loginuid(current)), from_kuid(&init_user_ns, uid), from_kgid(&init_user_ns, gid), audit_get_sessionid(current)); diff --git a/net/netlabel/netlabel_unlabeled.c b/net/netlabel/netlabel_unlabeled.c index e7ff694f1049..729a345c75a4 100644 --- a/net/netlabel/netlabel_unlabeled.c +++ b/net/netlabel/netlabel_unlabeled.c @@ -1541,7 +1541,7 @@ int __init netlbl_unlabel_defconf(void) * it is called is at bootup before the audit subsystem is reporting * messages so don't worry to much about these values. */ security_task_getsecid(current, &audit_info.secid); - audit_info.loginuid = 0; + audit_info.loginuid = GLOBAL_ROOT_UID; audit_info.sessionid = 0; entry = kzalloc(sizeof(*entry), GFP_KERNEL); diff --git a/net/netlabel/netlabel_user.c b/net/netlabel/netlabel_user.c index 9fae63f10298..9650c4ad5f88 100644 --- a/net/netlabel/netlabel_user.c +++ b/net/netlabel/netlabel_user.c @@ -109,7 +109,7 @@ struct audit_buffer *netlbl_audit_start_common(int type, return NULL; audit_log_format(audit_buf, "netlabel: auid=%u ses=%u", - audit_info->loginuid, + from_kuid(&init_user_ns, audit_info->loginuid), audit_info->sessionid); if (audit_info->secid != 0 && diff --git a/net/xfrm/xfrm_policy.c b/net/xfrm/xfrm_policy.c index c5a5165a5927..2f475151cea1 100644 --- a/net/xfrm/xfrm_policy.c +++ b/net/xfrm/xfrm_policy.c @@ -2630,12 +2630,12 @@ static void xfrm_policy_fini(struct net *net) flush_work(&net->xfrm.policy_hash_work); #ifdef CONFIG_XFRM_SUB_POLICY - audit_info.loginuid = -1; + audit_info.loginuid = INVALID_UID; audit_info.sessionid = -1; audit_info.secid = 0; xfrm_policy_flush(net, XFRM_POLICY_TYPE_SUB, &audit_info); #endif - audit_info.loginuid = -1; + audit_info.loginuid = INVALID_UID; audit_info.sessionid = -1; audit_info.secid = 0; xfrm_policy_flush(net, XFRM_POLICY_TYPE_MAIN, &audit_info); @@ -2742,7 +2742,7 @@ static void xfrm_audit_common_policyinfo(struct xfrm_policy *xp, } void xfrm_audit_policy_add(struct xfrm_policy *xp, int result, - uid_t auid, u32 sessionid, u32 secid) + kuid_t auid, u32 sessionid, u32 secid) { struct audit_buffer *audit_buf; @@ -2757,7 +2757,7 @@ void xfrm_audit_policy_add(struct xfrm_policy *xp, int result, EXPORT_SYMBOL_GPL(xfrm_audit_policy_add); void xfrm_audit_policy_delete(struct xfrm_policy *xp, int result, - uid_t auid, u32 sessionid, u32 secid) + kuid_t auid, u32 sessionid, u32 secid) { struct audit_buffer *audit_buf; diff --git a/net/xfrm/xfrm_state.c b/net/xfrm/xfrm_state.c index 5b228f97d4b3..fce6a49bc7c6 100644 --- a/net/xfrm/xfrm_state.c +++ b/net/xfrm/xfrm_state.c @@ -2045,7 +2045,7 @@ void xfrm_state_fini(struct net *net) unsigned int sz; flush_work(&net->xfrm.state_hash_work); - audit_info.loginuid = -1; + audit_info.loginuid = INVALID_UID; audit_info.sessionid = -1; audit_info.secid = 0; xfrm_state_flush(net, IPSEC_PROTO_ANY, &audit_info); @@ -2112,7 +2112,7 @@ static void xfrm_audit_helper_pktinfo(struct sk_buff *skb, u16 family, } void xfrm_audit_state_add(struct xfrm_state *x, int result, - uid_t auid, u32 sessionid, u32 secid) + kuid_t auid, u32 sessionid, u32 secid) { struct audit_buffer *audit_buf; @@ -2127,7 +2127,7 @@ void xfrm_audit_state_add(struct xfrm_state *x, int result, EXPORT_SYMBOL_GPL(xfrm_audit_state_add); void xfrm_audit_state_delete(struct xfrm_state *x, int result, - uid_t auid, u32 sessionid, u32 secid) + kuid_t auid, u32 sessionid, u32 secid) { struct audit_buffer *audit_buf; diff --git a/net/xfrm/xfrm_user.c b/net/xfrm/xfrm_user.c index e75d8e47f35c..9ea55db737b4 100644 --- a/net/xfrm/xfrm_user.c +++ b/net/xfrm/xfrm_user.c @@ -575,7 +575,7 @@ static int xfrm_add_sa(struct sk_buff *skb, struct nlmsghdr *nlh, struct xfrm_state *x; int err; struct km_event c; - uid_t loginuid = audit_get_loginuid(current); + kuid_t loginuid = audit_get_loginuid(current); u32 sessionid = audit_get_sessionid(current); u32 sid; @@ -654,7 +654,7 @@ static int xfrm_del_sa(struct sk_buff *skb, struct nlmsghdr *nlh, int err = -ESRCH; struct km_event c; struct xfrm_usersa_id *p = nlmsg_data(nlh); - uid_t loginuid = audit_get_loginuid(current); + kuid_t loginuid = audit_get_loginuid(current); u32 sessionid = audit_get_sessionid(current); u32 sid; @@ -1369,7 +1369,7 @@ static int xfrm_add_policy(struct sk_buff *skb, struct nlmsghdr *nlh, struct km_event c; int err; int excl; - uid_t loginuid = audit_get_loginuid(current); + kuid_t loginuid = audit_get_loginuid(current); u32 sessionid = audit_get_sessionid(current); u32 sid; @@ -1624,7 +1624,7 @@ static int xfrm_get_policy(struct sk_buff *skb, struct nlmsghdr *nlh, NETLINK_CB(skb).pid); } } else { - uid_t loginuid = audit_get_loginuid(current); + kuid_t loginuid = audit_get_loginuid(current); u32 sessionid = audit_get_sessionid(current); u32 sid; @@ -1918,7 +1918,7 @@ static int xfrm_add_pol_expire(struct sk_buff *skb, struct nlmsghdr *nlh, err = 0; if (up->hard) { - uid_t loginuid = audit_get_loginuid(current); + kuid_t loginuid = audit_get_loginuid(current); u32 sessionid = audit_get_sessionid(current); u32 sid; @@ -1961,7 +1961,7 @@ static int xfrm_add_sa_expire(struct sk_buff *skb, struct nlmsghdr *nlh, km_state_expired(x, ue->hard, current->pid); if (ue->hard) { - uid_t loginuid = audit_get_loginuid(current); + kuid_t loginuid = audit_get_loginuid(current); u32 sessionid = audit_get_sessionid(current); u32 sid; -- cgit v1.2.3 From cca080d9b622094831672a136e5ee4f702d116b1 Mon Sep 17 00:00:00 2001 From: "Eric W. Biederman" Date: Tue, 7 Feb 2012 16:53:48 -0800 Subject: userns: Convert audit to work with user namespaces enabled - Explicitly format uids gids in audit messges in the initial user namespace. This is safe because auditd is restrected to be in the initial user namespace. - Convert audit_sig_uid into a kuid_t. - Enable building the audit code and user namespaces at the same time. The net result is that the audit subsystem now uses kuid_t and kgid_t whenever possible making it almost impossible to confuse a raw uid_t with a kuid_t preventing bugs. Cc: Al Viro Cc: Eric Paris Cc: Greg Kroah-Hartman Signed-off-by: Eric W. Biederman --- drivers/tty/tty_audit.c | 5 +++-- init/Kconfig | 2 -- kernel/audit.c | 4 ++-- kernel/audit.h | 2 +- kernel/auditsc.c | 51 ++++++++++++++++++++++++++++++------------------- 5 files changed, 37 insertions(+), 27 deletions(-) (limited to 'kernel/auditsc.c') diff --git a/drivers/tty/tty_audit.c b/drivers/tty/tty_audit.c index 5b59bd7f4227..b0b39b823ccf 100644 --- a/drivers/tty/tty_audit.c +++ b/drivers/tty/tty_audit.c @@ -69,11 +69,12 @@ static void tty_audit_log(const char *description, struct task_struct *tsk, ab = audit_log_start(NULL, GFP_KERNEL, AUDIT_TTY); if (ab) { char name[sizeof(tsk->comm)]; - uid_t uid = task_uid(tsk); + kuid_t uid = task_uid(tsk); audit_log_format(ab, "%s pid=%u uid=%u auid=%u ses=%u " "major=%d minor=%d comm=", description, - tsk->pid, uid, + tsk->pid, + from_kuid(&init_user_ns, uid), from_kuid(&init_user_ns, loginuid), sessionid, major, minor); diff --git a/init/Kconfig b/init/Kconfig index fd8696b1a81e..b5ecb4e75518 100644 --- a/init/Kconfig +++ b/init/Kconfig @@ -927,8 +927,6 @@ config UIDGID_CONVERTED # Features depends on IMA = n depends on EVM = n - depends on AUDIT = n - depends on AUDITSYSCALL = n depends on TASKSTATS = n depends on TRACING = n depends on FS_POSIX_ACL = n diff --git a/kernel/audit.c b/kernel/audit.c index 44a4b13c9f00..511488a7bc71 100644 --- a/kernel/audit.c +++ b/kernel/audit.c @@ -105,7 +105,7 @@ static int audit_backlog_wait_time = 60 * HZ; static int audit_backlog_wait_overflow = 0; /* The identity of the user shutting down the audit system. */ -uid_t audit_sig_uid = -1; +kuid_t audit_sig_uid = INVALID_UID; pid_t audit_sig_pid = -1; u32 audit_sig_sid = 0; @@ -853,7 +853,7 @@ static int audit_receive_msg(struct sk_buff *skb, struct nlmsghdr *nlh) security_release_secctx(ctx, len); return -ENOMEM; } - sig_data->uid = audit_sig_uid; + sig_data->uid = from_kuid(&init_user_ns, audit_sig_uid); sig_data->pid = audit_sig_pid; if (audit_sig_sid) { memcpy(sig_data->ctx, ctx, len); diff --git a/kernel/audit.h b/kernel/audit.h index 4b428bb41ea3..9eb3d79482b6 100644 --- a/kernel/audit.h +++ b/kernel/audit.h @@ -146,7 +146,7 @@ extern void audit_kill_trees(struct list_head *); extern char *audit_unpack_string(void **, size_t *, size_t); extern pid_t audit_sig_pid; -extern uid_t audit_sig_uid; +extern kuid_t audit_sig_uid; extern u32 audit_sig_sid; #ifdef CONFIG_AUDITSYSCALL diff --git a/kernel/auditsc.c b/kernel/auditsc.c index 26fdfc092e35..ff4798fcb488 100644 --- a/kernel/auditsc.c +++ b/kernel/auditsc.c @@ -150,7 +150,7 @@ struct audit_aux_data_pids { struct audit_aux_data d; pid_t target_pid[AUDIT_AUX_PIDS]; kuid_t target_auid[AUDIT_AUX_PIDS]; - uid_t target_uid[AUDIT_AUX_PIDS]; + kuid_t target_uid[AUDIT_AUX_PIDS]; unsigned int target_sessionid[AUDIT_AUX_PIDS]; u32 target_sid[AUDIT_AUX_PIDS]; char target_comm[AUDIT_AUX_PIDS][TASK_COMM_LEN]; @@ -208,14 +208,14 @@ struct audit_context { size_t sockaddr_len; /* Save things to print about task_struct */ pid_t pid, ppid; - uid_t uid, euid, suid, fsuid; - gid_t gid, egid, sgid, fsgid; + kuid_t uid, euid, suid, fsuid; + kgid_t gid, egid, sgid, fsgid; unsigned long personality; int arch; pid_t target_pid; kuid_t target_auid; - uid_t target_uid; + kuid_t target_uid; unsigned int target_sessionid; u32 target_sid; char target_comm[TASK_COMM_LEN]; @@ -231,8 +231,8 @@ struct audit_context { long args[6]; } socketcall; struct { - uid_t uid; - gid_t gid; + kuid_t uid; + kgid_t gid; umode_t mode; u32 osid; int has_perm; @@ -1176,7 +1176,7 @@ static void audit_log_task_info(struct audit_buffer *ab, struct task_struct *tsk } static int audit_log_pid_context(struct audit_context *context, pid_t pid, - kuid_t auid, uid_t uid, unsigned int sessionid, + kuid_t auid, kuid_t uid, unsigned int sessionid, u32 sid, char *comm) { struct audit_buffer *ab; @@ -1190,7 +1190,7 @@ static int audit_log_pid_context(struct audit_context *context, pid_t pid, audit_log_format(ab, "opid=%d oauid=%d ouid=%d oses=%d", pid, from_kuid(&init_user_ns, auid), - uid, sessionid); + from_kuid(&init_user_ns, uid), sessionid); if (security_secid_to_secctx(sid, &ctx, &len)) { audit_log_format(ab, " obj=(none)"); rc = 1; @@ -1440,7 +1440,9 @@ static void show_special(struct audit_context *context, int *call_panic) u32 osid = context->ipc.osid; audit_log_format(ab, "ouid=%u ogid=%u mode=%#ho", - context->ipc.uid, context->ipc.gid, context->ipc.mode); + from_kuid(&init_user_ns, context->ipc.uid), + from_kgid(&init_user_ns, context->ipc.gid), + context->ipc.mode); if (osid) { char *ctx = NULL; u32 len; @@ -1553,8 +1555,8 @@ static void audit_log_name(struct audit_context *context, struct audit_names *n, MAJOR(n->dev), MINOR(n->dev), n->mode, - n->uid, - n->gid, + from_kuid(&init_user_ns, n->uid), + from_kgid(&init_user_ns, n->gid), MAJOR(n->rdev), MINOR(n->rdev)); } @@ -1632,10 +1634,15 @@ static void audit_log_exit(struct audit_context *context, struct task_struct *ts context->ppid, context->pid, from_kuid(&init_user_ns, tsk->loginuid), - context->uid, - context->gid, - context->euid, context->suid, context->fsuid, - context->egid, context->sgid, context->fsgid, tty, + from_kuid(&init_user_ns, context->uid), + from_kgid(&init_user_ns, context->gid), + from_kuid(&init_user_ns, context->euid), + from_kuid(&init_user_ns, context->suid), + from_kuid(&init_user_ns, context->fsuid), + from_kgid(&init_user_ns, context->egid), + from_kgid(&init_user_ns, context->sgid), + from_kgid(&init_user_ns, context->fsgid), + tty, tsk->sessionid); @@ -2315,7 +2322,8 @@ int audit_set_loginuid(kuid_t loginuid) audit_log_format(ab, "login pid=%d uid=%u " "old auid=%u new auid=%u" " old ses=%u new ses=%u", - task->pid, task_uid(task), + task->pid, + from_kuid(&init_user_ns, task_uid(task)), from_kuid(&init_user_ns, task->loginuid), from_kuid(&init_user_ns, loginuid), task->sessionid, sessionid); @@ -2540,7 +2548,7 @@ int __audit_signal_info(int sig, struct task_struct *t) struct audit_aux_data_pids *axp; struct task_struct *tsk = current; struct audit_context *ctx = tsk->audit_context; - uid_t uid = current_uid(), t_uid = task_uid(t); + kuid_t uid = current_uid(), t_uid = task_uid(t); if (audit_pid && t->tgid == audit_pid) { if (sig == SIGTERM || sig == SIGHUP || sig == SIGUSR1 || sig == SIGUSR2) { @@ -2666,8 +2674,8 @@ void __audit_mmap_fd(int fd, int flags) static void audit_log_abend(struct audit_buffer *ab, char *reason, long signr) { - uid_t auid, uid; - gid_t gid; + kuid_t auid, uid; + kgid_t gid; unsigned int sessionid; auid = audit_get_loginuid(current); @@ -2675,7 +2683,10 @@ static void audit_log_abend(struct audit_buffer *ab, char *reason, long signr) current_uid_gid(&uid, &gid); audit_log_format(ab, "auid=%u uid=%u gid=%u ses=%u", - auid, uid, gid, sessionid); + from_kuid(&init_user_ns, auid), + from_kuid(&init_user_ns, uid), + from_kgid(&init_user_ns, gid), + sessionid); audit_log_task_context(ab); audit_log_format(ab, " pid=%d comm=", current->pid); audit_log_untrustedstring(ab, current->comm); -- cgit v1.2.3 From 2dd8ad81e31d0d36a5d448329c646ab43eb17788 Mon Sep 17 00:00:00 2001 From: Konstantin Khlebnikov Date: Mon, 8 Oct 2012 16:28:51 -0700 Subject: mm: use mm->exe_file instead of first VM_EXECUTABLE vma->vm_file Some security modules and oprofile still uses VM_EXECUTABLE for retrieving a task's executable file. After this patch they will use mm->exe_file directly. mm->exe_file is protected with mm->mmap_sem, so locking stays the same. Signed-off-by: Konstantin Khlebnikov Acked-by: Chris Metcalf [arch/tile] Acked-by: Tetsuo Handa [tomoyo] Cc: Alexander Viro Cc: Carsten Otte Cc: Cyrill Gorcunov Cc: Eric Paris Cc: H. Peter Anvin Cc: Hugh Dickins Cc: Ingo Molnar Acked-by: James Morris Cc: Jason Baron Cc: Kentaro Takeda Cc: Matt Helsley Cc: Nick Piggin Cc: Oleg Nesterov Cc: Peter Zijlstra Cc: Robert Richter Cc: Suresh Siddha Cc: Venkatesh Pallipadi Acked-by: Linus Torvalds Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- arch/powerpc/oprofile/cell/spu_task_sync.c | 15 ++++----------- arch/tile/mm/elf.c | 19 +++++++------------ drivers/oprofile/buffer_sync.c | 17 +++-------------- kernel/auditsc.c | 13 ++----------- kernel/fork.c | 3 +-- security/tomoyo/util.c | 9 ++------- 6 files changed, 19 insertions(+), 57 deletions(-) (limited to 'kernel/auditsc.c') diff --git a/arch/powerpc/oprofile/cell/spu_task_sync.c b/arch/powerpc/oprofile/cell/spu_task_sync.c index 642fca137ccb..28f1af2db1f5 100644 --- a/arch/powerpc/oprofile/cell/spu_task_sync.c +++ b/arch/powerpc/oprofile/cell/spu_task_sync.c @@ -304,7 +304,7 @@ static inline unsigned long fast_get_dcookie(struct path *path) return cookie; } -/* Look up the dcookie for the task's first VM_EXECUTABLE mapping, +/* Look up the dcookie for the task's mm->exe_file, * which corresponds loosely to "application name". Also, determine * the offset for the SPU ELF object. If computed offset is * non-zero, it implies an embedded SPU object; otherwise, it's a @@ -321,7 +321,6 @@ get_exec_dcookie_and_offset(struct spu *spu, unsigned int *offsetp, { unsigned long app_cookie = 0; unsigned int my_offset = 0; - struct file *app = NULL; struct vm_area_struct *vma; struct mm_struct *mm = spu->mm; @@ -330,16 +329,10 @@ get_exec_dcookie_and_offset(struct spu *spu, unsigned int *offsetp, down_read(&mm->mmap_sem); - for (vma = mm->mmap; vma; vma = vma->vm_next) { - if (!vma->vm_file) - continue; - if (!(vma->vm_flags & VM_EXECUTABLE)) - continue; - app_cookie = fast_get_dcookie(&vma->vm_file->f_path); + if (mm->exe_file) { + app_cookie = fast_get_dcookie(&mm->exe_file->f_path); pr_debug("got dcookie for %s\n", - vma->vm_file->f_dentry->d_name.name); - app = vma->vm_file; - break; + mm->exe_file->f_dentry->d_name.name); } for (vma = mm->mmap; vma; vma = vma->vm_next) { diff --git a/arch/tile/mm/elf.c b/arch/tile/mm/elf.c index 758b6038c2b7..3cfa98bf9125 100644 --- a/arch/tile/mm/elf.c +++ b/arch/tile/mm/elf.c @@ -36,19 +36,14 @@ static void sim_notify_exec(const char *binary_name) } while (c); } -static int notify_exec(void) +static int notify_exec(struct mm_struct *mm) { int retval = 0; /* failure */ - struct vm_area_struct *vma = current->mm->mmap; - while (vma) { - if ((vma->vm_flags & VM_EXECUTABLE) && vma->vm_file) - break; - vma = vma->vm_next; - } - if (vma) { + + if (mm->exe_file) { char *buf = (char *) __get_free_page(GFP_KERNEL); if (buf) { - char *path = d_path(&vma->vm_file->f_path, + char *path = d_path(&mm->exe_file->f_path, buf, PAGE_SIZE); if (!IS_ERR(path)) { sim_notify_exec(path); @@ -106,16 +101,16 @@ int arch_setup_additional_pages(struct linux_binprm *bprm, unsigned long vdso_base; int retval = 0; + down_write(&mm->mmap_sem); + /* * Notify the simulator that an exec just occurred. * If we can't find the filename of the mapping, just use * whatever was passed as the linux_binprm filename. */ - if (!notify_exec()) + if (!notify_exec(mm)) sim_notify_exec(bprm->filename); - down_write(&mm->mmap_sem); - /* * MAYWRITE to allow gdb to COW and set breakpoints */ diff --git a/drivers/oprofile/buffer_sync.c b/drivers/oprofile/buffer_sync.c index f34b5b29fb95..d93b2b6b1f7a 100644 --- a/drivers/oprofile/buffer_sync.c +++ b/drivers/oprofile/buffer_sync.c @@ -216,7 +216,7 @@ static inline unsigned long fast_get_dcookie(struct path *path) } -/* Look up the dcookie for the task's first VM_EXECUTABLE mapping, +/* Look up the dcookie for the task's mm->exe_file, * which corresponds loosely to "application name". This is * not strictly necessary but allows oprofile to associate * shared-library samples with particular applications @@ -224,21 +224,10 @@ static inline unsigned long fast_get_dcookie(struct path *path) static unsigned long get_exec_dcookie(struct mm_struct *mm) { unsigned long cookie = NO_COOKIE; - struct vm_area_struct *vma; - - if (!mm) - goto out; - for (vma = mm->mmap; vma; vma = vma->vm_next) { - if (!vma->vm_file) - continue; - if (!(vma->vm_flags & VM_EXECUTABLE)) - continue; - cookie = fast_get_dcookie(&vma->vm_file->f_path); - break; - } + if (mm && mm->exe_file) + cookie = fast_get_dcookie(&mm->exe_file->f_path); -out: return cookie; } diff --git a/kernel/auditsc.c b/kernel/auditsc.c index 29e090cc0e46..f4a7756f999c 100644 --- a/kernel/auditsc.c +++ b/kernel/auditsc.c @@ -1151,7 +1151,6 @@ void audit_log_task_info(struct audit_buffer *ab, struct task_struct *tsk) const struct cred *cred; char name[sizeof(tsk->comm)]; struct mm_struct *mm = tsk->mm; - struct vm_area_struct *vma; char *tty; if (!ab) @@ -1191,16 +1190,8 @@ void audit_log_task_info(struct audit_buffer *ab, struct task_struct *tsk) if (mm) { down_read(&mm->mmap_sem); - vma = mm->mmap; - while (vma) { - if ((vma->vm_flags & VM_EXECUTABLE) && - vma->vm_file) { - audit_log_d_path(ab, " exe=", - &vma->vm_file->f_path); - break; - } - vma = vma->vm_next; - } + if (mm->exe_file) + audit_log_d_path(ab, " exe=", &mm->exe_file->f_path); up_read(&mm->mmap_sem); } audit_log_task_context(ab); diff --git a/kernel/fork.c b/kernel/fork.c index a2b1efc20928..a57a993681ed 100644 --- a/kernel/fork.c +++ b/kernel/fork.c @@ -656,8 +656,7 @@ struct file *get_mm_exe_file(struct mm_struct *mm) { struct file *exe_file; - /* We need mmap_sem to protect against races with removal of - * VM_EXECUTABLE vmas */ + /* We need mmap_sem to protect against races with removal of exe_file */ down_read(&mm->mmap_sem); exe_file = mm->exe_file; if (exe_file) diff --git a/security/tomoyo/util.c b/security/tomoyo/util.c index 867558c98334..2952ba576fb9 100644 --- a/security/tomoyo/util.c +++ b/security/tomoyo/util.c @@ -949,18 +949,13 @@ bool tomoyo_path_matches_pattern(const struct tomoyo_path_info *filename, const char *tomoyo_get_exe(void) { struct mm_struct *mm = current->mm; - struct vm_area_struct *vma; const char *cp = NULL; if (!mm) return NULL; down_read(&mm->mmap_sem); - for (vma = mm->mmap; vma; vma = vma->vm_next) { - if ((vma->vm_flags & VM_EXECUTABLE) && vma->vm_file) { - cp = tomoyo_realpath_from_path(&vma->vm_file->f_path); - break; - } - } + if (mm->exe_file) + cp = tomoyo_realpath_from_path(&mm->exe_file->f_path); up_read(&mm->mmap_sem); return cp; } -- cgit v1.2.3 From 1c2e51e8c162417d2831007ec256ede06c3a0201 Mon Sep 17 00:00:00 2001 From: Jeff Layton Date: Wed, 10 Oct 2012 15:25:20 -0400 Subject: audit: pass in dentry to audit_copy_inode wherever possible In some cases, we were passing in NULL even when we have a dentry. Reported-by: Eric Paris Signed-off-by: Jeff Layton Signed-off-by: Al Viro --- kernel/auditsc.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'kernel/auditsc.c') diff --git a/kernel/auditsc.c b/kernel/auditsc.c index f4a7756f999c..4d1bd62b090b 100644 --- a/kernel/auditsc.c +++ b/kernel/auditsc.c @@ -2212,7 +2212,7 @@ void __audit_inode_child(const struct dentry *dentry, if (!strcmp(dname, n->name) || !audit_compare_dname_path(dname, n->name, &dirlen)) { if (inode) - audit_copy_inode(n, NULL, inode); + audit_copy_inode(n, dentry, inode); else n->ino = (unsigned long)-1; found_child = n->name; @@ -2244,7 +2244,7 @@ add_names: } if (inode) - audit_copy_inode(n, NULL, inode); + audit_copy_inode(n, dentry, inode); } } EXPORT_SYMBOL_GPL(__audit_inode_child); -- cgit v1.2.3 From 9cec9d68ae53aae60b4a1fca4505c75a1d026392 Mon Sep 17 00:00:00 2001 From: Jeff Layton Date: Wed, 10 Oct 2012 15:25:21 -0400 Subject: audit: no need to walk list in audit_inode if name is NULL If name is NULL then the condition in the loop will never be true. Also, with this change, we can eliminate the check for n->name == NULL since the equivalence check will never be true if it is. Signed-off-by: Jeff Layton Signed-off-by: Al Viro --- kernel/auditsc.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) (limited to 'kernel/auditsc.c') diff --git a/kernel/auditsc.c b/kernel/auditsc.c index 4d1bd62b090b..2e481141b014 100644 --- a/kernel/auditsc.c +++ b/kernel/auditsc.c @@ -2147,11 +2147,15 @@ void __audit_inode(const char *name, const struct dentry *dentry) if (!context->in_syscall) return; + if (!name) + goto out_alloc; + list_for_each_entry_reverse(n, &context->names_list, list) { - if (n->name && (n->name == name)) + if (n->name == name) goto out; } +out_alloc: /* unable to find the name from a previous getname() */ n = audit_alloc_name(context); if (!n) -- cgit v1.2.3 From c43a25abba97c7d87131e71db6be24b24d7791a5 Mon Sep 17 00:00:00 2001 From: Jeff Layton Date: Wed, 10 Oct 2012 15:25:21 -0400 Subject: audit: reverse arguments to audit_inode_child Most of the callers get called with an inode and dentry in the reverse order. The compiler then has to reshuffle the arg registers and/or stack in order to pass them on to audit_inode_child. Reverse those arguments for a micro-optimization. Reported-by: Eric Paris Signed-off-by: Jeff Layton Signed-off-by: Al Viro --- fs/btrfs/ioctl.c | 2 +- fs/namei.c | 2 +- include/linux/audit.h | 18 +++++++++--------- include/linux/fsnotify.h | 8 ++++---- kernel/auditsc.c | 8 ++++---- 5 files changed, 19 insertions(+), 19 deletions(-) (limited to 'kernel/auditsc.c') diff --git a/fs/btrfs/ioctl.c b/fs/btrfs/ioctl.c index e568c472f807..49f4d59ac2c7 100644 --- a/fs/btrfs/ioctl.c +++ b/fs/btrfs/ioctl.c @@ -638,7 +638,7 @@ static int btrfs_may_delete(struct inode *dir,struct dentry *victim,int isdir) return -ENOENT; BUG_ON(victim->d_parent->d_inode != dir); - audit_inode_child(victim, dir); + audit_inode_child(dir, victim); error = inode_permission(dir, MAY_WRITE | MAY_EXEC); if (error) diff --git a/fs/namei.c b/fs/namei.c index f04ce1142297..a7ad35c66807 100644 --- a/fs/namei.c +++ b/fs/namei.c @@ -2176,7 +2176,7 @@ static int may_delete(struct inode *dir,struct dentry *victim,int isdir) return -ENOENT; BUG_ON(victim->d_parent->d_inode != dir); - audit_inode_child(victim, dir); + audit_inode_child(dir, victim); error = inode_permission(dir, MAY_WRITE | MAY_EXEC); if (error) diff --git a/include/linux/audit.h b/include/linux/audit.h index 2c83e5f7edb1..8c66fc248c75 100644 --- a/include/linux/audit.h +++ b/include/linux/audit.h @@ -464,8 +464,8 @@ extern void __audit_syscall_exit(int ret_success, long ret_value); extern void __audit_getname(const char *name); extern void audit_putname(const char *name); extern void __audit_inode(const char *name, const struct dentry *dentry); -extern void __audit_inode_child(const struct dentry *dentry, - const struct inode *parent); +extern void __audit_inode_child(const struct inode *parent, + const struct dentry *dentry); extern void __audit_seccomp(unsigned long syscall, long signr, int code); extern void __audit_ptrace(struct task_struct *t); @@ -504,10 +504,10 @@ static inline void audit_inode(const char *name, const struct dentry *dentry) { if (unlikely(!audit_dummy_context())) __audit_inode(name, dentry); } -static inline void audit_inode_child(const struct dentry *dentry, - const struct inode *parent) { +static inline void audit_inode_child(const struct inode *parent, + const struct dentry *dentry) { if (unlikely(!audit_dummy_context())) - __audit_inode_child(dentry, parent); + __audit_inode_child(parent, dentry); } void audit_core_dumps(long signr); @@ -657,13 +657,13 @@ static inline void audit_putname(const char *name) { } static inline void __audit_inode(const char *name, const struct dentry *dentry) { } -static inline void __audit_inode_child(const struct dentry *dentry, - const struct inode *parent) +static inline void __audit_inode_child(const struct inode *parent, + const struct dentry *dentry) { } static inline void audit_inode(const char *name, const struct dentry *dentry) { } -static inline void audit_inode_child(const struct dentry *dentry, - const struct inode *parent) +static inline void audit_inode_child(const struct inode *parent, + const struct dentry *dentry) { } static inline void audit_core_dumps(long signr) { } diff --git a/include/linux/fsnotify.h b/include/linux/fsnotify.h index a6dfe6944564..9c284714977d 100644 --- a/include/linux/fsnotify.h +++ b/include/linux/fsnotify.h @@ -109,7 +109,7 @@ static inline void fsnotify_move(struct inode *old_dir, struct inode *new_dir, if (source) fsnotify(source, FS_MOVE_SELF, moved->d_inode, FSNOTIFY_EVENT_INODE, NULL, 0); - audit_inode_child(moved, new_dir); + audit_inode_child(new_dir, moved); } /* @@ -155,7 +155,7 @@ static inline void fsnotify_inoderemove(struct inode *inode) */ static inline void fsnotify_create(struct inode *inode, struct dentry *dentry) { - audit_inode_child(dentry, inode); + audit_inode_child(inode, dentry); fsnotify(inode, FS_CREATE, dentry->d_inode, FSNOTIFY_EVENT_INODE, dentry->d_name.name, 0); } @@ -168,7 +168,7 @@ static inline void fsnotify_create(struct inode *inode, struct dentry *dentry) static inline void fsnotify_link(struct inode *dir, struct inode *inode, struct dentry *new_dentry) { fsnotify_link_count(inode); - audit_inode_child(new_dentry, dir); + audit_inode_child(dir, new_dentry); fsnotify(dir, FS_CREATE, inode, FSNOTIFY_EVENT_INODE, new_dentry->d_name.name, 0); } @@ -181,7 +181,7 @@ static inline void fsnotify_mkdir(struct inode *inode, struct dentry *dentry) __u32 mask = (FS_CREATE | FS_ISDIR); struct inode *d_inode = dentry->d_inode; - audit_inode_child(dentry, inode); + audit_inode_child(inode, dentry); fsnotify(inode, mask, d_inode, FSNOTIFY_EVENT_INODE, dentry->d_name.name, 0); } diff --git a/kernel/auditsc.c b/kernel/auditsc.c index 2e481141b014..40743af02d8f 100644 --- a/kernel/auditsc.c +++ b/kernel/auditsc.c @@ -2166,9 +2166,9 @@ out: } /** - * audit_inode_child - collect inode info for created/removed objects - * @dentry: dentry being audited + * __audit_inode_child - collect inode info for created/removed objects * @parent: inode of dentry parent + * @dentry: dentry being audited * * For syscalls that create or remove filesystem objects, audit_inode * can only collect information for the filesystem object's parent. @@ -2178,8 +2178,8 @@ out: * must be hooked prior, in order to capture the target inode during * unsuccessful attempts. */ -void __audit_inode_child(const struct dentry *dentry, - const struct inode *parent) +void __audit_inode_child(const struct inode *parent, + const struct dentry *dentry) { struct audit_context *context = current->audit_context; const char *found_parent = NULL, *found_child = NULL; -- cgit v1.2.3 From 78e2e802a8519031e5858595070b39713e26340d Mon Sep 17 00:00:00 2001 From: Jeff Layton Date: Wed, 10 Oct 2012 15:25:22 -0400 Subject: audit: add a new "type" field to audit_names struct For now, we just have two possibilities: UNKNOWN: for a new audit_names record that we don't know anything about yet NORMAL: for everything else In later patches, we'll add other types so we can distinguish and update records created under different circumstances. Signed-off-by: Jeff Layton Signed-off-by: Al Viro --- include/linux/audit.h | 5 +++++ kernel/auditsc.c | 15 ++++++++++----- 2 files changed, 15 insertions(+), 5 deletions(-) (limited to 'kernel/auditsc.c') diff --git a/include/linux/audit.h b/include/linux/audit.h index 8c66fc248c75..26408934ef2d 100644 --- a/include/linux/audit.h +++ b/include/linux/audit.h @@ -452,6 +452,11 @@ struct audit_field { extern int __init audit_register_class(int class, unsigned *list); extern int audit_classify_syscall(int abi, unsigned syscall); extern int audit_classify_arch(int arch); + +/* audit_names->type values */ +#define AUDIT_TYPE_UNKNOWN 0 /* we don't know yet */ +#define AUDIT_TYPE_NORMAL 1 /* a "normal" audit record */ + #ifdef CONFIG_AUDITSYSCALL /* These are defined in auditsc.c */ /* Public API */ diff --git a/kernel/auditsc.c b/kernel/auditsc.c index 40743af02d8f..19b232f86d70 100644 --- a/kernel/auditsc.c +++ b/kernel/auditsc.c @@ -120,6 +120,7 @@ struct audit_names { struct audit_cap_data fcap; unsigned int fcap_ver; int name_len; /* number of name's characters to log */ + unsigned char type; /* record type */ bool name_put; /* call __putname() for this name */ /* * This was an allocated audit_names and not from the array of @@ -1995,7 +1996,8 @@ retry: #endif } -static struct audit_names *audit_alloc_name(struct audit_context *context) +static struct audit_names *audit_alloc_name(struct audit_context *context, + unsigned char type) { struct audit_names *aname; @@ -2010,6 +2012,7 @@ static struct audit_names *audit_alloc_name(struct audit_context *context) } aname->ino = (unsigned long)-1; + aname->type = type; list_add_tail(&aname->list, &context->names_list); context->name_count++; @@ -2040,7 +2043,7 @@ void __audit_getname(const char *name) return; } - n = audit_alloc_name(context); + n = audit_alloc_name(context, AUDIT_TYPE_UNKNOWN); if (!n) return; @@ -2157,12 +2160,13 @@ void __audit_inode(const char *name, const struct dentry *dentry) out_alloc: /* unable to find the name from a previous getname() */ - n = audit_alloc_name(context); + n = audit_alloc_name(context, AUDIT_TYPE_NORMAL); if (!n) return; out: handle_path(dentry); audit_copy_inode(n, dentry, inode); + n->type = AUDIT_TYPE_NORMAL; } /** @@ -2219,6 +2223,7 @@ void __audit_inode_child(const struct inode *parent, audit_copy_inode(n, dentry, inode); else n->ino = (unsigned long)-1; + n->type = AUDIT_TYPE_NORMAL; found_child = n->name; goto add_names; } @@ -2226,14 +2231,14 @@ void __audit_inode_child(const struct inode *parent, add_names: if (!found_parent) { - n = audit_alloc_name(context); + n = audit_alloc_name(context, AUDIT_TYPE_NORMAL); if (!n) return; audit_copy_inode(n, NULL, parent); } if (!found_child) { - n = audit_alloc_name(context); + n = audit_alloc_name(context, AUDIT_TYPE_NORMAL); if (!n) return; -- cgit v1.2.3 From bfcec7087458812f575d9022b2d151641f34ee84 Mon Sep 17 00:00:00 2001 From: Jeff Layton Date: Wed, 10 Oct 2012 15:25:23 -0400 Subject: audit: set the name_len in audit_inode for parent lookups Currently, this gets set mostly by happenstance when we call into audit_inode_child. While that might be a little more efficient, it seems wrong. If the syscall ends up failing before audit_inode_child ever gets called, then you'll have an audit_names record that shows the full path but has the parent inode info attached. Fix this by passing in a parent flag when we call audit_inode that gets set to the value of LOOKUP_PARENT. We can then fix up the pathname for the audit entry correctly from the get-go. While we're at it, clean up the no-op macro for audit_inode in the !CONFIG_AUDITSYSCALL case. Signed-off-by: Jeff Layton Signed-off-by: Al Viro --- fs/namei.c | 14 +++++++------- fs/open.c | 4 ++-- fs/xattr.c | 8 ++++---- include/linux/audit.h | 15 ++++++++++----- ipc/mqueue.c | 8 ++++---- kernel/audit.h | 1 + kernel/auditfilter.c | 30 ++++++++++++++++++++++++++++++ kernel/auditsc.c | 41 +++++++++++++++++++++++++++++------------ 8 files changed, 87 insertions(+), 34 deletions(-) (limited to 'kernel/auditsc.c') diff --git a/fs/namei.c b/fs/namei.c index a7ad35c66807..6a92d988573f 100644 --- a/fs/namei.c +++ b/fs/namei.c @@ -1973,7 +1973,7 @@ static int do_path_lookup(int dfd, const char *name, retval = path_lookupat(dfd, name, flags | LOOKUP_REVAL, nd); if (likely(!retval)) - audit_inode(name, nd->path.dentry); + audit_inode(name, nd->path.dentry, flags & LOOKUP_PARENT); return retval; } @@ -2648,7 +2648,7 @@ static int do_last(struct nameidata *nd, struct path *path, error = complete_walk(nd); if (error) return error; - audit_inode(pathname, nd->path.dentry); + audit_inode(pathname, nd->path.dentry, 0); if (open_flag & O_CREAT) { error = -EISDIR; goto out; @@ -2658,7 +2658,7 @@ static int do_last(struct nameidata *nd, struct path *path, error = complete_walk(nd); if (error) return error; - audit_inode(pathname, dir); + audit_inode(pathname, dir, 0); goto finish_open; } @@ -2687,7 +2687,7 @@ static int do_last(struct nameidata *nd, struct path *path, if (error) return error; - audit_inode(pathname, dir); + audit_inode(pathname, dir, 0); error = -EISDIR; /* trailing slashes? */ if (nd->last.name[nd->last.len]) @@ -2717,7 +2717,7 @@ retry_lookup: !S_ISREG(file->f_path.dentry->d_inode->i_mode)) will_truncate = false; - audit_inode(pathname, file->f_path.dentry); + audit_inode(pathname, file->f_path.dentry, 0); goto opened; } @@ -2734,7 +2734,7 @@ retry_lookup: * create/update audit record if it already exists. */ if (path->dentry->d_inode) - audit_inode(pathname, path->dentry); + audit_inode(pathname, path->dentry, 0); /* * If atomic_open() acquired write access it is dropped now due to @@ -2799,7 +2799,7 @@ finish_lookup: error = -ENOTDIR; if ((nd->flags & LOOKUP_DIRECTORY) && !nd->inode->i_op->lookup) goto out; - audit_inode(pathname, nd->path.dentry); + audit_inode(pathname, nd->path.dentry, 0); finish_open: if (!S_ISREG(nd->inode->i_mode)) will_truncate = false; diff --git a/fs/open.c b/fs/open.c index 44da0feeca2c..a015437e1535 100644 --- a/fs/open.c +++ b/fs/open.c @@ -478,7 +478,7 @@ SYSCALL_DEFINE2(fchmod, unsigned int, fd, umode_t, mode) file = fget(fd); if (file) { - audit_inode(NULL, file->f_path.dentry); + audit_inode(NULL, file->f_path.dentry, 0); err = chmod_common(&file->f_path, mode); fput(file); } @@ -588,7 +588,7 @@ SYSCALL_DEFINE3(fchown, unsigned int, fd, uid_t, user, gid_t, group) error = mnt_want_write_file(f.file); if (error) goto out_fput; - audit_inode(NULL, f.file->f_path.dentry); + audit_inode(NULL, f.file->f_path.dentry, 0); error = chown_common(&f.file->f_path, user, group); mnt_drop_write_file(f.file); out_fput: diff --git a/fs/xattr.c b/fs/xattr.c index 1780f062dbaf..e164dddb8e96 100644 --- a/fs/xattr.c +++ b/fs/xattr.c @@ -412,7 +412,7 @@ SYSCALL_DEFINE5(fsetxattr, int, fd, const char __user *, name, if (!f.file) return error; dentry = f.file->f_path.dentry; - audit_inode(NULL, dentry); + audit_inode(NULL, dentry, 0); error = mnt_want_write_file(f.file); if (!error) { error = setxattr(dentry, name, value, size, flags); @@ -507,7 +507,7 @@ SYSCALL_DEFINE4(fgetxattr, int, fd, const char __user *, name, if (!f.file) return error; - audit_inode(NULL, f.file->f_path.dentry); + audit_inode(NULL, f.file->f_path.dentry, 0); error = getxattr(f.file->f_path.dentry, name, value, size); fdput(f); return error; @@ -586,7 +586,7 @@ SYSCALL_DEFINE3(flistxattr, int, fd, char __user *, list, size_t, size) if (!f.file) return error; - audit_inode(NULL, f.file->f_path.dentry); + audit_inode(NULL, f.file->f_path.dentry, 0); error = listxattr(f.file->f_path.dentry, list, size); fdput(f); return error; @@ -655,7 +655,7 @@ SYSCALL_DEFINE2(fremovexattr, int, fd, const char __user *, name) if (!f.file) return error; dentry = f.file->f_path.dentry; - audit_inode(NULL, dentry); + audit_inode(NULL, dentry, 0); error = mnt_want_write_file(f.file); if (!error) { error = removexattr(dentry, name); diff --git a/include/linux/audit.h b/include/linux/audit.h index 26408934ef2d..b11f517dce04 100644 --- a/include/linux/audit.h +++ b/include/linux/audit.h @@ -456,6 +456,7 @@ extern int audit_classify_arch(int arch); /* audit_names->type values */ #define AUDIT_TYPE_UNKNOWN 0 /* we don't know yet */ #define AUDIT_TYPE_NORMAL 1 /* a "normal" audit record */ +#define AUDIT_TYPE_PARENT 2 /* a parent audit record */ #ifdef CONFIG_AUDITSYSCALL /* These are defined in auditsc.c */ @@ -468,7 +469,8 @@ extern void __audit_syscall_entry(int arch, extern void __audit_syscall_exit(int ret_success, long ret_value); extern void __audit_getname(const char *name); extern void audit_putname(const char *name); -extern void __audit_inode(const char *name, const struct dentry *dentry); +extern void __audit_inode(const char *name, const struct dentry *dentry, + unsigned int parent); extern void __audit_inode_child(const struct inode *parent, const struct dentry *dentry); extern void __audit_seccomp(unsigned long syscall, long signr, int code); @@ -505,9 +507,10 @@ static inline void audit_getname(const char *name) if (unlikely(!audit_dummy_context())) __audit_getname(name); } -static inline void audit_inode(const char *name, const struct dentry *dentry) { +static inline void audit_inode(const char *name, const struct dentry *dentry, + unsigned int parent) { if (unlikely(!audit_dummy_context())) - __audit_inode(name, dentry); + __audit_inode(name, dentry, parent); } static inline void audit_inode_child(const struct inode *parent, const struct dentry *dentry) { @@ -660,12 +663,14 @@ static inline void audit_getname(const char *name) { } static inline void audit_putname(const char *name) { } -static inline void __audit_inode(const char *name, const struct dentry *dentry) +static inline void __audit_inode(const char *name, const struct dentry *dentry, + unsigned int parent) { } static inline void __audit_inode_child(const struct inode *parent, const struct dentry *dentry) { } -static inline void audit_inode(const char *name, const struct dentry *dentry) +static inline void audit_inode(const char *name, const struct dentry *dentry, + unsigned int parent) { } static inline void audit_inode_child(const struct inode *parent, const struct dentry *dentry) diff --git a/ipc/mqueue.c b/ipc/mqueue.c index 6b97e2466fad..9553ed006042 100644 --- a/ipc/mqueue.c +++ b/ipc/mqueue.c @@ -804,7 +804,7 @@ SYSCALL_DEFINE4(mq_open, const char __user *, u_name, int, oflag, umode_t, mode, if (oflag & O_CREAT) { if (path.dentry->d_inode) { /* entry already exists */ - audit_inode(name, path.dentry); + audit_inode(name, path.dentry, 0); if (oflag & O_EXCL) { error = -EEXIST; goto out; @@ -824,7 +824,7 @@ SYSCALL_DEFINE4(mq_open, const char __user *, u_name, int, oflag, umode_t, mode, error = -ENOENT; goto out; } - audit_inode(name, path.dentry); + audit_inode(name, path.dentry, 0); filp = do_open(&path, oflag); } @@ -978,7 +978,7 @@ SYSCALL_DEFINE5(mq_timedsend, mqd_t, mqdes, const char __user *, u_msg_ptr, goto out_fput; } info = MQUEUE_I(inode); - audit_inode(NULL, f.file->f_path.dentry); + audit_inode(NULL, f.file->f_path.dentry, 0); if (unlikely(!(f.file->f_mode & FMODE_WRITE))) { ret = -EBADF; @@ -1094,7 +1094,7 @@ SYSCALL_DEFINE5(mq_timedreceive, mqd_t, mqdes, char __user *, u_msg_ptr, goto out_fput; } info = MQUEUE_I(inode); - audit_inode(NULL, f.file->f_path.dentry); + audit_inode(NULL, f.file->f_path.dentry, 0); if (unlikely(!(f.file->f_mode & FMODE_READ))) { ret = -EBADF; diff --git a/kernel/audit.h b/kernel/audit.h index 9eb3d79482b6..163b9a5d9441 100644 --- a/kernel/audit.h +++ b/kernel/audit.h @@ -78,6 +78,7 @@ extern int audit_match_class(int class, unsigned syscall); extern int audit_comparator(const u32 left, const u32 op, const u32 right); extern int audit_uid_comparator(kuid_t left, u32 op, kuid_t right); extern int audit_gid_comparator(kgid_t left, u32 op, kgid_t right); +extern int parent_len(const char *path); extern int audit_compare_dname_path(const char *dname, const char *path, int *dirlen); extern struct sk_buff * audit_make_reply(int pid, int seq, int type, diff --git a/kernel/auditfilter.c b/kernel/auditfilter.c index c4bcdbaf4d4d..71bb13598df3 100644 --- a/kernel/auditfilter.c +++ b/kernel/auditfilter.c @@ -1298,6 +1298,36 @@ int audit_gid_comparator(kgid_t left, u32 op, kgid_t right) } } +/** + * parent_len - find the length of the parent portion of a pathname + * @path: pathname of which to determine length + */ +int parent_len(const char *path) +{ + int plen; + const char *p; + + plen = strlen(path); + + if (plen == 0) + return plen; + + /* disregard trailing slashes */ + p = path + plen - 1; + while ((*p == '/') && (p > path)) + p--; + + /* walk backward until we find the next slash or hit beginning */ + while ((*p != '/') && (p > path)) + p--; + + /* did we find a slash? Then increment to include it in path */ + if (*p == '/') + p++; + + return p - path; +} + /* Compare given dentry name with last component in given path, * return of 0 indicates a match. */ int audit_compare_dname_path(const char *dname, const char *path, diff --git a/kernel/auditsc.c b/kernel/auditsc.c index 19b232f86d70..b87b28947acc 100644 --- a/kernel/auditsc.c +++ b/kernel/auditsc.c @@ -2135,13 +2135,13 @@ static void audit_copy_inode(struct audit_names *name, const struct dentry *dent } /** - * audit_inode - store the inode and device from a lookup + * __audit_inode - store the inode and device from a lookup * @name: name being audited * @dentry: dentry being audited - * - * Called from fs/namei.c:path_lookup(). + * @parent: does this dentry represent the parent? */ -void __audit_inode(const char *name, const struct dentry *dentry) +void __audit_inode(const char *name, const struct dentry *dentry, + unsigned int parent) { struct audit_context *context = current->audit_context; const struct inode *inode = dentry->d_inode; @@ -2154,19 +2154,38 @@ void __audit_inode(const char *name, const struct dentry *dentry) goto out_alloc; list_for_each_entry_reverse(n, &context->names_list, list) { - if (n->name == name) - goto out; + /* does the name pointer match? */ + if (n->name != name) + continue; + + /* match the correct record type */ + if (parent) { + if (n->type == AUDIT_TYPE_PARENT || + n->type == AUDIT_TYPE_UNKNOWN) + goto out; + } else { + if (n->type != AUDIT_TYPE_PARENT) + goto out; + } } out_alloc: - /* unable to find the name from a previous getname() */ + /* unable to find the name from a previous getname(). Allocate a new + * anonymous entry. + */ n = audit_alloc_name(context, AUDIT_TYPE_NORMAL); if (!n) return; out: + if (parent) { + n->name_len = n->name ? parent_len(n->name) : AUDIT_NAME_FULL; + n->type = AUDIT_TYPE_PARENT; + } else { + n->name_len = AUDIT_NAME_FULL; + n->type = AUDIT_TYPE_NORMAL; + } handle_path(dentry); audit_copy_inode(n, dentry, inode); - n->type = AUDIT_TYPE_NORMAL; } /** @@ -2190,7 +2209,6 @@ void __audit_inode_child(const struct inode *parent, const struct inode *inode = dentry->d_inode; const char *dname = dentry->d_name.name; struct audit_names *n; - int dirlen = 0; if (!context->in_syscall) return; @@ -2204,8 +2222,7 @@ void __audit_inode_child(const struct inode *parent, continue; if (n->ino == parent->i_ino && - !audit_compare_dname_path(dname, n->name, &dirlen)) { - n->name_len = dirlen; /* update parent data in place */ + !audit_compare_dname_path(dname, n->name, NULL)) { found_parent = n->name; goto add_names; } @@ -2218,7 +2235,7 @@ void __audit_inode_child(const struct inode *parent, /* strcmp() is the more likely scenario */ if (!strcmp(dname, n->name) || - !audit_compare_dname_path(dname, n->name, &dirlen)) { + !audit_compare_dname_path(dname, n->name, NULL)) { if (inode) audit_copy_inode(n, dentry, inode); else -- cgit v1.2.3 From 563a0d1236c2c58d584ef122a5cdc9930e5860b3 Mon Sep 17 00:00:00 2001 From: Jeff Layton Date: Wed, 10 Oct 2012 15:25:24 -0400 Subject: audit: remove dirlen argument to audit_compare_dname_path All the callers set this to NULL now. Signed-off-by: Jeff Layton Signed-off-by: Al Viro --- kernel/audit.h | 3 +-- kernel/audit_watch.c | 2 +- kernel/auditfilter.c | 6 +----- kernel/auditsc.c | 4 ++-- 4 files changed, 5 insertions(+), 10 deletions(-) (limited to 'kernel/auditsc.c') diff --git a/kernel/audit.h b/kernel/audit.h index 163b9a5d9441..1038e23eb61c 100644 --- a/kernel/audit.h +++ b/kernel/audit.h @@ -79,8 +79,7 @@ extern int audit_comparator(const u32 left, const u32 op, const u32 right); extern int audit_uid_comparator(kuid_t left, u32 op, kuid_t right); extern int audit_gid_comparator(kgid_t left, u32 op, kgid_t right); extern int parent_len(const char *path); -extern int audit_compare_dname_path(const char *dname, const char *path, - int *dirlen); +extern int audit_compare_dname_path(const char *dname, const char *path); extern struct sk_buff * audit_make_reply(int pid, int seq, int type, int done, int multi, const void *payload, int size); diff --git a/kernel/audit_watch.c b/kernel/audit_watch.c index 1c22ec3d87bc..deb97c139e0c 100644 --- a/kernel/audit_watch.c +++ b/kernel/audit_watch.c @@ -265,7 +265,7 @@ static void audit_update_watch(struct audit_parent *parent, /* Run all of the watches on this parent looking for the one that * matches the given dname */ list_for_each_entry_safe(owatch, nextw, &parent->watches, wlist) { - if (audit_compare_dname_path(dname, owatch->path, NULL)) + if (audit_compare_dname_path(dname, owatch->path)) continue; /* If the update involves invalidating rules, do the inode-based diff --git a/kernel/auditfilter.c b/kernel/auditfilter.c index 71bb13598df3..ff4011c19b13 100644 --- a/kernel/auditfilter.c +++ b/kernel/auditfilter.c @@ -1330,8 +1330,7 @@ int parent_len(const char *path) /* Compare given dentry name with last component in given path, * return of 0 indicates a match. */ -int audit_compare_dname_path(const char *dname, const char *path, - int *dirlen) +int audit_compare_dname_path(const char *dname, const char *path) { int dlen, plen; const char *p; @@ -1360,9 +1359,6 @@ int audit_compare_dname_path(const char *dname, const char *path, p++; } - /* return length of path's directory component */ - if (dirlen) - *dirlen = p - path; return strncmp(p, dname, dlen); } diff --git a/kernel/auditsc.c b/kernel/auditsc.c index b87b28947acc..09c7b6b4f8e6 100644 --- a/kernel/auditsc.c +++ b/kernel/auditsc.c @@ -2222,7 +2222,7 @@ void __audit_inode_child(const struct inode *parent, continue; if (n->ino == parent->i_ino && - !audit_compare_dname_path(dname, n->name, NULL)) { + !audit_compare_dname_path(dname, n->name)) { found_parent = n->name; goto add_names; } @@ -2235,7 +2235,7 @@ void __audit_inode_child(const struct inode *parent, /* strcmp() is the more likely scenario */ if (!strcmp(dname, n->name) || - !audit_compare_dname_path(dname, n->name, NULL)) { + !audit_compare_dname_path(dname, n->name)) { if (inode) audit_copy_inode(n, dentry, inode); else -- cgit v1.2.3 From e3d6b07b8ba161f638b026feba0c3c97875d7f1c Mon Sep 17 00:00:00 2001 From: Jeff Layton Date: Wed, 10 Oct 2012 15:25:25 -0400 Subject: audit: optimize audit_compare_dname_path In the cases where we already know the length of the parent, pass it as a parm so we don't need to recompute it. In the cases where we don't know the length, pass in AUDIT_NAME_FULL (-1) to indicate that it should be determined. Signed-off-by: Jeff Layton Signed-off-by: Al Viro --- kernel/audit.h | 5 ++++- kernel/audit_watch.c | 3 ++- kernel/auditfilter.c | 16 +++++++++++----- kernel/auditsc.c | 8 +++----- 4 files changed, 20 insertions(+), 12 deletions(-) (limited to 'kernel/auditsc.c') diff --git a/kernel/audit.h b/kernel/audit.h index 1038e23eb61c..d51cba868e1b 100644 --- a/kernel/audit.h +++ b/kernel/audit.h @@ -74,12 +74,15 @@ static inline int audit_hash_ino(u32 ino) return (ino & (AUDIT_INODE_BUCKETS-1)); } +/* Indicates that audit should log the full pathname. */ +#define AUDIT_NAME_FULL -1 + extern int audit_match_class(int class, unsigned syscall); extern int audit_comparator(const u32 left, const u32 op, const u32 right); extern int audit_uid_comparator(kuid_t left, u32 op, kuid_t right); extern int audit_gid_comparator(kgid_t left, u32 op, kgid_t right); extern int parent_len(const char *path); -extern int audit_compare_dname_path(const char *dname, const char *path); +extern int audit_compare_dname_path(const char *dname, const char *path, int plen); extern struct sk_buff * audit_make_reply(int pid, int seq, int type, int done, int multi, const void *payload, int size); diff --git a/kernel/audit_watch.c b/kernel/audit_watch.c index deb97c139e0c..9a9ae6e3d290 100644 --- a/kernel/audit_watch.c +++ b/kernel/audit_watch.c @@ -265,7 +265,8 @@ static void audit_update_watch(struct audit_parent *parent, /* Run all of the watches on this parent looking for the one that * matches the given dname */ list_for_each_entry_safe(owatch, nextw, &parent->watches, wlist) { - if (audit_compare_dname_path(dname, owatch->path)) + if (audit_compare_dname_path(dname, owatch->path, + AUDIT_NAME_FULL)) continue; /* If the update involves invalidating rules, do the inode-based diff --git a/kernel/auditfilter.c b/kernel/auditfilter.c index d705eb17661b..7f19f23d38a3 100644 --- a/kernel/auditfilter.c +++ b/kernel/auditfilter.c @@ -1328,11 +1328,17 @@ int parent_len(const char *path) return p - path; } -/* Compare given dentry name with last component in given path, - * return of 0 indicates a match. */ -int audit_compare_dname_path(const char *dname, const char *path) +/** + * audit_compare_dname_path - compare given dentry name with last component in + * given path. Return of 0 indicates a match. + * @dname: dentry name that we're comparing + * @path: full pathname that we're comparing + * @parentlen: length of the parent if known. Passing in AUDIT_NAME_FULL + * here indicates that we must compute this value. + */ +int audit_compare_dname_path(const char *dname, const char *path, int parentlen) { - int dlen, pathlen, parentlen; + int dlen, pathlen; const char *p; dlen = strlen(dname); @@ -1340,7 +1346,7 @@ int audit_compare_dname_path(const char *dname, const char *path) if (pathlen < dlen) return 1; - parentlen = parent_len(path); + parentlen = parentlen == AUDIT_NAME_FULL ? parent_len(path) : parentlen; if (pathlen - parentlen != dlen) return 1; diff --git a/kernel/auditsc.c b/kernel/auditsc.c index 09c7b6b4f8e6..0160a68b4d7f 100644 --- a/kernel/auditsc.c +++ b/kernel/auditsc.c @@ -81,9 +81,6 @@ * a name dynamically and also add those to the list anchored by names_list. */ #define AUDIT_NAMES 5 -/* Indicates that audit should log the full pathname. */ -#define AUDIT_NAME_FULL -1 - /* no execve audit message should be longer than this (userspace limits) */ #define MAX_EXECVE_AUDIT_LEN 7500 @@ -2222,7 +2219,7 @@ void __audit_inode_child(const struct inode *parent, continue; if (n->ino == parent->i_ino && - !audit_compare_dname_path(dname, n->name)) { + !audit_compare_dname_path(dname, n->name, n->name_len)) { found_parent = n->name; goto add_names; } @@ -2235,7 +2232,8 @@ void __audit_inode_child(const struct inode *parent, /* strcmp() is the more likely scenario */ if (!strcmp(dname, n->name) || - !audit_compare_dname_path(dname, n->name)) { + !audit_compare_dname_path(dname, n->name, + AUDIT_NAME_FULL)) { if (inode) audit_copy_inode(n, dentry, inode); else -- cgit v1.2.3 From 4fa6b5ecbf092c6ee752ece8a55d71f663d23254 Mon Sep 17 00:00:00 2001 From: Jeff Layton Date: Wed, 10 Oct 2012 15:25:25 -0400 Subject: audit: overhaul __audit_inode_child to accomodate retrying In order to accomodate retrying path-based syscalls, we need to add a new "type" argument to audit_inode_child. This will tell us whether we're looking for a child entry that represents a create or a delete. If we find a parent, don't automatically assume that we need to create a new entry. Instead, use the information we have to try to find an existing entry first. Update it if one is found and create a new one if not. Signed-off-by: Jeff Layton Signed-off-by: Al Viro --- fs/btrfs/ioctl.c | 2 +- fs/namei.c | 2 +- include/linux/audit.h | 16 +++++++++----- include/linux/fsnotify.h | 8 +++---- kernel/auditsc.c | 57 +++++++++++++++++++++++++----------------------- 5 files changed, 47 insertions(+), 38 deletions(-) (limited to 'kernel/auditsc.c') diff --git a/fs/btrfs/ioctl.c b/fs/btrfs/ioctl.c index 49f4d59ac2c7..61168805f175 100644 --- a/fs/btrfs/ioctl.c +++ b/fs/btrfs/ioctl.c @@ -638,7 +638,7 @@ static int btrfs_may_delete(struct inode *dir,struct dentry *victim,int isdir) return -ENOENT; BUG_ON(victim->d_parent->d_inode != dir); - audit_inode_child(dir, victim); + audit_inode_child(dir, victim, AUDIT_TYPE_CHILD_DELETE); error = inode_permission(dir, MAY_WRITE | MAY_EXEC); if (error) diff --git a/fs/namei.c b/fs/namei.c index 6a92d988573f..ca14d8432d3d 100644 --- a/fs/namei.c +++ b/fs/namei.c @@ -2176,7 +2176,7 @@ static int may_delete(struct inode *dir,struct dentry *victim,int isdir) return -ENOENT; BUG_ON(victim->d_parent->d_inode != dir); - audit_inode_child(dir, victim); + audit_inode_child(dir, victim, AUDIT_TYPE_CHILD_DELETE); error = inode_permission(dir, MAY_WRITE | MAY_EXEC); if (error) diff --git a/include/linux/audit.h b/include/linux/audit.h index b11f517dce04..3df643d1ac5b 100644 --- a/include/linux/audit.h +++ b/include/linux/audit.h @@ -457,6 +457,8 @@ extern int audit_classify_arch(int arch); #define AUDIT_TYPE_UNKNOWN 0 /* we don't know yet */ #define AUDIT_TYPE_NORMAL 1 /* a "normal" audit record */ #define AUDIT_TYPE_PARENT 2 /* a parent audit record */ +#define AUDIT_TYPE_CHILD_DELETE 3 /* a child being deleted */ +#define AUDIT_TYPE_CHILD_CREATE 4 /* a child being created */ #ifdef CONFIG_AUDITSYSCALL /* These are defined in auditsc.c */ @@ -472,7 +474,8 @@ extern void audit_putname(const char *name); extern void __audit_inode(const char *name, const struct dentry *dentry, unsigned int parent); extern void __audit_inode_child(const struct inode *parent, - const struct dentry *dentry); + const struct dentry *dentry, + const unsigned char type); extern void __audit_seccomp(unsigned long syscall, long signr, int code); extern void __audit_ptrace(struct task_struct *t); @@ -513,9 +516,10 @@ static inline void audit_inode(const char *name, const struct dentry *dentry, __audit_inode(name, dentry, parent); } static inline void audit_inode_child(const struct inode *parent, - const struct dentry *dentry) { + const struct dentry *dentry, + const unsigned char type) { if (unlikely(!audit_dummy_context())) - __audit_inode_child(parent, dentry); + __audit_inode_child(parent, dentry, type); } void audit_core_dumps(long signr); @@ -667,13 +671,15 @@ static inline void __audit_inode(const char *name, const struct dentry *dentry, unsigned int parent) { } static inline void __audit_inode_child(const struct inode *parent, - const struct dentry *dentry) + const struct dentry *dentry, + const unsigned char type) { } static inline void audit_inode(const char *name, const struct dentry *dentry, unsigned int parent) { } static inline void audit_inode_child(const struct inode *parent, - const struct dentry *dentry) + const struct dentry *dentry, + const unsigned char type) { } static inline void audit_core_dumps(long signr) { } diff --git a/include/linux/fsnotify.h b/include/linux/fsnotify.h index 9c284714977d..0fbfb4646d1b 100644 --- a/include/linux/fsnotify.h +++ b/include/linux/fsnotify.h @@ -109,7 +109,7 @@ static inline void fsnotify_move(struct inode *old_dir, struct inode *new_dir, if (source) fsnotify(source, FS_MOVE_SELF, moved->d_inode, FSNOTIFY_EVENT_INODE, NULL, 0); - audit_inode_child(new_dir, moved); + audit_inode_child(new_dir, moved, AUDIT_TYPE_CHILD_CREATE); } /* @@ -155,7 +155,7 @@ static inline void fsnotify_inoderemove(struct inode *inode) */ static inline void fsnotify_create(struct inode *inode, struct dentry *dentry) { - audit_inode_child(inode, dentry); + audit_inode_child(inode, dentry, AUDIT_TYPE_CHILD_CREATE); fsnotify(inode, FS_CREATE, dentry->d_inode, FSNOTIFY_EVENT_INODE, dentry->d_name.name, 0); } @@ -168,7 +168,7 @@ static inline void fsnotify_create(struct inode *inode, struct dentry *dentry) static inline void fsnotify_link(struct inode *dir, struct inode *inode, struct dentry *new_dentry) { fsnotify_link_count(inode); - audit_inode_child(dir, new_dentry); + audit_inode_child(dir, new_dentry, AUDIT_TYPE_CHILD_CREATE); fsnotify(dir, FS_CREATE, inode, FSNOTIFY_EVENT_INODE, new_dentry->d_name.name, 0); } @@ -181,7 +181,7 @@ static inline void fsnotify_mkdir(struct inode *inode, struct dentry *dentry) __u32 mask = (FS_CREATE | FS_ISDIR); struct inode *d_inode = dentry->d_inode; - audit_inode_child(inode, dentry); + audit_inode_child(inode, dentry, AUDIT_TYPE_CHILD_CREATE); fsnotify(inode, mask, d_inode, FSNOTIFY_EVENT_INODE, dentry->d_name.name, 0); } diff --git a/kernel/auditsc.c b/kernel/auditsc.c index 0160a68b4d7f..d147585e9ef3 100644 --- a/kernel/auditsc.c +++ b/kernel/auditsc.c @@ -2189,6 +2189,7 @@ out: * __audit_inode_child - collect inode info for created/removed objects * @parent: inode of dentry parent * @dentry: dentry being audited + * @type: AUDIT_TYPE_* value that we're looking for * * For syscalls that create or remove filesystem objects, audit_inode * can only collect information for the filesystem object's parent. @@ -2199,13 +2200,13 @@ out: * unsuccessful attempts. */ void __audit_inode_child(const struct inode *parent, - const struct dentry *dentry) + const struct dentry *dentry, + const unsigned char type) { struct audit_context *context = current->audit_context; - const char *found_parent = NULL, *found_child = NULL; const struct inode *inode = dentry->d_inode; const char *dname = dentry->d_name.name; - struct audit_names *n; + struct audit_names *n, *found_parent = NULL, *found_child = NULL; if (!context->in_syscall) return; @@ -2213,63 +2214,65 @@ void __audit_inode_child(const struct inode *parent, if (inode) handle_one(inode); - /* parent is more likely, look for it first */ + /* look for a parent entry first */ list_for_each_entry(n, &context->names_list, list) { - if (!n->name) + if (!n->name || n->type != AUDIT_TYPE_PARENT) continue; if (n->ino == parent->i_ino && !audit_compare_dname_path(dname, n->name, n->name_len)) { - found_parent = n->name; - goto add_names; + found_parent = n; + break; } } - /* no matching parent, look for matching child */ + /* is there a matching child entry? */ list_for_each_entry(n, &context->names_list, list) { - if (!n->name) + /* can only match entries that have a name */ + if (!n->name || n->type != type) + continue; + + /* if we found a parent, make sure this one is a child of it */ + if (found_parent && (n->name != found_parent->name)) continue; - /* strcmp() is the more likely scenario */ if (!strcmp(dname, n->name) || !audit_compare_dname_path(dname, n->name, + found_parent ? + found_parent->name_len : AUDIT_NAME_FULL)) { - if (inode) - audit_copy_inode(n, dentry, inode); - else - n->ino = (unsigned long)-1; - n->type = AUDIT_TYPE_NORMAL; - found_child = n->name; - goto add_names; + found_child = n; + break; } } -add_names: if (!found_parent) { - n = audit_alloc_name(context, AUDIT_TYPE_NORMAL); + /* create a new, "anonymous" parent record */ + n = audit_alloc_name(context, AUDIT_TYPE_PARENT); if (!n) return; audit_copy_inode(n, NULL, parent); } if (!found_child) { - n = audit_alloc_name(context, AUDIT_TYPE_NORMAL); - if (!n) + found_child = audit_alloc_name(context, type); + if (!found_child) return; /* Re-use the name belonging to the slot for a matching parent * directory. All names for this context are relinquished in * audit_free_names() */ if (found_parent) { - n->name = found_parent; - n->name_len = AUDIT_NAME_FULL; + found_child->name = found_parent->name; + found_child->name_len = AUDIT_NAME_FULL; /* don't call __putname() */ - n->name_put = false; + found_child->name_put = false; } - - if (inode) - audit_copy_inode(n, dentry, inode); } + if (inode) + audit_copy_inode(found_child, dentry, inode); + else + found_child->ino = (unsigned long)-1; } EXPORT_SYMBOL_GPL(__audit_inode_child); -- cgit v1.2.3 From 91a27b2a756784714e924e5e854b919273082d26 Mon Sep 17 00:00:00 2001 From: Jeff Layton Date: Wed, 10 Oct 2012 15:25:28 -0400 Subject: vfs: define struct filename and have getname() return it getname() is intended to copy pathname strings from userspace into a kernel buffer. The result is just a string in kernel space. It would however be quite helpful to be able to attach some ancillary info to the string. For instance, we could attach some audit-related info to reduce the amount of audit-related processing needed. When auditing is enabled, we could also call getname() on the string more than once and not need to recopy it from userspace. This patchset converts the getname()/putname() interfaces to return a struct instead of a string. For now, the struct just tracks the string in kernel space and the original userland pointer for it. Later, we'll add other information to the struct as it becomes convenient. Signed-off-by: Jeff Layton Signed-off-by: Al Viro --- arch/alpha/kernel/osf_sys.c | 16 ++--- arch/arm64/kernel/sys.c | 4 +- arch/arm64/kernel/sys_compat.c | 6 +- arch/avr32/kernel/process.c | 4 +- arch/blackfin/kernel/process.c | 4 +- arch/cris/arch-v10/kernel/process.c | 4 +- arch/cris/arch-v32/kernel/process.c | 4 +- arch/h8300/kernel/process.c | 4 +- arch/hexagon/kernel/syscall.c | 4 +- arch/ia64/kernel/process.c | 4 +- arch/m32r/kernel/process.c | 4 +- arch/microblaze/kernel/sys_microblaze.c | 4 +- arch/mips/kernel/linux32.c | 4 +- arch/mips/kernel/syscall.c | 4 +- arch/openrisc/kernel/process.c | 4 +- arch/parisc/hpux/fs.c | 4 +- arch/parisc/kernel/process.c | 4 +- arch/parisc/kernel/sys_parisc32.c | 4 +- arch/score/kernel/sys_score.c | 4 +- arch/sh/kernel/process_32.c | 4 +- arch/sh/kernel/process_64.c | 4 +- arch/sparc/kernel/process_32.c | 4 +- arch/sparc/kernel/process_64.c | 4 +- arch/sparc/kernel/sys_sparc32.c | 4 +- arch/tile/kernel/process.c | 8 +-- arch/unicore32/kernel/sys.c | 4 +- arch/xtensa/kernel/process.c | 4 +- fs/compat.c | 12 ++-- fs/exec.c | 13 ++-- fs/filesystems.c | 4 +- fs/namei.c | 108 ++++++++++++++++++++------------ fs/namespace.c | 4 +- fs/open.c | 4 +- fs/quota/quota.c | 4 +- include/linux/audit.h | 12 ++-- include/linux/fs.h | 14 ++++- ipc/mqueue.c | 13 ++-- kernel/acct.c | 4 +- kernel/auditsc.c | 64 ++++++++++--------- mm/swapfile.c | 11 ++-- 40 files changed, 218 insertions(+), 175 deletions(-) (limited to 'kernel/auditsc.c') diff --git a/arch/alpha/kernel/osf_sys.c b/arch/alpha/kernel/osf_sys.c index 63e77e3944ce..9eb090582cf1 100644 --- a/arch/alpha/kernel/osf_sys.c +++ b/arch/alpha/kernel/osf_sys.c @@ -449,7 +449,7 @@ osf_ufs_mount(char *dirname, struct ufs_args __user *args, int flags) { int retval; struct cdfs_args tmp; - char *devname; + struct filename *devname; retval = -EFAULT; if (copy_from_user(&tmp, args, sizeof(tmp))) @@ -458,7 +458,7 @@ osf_ufs_mount(char *dirname, struct ufs_args __user *args, int flags) retval = PTR_ERR(devname); if (IS_ERR(devname)) goto out; - retval = do_mount(devname, dirname, "ext2", flags, NULL); + retval = do_mount(devname->name, dirname, "ext2", flags, NULL); putname(devname); out: return retval; @@ -469,7 +469,7 @@ osf_cdfs_mount(char *dirname, struct cdfs_args __user *args, int flags) { int retval; struct cdfs_args tmp; - char *devname; + struct filename *devname; retval = -EFAULT; if (copy_from_user(&tmp, args, sizeof(tmp))) @@ -478,7 +478,7 @@ osf_cdfs_mount(char *dirname, struct cdfs_args __user *args, int flags) retval = PTR_ERR(devname); if (IS_ERR(devname)) goto out; - retval = do_mount(devname, dirname, "iso9660", flags, NULL); + retval = do_mount(devname->name, dirname, "iso9660", flags, NULL); putname(devname); out: return retval; @@ -499,7 +499,7 @@ SYSCALL_DEFINE4(osf_mount, unsigned long, typenr, const char __user *, path, int, flag, void __user *, data) { int retval; - char *name; + struct filename *name; name = getname(path); retval = PTR_ERR(name); @@ -507,13 +507,13 @@ SYSCALL_DEFINE4(osf_mount, unsigned long, typenr, const char __user *, path, goto out; switch (typenr) { case 1: - retval = osf_ufs_mount(name, data, flag); + retval = osf_ufs_mount(name->name, data, flag); break; case 6: - retval = osf_cdfs_mount(name, data, flag); + retval = osf_cdfs_mount(name->name, data, flag); break; case 9: - retval = osf_procfs_mount(name, data, flag); + retval = osf_procfs_mount(name->name, data, flag); break; default: retval = -EINVAL; diff --git a/arch/arm64/kernel/sys.c b/arch/arm64/kernel/sys.c index 905fcfb0ddd0..b120df37de35 100644 --- a/arch/arm64/kernel/sys.c +++ b/arch/arm64/kernel/sys.c @@ -50,13 +50,13 @@ asmlinkage long sys_execve(const char __user *filenamei, struct pt_regs *regs) { long error; - char * filename; + struct filename *filename; filename = getname(filenamei); error = PTR_ERR(filename); if (IS_ERR(filename)) goto out; - error = do_execve(filename, argv, envp, regs); + error = do_execve(filename->name, argv, envp, regs); putname(filename); out: return error; diff --git a/arch/arm64/kernel/sys_compat.c b/arch/arm64/kernel/sys_compat.c index 93f10e27dc79..e521087cb0c4 100644 --- a/arch/arm64/kernel/sys_compat.c +++ b/arch/arm64/kernel/sys_compat.c @@ -56,14 +56,14 @@ asmlinkage int compat_sys_execve(const char __user *filenamei, struct pt_regs *regs) { int error; - char * filename; + struct filename *filename; filename = getname(filenamei); error = PTR_ERR(filename); if (IS_ERR(filename)) goto out; - error = compat_do_execve(filename, compat_ptr(argv), compat_ptr(envp), - regs); + error = compat_do_execve(filename->name, compat_ptr(argv), + compat_ptr(envp), regs); putname(filename); out: return error; diff --git a/arch/avr32/kernel/process.c b/arch/avr32/kernel/process.c index 92c5af98a6f7..1bb0a8abd79b 100644 --- a/arch/avr32/kernel/process.c +++ b/arch/avr32/kernel/process.c @@ -388,14 +388,14 @@ asmlinkage int sys_execve(const char __user *ufilename, struct pt_regs *regs) { int error; - char *filename; + struct filename *filename; filename = getname(ufilename); error = PTR_ERR(filename); if (IS_ERR(filename)) goto out; - error = do_execve(filename, uargv, uenvp, regs); + error = do_execve(filename->name, uargv, uenvp, regs); putname(filename); out: diff --git a/arch/blackfin/kernel/process.c b/arch/blackfin/kernel/process.c index 62bcea7dcc6d..bb1cc721fcf7 100644 --- a/arch/blackfin/kernel/process.c +++ b/arch/blackfin/kernel/process.c @@ -213,14 +213,14 @@ asmlinkage int sys_execve(const char __user *name, const char __user *const __user *envp) { int error; - char *filename; + struct filename *filename; struct pt_regs *regs = (struct pt_regs *)((&name) + 6); filename = getname(name); error = PTR_ERR(filename); if (IS_ERR(filename)) return error; - error = do_execve(filename, argv, envp, regs); + error = do_execve(filename->name, argv, envp, regs); putname(filename); return error; } diff --git a/arch/cris/arch-v10/kernel/process.c b/arch/cris/arch-v10/kernel/process.c index bee8df43c201..15ac7150371f 100644 --- a/arch/cris/arch-v10/kernel/process.c +++ b/arch/cris/arch-v10/kernel/process.c @@ -212,14 +212,14 @@ asmlinkage int sys_execve(const char *fname, struct pt_regs *regs) { int error; - char *filename; + struct filename *filename; filename = getname(fname); error = PTR_ERR(filename); if (IS_ERR(filename)) goto out; - error = do_execve(filename, argv, envp, regs); + error = do_execve(filename->name, argv, envp, regs); putname(filename); out: return error; diff --git a/arch/cris/arch-v32/kernel/process.c b/arch/cris/arch-v32/kernel/process.c index 0570e8ce603d..4e9992246359 100644 --- a/arch/cris/arch-v32/kernel/process.c +++ b/arch/cris/arch-v32/kernel/process.c @@ -224,7 +224,7 @@ sys_execve(const char *fname, struct pt_regs *regs) { int error; - char *filename; + struct filename *filename; filename = getname(fname); error = PTR_ERR(filename); @@ -232,7 +232,7 @@ sys_execve(const char *fname, if (IS_ERR(filename)) goto out; - error = do_execve(filename, argv, envp, regs); + error = do_execve(filename->name, argv, envp, regs); putname(filename); out: return error; diff --git a/arch/h8300/kernel/process.c b/arch/h8300/kernel/process.c index f153ed1a4c08..e8dc1393a13a 100644 --- a/arch/h8300/kernel/process.c +++ b/arch/h8300/kernel/process.c @@ -217,14 +217,14 @@ asmlinkage int sys_execve(const char *name, int dummy, ...) { int error; - char * filename; + struct filename *filename; struct pt_regs *regs = (struct pt_regs *) ((unsigned char *)&dummy-4); filename = getname(name); error = PTR_ERR(filename); if (IS_ERR(filename)) return error; - error = do_execve(filename, argv, envp, regs); + error = do_execve(filename->name, argv, envp, regs); putname(filename); return error; } diff --git a/arch/hexagon/kernel/syscall.c b/arch/hexagon/kernel/syscall.c index 553cd60ee659..25a9bfe3445d 100644 --- a/arch/hexagon/kernel/syscall.c +++ b/arch/hexagon/kernel/syscall.c @@ -40,7 +40,7 @@ asmlinkage int sys_execve(char __user *ufilename, const char __user *const __user *envp) { struct pt_regs *pregs = current_thread_info()->regs; - char *filename; + struct filename *filename; int retval; filename = getname(ufilename); @@ -48,7 +48,7 @@ asmlinkage int sys_execve(char __user *ufilename, if (IS_ERR(filename)) return retval; - retval = do_execve(filename, argv, envp, pregs); + retval = do_execve(filename->name, argv, envp, pregs); putname(filename); return retval; diff --git a/arch/ia64/kernel/process.c b/arch/ia64/kernel/process.c index ee31fe9b310e..35e106f2ed13 100644 --- a/arch/ia64/kernel/process.c +++ b/arch/ia64/kernel/process.c @@ -614,14 +614,14 @@ sys_execve (const char __user *filename, const char __user *const __user *envp, struct pt_regs *regs) { - char *fname; + struct filename *fname; int error; fname = getname(filename); error = PTR_ERR(fname); if (IS_ERR(fname)) goto out; - error = do_execve(fname, argv, envp, regs); + error = do_execve(fname->name, argv, envp, regs); putname(fname); out: return error; diff --git a/arch/m32r/kernel/process.c b/arch/m32r/kernel/process.c index 384e63f3a4c4..e7366276ef30 100644 --- a/arch/m32r/kernel/process.c +++ b/arch/m32r/kernel/process.c @@ -296,14 +296,14 @@ asmlinkage int sys_execve(const char __user *ufilename, unsigned long r6, struct pt_regs regs) { int error; - char *filename; + struct filename *filename; filename = getname(ufilename); error = PTR_ERR(filename); if (IS_ERR(filename)) goto out; - error = do_execve(filename, uargv, uenvp, ®s); + error = do_execve(filename->name, uargv, uenvp, ®s); putname(filename); out: return error; diff --git a/arch/microblaze/kernel/sys_microblaze.c b/arch/microblaze/kernel/sys_microblaze.c index e5b154f24f85..404c0f24bd41 100644 --- a/arch/microblaze/kernel/sys_microblaze.c +++ b/arch/microblaze/kernel/sys_microblaze.c @@ -54,13 +54,13 @@ asmlinkage long microblaze_execve(const char __user *filenamei, struct pt_regs *regs) { int error; - char *filename; + struct filename *filename; filename = getname(filenamei); error = PTR_ERR(filename); if (IS_ERR(filename)) goto out; - error = do_execve(filename, argv, envp, regs); + error = do_execve(filename->name, argv, envp, regs); putname(filename); out: return error; diff --git a/arch/mips/kernel/linux32.c b/arch/mips/kernel/linux32.c index 922a554cd108..3a21acedf882 100644 --- a/arch/mips/kernel/linux32.c +++ b/arch/mips/kernel/linux32.c @@ -83,13 +83,13 @@ out: asmlinkage int sys32_execve(nabi_no_regargs struct pt_regs regs) { int error; - char * filename; + struct filename *filename; filename = getname(compat_ptr(regs.regs[4])); error = PTR_ERR(filename); if (IS_ERR(filename)) goto out; - error = compat_do_execve(filename, compat_ptr(regs.regs[5]), + error = compat_do_execve(filename->name, compat_ptr(regs.regs[5]), compat_ptr(regs.regs[6]), ®s); putname(filename); diff --git a/arch/mips/kernel/syscall.c b/arch/mips/kernel/syscall.c index b08220c82113..2bd561bc05ae 100644 --- a/arch/mips/kernel/syscall.c +++ b/arch/mips/kernel/syscall.c @@ -133,13 +133,13 @@ _sys_clone(nabi_no_regargs struct pt_regs regs) asmlinkage int sys_execve(nabi_no_regargs struct pt_regs regs) { int error; - char * filename; + struct filename *filename; filename = getname((const char __user *) (long)regs.regs[4]); error = PTR_ERR(filename); if (IS_ERR(filename)) goto out; - error = do_execve(filename, + error = do_execve(filename->name, (const char __user *const __user *) (long)regs.regs[5], (const char __user *const __user *) (long)regs.regs[6], ®s); diff --git a/arch/openrisc/kernel/process.c b/arch/openrisc/kernel/process.c index 55210f37d1a3..c35f3ab1a8d3 100644 --- a/arch/openrisc/kernel/process.c +++ b/arch/openrisc/kernel/process.c @@ -271,7 +271,7 @@ asmlinkage long _sys_execve(const char __user *name, struct pt_regs *regs) { int error; - char *filename; + struct filename *filename; filename = getname(name); error = PTR_ERR(filename); @@ -279,7 +279,7 @@ asmlinkage long _sys_execve(const char __user *name, if (IS_ERR(filename)) goto out; - error = do_execve(filename, argv, envp, regs); + error = do_execve(filename->name, argv, envp, regs); putname(filename); out: diff --git a/arch/parisc/hpux/fs.c b/arch/parisc/hpux/fs.c index 6785de7bd2a0..a0760b87fd4e 100644 --- a/arch/parisc/hpux/fs.c +++ b/arch/parisc/hpux/fs.c @@ -34,14 +34,14 @@ int hpux_execve(struct pt_regs *regs) { int error; - char *filename; + struct filename *filename; filename = getname((const char __user *) regs->gr[26]); error = PTR_ERR(filename); if (IS_ERR(filename)) goto out; - error = do_execve(filename, + error = do_execve(filename->name, (const char __user *const __user *) regs->gr[25], (const char __user *const __user *) regs->gr[24], regs); diff --git a/arch/parisc/kernel/process.c b/arch/parisc/kernel/process.c index 8c6b6b6561f0..cbc37216bf90 100644 --- a/arch/parisc/kernel/process.c +++ b/arch/parisc/kernel/process.c @@ -342,13 +342,13 @@ unsigned long thread_saved_pc(struct task_struct *t) asmlinkage int sys_execve(struct pt_regs *regs) { int error; - char *filename; + struct filename *filename; filename = getname((const char __user *) regs->gr[26]); error = PTR_ERR(filename); if (IS_ERR(filename)) goto out; - error = do_execve(filename, + error = do_execve(filename->name, (const char __user *const __user *) regs->gr[25], (const char __user *const __user *) regs->gr[24], regs); diff --git a/arch/parisc/kernel/sys_parisc32.c b/arch/parisc/kernel/sys_parisc32.c index dc9a62462323..bf5b93a885d3 100644 --- a/arch/parisc/kernel/sys_parisc32.c +++ b/arch/parisc/kernel/sys_parisc32.c @@ -60,14 +60,14 @@ asmlinkage int sys32_execve(struct pt_regs *regs) { int error; - char *filename; + struct filename *filename; DBG(("sys32_execve(%p) r26 = 0x%lx\n", regs, regs->gr[26])); filename = getname((const char __user *) regs->gr[26]); error = PTR_ERR(filename); if (IS_ERR(filename)) goto out; - error = compat_do_execve(filename, compat_ptr(regs->gr[25]), + error = compat_do_execve(filename->name, compat_ptr(regs->gr[25]), compat_ptr(regs->gr[24]), regs); putname(filename); out: diff --git a/arch/score/kernel/sys_score.c b/arch/score/kernel/sys_score.c index 21e867974066..d45cf00a3351 100644 --- a/arch/score/kernel/sys_score.c +++ b/arch/score/kernel/sys_score.c @@ -92,14 +92,14 @@ asmlinkage long score_execve(struct pt_regs *regs) { int error; - char *filename; + struct filename *filename; filename = getname((char __user*)regs->regs[4]); error = PTR_ERR(filename); if (IS_ERR(filename)) return error; - error = do_execve(filename, + error = do_execve(filename->name, (const char __user *const __user *)regs->regs[5], (const char __user *const __user *)regs->regs[6], regs); diff --git a/arch/sh/kernel/process_32.c b/arch/sh/kernel/process_32.c index 59521e8a164d..ba7345f37bc9 100644 --- a/arch/sh/kernel/process_32.c +++ b/arch/sh/kernel/process_32.c @@ -298,14 +298,14 @@ asmlinkage int sys_execve(const char __user *ufilename, { struct pt_regs *regs = RELOC_HIDE(&__regs, 0); int error; - char *filename; + struct filename *filename; filename = getname(ufilename); error = PTR_ERR(filename); if (IS_ERR(filename)) goto out; - error = do_execve(filename, uargv, uenvp, regs); + error = do_execve(filename->name, uargv, uenvp, regs); putname(filename); out: return error; diff --git a/arch/sh/kernel/process_64.c b/arch/sh/kernel/process_64.c index 602545b12a86..98a709f0c3c4 100644 --- a/arch/sh/kernel/process_64.c +++ b/arch/sh/kernel/process_64.c @@ -491,14 +491,14 @@ asmlinkage int sys_execve(const char *ufilename, char **uargv, struct pt_regs *pregs) { int error; - char *filename; + struct filename *filename; filename = getname((char __user *)ufilename); error = PTR_ERR(filename); if (IS_ERR(filename)) goto out; - error = do_execve(filename, + error = do_execve(filename->name, (const char __user *const __user *)uargv, (const char __user *const __user *)uenvp, pregs); diff --git a/arch/sparc/kernel/process_32.c b/arch/sparc/kernel/process_32.c index 14006d8aca28..487bffb36f5e 100644 --- a/arch/sparc/kernel/process_32.c +++ b/arch/sparc/kernel/process_32.c @@ -482,7 +482,7 @@ int dump_fpu (struct pt_regs * regs, elf_fpregset_t * fpregs) asmlinkage int sparc_execve(struct pt_regs *regs) { int error, base = 0; - char *filename; + struct filename *filename; /* Check for indirect call. */ if(regs->u_regs[UREG_G1] == 0) @@ -492,7 +492,7 @@ asmlinkage int sparc_execve(struct pt_regs *regs) error = PTR_ERR(filename); if(IS_ERR(filename)) goto out; - error = do_execve(filename, + error = do_execve(filename->name, (const char __user *const __user *) regs->u_regs[base + UREG_I1], (const char __user *const __user *) diff --git a/arch/sparc/kernel/process_64.c b/arch/sparc/kernel/process_64.c index aff0c72fac09..fcaa59421126 100644 --- a/arch/sparc/kernel/process_64.c +++ b/arch/sparc/kernel/process_64.c @@ -722,7 +722,7 @@ EXPORT_SYMBOL(dump_fpu); asmlinkage int sparc_execve(struct pt_regs *regs) { int error, base = 0; - char *filename; + struct filename *filename; /* User register window flush is done by entry.S */ @@ -734,7 +734,7 @@ asmlinkage int sparc_execve(struct pt_regs *regs) error = PTR_ERR(filename); if (IS_ERR(filename)) goto out; - error = do_execve(filename, + error = do_execve(filename->name, (const char __user *const __user *) regs->u_regs[base + UREG_I1], (const char __user *const __user *) diff --git a/arch/sparc/kernel/sys_sparc32.c b/arch/sparc/kernel/sys_sparc32.c index d862499eb01c..c3239811a1b5 100644 --- a/arch/sparc/kernel/sys_sparc32.c +++ b/arch/sparc/kernel/sys_sparc32.c @@ -403,7 +403,7 @@ asmlinkage long compat_sys_rt_sigaction(int sig, asmlinkage long sparc32_execve(struct pt_regs *regs) { int error, base = 0; - char *filename; + struct filename *filename; /* User register window flush is done by entry.S */ @@ -416,7 +416,7 @@ asmlinkage long sparc32_execve(struct pt_regs *regs) if (IS_ERR(filename)) goto out; - error = compat_do_execve(filename, + error = compat_do_execve(filename->name, compat_ptr(regs->u_regs[base + UREG_I1]), compat_ptr(regs->u_regs[base + UREG_I2]), regs); diff --git a/arch/tile/kernel/process.c b/arch/tile/kernel/process.c index 6be799150501..622560030a58 100644 --- a/arch/tile/kernel/process.c +++ b/arch/tile/kernel/process.c @@ -594,13 +594,13 @@ SYSCALL_DEFINE4(execve, const char __user *, path, struct pt_regs *, regs) { long error; - char *filename; + struct filename *filename; filename = getname(path); error = PTR_ERR(filename); if (IS_ERR(filename)) goto out; - error = do_execve(filename, argv, envp, regs); + error = do_execve(filename->name, argv, envp, regs); putname(filename); if (error == 0) single_step_execve(); @@ -615,13 +615,13 @@ long compat_sys_execve(const char __user *path, struct pt_regs *regs) { long error; - char *filename; + struct filename *filename; filename = getname(path); error = PTR_ERR(filename); if (IS_ERR(filename)) goto out; - error = compat_do_execve(filename, argv, envp, regs); + error = compat_do_execve(filename->name, argv, envp, regs); putname(filename); if (error == 0) single_step_execve(); diff --git a/arch/unicore32/kernel/sys.c b/arch/unicore32/kernel/sys.c index 5fd9af773e15..fabdee96110b 100644 --- a/arch/unicore32/kernel/sys.c +++ b/arch/unicore32/kernel/sys.c @@ -51,13 +51,13 @@ asmlinkage long __sys_execve(const char __user *filename, struct pt_regs *regs) { int error; - char *fn; + struct filename *fn; fn = getname(filename); error = PTR_ERR(fn); if (IS_ERR(fn)) goto out; - error = do_execve(fn, argv, envp, regs); + error = do_execve(fn->name, argv, envp, regs); putname(fn); out: return error; diff --git a/arch/xtensa/kernel/process.c b/arch/xtensa/kernel/process.c index bc44311aa18c..bc020825cce5 100644 --- a/arch/xtensa/kernel/process.c +++ b/arch/xtensa/kernel/process.c @@ -328,13 +328,13 @@ long xtensa_execve(const char __user *name, struct pt_regs *regs) { long error; - char * filename; + struct filename *filename; filename = getname(name); error = PTR_ERR(filename); if (IS_ERR(filename)) goto out; - error = do_execve(filename, argv, envp, regs); + error = do_execve(filename->name, argv, envp, regs); putname(filename); out: return error; diff --git a/fs/compat.c b/fs/compat.c index b7a24d0ca30d..015e1e1f87c6 100644 --- a/fs/compat.c +++ b/fs/compat.c @@ -776,16 +776,16 @@ asmlinkage long compat_sys_mount(const char __user * dev_name, char *kernel_type; unsigned long data_page; char *kernel_dev; - char *dir_page; + struct filename *dir; int retval; retval = copy_mount_string(type, &kernel_type); if (retval < 0) goto out; - dir_page = getname(dir_name); - retval = PTR_ERR(dir_page); - if (IS_ERR(dir_page)) + dir = getname(dir_name); + retval = PTR_ERR(dir); + if (IS_ERR(dir)) goto out1; retval = copy_mount_string(dev_name, &kernel_dev); @@ -807,7 +807,7 @@ asmlinkage long compat_sys_mount(const char __user * dev_name, } } - retval = do_mount(kernel_dev, dir_page, kernel_type, + retval = do_mount(kernel_dev, dir->name, kernel_type, flags, (void*)data_page); out4: @@ -815,7 +815,7 @@ asmlinkage long compat_sys_mount(const char __user * dev_name, out3: kfree(kernel_dev); out2: - putname(dir_page); + putname(dir); out1: kfree(kernel_type); out: diff --git a/fs/exec.c b/fs/exec.c index ca434534ae9a..4e591e20e108 100644 --- a/fs/exec.c +++ b/fs/exec.c @@ -105,7 +105,7 @@ static inline void put_binfmt(struct linux_binfmt * fmt) SYSCALL_DEFINE1(uselib, const char __user *, library) { struct file *file; - char *tmp = getname(library); + struct filename *tmp = getname(library); int error = PTR_ERR(tmp); static const struct open_flags uselib_flags = { .open_flag = O_LARGEFILE | O_RDONLY | __FMODE_EXEC, @@ -116,7 +116,7 @@ SYSCALL_DEFINE1(uselib, const char __user *, library) if (IS_ERR(tmp)) goto out; - file = do_filp_open(AT_FDCWD, tmp, &uselib_flags, LOOKUP_FOLLOW); + file = do_filp_open(AT_FDCWD, tmp->name, &uselib_flags, LOOKUP_FOLLOW); putname(tmp); error = PTR_ERR(file); if (IS_ERR(file)) @@ -1664,10 +1664,10 @@ SYSCALL_DEFINE3(execve, const char __user *const __user *, argv, const char __user *const __user *, envp) { - const char *path = getname(filename); + struct filename *path = getname(filename); int error = PTR_ERR(path); if (!IS_ERR(path)) { - error = do_execve(path, argv, envp, current_pt_regs()); + error = do_execve(path->name, argv, envp, current_pt_regs()); putname(path); } return error; @@ -1677,10 +1677,11 @@ asmlinkage long compat_sys_execve(const char __user * filename, const compat_uptr_t __user * argv, const compat_uptr_t __user * envp) { - const char *path = getname(filename); + struct filename *path = getname(filename); int error = PTR_ERR(path); if (!IS_ERR(path)) { - error = compat_do_execve(path, argv, envp, current_pt_regs()); + error = compat_do_execve(path->name, argv, envp, + current_pt_regs()); putname(path); } return error; diff --git a/fs/filesystems.c b/fs/filesystems.c index 96f24286667a..da165f6adcbf 100644 --- a/fs/filesystems.c +++ b/fs/filesystems.c @@ -124,7 +124,7 @@ EXPORT_SYMBOL(unregister_filesystem); static int fs_index(const char __user * __name) { struct file_system_type * tmp; - char * name; + struct filename *name; int err, index; name = getname(__name); @@ -135,7 +135,7 @@ static int fs_index(const char __user * __name) err = -EINVAL; read_lock(&file_systems_lock); for (tmp=file_systems, index=0 ; tmp ; tmp=tmp->next, index++) { - if (strcmp(tmp->name,name) == 0) { + if (strcmp(tmp->name, name->name) == 0) { err = index; break; } diff --git a/fs/namei.c b/fs/namei.c index 9cc0fce7fc91..ec638d27642f 100644 --- a/fs/namei.c +++ b/fs/namei.c @@ -117,18 +117,37 @@ * POSIX.1 2.4: an empty pathname is invalid (ENOENT). * PATH_MAX includes the nul terminator --RR. */ -static char *getname_flags(const char __user *filename, int flags, int *empty) +void final_putname(struct filename *name) { - char *result = __getname(), *err; + __putname(name->name); + kfree(name); +} + +static struct filename * +getname_flags(const char __user *filename, int flags, int *empty) +{ + struct filename *result, *err; + char *kname; int len; + /* FIXME: create dedicated slabcache? */ + result = kzalloc(sizeof(*result), GFP_KERNEL); if (unlikely(!result)) return ERR_PTR(-ENOMEM); - len = strncpy_from_user(result, filename, PATH_MAX); - err = ERR_PTR(len); - if (unlikely(len < 0)) + kname = __getname(); + if (unlikely(!kname)) { + err = ERR_PTR(-ENOMEM); + goto error_free_name; + } + + result->name = kname; + result->uptr = filename; + len = strncpy_from_user(kname, filename, PATH_MAX); + if (unlikely(len < 0)) { + err = ERR_PTR(len); goto error; + } /* The empty path is special. */ if (unlikely(!len)) { @@ -146,22 +165,25 @@ static char *getname_flags(const char __user *filename, int flags, int *empty) } error: - __putname(result); + __putname(kname); +error_free_name: + kfree(result); return err; } -char *getname(const char __user * filename) +struct filename * +getname(const char __user * filename) { return getname_flags(filename, 0, NULL); } +EXPORT_SYMBOL(getname); #ifdef CONFIG_AUDITSYSCALL -void putname(const char *name) +void putname(struct filename *name) { if (unlikely(!audit_dummy_context())) - audit_putname(name); - else - __putname(name); + return audit_putname(name); + final_putname(name); } #endif @@ -2093,13 +2115,13 @@ int user_path_at_empty(int dfd, const char __user *name, unsigned flags, struct path *path, int *empty) { struct nameidata nd; - char *tmp = getname_flags(name, flags, empty); + struct filename *tmp = getname_flags(name, flags, empty); int err = PTR_ERR(tmp); if (!IS_ERR(tmp)) { BUG_ON(flags & LOOKUP_PARENT); - err = do_path_lookup(dfd, tmp, flags, &nd); + err = do_path_lookup(dfd, tmp->name, flags, &nd); putname(tmp); if (!err) *path = nd.path; @@ -2113,22 +2135,22 @@ int user_path_at(int dfd, const char __user *name, unsigned flags, return user_path_at_empty(dfd, name, flags, path, NULL); } -static int user_path_parent(int dfd, const char __user *path, - struct nameidata *nd, char **name) +static struct filename * +user_path_parent(int dfd, const char __user *path, struct nameidata *nd) { - char *s = getname(path); + struct filename *s = getname(path); int error; if (IS_ERR(s)) - return PTR_ERR(s); + return s; - error = do_path_lookup(dfd, s, LOOKUP_PARENT, nd); - if (error) + error = do_path_lookup(dfd, s->name, LOOKUP_PARENT, nd); + if (error) { putname(s); - else - *name = s; + return ERR_PTR(error); + } - return error; + return s; } /* @@ -3039,11 +3061,11 @@ EXPORT_SYMBOL(done_path_create); struct dentry *user_path_create(int dfd, const char __user *pathname, struct path *path, int is_dir) { - char *tmp = getname(pathname); + struct filename *tmp = getname(pathname); struct dentry *res; if (IS_ERR(tmp)) return ERR_CAST(tmp); - res = kern_path_create(dfd, tmp, path, is_dir); + res = kern_path_create(dfd, tmp->name, path, is_dir); putname(tmp); return res; } @@ -3248,13 +3270,13 @@ out: static long do_rmdir(int dfd, const char __user *pathname) { int error = 0; - char * name; + struct filename *name; struct dentry *dentry; struct nameidata nd; - error = user_path_parent(dfd, pathname, &nd, &name); - if (error) - return error; + name = user_path_parent(dfd, pathname, &nd); + if (IS_ERR(name)) + return PTR_ERR(name); switch(nd.last_type) { case LAST_DOTDOT: @@ -3343,14 +3365,14 @@ int vfs_unlink(struct inode *dir, struct dentry *dentry) static long do_unlinkat(int dfd, const char __user *pathname) { int error; - char *name; + struct filename *name; struct dentry *dentry; struct nameidata nd; struct inode *inode = NULL; - error = user_path_parent(dfd, pathname, &nd, &name); - if (error) - return error; + name = user_path_parent(dfd, pathname, &nd); + if (IS_ERR(name)) + return PTR_ERR(name); error = -EISDIR; if (nd.last_type != LAST_NORM) @@ -3434,7 +3456,7 @@ SYSCALL_DEFINE3(symlinkat, const char __user *, oldname, int, newdfd, const char __user *, newname) { int error; - char *from; + struct filename *from; struct dentry *dentry; struct path path; @@ -3447,9 +3469,9 @@ SYSCALL_DEFINE3(symlinkat, const char __user *, oldname, if (IS_ERR(dentry)) goto out_putname; - error = security_path_symlink(&path, dentry, from); + error = security_path_symlink(&path, dentry, from->name); if (!error) - error = vfs_symlink(path.dentry->d_inode, dentry, from); + error = vfs_symlink(path.dentry->d_inode, dentry, from->name); done_path_create(&path, dentry); out_putname: putname(from); @@ -3729,17 +3751,21 @@ SYSCALL_DEFINE4(renameat, int, olddfd, const char __user *, oldname, struct dentry *old_dentry, *new_dentry; struct dentry *trap; struct nameidata oldnd, newnd; - char *from; - char *to; + struct filename *from; + struct filename *to; int error; - error = user_path_parent(olddfd, oldname, &oldnd, &from); - if (error) + from = user_path_parent(olddfd, oldname, &oldnd); + if (IS_ERR(from)) { + error = PTR_ERR(from); goto exit; + } - error = user_path_parent(newdfd, newname, &newnd, &to); - if (error) + to = user_path_parent(newdfd, newname, &newnd); + if (IS_ERR(to)) { + error = PTR_ERR(to); goto exit1; + } error = -EXDEV; if (oldnd.path.mnt != newnd.path.mnt) diff --git a/fs/namespace.c b/fs/namespace.c index fc33207e28ad..24960626bb6b 100644 --- a/fs/namespace.c +++ b/fs/namespace.c @@ -2408,7 +2408,7 @@ SYSCALL_DEFINE5(mount, char __user *, dev_name, char __user *, dir_name, { int ret; char *kernel_type; - char *kernel_dir; + struct filename *kernel_dir; char *kernel_dev; unsigned long data_page; @@ -2430,7 +2430,7 @@ SYSCALL_DEFINE5(mount, char __user *, dev_name, char __user *, dir_name, if (ret < 0) goto out_data; - ret = do_mount(kernel_dev, kernel_dir, kernel_type, flags, + ret = do_mount(kernel_dev, kernel_dir->name, kernel_type, flags, (void *) data_page); free_page(data_page); diff --git a/fs/open.c b/fs/open.c index a015437e1535..81dd92ac10ff 100644 --- a/fs/open.c +++ b/fs/open.c @@ -895,13 +895,13 @@ long do_sys_open(int dfd, const char __user *filename, int flags, umode_t mode) { struct open_flags op; int lookup = build_open_flags(flags, mode, &op); - char *tmp = getname(filename); + struct filename *tmp = getname(filename); int fd = PTR_ERR(tmp); if (!IS_ERR(tmp)) { fd = get_unused_fd_flags(flags); if (fd >= 0) { - struct file *f = do_filp_open(dfd, tmp, &op, lookup); + struct file *f = do_filp_open(dfd, tmp->name, &op, lookup); if (IS_ERR(f)) { put_unused_fd(fd); fd = PTR_ERR(f); diff --git a/fs/quota/quota.c b/fs/quota/quota.c index ff0135d6bc51..af1661f7a54f 100644 --- a/fs/quota/quota.c +++ b/fs/quota/quota.c @@ -331,11 +331,11 @@ static struct super_block *quotactl_block(const char __user *special, int cmd) #ifdef CONFIG_BLOCK struct block_device *bdev; struct super_block *sb; - char *tmp = getname(special); + struct filename *tmp = getname(special); if (IS_ERR(tmp)) return ERR_CAST(tmp); - bdev = lookup_bdev(tmp); + bdev = lookup_bdev(tmp->name); putname(tmp); if (IS_ERR(bdev)) return ERR_CAST(bdev); diff --git a/include/linux/audit.h b/include/linux/audit.h index 3df643d1ac5b..94d29164803f 100644 --- a/include/linux/audit.h +++ b/include/linux/audit.h @@ -460,6 +460,8 @@ extern int audit_classify_arch(int arch); #define AUDIT_TYPE_CHILD_DELETE 3 /* a child being deleted */ #define AUDIT_TYPE_CHILD_CREATE 4 /* a child being created */ +struct filename; + #ifdef CONFIG_AUDITSYSCALL /* These are defined in auditsc.c */ /* Public API */ @@ -469,8 +471,8 @@ extern void __audit_syscall_entry(int arch, int major, unsigned long a0, unsigned long a1, unsigned long a2, unsigned long a3); extern void __audit_syscall_exit(int ret_success, long ret_value); -extern void __audit_getname(const char *name); -extern void audit_putname(const char *name); +extern void __audit_getname(struct filename *name); +extern void audit_putname(struct filename *name); extern void __audit_inode(const char *name, const struct dentry *dentry, unsigned int parent); extern void __audit_inode_child(const struct inode *parent, @@ -505,7 +507,7 @@ static inline void audit_syscall_exit(void *pt_regs) __audit_syscall_exit(success, return_code); } } -static inline void audit_getname(const char *name) +static inline void audit_getname(struct filename *name) { if (unlikely(!audit_dummy_context())) __audit_getname(name); @@ -663,9 +665,9 @@ static inline int audit_dummy_context(void) { return 1; } -static inline void audit_getname(const char *name) +static inline void audit_getname(struct filename *name) { } -static inline void audit_putname(const char *name) +static inline void audit_putname(struct filename *name) { } static inline void __audit_inode(const char *name, const struct dentry *dentry, unsigned int parent) diff --git a/include/linux/fs.h b/include/linux/fs.h index b44b4ca82164..6c93b46f46dc 100644 --- a/include/linux/fs.h +++ b/include/linux/fs.h @@ -2196,6 +2196,10 @@ static inline int break_lease(struct inode *inode, unsigned int mode) #endif /* CONFIG_FILE_LOCKING */ /* fs/open.c */ +struct filename { + const char *name; /* pointer to actual string */ + const __user char *uptr; /* original userland pointer */ +}; extern int do_truncate(struct dentry *, loff_t start, unsigned int time_attrs, struct file *filp); @@ -2208,7 +2212,9 @@ extern struct file *file_open_root(struct dentry *, struct vfsmount *, const char *, int); extern struct file * dentry_open(const struct path *, int, const struct cred *); extern int filp_close(struct file *, fl_owner_t id); -extern char * getname(const char __user *); + +extern struct filename *getname(const char __user *); + enum { FILE_CREATED = 1, FILE_OPENED = 2 @@ -2228,12 +2234,14 @@ extern void __init vfs_caches_init(unsigned long); extern struct kmem_cache *names_cachep; +extern void final_putname(struct filename *name); + #define __getname() kmem_cache_alloc(names_cachep, GFP_KERNEL) #define __putname(name) kmem_cache_free(names_cachep, (void *)(name)) #ifndef CONFIG_AUDITSYSCALL -#define putname(name) __putname(name) +#define putname(name) final_putname(name) #else -extern void putname(const char *name); +extern void putname(struct filename *name); #endif #ifdef CONFIG_BLOCK diff --git a/ipc/mqueue.c b/ipc/mqueue.c index 9553ed006042..6c5d9dcc9030 100644 --- a/ipc/mqueue.c +++ b/ipc/mqueue.c @@ -772,7 +772,7 @@ SYSCALL_DEFINE4(mq_open, const char __user *, u_name, int, oflag, umode_t, mode, { struct path path; struct file *filp; - char *name; + struct filename *name; struct mq_attr attr; int fd, error; struct ipc_namespace *ipc_ns = current->nsproxy->ipc_ns; @@ -795,7 +795,7 @@ SYSCALL_DEFINE4(mq_open, const char __user *, u_name, int, oflag, umode_t, mode, ro = mnt_want_write(mnt); /* we'll drop it in any case */ error = 0; mutex_lock(&root->d_inode->i_mutex); - path.dentry = lookup_one_len(name, root, strlen(name)); + path.dentry = lookup_one_len(name->name, root, strlen(name->name)); if (IS_ERR(path.dentry)) { error = PTR_ERR(path.dentry); goto out_putfd; @@ -804,7 +804,7 @@ SYSCALL_DEFINE4(mq_open, const char __user *, u_name, int, oflag, umode_t, mode, if (oflag & O_CREAT) { if (path.dentry->d_inode) { /* entry already exists */ - audit_inode(name, path.dentry, 0); + audit_inode(name->name, path.dentry, 0); if (oflag & O_EXCL) { error = -EEXIST; goto out; @@ -824,7 +824,7 @@ SYSCALL_DEFINE4(mq_open, const char __user *, u_name, int, oflag, umode_t, mode, error = -ENOENT; goto out; } - audit_inode(name, path.dentry, 0); + audit_inode(name->name, path.dentry, 0); filp = do_open(&path, oflag); } @@ -849,7 +849,7 @@ out_putname: SYSCALL_DEFINE1(mq_unlink, const char __user *, u_name) { int err; - char *name; + struct filename *name; struct dentry *dentry; struct inode *inode = NULL; struct ipc_namespace *ipc_ns = current->nsproxy->ipc_ns; @@ -863,7 +863,8 @@ SYSCALL_DEFINE1(mq_unlink, const char __user *, u_name) if (err) goto out_name; mutex_lock_nested(&mnt->mnt_root->d_inode->i_mutex, I_MUTEX_PARENT); - dentry = lookup_one_len(name, mnt->mnt_root, strlen(name)); + dentry = lookup_one_len(name->name, mnt->mnt_root, + strlen(name->name)); if (IS_ERR(dentry)) { err = PTR_ERR(dentry); goto out_unlock; diff --git a/kernel/acct.c b/kernel/acct.c index 5be01017d30f..08354195eecc 100644 --- a/kernel/acct.c +++ b/kernel/acct.c @@ -260,10 +260,10 @@ SYSCALL_DEFINE1(acct, const char __user *, name) return -EPERM; if (name) { - char *tmp = getname(name); + struct filename *tmp = getname(name); if (IS_ERR(tmp)) return (PTR_ERR(tmp)); - error = acct_on(tmp); + error = acct_on(tmp->name); putname(tmp); } else { struct bsd_acct_struct *acct; diff --git a/kernel/auditsc.c b/kernel/auditsc.c index d147585e9ef3..d4d82319eed5 100644 --- a/kernel/auditsc.c +++ b/kernel/auditsc.c @@ -103,28 +103,29 @@ struct audit_cap_data { * we don't let putname() free it (instead we free all of the saved * pointers at syscall exit time). * - * Further, in fs/namei.c:path_lookup() we store the inode and device. */ + * Further, in fs/namei.c:path_lookup() we store the inode and device. + */ struct audit_names { - struct list_head list; /* audit_context->names_list */ - const char *name; - unsigned long ino; - dev_t dev; - umode_t mode; - kuid_t uid; - kgid_t gid; - dev_t rdev; - u32 osid; - struct audit_cap_data fcap; - unsigned int fcap_ver; - int name_len; /* number of name's characters to log */ - unsigned char type; /* record type */ - bool name_put; /* call __putname() for this name */ + struct list_head list; /* audit_context->names_list */ + struct filename *name; + unsigned long ino; + dev_t dev; + umode_t mode; + kuid_t uid; + kgid_t gid; + dev_t rdev; + u32 osid; + struct audit_cap_data fcap; + unsigned int fcap_ver; + int name_len; /* number of name's characters to log */ + unsigned char type; /* record type */ + bool name_put; /* call __putname() for this name */ /* * This was an allocated audit_names and not from the array of * names allocated in the task audit context. Thus this name * should be freed on syscall exit */ - bool should_free; + bool should_free; }; struct audit_aux_data { @@ -996,7 +997,7 @@ static inline void audit_free_names(struct audit_context *context) context->ino_count); list_for_each_entry(n, &context->names_list, list) { printk(KERN_ERR "names[%d] = %p = %s\n", i, - n->name, n->name ?: "(null)"); + n->name, n->name->name ?: "(null)"); } dump_stack(); return; @@ -1553,7 +1554,7 @@ static void audit_log_name(struct audit_context *context, struct audit_names *n, case AUDIT_NAME_FULL: /* log the full path */ audit_log_format(ab, " name="); - audit_log_untrustedstring(ab, n->name); + audit_log_untrustedstring(ab, n->name->name); break; case 0: /* name was specified as a relative path and the @@ -1563,7 +1564,7 @@ static void audit_log_name(struct audit_context *context, struct audit_names *n, default: /* log the name's directory component */ audit_log_format(ab, " name="); - audit_log_n_untrustedstring(ab, n->name, + audit_log_n_untrustedstring(ab, n->name->name, n->name_len); } } else @@ -2026,7 +2027,7 @@ static struct audit_names *audit_alloc_name(struct audit_context *context, * Add a name to the list of audit names for this context. * Called from fs/namei.c:getname(). */ -void __audit_getname(const char *name) +void __audit_getname(struct filename *name) { struct audit_context *context = current->audit_context; struct audit_names *n; @@ -2040,6 +2041,11 @@ void __audit_getname(const char *name) return; } +#if AUDIT_DEBUG + /* The filename _must_ have a populated ->name */ + BUG_ON(!name->name); +#endif + n = audit_alloc_name(context, AUDIT_TYPE_UNKNOWN); if (!n) return; @@ -2059,7 +2065,7 @@ void __audit_getname(const char *name) * then we delay the putname until syscall exit. * Called from include/linux/fs.h:putname(). */ -void audit_putname(const char *name) +void audit_putname(struct filename *name) { struct audit_context *context = current->audit_context; @@ -2074,7 +2080,7 @@ void audit_putname(const char *name) list_for_each_entry(n, &context->names_list, list) printk(KERN_ERR "name[%d] = %p = %s\n", i, - n->name, n->name ?: "(null)"); + n->name, n->name->name ?: "(null)"); } #endif __putname(name); @@ -2088,8 +2094,8 @@ void audit_putname(const char *name) " put_count=%d\n", __FILE__, __LINE__, context->serial, context->major, - context->in_syscall, name, context->name_count, - context->put_count); + context->in_syscall, name->name, + context->name_count, context->put_count); dump_stack(); } } @@ -2152,7 +2158,7 @@ void __audit_inode(const char *name, const struct dentry *dentry, list_for_each_entry_reverse(n, &context->names_list, list) { /* does the name pointer match? */ - if (n->name != name) + if (!n->name || n->name->name != name) continue; /* match the correct record type */ @@ -2175,7 +2181,7 @@ out_alloc: return; out: if (parent) { - n->name_len = n->name ? parent_len(n->name) : AUDIT_NAME_FULL; + n->name_len = n->name ? parent_len(n->name->name) : AUDIT_NAME_FULL; n->type = AUDIT_TYPE_PARENT; } else { n->name_len = AUDIT_NAME_FULL; @@ -2220,7 +2226,7 @@ void __audit_inode_child(const struct inode *parent, continue; if (n->ino == parent->i_ino && - !audit_compare_dname_path(dname, n->name, n->name_len)) { + !audit_compare_dname_path(dname, n->name->name, n->name_len)) { found_parent = n; break; } @@ -2236,8 +2242,8 @@ void __audit_inode_child(const struct inode *parent, if (found_parent && (n->name != found_parent->name)) continue; - if (!strcmp(dname, n->name) || - !audit_compare_dname_path(dname, n->name, + if (!strcmp(dname, n->name->name) || + !audit_compare_dname_path(dname, n->name->name, found_parent ? found_parent->name_len : AUDIT_NAME_FULL)) { diff --git a/mm/swapfile.c b/mm/swapfile.c index 14e254c768fc..90d2ed591de9 100644 --- a/mm/swapfile.c +++ b/mm/swapfile.c @@ -1483,7 +1483,7 @@ SYSCALL_DEFINE1(swapoff, const char __user *, specialfile) struct file *swap_file, *victim; struct address_space *mapping; struct inode *inode; - char *pathname; + struct filename *pathname; int oom_score_adj; int i, type, prev; int err; @@ -1498,8 +1498,7 @@ SYSCALL_DEFINE1(swapoff, const char __user *, specialfile) if (IS_ERR(pathname)) goto out; - victim = filp_open(pathname, O_RDWR|O_LARGEFILE, 0); - putname(pathname); + victim = filp_open(pathname->name, O_RDWR|O_LARGEFILE, 0); err = PTR_ERR(victim); if (IS_ERR(victim)) goto out; @@ -1936,7 +1935,7 @@ static int setup_swap_map_and_extents(struct swap_info_struct *p, SYSCALL_DEFINE2(swapon, const char __user *, specialfile, int, swap_flags) { struct swap_info_struct *p; - char *name; + struct filename *name; struct file *swap_file = NULL; struct address_space *mapping; int i; @@ -1967,7 +1966,7 @@ SYSCALL_DEFINE2(swapon, const char __user *, specialfile, int, swap_flags) name = NULL; goto bad_swap; } - swap_file = filp_open(name, O_RDWR|O_LARGEFILE, 0); + swap_file = filp_open(name->name, O_RDWR|O_LARGEFILE, 0); if (IS_ERR(swap_file)) { error = PTR_ERR(swap_file); swap_file = NULL; @@ -2053,7 +2052,7 @@ SYSCALL_DEFINE2(swapon, const char __user *, specialfile, int, swap_flags) printk(KERN_INFO "Adding %uk swap on %s. " "Priority:%d extents:%d across:%lluk %s%s%s\n", - p->pages<<(PAGE_SHIFT-10), name, p->prio, + p->pages<<(PAGE_SHIFT-10), name->name, p->prio, nr_extents, (unsigned long long)span<<(PAGE_SHIFT-10), (p->flags & SWP_SOLIDSTATE) ? "SS" : "", (p->flags & SWP_DISCARDABLE) ? "D" : "", -- cgit v1.2.3 From 7ac86265dc8f665cc49d6e60a125e608cd2fca14 Mon Sep 17 00:00:00 2001 From: Jeff Layton Date: Wed, 10 Oct 2012 15:25:28 -0400 Subject: audit: allow audit code to satisfy getname requests from its names_list Currently, if we call getname() on a userland string more than once, we'll get multiple copies of the string and multiple audit_names records. Add a function that will allow the audit_names code to satisfy getname requests using info from the audit_names list, avoiding a new allocation and audit_names records. Signed-off-by: Jeff Layton Signed-off-by: Al Viro --- fs/namei.c | 4 ++++ include/linux/audit.h | 11 +++++++++++ kernel/auditsc.c | 23 +++++++++++++++++++++++ 3 files changed, 38 insertions(+) (limited to 'kernel/auditsc.c') diff --git a/fs/namei.c b/fs/namei.c index ec638d27642f..5dbc3f836934 100644 --- a/fs/namei.c +++ b/fs/namei.c @@ -130,6 +130,10 @@ getname_flags(const char __user *filename, int flags, int *empty) char *kname; int len; + result = audit_reusename(filename); + if (result) + return result; + /* FIXME: create dedicated slabcache? */ result = kzalloc(sizeof(*result), GFP_KERNEL); if (unlikely(!result)) diff --git a/include/linux/audit.h b/include/linux/audit.h index 94d29164803f..d5d7952ab7d8 100644 --- a/include/linux/audit.h +++ b/include/linux/audit.h @@ -471,6 +471,7 @@ extern void __audit_syscall_entry(int arch, int major, unsigned long a0, unsigned long a1, unsigned long a2, unsigned long a3); extern void __audit_syscall_exit(int ret_success, long ret_value); +extern struct filename *__audit_reusename(const __user char *uptr); extern void __audit_getname(struct filename *name); extern void audit_putname(struct filename *name); extern void __audit_inode(const char *name, const struct dentry *dentry, @@ -507,6 +508,12 @@ static inline void audit_syscall_exit(void *pt_regs) __audit_syscall_exit(success, return_code); } } +static inline struct filename *audit_reusename(const __user char *name) +{ + if (unlikely(!audit_dummy_context())) + return __audit_reusename(name); + return NULL; +} static inline void audit_getname(struct filename *name) { if (unlikely(!audit_dummy_context())) @@ -665,6 +672,10 @@ static inline int audit_dummy_context(void) { return 1; } +static inline struct filename *audit_reusename(const __user char *name) +{ + return NULL; +} static inline void audit_getname(struct filename *name) { } static inline void audit_putname(struct filename *name) diff --git a/kernel/auditsc.c b/kernel/auditsc.c index d4d82319eed5..521163a5d65f 100644 --- a/kernel/auditsc.c +++ b/kernel/auditsc.c @@ -2020,6 +2020,29 @@ static struct audit_names *audit_alloc_name(struct audit_context *context, return aname; } +/** + * audit_reusename - fill out filename with info from existing entry + * @uptr: userland ptr to pathname + * + * Search the audit_names list for the current audit context. If there is an + * existing entry with a matching "uptr" then return the filename + * associated with that audit_name. If not, return NULL. + */ +struct filename * +__audit_reusename(const __user char *uptr) +{ + struct audit_context *context = current->audit_context; + struct audit_names *n; + + list_for_each_entry(n, &context->names_list, list) { + if (!n->name) + continue; + if (n->name->uptr == uptr) + return n->name; + } + return NULL; +} + /** * audit_getname - add a name to the list * @name: name to add -- cgit v1.2.3 From adb5c2473d3f91526c79db972aafb20a56d3fbb3 Mon Sep 17 00:00:00 2001 From: Jeff Layton Date: Wed, 10 Oct 2012 16:43:13 -0400 Subject: audit: make audit_inode take struct filename Keep a pointer to the audit_names "slot" in struct filename. Have all of the audit_inode callers pass a struct filename ponter to audit_inode instead of a string pointer. If the aname field is already populated, then we can skip walking the list altogether and just use it directly. Signed-off-by: Jeff Layton Signed-off-by: Al Viro --- fs/namei.c | 16 +++++++--------- include/linux/audit.h | 10 ++++++---- include/linux/fs.h | 6 ++++-- ipc/mqueue.c | 4 ++-- kernel/auditsc.c | 25 +++++++++++++++++++++++-- 5 files changed, 42 insertions(+), 19 deletions(-) (limited to 'kernel/auditsc.c') diff --git a/fs/namei.c b/fs/namei.c index 6bbd8fdfb1f5..80b162b142f9 100644 --- a/fs/namei.c +++ b/fs/namei.c @@ -1999,8 +1999,7 @@ static int filename_lookup(int dfd, struct filename *name, flags | LOOKUP_REVAL, nd); if (likely(!retval)) - audit_inode(name->name, nd->path.dentry, - flags & LOOKUP_PARENT); + audit_inode(name, nd->path.dentry, flags & LOOKUP_PARENT); return retval; } @@ -2674,7 +2673,6 @@ static int do_last(struct nameidata *nd, struct path *path, struct path save_parent = { .dentry = NULL, .mnt = NULL }; bool retried = false; int error; - const char *pathname = name->name; nd->flags &= ~LOOKUP_PARENT; nd->flags |= op->intent; @@ -2690,7 +2688,7 @@ static int do_last(struct nameidata *nd, struct path *path, error = complete_walk(nd); if (error) return error; - audit_inode(pathname, nd->path.dentry, 0); + audit_inode(name, nd->path.dentry, 0); if (open_flag & O_CREAT) { error = -EISDIR; goto out; @@ -2700,7 +2698,7 @@ static int do_last(struct nameidata *nd, struct path *path, error = complete_walk(nd); if (error) return error; - audit_inode(pathname, dir, 0); + audit_inode(name, dir, 0); goto finish_open; } @@ -2729,7 +2727,7 @@ static int do_last(struct nameidata *nd, struct path *path, if (error) return error; - audit_inode(pathname, dir, 0); + audit_inode(name, dir, 0); error = -EISDIR; /* trailing slashes? */ if (nd->last.name[nd->last.len]) @@ -2759,7 +2757,7 @@ retry_lookup: !S_ISREG(file->f_path.dentry->d_inode->i_mode)) will_truncate = false; - audit_inode(pathname, file->f_path.dentry, 0); + audit_inode(name, file->f_path.dentry, 0); goto opened; } @@ -2776,7 +2774,7 @@ retry_lookup: * create/update audit record if it already exists. */ if (path->dentry->d_inode) - audit_inode(pathname, path->dentry, 0); + audit_inode(name, path->dentry, 0); /* * If atomic_open() acquired write access it is dropped now due to @@ -2841,7 +2839,7 @@ finish_lookup: error = -ENOTDIR; if ((nd->flags & LOOKUP_DIRECTORY) && !nd->inode->i_op->lookup) goto out; - audit_inode(pathname, nd->path.dentry, 0); + audit_inode(name, nd->path.dentry, 0); finish_open: if (!S_ISREG(nd->inode->i_mode)) will_truncate = false; diff --git a/include/linux/audit.h b/include/linux/audit.h index d5d7952ab7d8..e5884f950b4b 100644 --- a/include/linux/audit.h +++ b/include/linux/audit.h @@ -474,7 +474,7 @@ extern void __audit_syscall_exit(int ret_success, long ret_value); extern struct filename *__audit_reusename(const __user char *uptr); extern void __audit_getname(struct filename *name); extern void audit_putname(struct filename *name); -extern void __audit_inode(const char *name, const struct dentry *dentry, +extern void __audit_inode(struct filename *name, const struct dentry *dentry, unsigned int parent); extern void __audit_inode_child(const struct inode *parent, const struct dentry *dentry, @@ -519,7 +519,7 @@ static inline void audit_getname(struct filename *name) if (unlikely(!audit_dummy_context())) __audit_getname(name); } -static inline void audit_inode(const char *name, const struct dentry *dentry, +static inline void audit_inode(struct filename *name, const struct dentry *dentry, unsigned int parent) { if (unlikely(!audit_dummy_context())) __audit_inode(name, dentry, parent); @@ -680,14 +680,16 @@ static inline void audit_getname(struct filename *name) { } static inline void audit_putname(struct filename *name) { } -static inline void __audit_inode(const char *name, const struct dentry *dentry, +static inline void __audit_inode(struct filename *name, + const struct dentry *dentry, unsigned int parent) { } static inline void __audit_inode_child(const struct inode *parent, const struct dentry *dentry, const unsigned char type) { } -static inline void audit_inode(const char *name, const struct dentry *dentry, +static inline void audit_inode(struct filename *name, + const struct dentry *dentry, unsigned int parent) { } static inline void audit_inode_child(const struct inode *parent, diff --git a/include/linux/fs.h b/include/linux/fs.h index b6b10e7f0ac0..4aa7160a51ce 100644 --- a/include/linux/fs.h +++ b/include/linux/fs.h @@ -2196,9 +2196,11 @@ static inline int break_lease(struct inode *inode, unsigned int mode) #endif /* CONFIG_FILE_LOCKING */ /* fs/open.c */ +struct audit_names; struct filename { - const char *name; /* pointer to actual string */ - const __user char *uptr; /* original userland pointer */ + const char *name; /* pointer to actual string */ + const __user char *uptr; /* original userland pointer */ + struct audit_names *aname; }; extern int do_truncate(struct dentry *, loff_t start, unsigned int time_attrs, diff --git a/ipc/mqueue.c b/ipc/mqueue.c index 6c5d9dcc9030..71a3ca18c873 100644 --- a/ipc/mqueue.c +++ b/ipc/mqueue.c @@ -804,7 +804,7 @@ SYSCALL_DEFINE4(mq_open, const char __user *, u_name, int, oflag, umode_t, mode, if (oflag & O_CREAT) { if (path.dentry->d_inode) { /* entry already exists */ - audit_inode(name->name, path.dentry, 0); + audit_inode(name, path.dentry, 0); if (oflag & O_EXCL) { error = -EEXIST; goto out; @@ -824,7 +824,7 @@ SYSCALL_DEFINE4(mq_open, const char __user *, u_name, int, oflag, umode_t, mode, error = -ENOENT; goto out; } - audit_inode(name->name, path.dentry, 0); + audit_inode(name, path.dentry, 0); filp = do_open(&path, oflag); } diff --git a/kernel/auditsc.c b/kernel/auditsc.c index 521163a5d65f..2f186ed80c40 100644 --- a/kernel/auditsc.c +++ b/kernel/auditsc.c @@ -2076,6 +2076,7 @@ void __audit_getname(struct filename *name) n->name = name; n->name_len = AUDIT_NAME_FULL; n->name_put = true; + name->aname = n; if (!context->pwd.dentry) get_fs_pwd(current->fs, &context->pwd); @@ -2166,7 +2167,7 @@ static void audit_copy_inode(struct audit_names *name, const struct dentry *dent * @dentry: dentry being audited * @parent: does this dentry represent the parent? */ -void __audit_inode(const char *name, const struct dentry *dentry, +void __audit_inode(struct filename *name, const struct dentry *dentry, unsigned int parent) { struct audit_context *context = current->audit_context; @@ -2179,9 +2180,29 @@ void __audit_inode(const char *name, const struct dentry *dentry, if (!name) goto out_alloc; +#if AUDIT_DEBUG + /* The struct filename _must_ have a populated ->name */ + BUG_ON(!name->name); +#endif + /* + * If we have a pointer to an audit_names entry already, then we can + * just use it directly if the type is correct. + */ + n = name->aname; + if (n) { + if (parent) { + if (n->type == AUDIT_TYPE_PARENT || + n->type == AUDIT_TYPE_UNKNOWN) + goto out; + } else { + if (n->type != AUDIT_TYPE_PARENT) + goto out; + } + } + list_for_each_entry_reverse(n, &context->names_list, list) { /* does the name pointer match? */ - if (!n->name || n->name->name != name) + if (!n->name || n->name->name != name->name) continue; /* match the correct record type */ -- cgit v1.2.3