apparmor/parser/lib.c
Steve Beattie 53216edd1d parser: find SIZE_MAX on older versions of glibc and g++
Another issue with commit 2456 is that with older versions of glibc and
g++, a definition for SIZE_MAX was not being found; e.g. on Ubuntu 12.04
LTS and 12.10, the parser fails to compile with the following error:

  g++ -g -O2 -pipe -Wall -Wsign-compare -Wmissing-field-initializers -Wformat-security -Wunused-parameter -std=gnu++0x -D_GNU_SOURCE -DPACKAGE=\"apparmor-parser\" -DLOCALEDIR=\"/usr/share/locale\" -DSUBDOMAIN_CONFDIR=\"/etc/apparmor\" -I../libraries/libapparmor//include -c -o lib.o lib.c
  lib.c: In function 'int str_escseq(const char**, const char*)':
  lib.c:292:32: error: 'SIZE_MAX' was not declared in this scope

The following patch addresses the issue by explicitly including the C stdint
header which contains the definition for SIZE_MAX.

Signed-off-by: Steve Beattie <steve@nxnw.org>
Acked-by: John Johansen <john.johansen@canonical.com>
2014-04-17 11:10:41 -07:00

524 lines
15 KiB
C

/*
* Copyright (c) 2012
* Canonical Ltd. (All rights reserved)
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of version 2 of the GNU General Public
* License published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, contact Novell, Inc. or Canonical,
* Ltd.
*/
#include <dirent.h>
#include <errno.h>
#include <fcntl.h>
#include <stdio.h>
#include <string.h>
#include <stddef.h>
#include <stdlib.h>
#include <unistd.h>
#define _(s) gettext(s)
#include <sys/stat.h>
#include <sys/types.h>
#include <cstdint>
#include "lib.h"
#include "parser.h"
/**
* dirat_for_each: iterate over a directory calling cb for each entry
* @dir: already opened directory (MAY BE NULL)
* @name: name of the directory (MAY BE NULL)
* @data: data pointer to pass to the callback fn (MAY BE NULL)
* @cb: the callback to pass entry too (NOT NULL)
*
* Iterate over the entries in a directory calling cb for each entry.
* The directory to iterate is determined by a combination of @dir and
* @name.
*
* IF @name is a relative path it is determine relative to at @dir if it
* is specified, else it the lookup is done relative to the current
* working directory.
*
* If @name is not specified then @dir is used as the directory to iterate
* over.
*
* It is an error if both @name and @dir are null
*
* The cb function is called with the DIR in use and the name of the
* file in that directory. If the file is to be opened it should
* use the openat, fstatat, and related fns.
*
* Returns: 0 on success, else -1 and errno is set to the error code
*/
int dirat_for_each(DIR *dir, const char *name, void *data,
int (* cb)(DIR *, const char *, struct stat *, void *))
{
struct dirent *dirent = NULL, *ent;
DIR *d = NULL;
int error = 0;
if (!cb || (!dir && !name)) {
errno = EINVAL;
return -1;
}
if (dir && (!name || *name != '/')) {
dirent = (struct dirent *)
malloc(offsetof(struct dirent, d_name) +
fpathconf(dirfd(dir), _PC_NAME_MAX) + 1);
} else {
dirent = (struct dirent *)
malloc(offsetof(struct dirent, d_name) +
pathconf(name, _PC_NAME_MAX) + 1);
}
if (!dirent) {
PDEBUG("could not alloc dirent");
return -1;
}
if (name) {
if (dir && *name != '/') {
int fd = openat(dirfd(dir), name, O_RDONLY);
if (fd == -1)
goto fail;
d = fdopendir(fd);
} else {
d = opendir(name);
}
PDEBUG("Open dir '%s': %s\n", name, d ? "succeeded" : "failed");
if (!(d))
goto fail;
} else { /* dir && !name */
PDEBUG("Recieved cache directory\n");
d = dir;
}
for (error = readdir_r(d, dirent, &ent);
error == 0 && ent != NULL;
error = readdir_r(d, dirent, &ent)) {
struct stat my_stat;
if (strcmp(ent->d_name, ".") == 0 ||
strcmp(ent->d_name, "..") == 0)
continue;
if (fstatat(dirfd(d), ent->d_name, &my_stat, 0)) {
PDEBUG("stat failed for '%s'", name);
goto fail;
}
if (cb(d, ent->d_name, &my_stat, data)) {
PDEBUG("dir_for_each callback failed\n");
goto fail;
}
}
if (d != dir)
closedir(d);
free(dirent);
return error;
fail:
error = errno;
if (d && d != dir)
closedir(d);
free(dirent);
errno = error;
return -1;
}
/**
* isodigit - test if a character is an octal digit
* @c: character to test
*
* Returns: true if an octal digit, else false
*/
bool isodigit(char c)
{
return (c >= '0' && c <= '7') ? true : false;
}
/* convert char character 0..9a..z into a number 0-35
*
* Returns: digit value of character or -1 if character is invalid
*/
static int chrtoi(char c, int base)
{
int val = -1;
if (base < 2 || base > 36)
return -1;
if (isdigit(c))
val = c - '0';
else if (isalpha(c) && isascii(c))
val = tolower(c) - 'a' + 10;
if (val >= base)
return -1;
return val;
}
/**
* strntol - convert a sequence of characters as a hex number
* @str: pointer to a string of character to convert
* @endptr: RETURNS: if not NULL, the first char after converted chars.
* @base: base of convertion
* @maxval: maximum value. don't consume next char if value will exceed @maxval
* @n: maximum number of characters to consume doing the conversion
*
* Returns: converted number. If there is no conversion 0 is returned and
* *@endptr = @str
*
* Not a complete replacement for strtol yet, Does not process base prefixes,
* nor +/- sign yet.
*
* - take the largest sequence of character that is in range of 0-@maxval
* - will consume the minimum of @maxlen or @base digits in @maxval
* - if there is not n valid characters for the base only the n-1 will be taken
* eg. for the sequence string 4z with base 16 only 4 will be taken as the
* hex number
*/
long strntol(const char *str, const char **endptr, int base, long maxval,
size_t n)
{
long c, val = 0;
if (base > 1 && base < 37) {
for (; n && (c = chrtoi(*str, base)) != -1; str++, n--) {
long tmp = (val * base) + c;
if (tmp > maxval)
break;
val = tmp;
}
}
if (endptr)
*endptr = str;
return val;
}
/**
* strn_escseq -
* @pos: position of first character in esc sequence
* @chrs: list of exact return chars to support eg. \+ returns + instead of -1
* @n: maximum length of string to processes
*
* Returns: character for escape sequence or -1 if an error
*
* pos will point to first character after esc sequence
* OR
* pos will point to first character where an error was discovered
* errors can be unrecognized esc character, octal, decimal, or hex
* character encoding with no valid number. eg. \xT
*/
int strn_escseq(const char **pos, const char *chrs, size_t n)
{
const char *end;
long tmp;
if (n < 1)
return -1;
if (isodigit(**pos)) {
tmp = strntol(*pos, &end, 8, 255, min((size_t) 3, n));
if (tmp == 0 && end == *pos) {
/* this should never happen because of isodigit test */
return -1;
}
*pos = end;
return tmp;
}
char c = *(*pos)++;
switch(c) {
case '\\':
return '\\';
case '"':
return '"';
case 'd':
tmp = strntol(*pos, &end, 10, 255, min((size_t) 3, n));
if (tmp == 0 && end == *pos) {
/* \d no valid encoding */
return -1;
}
*pos = end;
return tmp;
case 'x':
tmp = strntol(*pos, &end, 16, 255, min((size_t) 2, n));
if (tmp == 0 && end == *pos) {
/* \x no valid encoding */
return -1;
}
*pos = end;
return tmp;
case 'a':
return '\a';
case 'e':
return 033 /* ESC */;
case 'f':
return '\f';
case 'n':
return '\n';
case 'r':
return '\r';
case 't':
return '\t';
}
if (strchr(chrs, c))
return c;
/* unsupported escap sequence, backup to return that char */
pos--;
return -1;
}
int str_escseq(const char **pos, const char *chrs)
{
/* no len limit just use end of string, yes could use strlen(pos) */
return strn_escseq(pos, chrs, SIZE_MAX);
}
#ifdef UNIT_TEST
#include "lib.h"
#include "parser.h"
#include "unit_test.h"
static int test_oct(const char *str)
{
const char *end;
long retval = strntol(str, &end, 8, 255, 3);
if (retval == 0 && str == end)
return -1;
return retval;
}
static int test_dec(const char *str)
{
const char *end;
long retval = strntol(str, &end, 10, 255, 3);
if (retval == 0 && str == end)
return -1;
return retval;
}
static int test_hex(const char *str)
{
const char *end;
long retval = strntol(str, &end, 16, 255, 2);
if (retval == 0 && str == end)
return -1;
return retval;
}
int main(void)
{
int rc = 0;
int retval;
struct test_struct {
const char *test; /* test string */
int expected; /* expected result */
const char *msg; /* failure message */
};
struct test_struct oct_tests[] = {
{ "0a", 0, "oct conversion of \\0a failed" },
{ "00000003a", 0, "oct conversion of \\00000003a failed" },
{ "62", 50, "oct conversion of \\62 failed" },
{ "623", 50, "oct conversion of \\623 failed" },
{ "123", 83, "oct conversion of \\123 failed" },
{ "123;", 83, "oct conversion of \\123; failed" },
{ "2234", 147, "oct conversion of \\2234 failed" },
{ "xx", -1, "oct conversion of \\xx failed" },
{ NULL, 0, NULL }
};
struct test_struct dec_tests[] = {
{ "0a", 0, "dec conversion of \\d0a failed" },
{ "00000003a", 0, "dec conversion of \\d00000003a failed" },
{ "62", 62, "dec conversion of \\d62 failed" },
{ "623", 62, "dec conversion of \\d623 failed" },
{ "132", 132, "dec conversion of \\d132 failed" },
{ "132UL", 132, "dec conversion of \\d132UL failed" },
{ "255", 255, "dec conversion of \\d255 failed" },
{ "256", 25, "dec conversion of \\d256 failed" },
{ "2234", 223, "dec conversion of \\d2234 failed" },
{ "xx", -1, "dec conversion of \\dxx failed" },
{ NULL, 0, NULL }
};
struct test_struct hex_tests[] = {
{ "0", 0x0, "hex conversion of 0x0 failed" },
{ "0x1", 0x0, "hex conversion of 0x0x1 failed" },
{ "1x", 0x1, "hex conversion of 0x1x failed" },
{ "00", 0x0, "hex conversion of 0x00 failed" },
{ "00x", 0x0, "hex conversion of 0x00x failed" },
{ "01", 0x1, "hex conversion of 0x01 failed" },
{ "01x", 0x1, "hex conversion of 0x01x failed" },
{ "ab", 0xab, "hex conversion of 0xAb failed" },
{ "AB", 0xab, "hex conversion of 0xAB failed" },
{ "Ab", 0xab, "hex conversion of 0xAb failed" },
{ "aB", 0xab, "hex conversion of 0xaB failed" },
{ "4z", 0x4, "hex conversion of 0x4z failed" },
{ "123", 0x12, "hex conversion of 0x123 failed" },
{ "12M", 0x12, "hex conversion of 0x12M failed" },
{ "ff", 0xff, "hex conversion of 0x255 failed" },
{ "FF", 0xff, "hex conversion of 0x255 failed" },
{ "XX", -1, "hex conversion of 0xXX failed" },
{ NULL, 0, NULL }
};
struct test_struct escseq_tests[] = {
{ "", -1, "escseq conversion of \"\" failed" },
{ "0a", 0, "escseq oct conversion of \\0a failed" },
{ "00000003a", 0, "escseq oct conversion of \\00000003a failed" },
{ "62", 50, "escseq oct conversion of \\62 failed" },
{ "623", 50, "escseq oct conversion of \\623 failed" },
{ "123", 83, "escseq oct conversion of \\123 failed" },
{ "123;", 83, "escseq oct conversion of \\123; failed" },
{ "2234", 147, "escseq oct conversion of \\2234 failed" },
{ "xx", -1, "escseq oct conversion of \\xx failed" },
{ "d0a", 0, "escseq dec conversion of \\d0a failed" },
{ "d00000003a", 0, "escseq dec conversion of \\d00000003a failed" },
{ "d62", 62, "escseq dec conversion of \\d62 failed" },
{ "d623", 62, "escseq dec conversion of \\d623 failed" },
{ "d132", 132, "escseq dec conversion of \\d132 failed" },
{ "d132UL", 132, "escseq dec conversion of \\d132UL failed" },
{ "d255", 255, "escseq dec conversion of \\d255 failed" },
{ "d256", 25, "escseq dec conversion of \\d256 failed" },
{ "d2234", 223, "escseq dec conversion of \\d2234 failed" },
{ "dxx", -1, "escseq dec conversion of \\dxx failed" },
{ "x0", 0x0, "escseq hex conversion of 0x0 failed" },
{ "x0x1", 0x0, "escseq hex conversion of 0x0x1 failed" },
{ "x1x", 0x1, "escseq hex conversion of 0x1x failed" },
{ "x00", 0x0, "escseq hex conversion of 0x00 failed" },
{ "x00x", 0x0, "escseq hex conversion of 0x00x failed" },
{ "x01", 0x1, "escseq hex conversion of 0x01 failed" },
{ "x01x", 0x1, "escseq hex conversion of 0x01x failed" },
{ "xab", 0xab, "escseq hex conversion of 0xAb failed" },
{ "xAB", 0xab, "escseq hex conversion of 0xAB failed" },
{ "xAb", 0xab, "escseq hex conversion of 0xAb failed" },
{ "xaB", 0xab, "escseq hex conversion of 0xaB failed" },
{ "x4z", 0x4, "escseq hex conversion of 0x4z failed" },
{ "x123", 0x12, "escseq hex conversion of 0x123 failed" },
{ "x12M", 0x12, "escseq hex conversion of 0x12M failed" },
{ "xff", 0xff, "escseq hex conversion of 0x255 failed" },
{ "xFF", 0xff, "escseq hex conversion of 0x255 failed" },
{ "xXX", -1, "escseq hex conversion of 0xXX failed" },
{ "\\", '\\', "escseq '\\\\' failed" },
{ "\"", '"', "escseq '\\\"' failed" },
{ "a", '\a', "escseq '\\a' failed" },
{ "e", '\033', "escseq '\\e' failed" },
{ "f", '\f', "escseq '\\f' failed" },
{ "n", '\n', "escseq '\\n' failed" },
{ "r", '\r', "escseq '\\r' failed" },
{ "t", '\t', "escseq '\\t' failed" },
{ NULL, 0, NULL }
};
struct test_struct escseqextra_tests[] = {
{ "+", '+', "escseq extra conversion of \\+ failed" },
{ "-", '-', "escseq conversion of \\- failed" },
{ "*", '*', "escseq conversion of \\* failed" },
{ "(", '(', "escseq conversion of \\( failed" },
{ ")", ')', "escseq conversion of \\) failed" },
{ "|", '|', "escseq conversion of \\| failed" },
{ ".", '.', "escseq conversion of \\. failed" },
{ "[", '[', "escseq conversion of \\[ failed" },
{ "]", ']', "escseq conversion of \\] failed" },
{ "^", '^', "escseq conversion of \\^ failed" },
{ NULL, 0, NULL }
};
/* test chrtoi */
for (int base = -1; base < 38; base++) {
for (int c = 0; c < 256; c++) {
int expected;
int i = chrtoi(c, base);
if (base < 2 || base > 36 || !isascii(c) || !(isdigit(c) || isalpha(c)))
expected = -1;
else if (isdigit(c) && (c - '0') < base)
expected = c - '0';
else if (isalpha(c) && (toupper(c) - 'A') + 10 < base)
expected = (toupper(c) - 'A') + 10;
else
expected = -1;
if (i != expected)
// printf(" chrtoi test: convert base %d '%c'(%d)\texpected %d\tresult: %d\n", base, c, c, expected, i);
MY_TEST(i == expected, "failed");
}
}
/* test strntol */
for (struct test_struct *t = oct_tests; t->test; t++) {
retval = test_oct(t->test);
// printf(" oct test: %s\texpected %d\tresult: %d\n", t->test, t->expected, retval);
MY_TEST(retval == t->expected, t->msg);
}
for (struct test_struct *t = dec_tests; t->test; t++) {
retval = test_dec(t->test);
// printf(" dec test: %s\texpected %d\tresult: %d\n", t->test, t->expected, retval);
MY_TEST(retval == t->expected, t->msg);
}
for (struct test_struct *t = hex_tests; t->test; t++) {
retval = test_hex(t->test);
// printf(" hex test: %s\texpected %d\tresult: %d\n", t->test, t->expected, retval);
MY_TEST(retval == t->expected, t->msg);
}
/* test strn_escseq */
for (struct test_struct *t = escseq_tests; t->test; t++) {
const char *pos = t->test;
retval = strn_escseq(&pos, "", strlen(t->test));
// printf(" strn_escseq test: %s\texpected %d\tresult: %d\n", t->test, t->expected, retval);
MY_TEST(retval == t->expected, t->msg);
}
for (struct test_struct *t = escseqextra_tests; t->test; t++) {
const char *pos = t->test;
retval = strn_escseq(&pos, "", strlen(t->test));
// printf(" strn_escseq test: %s\texpected %d\tresult: %d\n", t->test, t->expected, retval);
MY_TEST(retval == -1, t->msg);
pos = t->test;
retval = strn_escseq(&pos, "*+.|^-[]()", strlen(t->test));
MY_TEST(retval == t->expected, t->msg);
}
for (int c = 1; c < 256; c++) {
const char *pos;
char str[2] = " ";
if (strchr("01234567\\\"dxaefnrt", c))
/* skip chars already tested above */
continue;
str[0] = c;
pos = str;
retval = strn_escseq(&pos, "", 2);
MY_TEST(retval == -1, " strn_escseq: of unsupported char failed");
}
return rc;
}
#endif /* UNIT_TEST */