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

Attachment: signature.asc
Description: Digital signature

-- 
AppArmor mailing list
AppArmor@lists.ubuntu.com
Modify settings or unsubscribe at: 
https://lists.ubuntu.com/mailman/listinfo/apparmor

Reply via email to