2015-03-25 17:09:25 -05:00
|
|
|
/*
|
2017-11-02 18:19:45 +00:00
|
|
|
* Copyright (c) 2014-2017
|
2015-03-25 17:09:25 -05:00
|
|
|
* 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 <errno.h>
|
2015-03-25 17:09:26 -05:00
|
|
|
#include <ctype.h>
|
2015-03-25 17:09:27 -05:00
|
|
|
#include <dirent.h>
|
2015-03-25 17:09:25 -05:00
|
|
|
#include <fcntl.h>
|
2017-11-02 18:19:45 +00:00
|
|
|
#include <inttypes.h>
|
2015-03-25 17:09:25 -05:00
|
|
|
#include <stdio.h>
|
|
|
|
#include <string.h>
|
|
|
|
#include <stdarg.h>
|
2016-09-30 15:03:07 -05:00
|
|
|
#include <stddef.h>
|
2015-03-25 17:09:25 -05:00
|
|
|
#include <stdlib.h>
|
|
|
|
#include <sys/types.h>
|
|
|
|
#include <sys/stat.h>
|
|
|
|
#include <unistd.h>
|
2015-03-25 17:09:27 -05:00
|
|
|
#include <sys/apparmor.h>
|
2015-03-25 17:09:25 -05:00
|
|
|
|
2015-03-25 17:09:27 -05:00
|
|
|
#include "private.h"
|
2018-02-19 21:26:24 -08:00
|
|
|
#include "PMurHash.h"
|
2015-03-25 17:09:25 -05:00
|
|
|
|
2015-03-25 17:09:27 -05:00
|
|
|
#define FEATURES_FILE "/sys/kernel/security/apparmor/features"
|
2015-03-25 17:09:26 -05:00
|
|
|
|
2017-11-02 18:19:45 +00:00
|
|
|
#define HASH_SIZE (8 + 1) /* 32 bits binary to hex + NUL terminator */
|
2015-03-25 17:09:26 -05:00
|
|
|
#define STRING_SIZE 8192
|
|
|
|
|
|
|
|
struct aa_features {
|
|
|
|
unsigned int ref_count;
|
2017-11-02 18:19:45 +00:00
|
|
|
char hash[HASH_SIZE];
|
2015-03-25 17:09:26 -05:00
|
|
|
char string[STRING_SIZE];
|
|
|
|
};
|
2015-03-25 17:09:25 -05:00
|
|
|
|
2015-03-25 17:09:26 -05:00
|
|
|
struct features_struct {
|
2015-03-25 17:09:26 -05:00
|
|
|
char *buffer;
|
2016-09-30 15:03:07 -05:00
|
|
|
size_t size;
|
2015-03-25 17:09:26 -05:00
|
|
|
char *pos;
|
|
|
|
};
|
|
|
|
|
2015-03-25 17:09:26 -05:00
|
|
|
struct component {
|
|
|
|
const char *str;
|
|
|
|
size_t len;
|
|
|
|
};
|
|
|
|
|
2016-09-30 15:03:07 -05:00
|
|
|
static int features_buffer_remaining(struct features_struct *fst,
|
|
|
|
size_t *remaining)
|
2015-03-25 17:09:25 -05:00
|
|
|
{
|
2016-09-30 15:03:07 -05:00
|
|
|
ptrdiff_t offset = fst->pos - fst->buffer;
|
2015-03-25 17:09:26 -05:00
|
|
|
|
2016-09-30 15:03:07 -05:00
|
|
|
if (offset < 0 || fst->size < (size_t)offset) {
|
2015-03-25 17:09:26 -05:00
|
|
|
errno = EINVAL;
|
2016-09-30 15:03:07 -05:00
|
|
|
PERROR("Invalid features buffer offset (%td)\n", offset);
|
2015-03-25 17:09:26 -05:00
|
|
|
return -1;
|
|
|
|
}
|
2015-03-25 17:09:25 -05:00
|
|
|
|
2016-09-30 15:03:07 -05:00
|
|
|
*remaining = fst->size - offset;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int features_snprintf(struct features_struct *fst, const char *fmt, ...)
|
|
|
|
{
|
|
|
|
va_list args;
|
|
|
|
int i;
|
|
|
|
size_t remaining;
|
|
|
|
|
|
|
|
if (features_buffer_remaining(fst, &remaining) == -1)
|
|
|
|
return -1;
|
|
|
|
|
2015-03-25 17:09:25 -05:00
|
|
|
va_start(args, fmt);
|
2015-03-25 17:09:26 -05:00
|
|
|
i = vsnprintf(fst->pos, remaining, fmt, args);
|
2015-03-25 17:09:25 -05:00
|
|
|
va_end(args);
|
|
|
|
|
2015-03-25 17:09:26 -05:00
|
|
|
if (i < 0) {
|
|
|
|
errno = EIO;
|
2015-03-25 17:09:27 -05:00
|
|
|
PERROR("Failed to write to features buffer\n");
|
2015-03-25 17:09:26 -05:00
|
|
|
return -1;
|
2016-09-30 15:03:07 -05:00
|
|
|
} else if ((size_t)i >= remaining) {
|
2015-03-25 17:09:26 -05:00
|
|
|
errno = ENOBUFS;
|
2015-03-25 17:09:27 -05:00
|
|
|
PERROR("Feature buffer full.");
|
2015-03-25 17:09:26 -05:00
|
|
|
return -1;
|
2015-03-25 17:09:25 -05:00
|
|
|
}
|
|
|
|
|
2015-03-25 17:09:26 -05:00
|
|
|
fst->pos += i;
|
|
|
|
return 0;
|
2015-03-25 17:09:25 -05:00
|
|
|
}
|
|
|
|
|
2015-06-15 15:11:51 -05:00
|
|
|
/* load_features_file - opens and reads a file into @buffer and then NUL-terminates @buffer
|
|
|
|
* @dirfd: a directory file descriptory or AT_FDCWD (see openat(2))
|
|
|
|
* @path: name of the file
|
|
|
|
* @buffer: the buffer to read the features file into (will be NUL-terminated on success)
|
|
|
|
* @size: the size of @buffer
|
|
|
|
*
|
|
|
|
* Returns: The number of bytes copied into @buffer on success (not counting
|
|
|
|
* the NUL-terminator), else -1 and errno is set. Note that @size must be
|
|
|
|
* larger than the size of the file or -1 will be returned with errno set to
|
|
|
|
* ENOBUFS indicating that @buffer was not large enough to contain all of the
|
|
|
|
* file contents.
|
|
|
|
*/
|
2016-09-30 15:03:02 -05:00
|
|
|
static ssize_t load_features_file(int dirfd, const char *path,
|
|
|
|
char *buffer, size_t size)
|
2015-06-15 15:11:51 -05:00
|
|
|
{
|
|
|
|
autoclose int file = -1;
|
|
|
|
char *pos = buffer;
|
|
|
|
ssize_t len;
|
|
|
|
|
|
|
|
file = openat(dirfd, path, O_RDONLY);
|
|
|
|
if (file < 0) {
|
|
|
|
PDEBUG("Could not open '%s'\n", path);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
PDEBUG("Opened features \"%s\"\n", path);
|
|
|
|
|
|
|
|
if (!size) {
|
|
|
|
errno = ENOBUFS;
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Save room for a NUL-terminator at the end of @buffer */
|
|
|
|
size--;
|
|
|
|
|
|
|
|
do {
|
|
|
|
len = read(file, pos, size);
|
|
|
|
if (len > 0) {
|
|
|
|
size -= len;
|
|
|
|
pos += len;
|
|
|
|
}
|
|
|
|
} while (len > 0 && size);
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Possible error conditions:
|
|
|
|
* - len == -1: read failed and errno is already set so return -1
|
|
|
|
* - len > 0: read() never returned 0 (EOF) meaning that @buffer was
|
|
|
|
* too small to contain all of the file contents so set
|
|
|
|
* errno to ENOBUFS and return -1
|
|
|
|
*/
|
|
|
|
if (len) {
|
|
|
|
if (len > 0)
|
|
|
|
errno = ENOBUFS;
|
|
|
|
|
|
|
|
PDEBUG("Error reading features file '%s': %m\n", path);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* NUL-terminate @buffer */
|
|
|
|
*pos = 0;
|
|
|
|
|
|
|
|
return pos - buffer;
|
|
|
|
}
|
|
|
|
|
2015-06-15 15:11:51 -05:00
|
|
|
static int features_dir_cb(int dirfd, const char *name, struct stat *st,
|
2015-03-25 17:09:25 -05:00
|
|
|
void *data)
|
|
|
|
{
|
|
|
|
struct features_struct *fst = (struct features_struct *) data;
|
|
|
|
|
|
|
|
/* skip dot files and files with no name */
|
|
|
|
if (*name == '.' || !strlen(name))
|
|
|
|
return 0;
|
|
|
|
|
2015-03-25 17:09:26 -05:00
|
|
|
if (features_snprintf(fst, "%s {", name) == -1)
|
|
|
|
return -1;
|
2015-03-25 17:09:25 -05:00
|
|
|
|
|
|
|
if (S_ISREG(st->st_mode)) {
|
2016-09-30 15:03:02 -05:00
|
|
|
ssize_t len;
|
2016-09-30 15:03:07 -05:00
|
|
|
size_t remaining;
|
|
|
|
|
|
|
|
if (features_buffer_remaining(fst, &remaining) == -1)
|
|
|
|
return -1;
|
2015-03-25 17:09:25 -05:00
|
|
|
|
2015-06-15 15:11:51 -05:00
|
|
|
len = load_features_file(dirfd, name, fst->pos, remaining);
|
|
|
|
if (len < 0)
|
2015-03-25 17:09:25 -05:00
|
|
|
return -1;
|
|
|
|
|
2015-06-15 15:11:51 -05:00
|
|
|
fst->pos += len;
|
2015-03-25 17:09:25 -05:00
|
|
|
} else if (S_ISDIR(st->st_mode)) {
|
2015-06-15 15:11:51 -05:00
|
|
|
if (_aa_dirat_for_each(dirfd, name, fst, features_dir_cb))
|
2015-03-25 17:09:25 -05:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2015-03-25 17:09:26 -05:00
|
|
|
if (features_snprintf(fst, "}\n") == -1)
|
|
|
|
return -1;
|
2015-03-25 17:09:25 -05:00
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2016-09-30 15:03:02 -05:00
|
|
|
static ssize_t load_features_dir(int dirfd, const char *path,
|
2016-09-30 15:03:07 -05:00
|
|
|
char *buffer, size_t size)
|
2015-03-25 17:09:25 -05:00
|
|
|
{
|
2015-06-15 15:11:51 -05:00
|
|
|
struct features_struct fst = { buffer, size, buffer };
|
2015-03-25 17:09:25 -05:00
|
|
|
|
2015-06-15 15:11:51 -05:00
|
|
|
if (_aa_dirat_for_each(dirfd, path, &fst, features_dir_cb)) {
|
|
|
|
PDEBUG("Failed evaluating %s\n", path);
|
2015-03-25 17:09:26 -05:00
|
|
|
return -1;
|
2015-03-25 17:09:25 -05:00
|
|
|
}
|
|
|
|
|
2015-03-25 17:09:26 -05:00
|
|
|
return 0;
|
2015-03-25 17:09:25 -05:00
|
|
|
}
|
|
|
|
|
2017-11-02 18:19:45 +00:00
|
|
|
static int init_features_hash(aa_features *features)
|
|
|
|
{
|
|
|
|
const char *string = features->string;
|
2018-02-19 21:26:24 -08:00
|
|
|
uint32_t seed = 5381;
|
|
|
|
uint32_t hash = seed, carry = 0;
|
|
|
|
size_t len = strlen(string);
|
2017-11-02 18:19:45 +00:00
|
|
|
|
2018-02-19 21:26:24 -08:00
|
|
|
/* portable murmur3 hash
|
|
|
|
* https://github.com/aappleby/smhasher/wiki/MurmurHash3
|
|
|
|
*/
|
2020-07-21 06:02:25 -07:00
|
|
|
PMurHash32_Process(&hash, &carry, string, len);
|
2018-02-19 21:26:24 -08:00
|
|
|
hash = PMurHash32_Result(hash, carry, len);
|
2017-11-02 18:19:45 +00:00
|
|
|
|
|
|
|
if (snprintf(features->hash, HASH_SIZE,
|
|
|
|
"%08" PRIx32, hash) >= HASH_SIZE) {
|
|
|
|
errno = ENOBUFS;
|
|
|
|
PERROR("Hash buffer full.");
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2015-03-25 17:09:26 -05:00
|
|
|
static bool islbrace(int c)
|
|
|
|
{
|
|
|
|
return c == '{';
|
|
|
|
}
|
|
|
|
|
|
|
|
static bool isrbrace(int c)
|
|
|
|
{
|
|
|
|
return c == '}';
|
|
|
|
}
|
|
|
|
|
|
|
|
static bool isnul(int c)
|
|
|
|
{
|
|
|
|
return c == '\0';
|
|
|
|
}
|
|
|
|
|
|
|
|
static bool isbrace(int c)
|
|
|
|
{
|
|
|
|
return islbrace(c) || isrbrace(c);
|
|
|
|
}
|
|
|
|
|
|
|
|
static bool isbrace_or_nul(int c)
|
|
|
|
{
|
|
|
|
return isbrace(c) || isnul(c);
|
|
|
|
}
|
|
|
|
|
|
|
|
static bool isbrace_space_or_nul(int c)
|
|
|
|
{
|
|
|
|
return isbrace(c) || isspace(c) || isnul(c);
|
|
|
|
}
|
|
|
|
|
|
|
|
static size_t tokenize_path_components(const char *str,
|
|
|
|
struct component *components,
|
|
|
|
size_t max_components)
|
|
|
|
{
|
|
|
|
size_t i = 0;
|
|
|
|
|
|
|
|
memset(components, 0, sizeof(*components) * max_components);
|
|
|
|
|
|
|
|
if (!str)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
while (*str && i < max_components) {
|
|
|
|
const char *fwdslash = strchrnul(str, '/');
|
|
|
|
|
|
|
|
/* Save the token if it is not "/" */
|
|
|
|
if (fwdslash != str) {
|
|
|
|
components[i].str = str;
|
|
|
|
components[i].len = fwdslash - str;
|
|
|
|
i++;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (isnul(*fwdslash))
|
|
|
|
break;
|
|
|
|
|
|
|
|
str = fwdslash + 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
return i;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* walk_one - walk one component of a features string
|
|
|
|
* @str: a pointer to the current position in a features string
|
|
|
|
* @component: the component to walk
|
|
|
|
* @is_top_level: true if component is a top-level component
|
|
|
|
*
|
|
|
|
* Imagine a features string such as:
|
|
|
|
*
|
|
|
|
* "feat1 { subfeat1.1 subfeat1.2 } feat2 { subfeat2.1 { subfeat2.1.1 } }"
|
|
|
|
*
|
|
|
|
* You want to know if "feat2/subfeat2.1/subfeat2.8" is valid. It will take 3
|
|
|
|
* invocations of this function to determine if that string is valid. On the
|
|
|
|
* first call, *@str will point to the beginning of the features string,
|
|
|
|
* component->str will be "feat2", and is_top_level will be true since feat2 is
|
|
|
|
* a top level feature. The function will return true and *@str will now point
|
|
|
|
* at the the left brace after "feat2" in the features string. You can call
|
|
|
|
* this function again with component->str being equal to "subfeat2.1" and it
|
|
|
|
* will return true and *@str will now point at the left brace after
|
|
|
|
* "subfeat2.1" in the features string. A third call to the function, with
|
|
|
|
* component->str equal to "subfeat2.8", will return false and *@str will not
|
|
|
|
* be changed.
|
|
|
|
*
|
|
|
|
* Returns true if the walk was successful and false otherwise. If the walk was
|
|
|
|
* successful, *@str will point to the first encountered brace after the walk.
|
|
|
|
* If the walk was unsuccessful, *@str is not updated.
|
|
|
|
*/
|
|
|
|
static bool walk_one(const char **str, const struct component *component,
|
|
|
|
bool is_top_level)
|
|
|
|
{
|
|
|
|
const char *cur;
|
|
|
|
uint32_t brace_count = 0;
|
|
|
|
size_t i = 0;
|
|
|
|
|
|
|
|
/* NULL pointers and empty strings are not accepted */
|
|
|
|
if (!str || !*str || !component || !component->str || !component->len)
|
|
|
|
return false;
|
|
|
|
|
|
|
|
cur = *str;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* If @component is not top-level, the first character in the string to
|
|
|
|
* search MUST be '{'
|
|
|
|
*/
|
|
|
|
if (!is_top_level) {
|
|
|
|
if (!islbrace(*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(!isnul(*cur) && i < component->len) {
|
|
|
|
if (!isascii(*cur)) {
|
|
|
|
/* Only ASCII is expected */
|
|
|
|
return false;
|
|
|
|
} else if (islbrace(*cur)) {
|
|
|
|
/* There's a limit to the number of left braces */
|
|
|
|
if (brace_count == UINT32_MAX)
|
|
|
|
return false;
|
|
|
|
|
|
|
|
brace_count++;
|
|
|
|
} else if (isrbrace(*cur)) {
|
|
|
|
/* Check for unexpected right braces */
|
|
|
|
if (brace_count == 0)
|
|
|
|
return false;
|
|
|
|
|
|
|
|
brace_count--;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Move to the next character in @component if we're not inside
|
|
|
|
* of braces and we have a character match
|
|
|
|
*/
|
|
|
|
if (brace_count == 0 && *cur == component->str[i])
|
|
|
|
i++;
|
|
|
|
else
|
|
|
|
i = 0;
|
|
|
|
|
|
|
|
cur++;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Return false if a full match was not found */
|
|
|
|
if (i != component->len) {
|
|
|
|
return false;
|
|
|
|
} else if (!isbrace_space_or_nul(*cur))
|
|
|
|
return false;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* This loop eats up valid (ASCII) characters until a brace or NUL
|
|
|
|
* character is found so that *@str is properly set to call back into
|
|
|
|
* this function
|
|
|
|
*/
|
|
|
|
while (!isbrace_or_nul(*cur)) {
|
|
|
|
if (!isascii(*cur))
|
|
|
|
return false;
|
|
|
|
|
|
|
|
cur++;
|
|
|
|
}
|
|
|
|
|
|
|
|
*str = cur;
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2015-03-25 17:09:26 -05:00
|
|
|
/**
|
2015-06-15 15:11:51 -05:00
|
|
|
* aa_features_new - create a new aa_features object based on a path
|
2015-03-25 17:09:26 -05:00
|
|
|
* @features: will point to the address of an allocated and initialized
|
|
|
|
* aa_features object upon success
|
2015-06-15 15:11:51 -05:00
|
|
|
* @dirfd: directory file descriptor or AT_FDCWD (see openat(2))
|
2015-03-25 17:09:26 -05:00
|
|
|
* @path: path to a features file or directory
|
|
|
|
*
|
|
|
|
* Returns: 0 on success, -1 on error with errno set and *@features pointing to
|
|
|
|
* NULL
|
|
|
|
*/
|
2015-06-15 15:11:51 -05:00
|
|
|
int aa_features_new(aa_features **features, int dirfd, const char *path)
|
2015-03-25 17:09:25 -05:00
|
|
|
{
|
|
|
|
struct stat stat_file;
|
2015-03-25 17:09:26 -05:00
|
|
|
aa_features *f;
|
2016-09-30 15:03:02 -05:00
|
|
|
ssize_t retval;
|
2015-03-25 17:09:25 -05:00
|
|
|
|
2015-03-25 17:09:26 -05:00
|
|
|
*features = NULL;
|
|
|
|
|
2015-06-15 15:11:51 -05:00
|
|
|
if (fstatat(dirfd, path, &stat_file, 0) == -1)
|
2015-03-25 17:09:26 -05:00
|
|
|
return -1;
|
|
|
|
|
2015-03-25 17:09:27 -05:00
|
|
|
f = calloc(1, sizeof(*f));
|
2015-03-25 17:09:26 -05:00
|
|
|
if (!f) {
|
|
|
|
errno = ENOMEM;
|
2015-03-25 17:09:25 -05:00
|
|
|
return -1;
|
2015-03-25 17:09:26 -05:00
|
|
|
}
|
|
|
|
aa_features_ref(f);
|
2015-03-25 17:09:25 -05:00
|
|
|
|
2015-03-25 17:09:26 -05:00
|
|
|
retval = S_ISDIR(stat_file.st_mode) ?
|
2015-06-15 15:11:51 -05:00
|
|
|
load_features_dir(dirfd, path, f->string, STRING_SIZE) :
|
|
|
|
load_features_file(dirfd, path, f->string, STRING_SIZE);
|
2015-06-15 15:11:51 -05:00
|
|
|
if (retval == -1) {
|
2015-03-25 17:09:26 -05:00
|
|
|
aa_features_unref(f);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2017-11-02 18:19:45 +00:00
|
|
|
if (init_features_hash(f) == -1) {
|
|
|
|
int save = errno;
|
|
|
|
|
|
|
|
aa_features_unref(f);
|
|
|
|
errno = save;
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2015-03-25 17:09:26 -05:00
|
|
|
*features = f;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2015-06-15 15:11:51 -05:00
|
|
|
* aa_features_new_from_string - create a new aa_features object based on a string
|
2015-03-25 17:09:26 -05:00
|
|
|
* @features: will point to the address of an allocated and initialized
|
|
|
|
* aa_features object upon success
|
|
|
|
* @string: a NUL-terminated string representation of features
|
|
|
|
* @size: the size of @string, not counting the NUL-terminator
|
|
|
|
*
|
|
|
|
* Returns: 0 on success, -1 on error with errno set and *@features pointing to
|
|
|
|
* NULL
|
|
|
|
*/
|
|
|
|
int aa_features_new_from_string(aa_features **features,
|
|
|
|
const char *string, size_t size)
|
|
|
|
{
|
|
|
|
aa_features *f;
|
|
|
|
|
|
|
|
*features = NULL;
|
|
|
|
|
|
|
|
/* Require size to be less than STRING_SIZE so there's room for a NUL */
|
|
|
|
if (size >= STRING_SIZE)
|
|
|
|
return ENOBUFS;
|
|
|
|
|
2015-03-25 17:09:27 -05:00
|
|
|
f = calloc(1, sizeof(*f));
|
2015-03-25 17:09:26 -05:00
|
|
|
if (!f) {
|
|
|
|
errno = ENOMEM;
|
|
|
|
return -1;
|
2015-03-25 17:09:25 -05:00
|
|
|
}
|
2015-03-25 17:09:26 -05:00
|
|
|
aa_features_ref(f);
|
|
|
|
|
|
|
|
memcpy(f->string, string, size);
|
|
|
|
f->string[size] = '\0';
|
2017-11-02 18:19:45 +00:00
|
|
|
|
|
|
|
if (init_features_hash(f) == -1) {
|
|
|
|
int save = errno;
|
|
|
|
|
|
|
|
aa_features_unref(f);
|
|
|
|
errno = save;
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2015-03-25 17:09:26 -05:00
|
|
|
*features = f;
|
2015-03-25 17:09:25 -05:00
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
2015-03-25 17:09:26 -05:00
|
|
|
|
|
|
|
/**
|
2015-06-15 15:11:51 -05:00
|
|
|
* aa_features_new_from_kernel - create a new aa_features object based on the current kernel
|
2015-03-25 17:09:26 -05:00
|
|
|
* @features: will point to the address of an allocated and initialized
|
|
|
|
* aa_features object upon success
|
|
|
|
*
|
|
|
|
* Returns: 0 on success, -1 on error with errno set and *@features pointing to
|
|
|
|
* NULL
|
|
|
|
*/
|
|
|
|
int aa_features_new_from_kernel(aa_features **features)
|
|
|
|
{
|
2015-06-15 15:11:51 -05:00
|
|
|
return aa_features_new(features, -1, FEATURES_FILE);
|
2015-03-25 17:09:26 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2015-06-15 15:11:51 -05:00
|
|
|
* aa_features_ref - increments the ref count of an aa_features object
|
2015-03-25 17:09:26 -05:00
|
|
|
* @features: the features
|
|
|
|
*
|
|
|
|
* Returns: the features
|
|
|
|
*/
|
|
|
|
aa_features *aa_features_ref(aa_features *features)
|
|
|
|
{
|
|
|
|
atomic_inc(&features->ref_count);
|
|
|
|
return features;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2015-06-15 15:11:51 -05:00
|
|
|
* aa_features_unref - decrements the ref count and frees the aa_features object when 0
|
2015-03-25 17:09:26 -05:00
|
|
|
* @features: the features (can be NULL)
|
|
|
|
*/
|
|
|
|
void aa_features_unref(aa_features *features)
|
|
|
|
{
|
2017-11-03 15:18:45 +00:00
|
|
|
int save = errno;
|
|
|
|
|
2015-03-25 17:09:26 -05:00
|
|
|
if (features && atomic_dec_and_test(&features->ref_count))
|
|
|
|
free(features);
|
2017-11-03 15:18:45 +00:00
|
|
|
|
|
|
|
errno = save;
|
2015-03-25 17:09:26 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2015-06-15 15:11:51 -05:00
|
|
|
* aa_features_write_to_file - write a string representation of an aa_features object to a file
|
2015-03-25 17:09:26 -05:00
|
|
|
* @features: the features
|
2015-06-15 15:11:51 -05:00
|
|
|
* @dirfd: directory file descriptor or AT_FDCWD (see openat(2))
|
2015-03-25 17:09:26 -05:00
|
|
|
* @path: the path to write to
|
2015-03-25 17:09:26 -05:00
|
|
|
*
|
2015-03-25 17:09:26 -05:00
|
|
|
* Returns: 0 on success, -1 on error with errno set
|
2015-03-25 17:09:26 -05:00
|
|
|
*/
|
2015-06-15 15:11:51 -05:00
|
|
|
int aa_features_write_to_file(aa_features *features,
|
|
|
|
int dirfd, const char *path)
|
2015-03-25 17:09:26 -05:00
|
|
|
{
|
2015-03-25 17:09:26 -05:00
|
|
|
autoclose int fd = -1;
|
|
|
|
size_t size;
|
|
|
|
ssize_t retval;
|
|
|
|
char *string;
|
|
|
|
|
2015-06-15 15:11:51 -05:00
|
|
|
fd = openat(dirfd, path,
|
|
|
|
O_WRONLY | O_CREAT | O_TRUNC | O_SYNC | O_CLOEXEC,
|
|
|
|
S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
|
2015-03-25 17:09:26 -05:00
|
|
|
if (fd == -1)
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
string = features->string;
|
|
|
|
size = strlen(string);
|
|
|
|
do {
|
|
|
|
retval = write(fd, string, size);
|
|
|
|
if (retval == -1)
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
size -= retval;
|
|
|
|
string += retval;
|
|
|
|
} while (size);
|
|
|
|
|
|
|
|
return 0;
|
2015-03-25 17:09:26 -05:00
|
|
|
}
|
2015-03-25 17:09:26 -05:00
|
|
|
|
|
|
|
/**
|
2015-06-15 15:11:51 -05:00
|
|
|
* aa_features_is_equal - equality test for two aa_features objects
|
2015-03-25 17:09:26 -05:00
|
|
|
* @features1: the first features (can be NULL)
|
|
|
|
* @features2: the second features (can be NULL)
|
|
|
|
*
|
|
|
|
* Returns: true if they're equal, false if they're not or either are NULL
|
|
|
|
*/
|
|
|
|
bool aa_features_is_equal(aa_features *features1, aa_features *features2)
|
|
|
|
{
|
|
|
|
return features1 && features2 &&
|
|
|
|
strcmp(features1->string, features2->string) == 0;
|
|
|
|
}
|
2015-03-25 17:09:26 -05:00
|
|
|
|
|
|
|
/**
|
2015-06-15 15:11:51 -05:00
|
|
|
* aa_features_supports - provides aa_features object support status
|
2015-03-25 17:09:26 -05:00
|
|
|
* @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, const char *str)
|
|
|
|
{
|
|
|
|
const char *features_string = features->string;
|
|
|
|
struct component components[32];
|
|
|
|
size_t i, num_components;
|
|
|
|
|
|
|
|
/* 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", 5 } as the first
|
|
|
|
* component, { "mask", 4 } as the second, and { "mount", 5 } as the
|
|
|
|
* third
|
|
|
|
*/
|
|
|
|
num_components = tokenize_path_components(str, components,
|
|
|
|
sizeof(components) / sizeof(*components));
|
|
|
|
|
|
|
|
/* At least one valid token is required */
|
|
|
|
if (!num_components)
|
|
|
|
return false;
|
|
|
|
|
|
|
|
/* Ensure that all components are valid and found */
|
|
|
|
for (i = 0; i < num_components; i++) {
|
|
|
|
if (!walk_one(&features_string, &components[i], i == 0))
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
2017-11-02 18:20:01 +00:00
|
|
|
|
|
|
|
/**
|
|
|
|
* aa_features_id - provides unique identifier for an aa_features object
|
|
|
|
* @features: the features
|
|
|
|
*
|
|
|
|
* Allocates and returns a string representation of an identifier that can
|
|
|
|
* be used to uniquely identify an aa_features object. The mechanism for
|
|
|
|
* generating the string representation is internal to libapparmor and
|
|
|
|
* subject to change but an example implementation is applying a hash
|
|
|
|
* function to the features string.
|
|
|
|
*
|
|
|
|
* Returns: a string identifying @features which must be freed by the
|
|
|
|
* caller or NULL, with errno set, upon error
|
|
|
|
*/
|
|
|
|
char *aa_features_id(aa_features *features)
|
|
|
|
{
|
|
|
|
return strdup(features->hash);
|
|
|
|
}
|