On Fri, Apr 21, 2023 at 10:42:57AM -0700, Casey Schaufler wrote: > Add hooks for setselfattr and getselfattr. These hooks are not very > different from their setprocattr and getprocattr equivalents, and > much of the code is shared. > > Signed-off-by: Casey Schaufler <casey@xxxxxxxxxxxxxxxx> > Cc: John Johansen <john.johansen@xxxxxxxxxxxxx> > --- > security/apparmor/include/procattr.h | 2 +- > security/apparmor/lsm.c | 96 ++++++++++++++++++++++++++-- > security/apparmor/procattr.c | 11 +++- > 3 files changed, 99 insertions(+), 10 deletions(-) > > diff --git a/security/apparmor/include/procattr.h b/security/apparmor/include/procattr.h > index 31689437e0e1..03dbfdb2f2c0 100644 > --- a/security/apparmor/include/procattr.h > +++ b/security/apparmor/include/procattr.h > @@ -11,7 +11,7 @@ > #ifndef __AA_PROCATTR_H > #define __AA_PROCATTR_H > > -int aa_getprocattr(struct aa_label *label, char **string); > +int aa_getprocattr(struct aa_label *label, char **string, bool newline); > int aa_setprocattr_changehat(char *args, size_t size, int flags); > > #endif /* __AA_PROCATTR_H */ > diff --git a/security/apparmor/lsm.c b/security/apparmor/lsm.c > index ce6ccb7e06ec..bdaa8bac0404 100644 > --- a/security/apparmor/lsm.c > +++ b/security/apparmor/lsm.c > @@ -630,6 +630,45 @@ static int apparmor_sb_pivotroot(const struct path *old_path, > return error; > } > > +static int apparmor_getselfattr(unsigned int __user attr, > + struct lsm_ctx __user *lx, size_t *size, > + u32 __user flags) > +{ > + int error = -ENOENT; > + struct aa_task_ctx *ctx = task_ctx(current); > + struct aa_label *label = NULL; > + size_t total_len; > + char *value; > + > + if (attr == LSM_ATTR_CURRENT) > + label = aa_get_newest_label(cred_label(current_cred())); > + else if (attr == LSM_ATTR_PREV && ctx->previous) > + label = aa_get_newest_label(ctx->previous); > + else if (attr == LSM_ATTR_EXEC && ctx->onexec) > + label = aa_get_newest_label(ctx->onexec); > + else > + error = -EOPNOTSUPP; Is "-EOPNOTSUPP" correct for LSM_ATTR_PREV when !ctx->previous? That seems like it should be -EINVAL? I think this would be better served as a switch statement. > + > + if (label) { > + error = aa_getprocattr(label, &value, false); > + if (error > 0) { > + total_len = ALIGN(error + sizeof(*ctx), 8); > + if (total_len > *size) > + error = -E2BIG; > + else > + lsm_fill_user_ctx(lx, value, error, > + LSM_ID_APPARMOR, 0); > + } > + } > + > + aa_put_label(label); > + > + *size = total_len; > + if (error > 0) > + return 1; > + return error; > +} > + > static int apparmor_getprocattr(struct task_struct *task, const char *name, > char **value) > { > @@ -649,7 +688,7 @@ static int apparmor_getprocattr(struct task_struct *task, const char *name, > error = -EINVAL; > > if (label) > - error = aa_getprocattr(label, value); > + error = aa_getprocattr(label, value, true); > > aa_put_label(label); > put_cred(cred); > @@ -657,8 +696,7 @@ static int apparmor_getprocattr(struct task_struct *task, const char *name, > return error; > } > > -static int apparmor_setprocattr(const char *name, void *value, > - size_t size) > +static int do_setattr(u64 attr, void *value, size_t size) > { > char *command, *largs = NULL, *args = value; > size_t arg_size; > @@ -689,7 +727,7 @@ static int apparmor_setprocattr(const char *name, void *value, > goto out; > > arg_size = size - (args - (largs ? largs : (char *) value)); > - if (strcmp(name, "current") == 0) { > + if (attr == LSM_ATTR_CURRENT) { > if (strcmp(command, "changehat") == 0) { > error = aa_setprocattr_changehat(args, arg_size, > AA_CHANGE_NOFLAGS); > @@ -704,7 +742,7 @@ static int apparmor_setprocattr(const char *name, void *value, > error = aa_change_profile(args, AA_CHANGE_STACK); > } else > goto fail; > - } else if (strcmp(name, "exec") == 0) { > + } else if (attr == LSM_ATTR_EXEC) { > if (strcmp(command, "exec") == 0) > error = aa_change_profile(args, AA_CHANGE_ONEXEC); > else if (strcmp(command, "stack") == 0) > @@ -724,13 +762,57 @@ static int apparmor_setprocattr(const char *name, void *value, > > fail: > aad(&sa)->label = begin_current_label_crit_section(); > - aad(&sa)->info = name; > + if (attr == LSM_ATTR_CURRENT) > + aad(&sa)->info = "current"; > + else if (attr == LSM_ATTR_EXEC) > + aad(&sa)->info = "exec"; > + else > + aad(&sa)->info = "invalid"; > aad(&sa)->error = error = -EINVAL; > aa_audit_msg(AUDIT_APPARMOR_DENIED, &sa, NULL); > end_current_label_crit_section(aad(&sa)->label); > goto out; > } > > +static int apparmor_setselfattr(unsigned int __user attr, > + struct lsm_ctx __user *ctx, size_t __user size, > + u32 __user flags) > +{ > + struct lsm_ctx *lctx; > + void *context; > + int rc; > + > + if (attr != LSM_ATTR_CURRENT && attr != LSM_ATTR_EXEC) > + return -EOPNOTSUPP; > + > + context = kmalloc(size, GFP_KERNEL); > + if (context == NULL) > + return -ENOMEM; > + > + lctx = (struct lsm_ctx *)context; > + if (copy_from_user(context, ctx, size)) > + rc = -EFAULT; > + else if (lctx->ctx_len > size) > + rc = -EINVAL; > + else > + rc = do_setattr(attr, lctx + 1, lctx->ctx_len); > + > + kfree(context); > + if (rc > 0) > + return 0; > + return rc; > +} > + > +static int apparmor_setprocattr(const char *name, void *value, > + size_t size) > +{ > + int attr = lsm_name_to_attr(name); > + > + if (attr) > + return do_setattr(attr, value, size); > + return -EINVAL; > +} > + > /** > * apparmor_bprm_committing_creds - do task cleanup on committing new creds > * @bprm: binprm for the exec (NOT NULL) > @@ -1253,6 +1335,8 @@ static struct security_hook_list apparmor_hooks[] __lsm_ro_after_init = { > LSM_HOOK_INIT(file_lock, apparmor_file_lock), > LSM_HOOK_INIT(file_truncate, apparmor_file_truncate), > > + LSM_HOOK_INIT(getselfattr, apparmor_getselfattr), > + LSM_HOOK_INIT(setselfattr, apparmor_setselfattr), > LSM_HOOK_INIT(getprocattr, apparmor_getprocattr), > LSM_HOOK_INIT(setprocattr, apparmor_setprocattr), > > diff --git a/security/apparmor/procattr.c b/security/apparmor/procattr.c > index 197d41f9c32b..196f319aa3b2 100644 > --- a/security/apparmor/procattr.c > +++ b/security/apparmor/procattr.c > @@ -20,6 +20,7 @@ > * aa_getprocattr - Return the label information for @label > * @label: the label to print label info about (NOT NULL) > * @string: Returns - string containing the label info (NOT NULL) > + * @newline: indicates that a newline should be added > * > * Requires: label != NULL && string != NULL > * > @@ -27,7 +28,7 @@ > * > * Returns: size of string placed in @string else error code on failure > */ > -int aa_getprocattr(struct aa_label *label, char **string) > +int aa_getprocattr(struct aa_label *label, char **string, bool newline) > { > struct aa_ns *ns = labels_ns(label); > struct aa_ns *current_ns = aa_get_current_ns(); > @@ -57,10 +58,14 @@ int aa_getprocattr(struct aa_label *label, char **string) > return len; > } > > - (*string)[len] = '\n'; > - (*string)[len + 1] = 0; > + if (newline) > + (*string)[len++] = '\n'; > + (*string)[len] = 0; > > aa_put_ns(current_ns); > + > + if (newline) > + return len; > return len + 1; > } This is returning the count including trailing %NUL, yes? Why is this not always just "return len"? i.e.: if (newline) (*string)[len++] = '\n'; (*string)[len++] = 0; aa_put_ns(current_ns); return len; > > -- > 2.39.2 > -- Kees Cook