[apparmor] [PATCH 24/36] apparmor: add abilitiy to print labels and update interface to use
Seth Arnold
seth.arnold at canonical.com
Wed May 29 07:13:25 UTC 2013
On Wed, May 01, 2013 at 02:31:09PM -0700, John Johansen wrote:
A few comments in line..
> +bool aa_update_label_name(struct aa_namespace *ns, struct aa_label *label,
> + gfp_t gfp);
> +
> +int aa_profile_snprint(char *str, size_t size, struct aa_namespace *ns,
> + struct aa_profile *profile, bool mode);
> +int aa_label_snprint(char *str, size_t size, struct aa_namespace *ns,
> + struct aa_label *label, bool mode);
> +int aa_label_asprint(char **strp, struct aa_namespace *ns,
> + struct aa_label *label, bool mode, gfp_t gfp);
> +int aa_label_acntsprint(char __counted **strp, struct aa_namespace *ns,
> + struct aa_label *label, bool mode, gfp_t gfp);
> +void aa_label_audit(struct audit_buffer *ab, struct aa_namespace *ns,
> + struct aa_label *label, bool mode, gfp_t gfp);
> +void aa_label_seq_print(struct seq_file *f, struct aa_namespace *ns,
> + struct aa_label *label, bool mode, gfp_t gfp);
> +void aa_label_printk(struct aa_namespace *ns, struct aa_label *label,
> + bool mode, gfp_t gfp);
> +struct aa_label *aa_label_parse(struct aa_namespace *base, char *str,
> + gfp_t gfp);
Would you mind renaming the functions to _printf() where appropriate?
These names don't really speak to me.. (Also, are all those really
needed? :)
> struct aa_profile *aa_match_profile(struct aa_namespace *ns, const char *name);
>
> ssize_t aa_replace_profiles(void *udata, size_t size, bool noreplace);
> diff --git a/security/apparmor/label.c b/security/apparmor/label.c
> index 0c67a80..338b6bd 100644
> --- a/security/apparmor/label.c
> +++ b/security/apparmor/label.c
> @@ -517,6 +517,535 @@ static struct aa_label *__aa_label_insert(struct aa_labelset *ls,
> }
>
> /**
> + * aa_update_label_name - update a label to have a stored name
> + * @ns: ns being viewed from (NOT NULL)
> + * @label: label to update (NOT NULL)
> + * @gfp: type of memory allocation
> + *
> + * Requires: labels_set(label) not locked in caller
> + *
> + * note: only updates the label name if it does not have a name already
> + * and if it is in the labelset
Why exactly the constraint on labelset? (Is that 1:1 with FLAG_IN_TREE?)
> + */
> +bool aa_update_label_name(struct aa_namespace *ns, struct aa_label *label,
> + gfp_t gfp)
> +{
> + struct aa_labelset *ls;
> + unsigned long flags;
> + char __counted *name;
> + bool res = false;
> +
> + AA_WARN(!ns);
> + AA_WARN(!label);
> +
> + if (label->hname || !(label->flags & FLAG_IN_TREE) ||
> + labels_ns(label) != ns)
> + return res;
> +
> + if (aa_label_acntsprint(&name, ns, label, false, gfp) == -1)
> + return res;
> +
> + ls = labels_set(label);
> + write_lock_irqsave(&ls->lock, flags);
> + if (!label->hname && label->flags & FLAG_IN_TREE) {
> + label->hname = name;
> + res = true;
> + } else
> + aa_put_str(name);
> + write_unlock_irqrestore(&ls->lock, flags);
> +
> + return res;
> +}
> +
> +/* cached label name is present and visible
> + * @label->hname only exists if label is namespace hierachical */
> +static inline bool label_name_visible(struct aa_namespace *ns,
> + struct aa_label *label)
> +{
> + if (label->hname && labels_ns(label) == ns)
> + return true;
> +
> + return false;
> +}
This doesn't feel hierarchical enough, if you know what I mean:
it only checks the namespace of the last profile on the label,
rather than checking all the labels..
It also looks like it intends to enforce visibility only within a single
specific ns -- not at all hierarchical. (No visibility to parents _or_
children.) Right?
> +/* helper macro for snprint routines */
> +#define update_for_len(total, len, size, str) \
> +do { \
> + if (len < 0) \
> + return len; \
I don't think any callers that set 'len' will ever set len < 0. Or, if
they will, I didn't see it documented. It -is- good defense, I guess,
but perhaps a warning would be nice?
> + total += len; \
> + len = min(len, size); \
> + size -= len; \
> + str += len; \
> +} while (0)
> +
> +/**
> + * aa_modename_snprint - print the mode name of a profile or label to a buffer
> + * @str: buffer to write to (MAY BE NULL if @size == 0)
> + * @size: size of buffer
> + * @ns: namespace profile is being viewed from (NOT NULL)
> + * @label: label to print the mode of (NOT NULL)
> + *
> + * Returns: size of name written or would be written if larger than
> + * available buffer
> + *
> + * Note: will print every mode name visible (mode1)(mode2)(mode3)
> + * this is likely not what is desired for most interfaces
> + * use aa_mode_snprint to get the standard mode format
> + */
> +static int aa_modename_snprint(char *str, size_t size, struct aa_namespace *ns,
> + struct aa_label *label)
> +{
> + struct aa_profile *profile;
> + int i, total = 0;
> + size_t len;
> +
> + label_for_each(i, label, profile) {
> + const char *modestr;
> + if (!aa_ns_visible(ns, profile->ns))
> + continue;
> + /* no mode for 'unconfined' */
> + if (profile_unconfined(profile) &&
> + profile == profile->ns->unconfined)
> + break;
> + modestr = aa_profile_mode_names[profile->mode];
> + len = snprintf(str, size, "(%s)", modestr);
> + update_for_len(total, len, size, str);
> + }
> +
> + return total;
> +}
> +
> +/**
> + * aa_modechr_snprint - print the mode chr of a profile or labels to a buffer
> + * @str: buffer to write to (MAY BE NULL if @size == 0)
> + * @size: size of buffer
> + * @ns: namespace profile is being viewed from (NOT NULL)
> + * @label: label to print the mode chr of (NOT NULL)
> + *
> + * Returns: size of mode string written or would be written if larger than
> + * available buffer
> + *
> + * Note: will print the chr of every visible profile (123)
> + * this is likely not what is desired for most interfaces
> + * use aa_mode_snprint to get the standard mode format
> + */
> +static int aa_modechr_snprint(char *str, size_t size, struct aa_namespace *ns,
> + struct aa_label *label)
> +{
> + struct aa_profile *profile;
> + int i, total = 0;
> + size_t len;
> +
> + len = snprintf(str, size, "(");
> + update_for_len(total, len, size, str);
> + label_for_each(i, label, profile) {
> + const char *modestr;
> + if (!aa_ns_visible(ns, profile->ns))
> + continue;
> + modestr = aa_profile_mode_names[profile->mode];
> + /* just the first char of the modestr */
> + len = snprintf(str, size, "%c", *modestr);
> + update_for_len(total, len, size, str);
> + }
> + len = snprintf(str, size, ")");
> + update_for_len(total, len, size, str);
> +
> + return total;
> +}
> +
> +/**
> + * aa_mode_snprint - print the mode of a profile or label to a buffer
> + * @str: buffer to write to (MAY BE NULL if @size == 0)
> + * @size: size of buffer
> + * @ns: namespace profile is being viewed from (NOT NULL)
> + * @label: label to print the mode of (NOT NULL)
> + * @count: number of label entries to be printed (<= 0 if unknown)
> + *
> + * Returns: size of name written or would be written if larger than
> + * available buffer
> + *
> + * Note: dynamically switches between mode name, and mode char format as
> + * appropriate
> + * will not print anything if the label is not visible
> + */
> +static int aa_mode_snprint(char *str, size_t size, struct aa_namespace *ns,
> + struct aa_label *label, int count)
> +{
> + struct aa_profile *profile;
> + int i;
> +
> + if (count <= 0) {
> + count = 0;
> + label_for_each(i, label, profile) {
> + if (aa_ns_visible(ns, profile->ns))
> + count++;
> + }
> + }
> +
> + if (count == 0)
> + return 0;
> +
> + if (count == 1)
> + return aa_modename_snprint(str, size, ns, label);
> +
> + return aa_modechr_snprint(str, size, ns, label);
> +}
> +
> +/**
> + * aa_snprint_profile - print a profile name to a buffer
> + * @str: buffer to write to. (MAY BE NULL if @size == 0)
> + * @size: size of buffer
> + * @ns: namespace profile is being viewed from (NOT NULL)
> + * @profile: profile to view (NOT NULL)
> + * @mode: whether to include the mode string
> + *
> + * Returns: size of name written or would be written if larger than
> + * available buffer
> + *
> + * Note: will not print anything if the profile is not visible
> + */
> +int aa_profile_snprint(char *str, size_t size, struct aa_namespace *ns,
> + struct aa_profile *profile, bool mode)
> +{
> + const char *ns_name = aa_ns_name(ns, profile->ns);
> +
> + AA_WARN(!str && size != 0);
> + AA_WARN(!ns);
> + AA_WARN(!profile);
> +
> + if (!ns_name)
> + return 0;
> +
> + if (mode && profile != profile->ns->unconfined) {
> + const char *modestr = aa_profile_mode_names[profile->mode];
> + if (strlen(ns_name))
> + return snprintf(str, size, ":%s://%s (%s)", ns_name,
> + profile->base.hname, modestr);
> + return snprintf(str, size, "%s (%s)", profile->base.hname,
> + modestr);
> + }
> +
> + if (strlen(ns_name))
> + return snprintf(str, size, ":%s://%s", ns_name,
> + profile->base.hname);
> + return snprintf(str, size, "%s", profile->base.hname);
> +}
> +
> +/**
> + * aa_label_snprint - print a label name to a string buffer
> + * @str: buffer to write to. (MAY BE NULL if @size == 0)
> + * @size: size of buffer
> + * @ns: namespace profile is being viewed from (NOT NULL)
> + * @label: label to view (NOT NULL)
> + * @mode: whether to include the mode string
> + *
> + * Returns: size of name written or would be written if larger than
> + * available buffer
> + *
> + * Note: labels do not have to be strictly hierarchical to the ns as
> + * objects may be shared across different namespaces and thus
> + * pickup labeling from each ns. If a particular part of the
> + * label is not visible it will just be excluded. And if none
> + * of the label is visble "---" will be used.
Where's the "---" bit implemented?
Also "visble".
> + */
> +int aa_label_snprint(char *str, size_t size, struct aa_namespace *ns,
> + struct aa_label *label, bool mode)
> +{
> + struct aa_profile *profile;
> + int i, count = 0, total = 0;
> + size_t len;
> +
> + AA_WARN(!str && size != 0);
> + AA_WARN(!ns);
> + AA_WARN(!label);
> +
> + label_for_each(i, label, profile) {
> + if (aa_ns_visible(ns, profile->ns)) {
> + if (count > 0) {
> + len = snprintf(str, size, "//&");
> + update_for_len(total, len, size, str);
> + }
> + len = aa_profile_snprint(str, size, ns, profile, false);
> + update_for_len(total, len, size, str);
> + count++;
> + }
> + }
> +
> + if (count == 0)
> + return snprintf(str, size, aa_hidden_ns_name);
> +
> + /* count == 1 && ... is for backwards compat where the mode
> + * is not displayed for 'unconfined' in the current ns
> + */
> + if (mode &&
> + !(count == 1 && labels_ns(label) == ns &&
> + labels_profile(label) == ns->unconfined)) {
> + len = snprintf(str, size, " ");
> + update_for_len(total, len, size, str);
> + len = aa_mode_snprint(str, size, ns, label, count);
> + update_for_len(total, len, size, str);
> + }
> +
> + return total;
> +}
> +#undef update_for_len
> +
> +/**
> + * aa_label_asprint - allocate a string buffer and print label into it
> + * @strp: buffer to write to. (MAY BE NULL if @size == 0)
@size isn't a parameter in this function.. strp is allocated here,
that'd be nice to report too.
> + * @ns: namespace profile is being viewed from (NOT NULL)
> + * @label: label to view (NOT NULL)
> + * @mode: whether to include the mode string
> + * @gfp: kernel memory allocation type
> + *
> + * Returns: size of name written or would be written if larger than
> + * available buffer
> + */
> +int aa_label_asprint(char **strp, struct aa_namespace *ns,
> + struct aa_label *label, bool mode, gfp_t gfp)
> +{
> + int size;
> +
> + AA_WARN(!strp);
> + AA_WARN(!ns);
> + AA_WARN(!label);
> +
> + size = aa_label_snprint(NULL, 0, ns, label, mode);
> + if (size < 0)
> + return size;
> +
> + *strp = kmalloc(size + 1, gfp);
> + if (!*strp)
> + return -ENOMEM;
> + return aa_label_snprint(*strp, size + 1, ns, label, mode);
> +}
> +
> +/**
> + * aa_label_acntsprint - allocate a __counted string buffer and print label
> + * @strp: buffer to write to. (MAY BE NULL if @size == 0)
> + * @ns: namespace profile is being viewed from (NOT NULL)
> + * @label: label to view (NOT NULL)
> + * @mode: whether to include the mode string
> + * @gfp: kernel memory allocation type
> + *
> + * Returns: size of name written or would be written if larger than
> + * available buffer
> + */
> +int aa_label_acntsprint(char __counted **strp, struct aa_namespace *ns,
> + struct aa_label *label, bool mode, gfp_t gfp)
> +{
> + int size;
> +
> + AA_WARN(!strp);
> + AA_WARN(!ns);
> + AA_WARN(!label);
> +
> + size = aa_label_snprint(NULL, 0, ns, label, mode);
> + if (size < 0)
> + return size;
> +
> + *strp = aa_str_alloc(size + 1, gfp);
> + if (!*strp)
> + return -ENOMEM;
> + return aa_label_snprint(*strp, size + 1, ns, label, mode);
> +}
> +
> +
> +void aa_label_audit(struct audit_buffer *ab, struct aa_namespace *ns,
> + struct aa_label *label, bool mode, gfp_t gfp)
> +{
> + const char *str;
> + char *name = NULL;
> + int len;
> +
> + AA_WARN(!ab);
> + AA_WARN(!ns);
> + AA_WARN(!label);
> +
> + if (label_name_visible(ns, label)) {
> + str = (char *) label->hname;
> + len = strlen(str);
> + } else {
> + labelstats_inc(audit_name_alloc);
> + len = aa_label_asprint(&name, ns, label, mode, gfp);
> + if (len == -1) {
> + labelstats_inc(audit_name_fail);
> + printk("<label error>");
printk is probably the wrong choice here.
> + return;
> + }
> + str = name;
> + }
> +
> + if (audit_string_contains_control(str, len))
> + audit_log_n_hex(ab, str, len);
> + else
> + audit_log_n_string(ab, str, len);
> +
> + kfree(name);
> +}
> +
> +void aa_label_seq_print(struct seq_file *f, struct aa_namespace *ns,
> + struct aa_label *label, bool mode, gfp_t gfp)
> +{
> + AA_WARN(!f);
> + AA_WARN(!ns);
> + AA_WARN(!label);
> +
> + if (!label_name_visible(ns, label)) {
> + char *str;
> + int len;
> +
> + labelstats_inc(seq_print_name_alloc);
> + len = aa_label_asprint(&str, ns, label, mode, gfp);
> + if (len == -1) {
> + labelstats_inc(seq_print_name_fail);
> + printk("<label error>");
printk is probably wrong here, as well.
> + return;
> + }
> + seq_printf(f, "%s", str);
> + kfree(str);
> + } else
> + seq_printf(f, "%s", label->hname);
> +}
> +
> +void aa_label_printk(struct aa_namespace *ns, struct aa_label *label, bool mode,
> + gfp_t gfp)
> +{
> + char *str;
> + int len;
> +
> + AA_WARN(!ns);
> + AA_WARN(!label);
> +
> + if (!label_name_visible(ns, label)) {
> + labelstats_inc(printk_name_alloc);
> + len = aa_label_asprint(&str, ns, label, mode, gfp);
> + if (len == -1) {
> + labelstats_inc(printk_name_fail);
> + printk("<label error>");
Here we go :) though it does read oddly. I'd kind of rather see
something like:
printk("%s", foo ? str : "<label error>");
but maybe that'd be too ugly to work into the flow.
> + return;
> + }
> + printk("%s", str);
> + kfree(str);
> + } else
> + printk("%s", label->hname);
> +}
> +
> +
> +static int label_count_str_entries(const char *str)
> +{
> + const char *split;
> + int count = 1;
> +
> + AA_WARN(!str);
> +
> + for (split = strstr(str, "//&"); split; split = strstr(str, "//&")) {
> + count++;
> + str = split + 3;
> + }
> +
> + return count;
> +}
> +
> +/**
> + * aa_sort_and_merge_profiles - canonical sort and merge a list of profiles
> + * @n: number of refcounted profiles in the list (@n > 0)
> + * @ps: list of profiles to sort and merge
> + *
> + * Returns: the number of duplicates eliminated == references put
> + */
> +static int aa_sort_and_merge_profiles(int n, struct aa_profile **ps)
> +{
> + int i, dups = 0;
> +
> + AA_WARN(n < 1);
> + AA_WARN(!ps);
> +
> + /* label lists are usually small so just use insertion sort */
> + for (i = 1; i < n; i++) {
> + struct aa_profile *tmp = ps[i];
> + int pos, j;
> +
> + for (pos = i - 1 - dups; pos >= 0; pos--) {
> + int res = profile_cmp(ps[pos], tmp);
> + if (res == 0) {
> + aa_put_profile(tmp);
> + dups++;
> + continue;
> + } else if (res < 0)
> + break;
> + }
> + pos++;
> +
> + for (j = i - dups; j > pos; j--)
> + ps[j] = ps[j - 1];
> +
> + ps[pos] = tmp;
> + }
> +
> + return dups;
> +}
I'm a touch surprised the need for merging would arise; is it due to
rename replace loads?
> +
> +/**
> + * aa_label_parse - parse, validate and convert a text string to a label
> + * @base: base namespace to use for lookups (NOT NULL)
> + * @str: null terminated text string (NOT NULL)
> + * @gfp: allocation type
> + *
> + * Returns: the matching refcounted label if present
> + * else ERRPTR
> + */
> +struct aa_label *aa_label_parse(struct aa_namespace *base, char *str, gfp_t gfp)
> +{
> + struct aa_profile *profile;
> + struct aa_label *l, *label;
> + int i, len, unconf;
> + char *split;
> +
> + AA_WARN(!base);
> + AA_WARN(!str);
> +
> + len = label_count_str_entries(str);
> + label = aa_label_alloc(len, gfp);
> + if (!label)
> + return ERR_PTR(-ENOMEM);
> +
> + for (split = strstr(str, "//&"), i = 0; split && i < len; i++) {
> + *split = 0;
> + label->ent[i] = aa_fqlookupn_profile(base, str, split - str);
> + if (!label->ent[i])
> + goto fail;
> + str = split + 3;
> + split = strstr(str, "//&");
> + }
> + label->ent[i] = aa_fqlookupn_profile(base, str, strlen(str));
> + if (!label->ent[i])
> + goto fail;
> +
> + i = aa_sort_and_merge_profiles(len, &label->ent[0]);
> + label->size -= i;
> +
> + unconf = 1;
> + label_for_each(i, label, profile) {
> + if (!profile_unconfined(profile)) {
> + unconf = 0;
> + break;
> + }
> + }
> +
> + if (unconf)
> + label->flags = FLAG_UNCONFINED;
> +
> + l = aa_label_find(labels_set(label), label);
> + aa_put_label(label);
> + return l;
> +
> +fail:
> + aa_label_free(label);
> + return ERR_PTR(-ENOENT);
> +}
> +/**
> * aa_label_insert - insert label @l into @ls or return existing label
> * @ls - labelset to insert @l into
> * @l - label to insert
> diff --git a/security/apparmor/policy.c b/security/apparmor/policy.c
> index d77a5c5..331bd02 100644
> --- a/security/apparmor/policy.c
> +++ b/security/apparmor/policy.c
> @@ -93,6 +93,9 @@
> /* root profile namespace */
> struct aa_namespace *root_ns;
>
> +/* Note: mode names must be unique in the first character because of
> + * modechrs used to print modes on compound labels on some interfaces
> + */
> const char *const aa_profile_mode_names[] = {
> "enforce",
> "complain",
> @@ -897,6 +900,25 @@ struct aa_profile *aa_lookup_profile(struct aa_namespace *ns, const char *hname)
> return aa_lookupn_profile(ns, hname, strlen(hname));
> }
>
> +struct aa_profile *aa_fqlookupn_profile(struct aa_namespace *base, char *fqname,
> + int n)
> +{
> + struct aa_profile *profile;
> + struct aa_namespace *ns;
> + char *name, *ns_name;
> +
> + name = aa_split_fqname(fqname, &ns_name);
> + if (ns_name) {
> + ns = aa_find_namespace(base, ns_name);
> + if (!ns)
> + return NULL;
> + } else
> + ns = aa_get_namespace(base);
> + profile = aa_lookupn_profile(ns, name, n - (name - fqname));
> + aa_put_namespace(ns);
> +
> + return profile;
> +}
If 'n' points somewhere within the namespace rather than the profile,
how's this thing handle it? Could we enforce sane values for 'n' here?
> /**
> * replacement_allowed - test to see if replacement is allowed
> diff --git a/security/apparmor/procattr.c b/security/apparmor/procattr.c
> index 82666b0..515c5e2 100644
> --- a/security/apparmor/procattr.c
> +++ b/security/apparmor/procattr.c
> @@ -35,50 +35,27 @@
> */
> int aa_getprocattr(struct aa_label *label, char **string)
> {
> struct aa_namespace *ns = labels_ns(label);
> struct aa_namespace *current_ns = labels_ns(__aa_current_label());
> + int len;
>
> + if (!aa_ns_visible(current_ns, ns))
> + return -EACCES;
There's some funny indenting here.. maybe it's just funny because I
deleted all the - lines to read this here...
> + len = aa_label_snprint(NULL, 0, current_ns, label, true);
> + if (len < 0)
> + return len;
Again, I don't think this condition will ever be true; I think it'd be
worth throwing in a warning.
> + *string = kmalloc(len + 2, GFP_KERNEL);
> + if (!*string)
> return -ENOMEM;
>
> + len = aa_label_snprint(*string, len + 2, current_ns, label, true);
> + if (len < 0)
> + return len;
> + (*string)[len] = '\n';
> + (*string)[len + 1] = 0;
> +
> + return len + 1;
> }
Thanks John
-------------- next part --------------
A non-text attachment was scrubbed...
Name: signature.asc
Type: application/pgp-signature
Size: 490 bytes
Desc: Digital signature
URL: <https://lists.ubuntu.com/archives/apparmor/attachments/20130529/5f939ca5/attachment-0001.pgp>
More information about the AppArmor
mailing list