[apparmor] [PATCH 1/3] Generate the features list from the features directory

John Johansen john.johansen at canonical.com
Thu Feb 23 09:35:05 UTC 2012


On 02/22/2012 05:00 PM, Steve Beattie wrote:
> On Wed, Feb 22, 2012 at 03:04:56PM -0800, John Johansen wrote:
>> Newer versions of AppArmor use a features directory instead of a file
>> update the parser to use this to determine features and match string
>>
>> This is just a first pass at this to get things up quickly.  A much
>> more comprehensive rework that can parse and use the full information
>> set is needed.
>>
>> Signed-off-by: John Johansen <john.johansen at canonical.com>
>> ---
>>  parser/parser.h        |    1 +
>>  parser/parser_common.c |    1 +
>>  parser/parser_main.c   |  121 +++++++++++++++++++++++++++++++++++++++++++++++-
>>  3 files changed, 122 insertions(+), 1 deletions(-)
>>
>> diff --git a/parser/parser.h b/parser/parser.h
>> index 8d3ef6c..7be0f83 100644
>> --- a/parser/parser.h
>> +++ b/parser/parser.h
>> @@ -247,6 +247,7 @@ extern int perms_create;
>>  extern int net_af_max_override;
>>  extern int kernel_load;
>>  extern int kernel_supports_network;
>> +extern int kernel_supports_mount;
>>  extern int flag_changehat_version;
>>  extern int conf_verbose;
>>  extern int conf_quiet;
>> diff --git a/parser/parser_common.c b/parser/parser_common.c
>> index df78a10..572eaad 100644
>> --- a/parser/parser_common.c
>> +++ b/parser/parser_common.c
>> @@ -27,6 +27,7 @@ int perms_create = 0;                   /* perms contain create flag */
>>  int net_af_max_override = -1;           /* use kernel to determine af_max */
>>  int kernel_load = 1;
>>  int kernel_supports_network = 1;        /* kernel supports network rules */
>> +int kernel_supports_mount = 0;		/* kernel supports mount rules */
> 
> (minor nit) tabs vs whitespace?
> 
yep, I used tab and it lined up with what was there, which is using whitespace :/

