[apparmor] [PATCH v2 21/42] parser: Add functions for features support tests

Tyler Hicks tyhicks at canonical.com
Thu Mar 12 22:12:48 UTC 2015


On 2015-03-12 04:17:56, John Johansen wrote:
> On 03/06/2015 01:48 PM, Tyler Hicks wrote:
> > Defines a function that can be called to test features support. It is
> > string based which allows the support tests to work with new kernel
> > features without any changes.
> > 
> > The use of global variables in the parser to store and check features
> > support is still preserved. The parser should probably move over to
> > passing the aa_features object around but that's left for later.
> > 
> > Signed-off-by: Tyler Hicks <tyhicks at canonical.com>
> > ---
> >  parser/features.c    | 128 +++++++++++++++++++++++++++++++++++++++++++++++++++
> >  parser/features.h    |   1 +
> >  parser/parser_main.c |  52 ++++++++++-----------
> >  3 files changed, 155 insertions(+), 26 deletions(-)
> > 
> > diff --git a/parser/features.c b/parser/features.c
> > index 07d6e6c..50e0024 100644
> > --- a/parser/features.c
> > +++ b/parser/features.c
> > @@ -17,6 +17,7 @@
> >   */
> >  
> >  #include <errno.h>
> > +#include <ctype.h>
> >  #include <fcntl.h>
> >  #include <stdio.h>
> >  #include <string.h>
> > @@ -163,6 +164,86 @@ static int load_features_file(const char *name, char *buffer, size_t size)
> >  	return 0;
> >  }
> >  
> > +static bool walk_one(const char **str, const char *component, bool is_top_level)
> > +{
> > +	const char *cur = *str;
> > +	uint32_t bracket_count = 0;
> > +	int i = 0;
> > +
> > +	/* Empty strings are not accepted */
> > +	if (!*cur || !component[0])
> > +		return false;
> > +
> > +	/**
> > +	 * If @component is not top-level, the first character in the string to
> > +	 * search MUST be '{'
> > +	 */
> > +	if (!is_top_level) {
> > +		if (*cur != '{')
> > +			return false;
> > +
> > +		cur++;
> > +	}
> > +
> > +	/**
> > +	 * This loop tries to find the @component in *@str. When this loops
> > +	 * completes, cur will either point one character past the end of the
> > +	 * matched @component or to the NUL terminator of *@str.
> > +	 */
> > +	while(*cur && component[i]) {
> > +		if (!isascii(*cur)) {
> > +			/* Only ASCII is expected */
> > +			return false;
> > +		} else if (*cur == '{') {
> > +			/* There's a limit to the number of opening brackets */
> > +			if (bracket_count == UINT32_MAX)
> > +				return false;
> > +
> > +			bracket_count++;
> > +		} else if (*cur == '}') {
> > +			/* Check for unexpected closing brackets */
> > +			if (bracket_count == 0)
> > +				return false;
> > +
> > +			bracket_count--;
> > +		}
> > +
> > +		/**
> > +		 * Move to the next character in @component if we have a match
> > +		 * and either @component is not top-level or, if @component is
> > +		 * top-level, we're not inside of brackets
> > +		 */
> > +		if (*cur == component[i] &&
> > +		    (!is_top_level || bracket_count == 0))
> > +			i++;
> > +		else
> > +			i = 0;
> > +
> This isn't correct. IF we are !is_top_level then we can have a partial leading
> match, hit a bracket, increase bracket_count and continue matching, so that
> the component
>   frontback 
> will match either of
>   front{back
>   front}back

Hmm... I don't think that's possible since there's no 'continue' when we
hit a bracket. The (*cur == component[i]) test will fail and i will be
reset back to 0.

Either way, this is way too complicated of a function for me to be
sending out without tests. I apologize for that. I'll add tests,
including the case you mentioned above, and then fix issues and resend
this patch out.

> > +		cur++;
> > +	}
> > +
> > +	/* A full match was not found if component[i] is non-NUL */
> > +	if (component[i])
> > +		return false;
> > +
> > +	/**
> > +	 * This loop eats up valid (ASCII) characters until a non-bracket or
> > +	 * non-space character is found so that *@str is properly set to call
> > +	 * back into this function, if necessary
> > +	 */
> > +	while (*cur) {
> > +		if (!isascii(*cur))
> > +			return false;
> > +		else if (*cur == '{' || *cur == '}' || !isspace(*cur))
> > +			break;
> > +
> > +		cur++;
> > +	}
> > +
> > +	*str = cur;
> > +	return true;
> > +}
> > +
> >  /**
> >   * aa_features_new - create a new features based on a path
> >   * @features: will point to the address of an allocated and initialized
> > @@ -299,3 +380,50 @@ bool aa_features_is_equal(aa_features *features1, aa_features *features2)
> >  	return features1 && features2 &&
> >  	       strcmp(features1->string, features2->string) == 0;
> >  }
> > +
> > +/**
> > + * aa_features_supports - provides features support status
> > + * @features: the features
> > + * @str: the string representation of a feature to check
> > + *
> > + * Example @str values are "dbus/mask/send", "caps/mask/audit_read", and
> > + * "policy/versions/v7".
> > + *
> > + * Returns: a bool specifying the support status of @str feature
> > + */
> > +bool aa_features_supports(aa_features *features, char *str)
> > +{
> > +	const char *features_string = features->string;
> > +	char *components[32];
> > +	char *saveptr = NULL;
> > +	size_t i;
> > +
> > +	/* Empty strings are not accepted. Neither are leading '/' chars. */
> > +	if (!str || str[0] == '/')
> > +		return false;
> > +
> > +	/**
> > +	 * Break @str into an array of components. For example,
> > +	 * "mount/mask/mount" would turn into "mount" as the first component,
> > +	 * "mask" as the second, and "mount" as the third
> > +	 */
> > +	for (i = 0; i < sizeof(components); i++) {
> > +		components[i] = strtok_r(str, "/", &saveptr);
> > +		if (!components[i])
> > +			break;
> > +
> > +		str = NULL;
> > +	}
> I am not a fan of the use of strtok in this fn. It means const strings can
> not be passed, nor could the same query string be used multiple times (not
> that you generally should but ...).
> I would rather see an api that can use constant strings, but at the very
> least @str needs to document that it will be modified.

Yeah, I completely agree. I'll see if I can come up with something that
works off of string lengths instead of NUL terminators.

Tyler

> > +
> > +	/* At least one valid token is required */
> > +	if (!components[0])
> > +		return false;
> > +
> > +	/* Ensure that all components are valid and found */
> > +	for (i = 0; i < sizeof(components) && components[i]; i++) {
> > +		if (!walk_one(&features_string, components[i], i == 0))
> > +			return false;
> > +	}
> > +
> > +	return true;
> > +}
> > diff --git a/parser/features.h b/parser/features.h
> > index a0c177f..96d0ee9 100644
> > --- a/parser/features.h
> > +++ b/parser/features.h
> > @@ -29,5 +29,6 @@ aa_features *aa_features_ref(aa_features *features);
> >  void aa_features_unref(aa_features *features);
> >  const char *aa_features_get_string(aa_features *features);
> >  bool aa_features_is_equal(aa_features *features1, aa_features *features2);
> > +bool aa_features_supports(aa_features *features, char *str);
> >  
> >  #endif /* __AA_FEATURES_H */
> > diff --git a/parser/parser_main.c b/parser/parser_main.c
> > index 3f8ca6a..2d220a7 100644
> > --- a/parser/parser_main.c
> > +++ b/parser/parser_main.c
> > @@ -574,7 +574,17 @@ no_match:
> >  
> >  static void set_supported_features(void)
> >  {
> > -	const char *features_string;
> > +	char file[] = "file";
> > +	char network[] = "network";
> > +	char af_unix[] = "network/af_unix";
> > +	char mount[] = "mount";
> > +	char dbus[] = "dbus";
> > +	char signal[] = "signal";
> > +	char ptrace[] = "ptrace";
> > +	char set_load[] = "policy/set_load";
> > +	char diff_encode[] = "policy/diff_encode";
> > +	char v7[] = "policy/versions/v7";
> > +	char v6[] = "policy/versions/v6";
> >  
> >  	/* has process_args() already assigned a match string? */
> >  	if (!features && aa_features_new_from_kernel(&features) == -1) {
> > @@ -582,33 +592,23 @@ static void set_supported_features(void)
> >  		return;
> >  	}
> >  
> > -	features_string = aa_features_get_string(features);
> >  	perms_create = 1;
> > -
> > -	/* TODO: make this real parsing and config setting */
> > -	if (strstr(features_string, "file {"))	/* pre policydb is file= */
> > -		kernel_supports_policydb = 1;
> > -	if (strstr(features_string, "v6"))
> > -		kernel_abi_version = 6;
> > -	if (strstr(features_string, "v7"))
> > +	kernel_supports_policydb = aa_features_supports(features, file);
> > +	kernel_supports_network = aa_features_supports(features, network);
> > +	kernel_supports_unix = aa_features_supports(features, af_unix);
> > +	kernel_supports_mount = aa_features_supports(features, mount);
> > +	kernel_supports_dbus = aa_features_supports(features, dbus);
> > +	kernel_supports_signal = aa_features_supports(features, signal);
> > +	kernel_supports_ptrace = aa_features_supports(features, ptrace);
> > +	kernel_supports_setload = aa_features_supports(features, set_load);
> > +	kernel_supports_diff_encode = aa_features_supports(features, diff_encode);
> > +
> > +	if (aa_features_supports(features, v7))
> >  		kernel_abi_version = 7;
> > -	if (strstr(features_string, "set_load"))
> > -		kernel_supports_setload = 1;
> > -	if (strstr(features_string, "network"))
> > -		kernel_supports_network = 1;
> > -	if (strstr(features_string, "af_unix"))
> > -		kernel_supports_unix = 1;
> > -	if (strstr(features_string, "mount"))
> > -		kernel_supports_mount = 1;
> > -	if (strstr(features_string, "dbus"))
> > -		kernel_supports_dbus = 1;
> > -	if (strstr(features_string, "signal"))
> > -		kernel_supports_signal = 1;
> > -	if (strstr(features_string, "ptrace {"))
> > -		kernel_supports_ptrace = 1;
> > -	if (strstr(features_string, "diff_encode"))
> > -		kernel_supports_diff_encode = 1;
> > -	else if (dfaflags & DFA_CONTROL_DIFF_ENCODE)
> > +	else if (aa_features_supports(features, v6))
> > +		kernel_abi_version = 6;
> > +
> > +	if (!kernel_supports_diff_encode)
> >  		/* clear diff_encode because it is not supported */
> >  		dfaflags &= ~DFA_CONTROL_DIFF_ENCODE;
> >  }
> > 
> 
-------------- next part --------------
A non-text attachment was scrubbed...
Name: signature.asc
Type: application/pgp-signature
Size: 819 bytes
Desc: Digital signature
URL: <https://lists.ubuntu.com/archives/apparmor/attachments/20150312/5f4de7a2/attachment.pgp>


More information about the AppArmor mailing list