mirror of
https://gitlab.com/apparmor/apparmor.git
synced 2025-03-04 16:35:02 +01:00

Signed-off-by: John Johansen <john.johansen@canonical.com> Acked-by: Seth Arnold <seth.arnold@canonical.com> Acked-by: Steve Beattie <steve@nxnw.org>
523 lines
15 KiB
C
523 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>
|
|
|
|
#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 */
|