>>  int flag_changehat_version = FLAG_CHANGEHAT_1_5;
>>  int conf_verbose = 0;
>>  int conf_quiet = 0;
>> diff --git a/parser/parser_main.c b/parser/parser_main.c
>> index 721582d..94ad2b9 100644
>> --- a/parser/parser_main.c
>> +++ b/parser/parser_main.c
>> @@ -30,6 +30,7 @@
>>  #include <mntent.h>
>>  #include <libintl.h>
>>  #include <locale.h>
>> +#include <dirent.h>
>>  #define _(s) gettext(s)
>>  
>>  /* enable the following line to get voluminous debug info */
>> @@ -667,17 +668,134 @@ int have_enough_privilege(void)
>>  	return 0;
>>  }
>>  
>> +static char *handle_features_dir(const char *filename, char **buffer, int size,
>> +				 char *pos)
>> +{
>> +	DIR *dir = NULL;
>> +	char *dirent_path = NULL;
>> +	struct dirent *dirent;
>> +	struct stat my_stat;
>> +	int len;
>> +
>> +	PDEBUG("Opened features directory \"%s\"\n", filename);
>> +	if (!(dir = opendir(filename))) {
>> +		PDEBUG("opendir failed '%s'", filename);
>> +		exit(1);
>> +	}
>> +
>> +	while ((dirent = readdir(dir)) != NULL) {
>> +		int name_len, remaining;
>> +		/* skip dotfiles silently. */
>> +		if (dirent->d_name[0] == '.')
>> +			continue;
>> +
>> +		if (dirent_path)
>> +			free(dirent_path);
>> +		if (asprintf(&dirent_path, "%s/%s", filename, dirent->d_name) < 0)
>> +		{
>> +			PERROR(_("Memory allocation error."));
>> +			exit(1);
>> +		}
>> +
>> +		name_len = strlen(dirent->d_name);
>> +		if (!name_len)
>> +			continue;
>> +
>> +		if (stat(dirent_path, &my_stat)) {
>> +			PERROR(_("stat failed for '%s'"), dirent_path);
>> +			exit(1);
>> +		}
>> +
>> +		remaining = size - (pos - *buffer);
>> +		len = snprintf(pos, remaining, "%s { ", dirent->d_name);
>> +		if (len > remaining) {
> 
> I think there's an off by one error here, in that if len == remaining,
> then the output has been truncated, as the value returned by snprintf()
> is the number of characters that would have been written not including
> the trailing \0 character. When len == remaining, then it has been
> truncated by 1 character.
> 
yep

>> +			PERROR(_("Feature buffer full."));
>> +			exit(1);
>> +		}
>> +		pos += len;
>> +
>> +		if (S_ISREG(my_stat.st_mode)) {
>> +			int file;
>> +			int remaining = size - (pos - *buffer);
>> +			if (!(file = open(dirent_path, O_RDONLY))) {
>> +				PDEBUG("Could not open '%s' in '%s'", dirent_path, filename);
>> +				exit(1);
>> +				break;
>> +			}
>> +			PDEBUG("Opened features \"%s\" in \"%s\"\n", dirent_path, filename);
>> +			if (my_stat.st_size > remaining) {
>> +				PERROR(_("Feature buffer full."));
>> +				exit(1);
>> +			}
>> +
>> +			do {
>> +				len = read(file, pos, remaining);
>> +				if (pos > 0) {
> 
> Is this test correct? Given that pos is a pointer to a character
> string, it's, uh, likely to not be 0. Did you mean (len > 0)?
> 
indeed

>> +					remaining -= len;
>> +					pos += len;
>> +					*pos = 0;
>> +				}
>> +			} while (len > 0);
>> +			if (len < 0) {
>> +				PDEBUG("Error reading feature file '%s'\n",
>> +				       dirent_path);
>> +				exit(1);
>> +			}
>> +			close(file);
>> +
>> +		} else if (S_ISDIR(my_stat.st_mode)) {
>> +			pos = handle_features_dir(dirent_path, buffer, size,
>> +						  pos);
>> +			if (!pos)
>> +				break;
>> +
>> +		}
>> +
>> +		remaining = size - (pos - *buffer);
>> +		len = snprintf(pos, remaining, " }\n");
> 
> With the newline, I think your formatting might end up a little goofy
> if you have multiply nested directories.
> 
oh it is, however with out its goofy too as the file reads have a new line at their
end as well.  I thought about stripping it but first a first pass I decided I didn't
care.

As I said what we really need to do is move to a more complete parse of the file

>> +		if (len > remaining) {
> 
> Same problem here as the earlier snprintf case. It might be useful to
> abstract appending to the feature string to a helper function, one that
> you can write unit tests for. (Hooray for handling strings in C. Sigh.)
> 
yeah, truth be told I actually considered that on the first pass and skipped
it because I did feel like looking up varags syntax :/

>> +			PERROR(_("Feature buffer full."));
>> +			exit(1);
>> +		}
>> +		pos += len;
>> +	}
>> +	if (dirent_path)
>> +		free(dirent_path);
>> +	closedir(dir);
>> +
>> +	return pos;
>> +}
>> +
>>  /* match_string == NULL --> no match_string available
>>     match_string != NULL --> either a matching string specified on the
>>     command line, or the kernel supplied a match string */
>>  static void get_match_string(void) {
>>  
>>  	FILE *ms = NULL;
>> +	struct stat stat_file;
>>  
>>  	/* has process_args() already assigned a match string? */
>>  	if (match_string)
>>  		goto out;
>>  
>> +	if (stat(FLAGS_FILE, &stat_file) == -1)
>> +		goto out;
>> +
>> +	if (S_ISDIR(stat_file.st_mode)) {
>> +		/* if we have a features directory default to */
>> +		regex_type = AARE_DFA;
>> +		perms_create = 1;
>> +		flag_changehat_version = FLAG_CHANGEHAT_1_4;
> 
> Should this be 1.5 or 1.4? The default setting is 1.5 and the comments
> around the checks for FLAG_CHANGEHAT_1_4 indicate it to be an apparmor
> 2.3 specific thing (though I know better than to trust comments :-) ).
> 
heh it is 1.5, which means we can drop it as that is the default.

>> +
>> +		flags_string = malloc(1024);
>> +		handle_features_dir(FLAGS_FILE, &flags_string, 2048, flags_string);
> 
> Kees already highlighted the disparity, but that's a reason to use
> a #defined value or statically allocated memory + sizeof().
> 
yep

>> +		if (strstr(flags_string, "network"))
>> +			kernel_supports_network = 1;
>> +		if (strstr(flags_string, "mount"))
>> +			kernel_supports_mount = 1;
>> +		return;
>> +	}
>> +
>>  	ms = fopen(MATCH_STRING, "r");
>>  	if (!ms)
>>  		goto out;
>> @@ -718,7 +836,8 @@ static void get_flags_string(char **flags, char *flags_file) {
>>  	FILE *f = NULL;
>>  
>>  	/* abort if missing or already set */
>> -	if (!flags || *flags) return;
>> +	if (!flags || *flags)
>> +		return;
> 
> *cough* Thank you. :-)
> 
>>  
>>  	f = fopen(flags_file, "r");
>>  	if (!f)
> 
> 
> 




More information about the AppArmor mailing list