The existing kernel_interface.c file collides with the expected file name of the implementation of the aa_kernel_interface API.
Signed-off-by: Tyler Hicks <[email protected]> --- libraries/libapparmor/src/Makefile.am | 2 +- libraries/libapparmor/src/kernel.c | 778 +++++++++++++++++++++++++++ libraries/libapparmor/src/kernel_interface.c | 778 --------------------------- 3 files changed, 779 insertions(+), 779 deletions(-) create mode 100644 libraries/libapparmor/src/kernel.c delete mode 100644 libraries/libapparmor/src/kernel_interface.c diff --git a/libraries/libapparmor/src/Makefile.am b/libraries/libapparmor/src/Makefile.am index bc7412c..5251092 100644 --- a/libraries/libapparmor/src/Makefile.am +++ b/libraries/libapparmor/src/Makefile.am @@ -48,7 +48,7 @@ af_protos.h: /usr/include/netinet/in.h lib_LTLIBRARIES = libapparmor.la noinst_HEADERS = grammar.h parser.h scanner.h af_protos.h private.h -libapparmor_la_SOURCES = grammar.y libaalogparse.c kernel_interface.c scanner.c private.c match.c features.c +libapparmor_la_SOURCES = grammar.y libaalogparse.c kernel.c scanner.c private.c match.c features.c libapparmor_la_LDFLAGS = -version-info $(AA_LIB_CURRENT):$(AA_LIB_REVISION):$(AA_LIB_AGE) -XCClinker -dynamic -pthread \ -Wl,--version-script=$(top_srcdir)/src/libapparmor.map diff --git a/libraries/libapparmor/src/kernel.c b/libraries/libapparmor/src/kernel.c new file mode 100644 index 0000000..e3ef04a --- /dev/null +++ b/libraries/libapparmor/src/kernel.c @@ -0,0 +1,778 @@ +/* + * Copyright (c) 2003-2008 Novell, Inc. (All rights reserved) + * Copyright 2009-2010 Canonical Ltd. + * + * The libapparmor library is licensed under the terms of the GNU + * Lesser General Public License, version 2.1. Please see the file + * COPYING.LGPL. + * + * This library 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 Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#include <unistd.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <sys/syscall.h> +#include <sys/socket.h> +#include <fcntl.h> +#include <errno.h> +#include <limits.h> +#include <stdarg.h> +#include <mntent.h> +#include <inttypes.h> +#include <pthread.h> + +#include <sys/apparmor.h> + +/* some non-Linux systems do not define a static value */ +#ifndef PATH_MAX +# define PATH_MAX 4096 +#endif + +#define symbol_version(real, name, version) \ + __asm__ (".symver " #real "," #name "@" #version) +#define default_symbol_version(real, name, version) \ + __asm__ (".symver " #real "," #name "@@" #version) + +/** + * aa_find_mountpoint - find where the apparmor interface filesystem is mounted + * @mnt: returns buffer with the mountpoint string + * + * Returns: 0 on success else -1 on error + * + * NOTE: this function only supports versions of apparmor using securityfs + */ +int aa_find_mountpoint(char **mnt) +{ + struct stat statbuf; + struct mntent *mntpt; + FILE *mntfile; + int rc = -1; + + if (!mnt) { + errno = EINVAL; + return -1; + } + + mntfile = setmntent("/proc/mounts", "r"); + if (!mntfile) + return -1; + + while ((mntpt = getmntent(mntfile))) { + char *proposed = NULL; + if (strcmp(mntpt->mnt_type, "securityfs") != 0) + continue; + + if (asprintf(&proposed, "%s/apparmor", mntpt->mnt_dir) < 0) + /* ENOMEM */ + break; + + if (stat(proposed, &statbuf) == 0) { + *mnt = proposed; + rc = 0; + break; + } + free(proposed); + } + endmntent(mntfile); + if (rc == -1) + errno = ENOENT; + return rc; +} + +/** + * aa_is_enabled - determine if apparmor is enabled + * + * Returns: 1 if enabled else reason it is not, or 0 on error + * + * ENOSYS - no indication apparmor is present in the system + * ENOENT - enabled but interface could not be found + * ECANCELED - disabled at boot + * ENOMEM - out of memory + */ +int aa_is_enabled(void) +{ + int serrno, fd, rc, size; + char buffer[2]; + char *mnt; + + /* if the interface mountpoint is available apparmor is enabled */ + rc = aa_find_mountpoint(&mnt); + if (rc == 0) { + free(mnt); + return 1; + } + + /* determine why the interface mountpoint isn't available */ + fd = open("/sys/module/apparmor/parameters/enabled", O_RDONLY); + if (fd == -1) { + if (errno == ENOENT) + errno = ENOSYS; + return 0; + } + + size = read(fd, &buffer, 2); + serrno = errno; + close(fd); + errno = serrno; + + if (size > 0) { + if (buffer[0] == 'Y') + errno = ENOENT; + else + errno = ECANCELED; + } + return 0; +} + +static inline pid_t aa_gettid(void) +{ +#ifdef SYS_gettid + return syscall(SYS_gettid); +#else + return getpid(); +#endif +} + +static char *procattr_path(pid_t pid, const char *attr) +{ + char *path = NULL; + if (asprintf(&path, "/proc/%d/attr/%s", pid, attr) > 0) + return path; + return NULL; +} + +/** + * parse_confinement_mode - get the mode from the confinement string + * @con: the confinement string + * @size: size of the confinement string + * + * Modifies con to NUL-terminate the label string and the mode string. + * + * Returns: a pointer to the NUL-terminated mode inside the confinement string + * or NULL if the mode was not found + */ +static char *parse_confinement_mode(char *con, int size) +{ + if (strcmp(con, "unconfined") != 0 && + size > 4 && con[size - 2] == ')') { + int pos = size - 3; + + while (pos > 0 && !(con[pos] == ' ' && con[pos + 1] == '(')) + pos--; + if (pos > 0) { + con[pos] = 0; /* overwrite ' ' */ + con[size - 2] = 0; /* overwrite trailing ) */ + return &con[pos + 2]; /* skip '(' */ + } + } + return NULL; +} + +/** + * aa_getprocattr_raw - get the contents of @attr for @tid into @buf + * @tid: tid of task to query + * @attr: which /proc/<tid>/attr/<attr> to query + * @buf: buffer to store the result in + * @len: size of the buffer + * @mode: if non-NULL and a mode is present, will point to mode string in @buf + * + * Returns: size of data read or -1 on error, and sets errno + */ +int aa_getprocattr_raw(pid_t tid, const char *attr, char *buf, int len, + char **mode) +{ + int rc = -1; + int fd, ret; + char *tmp = NULL; + char *mode_str; + int size = 0; + + if (!buf || len <= 0) { + errno = EINVAL; + goto out; + } + + tmp = procattr_path(tid, attr); + if (!tmp) + goto out; + + fd = open(tmp, O_RDONLY); + free(tmp); + if (fd == -1) { + goto out; + } + + tmp = buf; + do { + ret = read(fd, tmp, len); + if (ret <= 0) + break; + tmp += ret; + size += ret; + len -= ret; + if (len < 0) { + errno = ERANGE; + goto out2; + } + } while (ret > 0); + + if (ret < 0) { + int saved; + if (ret != -1) { + errno = EPROTO; + } + saved = errno; + (void)close(fd); + errno = saved; + goto out; + } else if (size > 0 && buf[size - 1] != 0) { + /* check for null termination */ + if (buf[size - 1] == '\n') { + buf[size - 1] = 0; + } else if (len == 0) { + errno = ERANGE; + goto out2; + } else { + buf[size] = 0; + size++; + } + + mode_str = parse_confinement_mode(buf, size); + if (mode) + *mode = mode_str; + } + rc = size; + +out2: + (void)close(fd); +out: + return rc; +} + +#define INITIAL_GUESS_SIZE 128 + +/** + * aa_getprocattr - get the contents of @attr for @tid into @buf + * @tid: tid of task to query + * @attr: which /proc/<tid>/attr/<attr> to query + * @con: allocated buffer the result is stored in + * @mode: if non-NULL and a mode is present, will point to mode string in @con + * + * Returns: size of data read or -1 on error, and sets errno + * + * Guarantees that @con and @mode are null terminated. The length returned + * is for all data including both @con and @mode, and maybe > than strlen(@con) + * even if @mode is NULL + * + * Caller is responsible for freeing the buffer returned in @con. @mode is + * always contained within @con's buffer and so NEVER do free(@mode) + */ +int aa_getprocattr(pid_t tid, const char *attr, char **con, char **mode) +{ + int rc, size = INITIAL_GUESS_SIZE/2; + char *buffer = NULL; + + if (!con) { + errno = EINVAL; + return -1; + } + + do { + size <<= 1; + buffer = realloc(buffer, size); + if (!buffer) + return -1; + memset(buffer, 0, size); + + rc = aa_getprocattr_raw(tid, attr, buffer, size, mode); + } while (rc == -1 && errno == ERANGE); + + if (rc == -1) { + free(buffer); + *con = NULL; + if (mode) + *mode = NULL; + } else + *con = buffer; + + return rc; +} + +static int setprocattr(pid_t tid, const char *attr, const char *buf, int len) +{ + int rc = -1; + int fd, ret; + char *ctl = NULL; + + if (!buf) { + errno = EINVAL; + goto out; + } + + ctl = procattr_path(tid, attr); + if (!ctl) + goto out; + + fd = open(ctl, O_WRONLY); + if (fd == -1) { + goto out; + } + + ret = write(fd, buf, len); + if (ret != len) { + int saved; + if (ret != -1) { + errno = EPROTO; + } + saved = errno; + (void)close(fd); + errno = saved; + goto out; + } + + rc = 0; + (void)close(fd); + +out: + if (ctl) { + free(ctl); + } + return rc; +} + +int aa_change_hat(const char *subprofile, unsigned long token) +{ + int rc = -1; + int len = 0; + char *buf = NULL; + const char *fmt = "changehat %016lx^%s"; + + /* both may not be null */ + if (!(token || subprofile)) { + errno = EINVAL; + goto out; + } + + if (subprofile && strnlen(subprofile, PATH_MAX + 1) > PATH_MAX) { + errno = EPROTO; + goto out; + } + + len = asprintf(&buf, fmt, token, subprofile ? subprofile : ""); + if (len < 0) { + goto out; + } + + rc = setprocattr(aa_gettid(), "current", buf, len); +out: + if (buf) { + /* clear local copy of magic token before freeing */ + memset(buf, '\0', len); + free(buf); + } + return rc; +} + +/* original change_hat interface */ +int __change_hat(char *subprofile, unsigned int token) +{ + return aa_change_hat(subprofile, (unsigned long) token); +} + +int aa_change_profile(const char *profile) +{ + char *buf = NULL; + int len; + int rc; + + if (!profile) { + errno = EINVAL; + return -1; + } + + len = asprintf(&buf, "changeprofile %s", profile); + if (len < 0) + return -1; + + rc = setprocattr(aa_gettid(), "current", buf, len); + + free(buf); + return rc; +} + +int aa_change_onexec(const char *profile) +{ + char *buf = NULL; + int len; + int rc; + + if (!profile) { + errno = EINVAL; + return -1; + } + + len = asprintf(&buf, "exec %s", profile); + if (len < 0) + return -1; + + rc = setprocattr(aa_gettid(), "exec", buf, len); + + free(buf); + return rc; +} + +/* create an alias for the old change_hat@IMMUNIX_1.0 symbol */ +extern typeof((__change_hat)) __old_change_hat __attribute__((alias ("__change_hat"))); +symbol_version(__old_change_hat, change_hat, IMMUNIX_1.0); +default_symbol_version(__change_hat, change_hat, APPARMOR_1.0); + + +int aa_change_hatv(const char *subprofiles[], unsigned long token) +{ + int size, totallen = 0, hatcount = 0; + int rc = -1; + const char **hats; + char *pos, *buf = NULL; + const char *cmd = "changehat"; + + /* both may not be null */ + if (!token && !(subprofiles && *subprofiles)) { + errno = EINVAL; + goto out; + } + + /* validate hat lengths and while we are at it count how many and + * mem required */ + if (subprofiles) { + for (hats = subprofiles; *hats; hats++) { + int len = strnlen(*hats, PATH_MAX + 1); + if (len > PATH_MAX) { + errno = EPROTO; + goto out; + } + totallen += len + 1; + hatcount++; + } + } + + /* allocate size of cmd + space + token + ^ + vector of hats */ + size = strlen(cmd) + 18 + totallen + 1; + buf = malloc(size); + if (!buf) { + goto out; + } + + /* setup command string which is of the form + * changehat <token>^hat1\0hat2\0hat3\0..\0 + */ + sprintf(buf, "%s %016lx^", cmd, token); + pos = buf + strlen(buf); + if (subprofiles) { + for (hats = subprofiles; *hats; hats++) { + strcpy(pos, *hats); + pos += strlen(*hats) + 1; + } + } else + /* step pos past trailing \0 */ + pos++; + + rc = setprocattr(aa_gettid(), "current", buf, pos - buf); + +out: + if (buf) { + /* clear local copy of magic token before freeing */ + memset(buf, '\0', size); + free(buf); + } + + return rc; +} + +/** + * change_hat_vargs - change_hatv but passing the hats as fn arguments + * @token: the magic token + * @nhat: the number of hats being passed in the arguments + * ...: a argument list of const char * being passed + * + * change_hat_vargs can be called directly but it is meant to be called + * through its macro wrapper of the same name. Which automatically + * fills in the nhats arguments based on the number of parameters + * passed. + * to call change_hat_vargs direction do + * (change_hat_vargs)(token, nhats, hat1, hat2...) + */ +int (aa_change_hat_vargs)(unsigned long token, int nhats, ...) +{ + va_list ap; + const char *argv[nhats+1]; + int i; + + va_start(ap, nhats); + for (i = 0; i < nhats ; i++) { + argv[i] = va_arg(ap, char *); + } + argv[nhats] = NULL; + va_end(ap); + return aa_change_hatv(argv, token); +} + +/** + * aa_gettaskcon - get the confinement for task @target in an allocated buffer + * @target: task to query + * @con: pointer to returned buffer with the confinement string + * @mode: if non-NULL and a mode is present, will point to mode string in @con + * + * Returns: length of confinement data or -1 on error and sets errno + * + * Guarantees that @con and @mode are null terminated. The length returned + * is for all data including both @con and @mode, and maybe > than strlen(@con) + * even if @mode is NULL + * + * Caller is responsible for freeing the buffer returned in @con. @mode is + * always contained within @con's buffer and so NEVER do free(@mode) + */ +int aa_gettaskcon(pid_t target, char **con, char **mode) +{ + return aa_getprocattr(target, "current", con, mode); +} + +/** + * aa_getcon - get the confinement for current task in an allocated buffer + * @con: pointer to return buffer with the confinement if successful + * @mode: if non-NULL and a mode is present, will point to mode string in @con + * + * Returns: length of confinement data or -1 on error and sets errno + * + * Guarantees that @con and @mode are null terminated. The length returned + * is for all data including both @con and @mode, and may > than strlen(@con) + * even if @mode is NULL + * + * Caller is responsible for freeing the buffer returned in @con. @mode is + * always contained within @con's buffer and so NEVER do free(@mode) + */ +int aa_getcon(char **con, char **mode) +{ + return aa_gettaskcon(aa_gettid(), con, mode); +} + + +#ifndef SO_PEERSEC +#define SO_PEERSEC 31 +#endif + +/** + * aa_getpeercon_raw - get the confinement of the socket's peer (other end) + * @fd: socket to get peer confinement for + * @buf: buffer to store the result in + * @len: initially contains size of the buffer, returns size of data read + * @mode: if non-NULL and a mode is present, will point to mode string in @buf + * + * Returns: length of confinement data including null termination or -1 on error + * if errno == ERANGE then @len will hold the size needed + */ +int aa_getpeercon_raw(int fd, char *buf, int *len, char **mode) +{ + socklen_t optlen = *len; + char *mode_str; + int rc; + + if (optlen <= 0 || buf == NULL) { + errno = EINVAL; + return -1; + } + + rc = getsockopt(fd, SOL_SOCKET, SO_PEERSEC, buf, &optlen); + if (rc == -1 || optlen <= 0) + goto out; + + /* check for null termination */ + if (buf[optlen - 1] != 0) { + if (optlen < *len) { + buf[optlen] = 0; + optlen++; + } else { + /* buf needs to be bigger by 1 */ + rc = -1; + errno = ERANGE; + optlen++; + goto out; + } + } + + mode_str = parse_confinement_mode(buf, optlen); + if (mode) + *mode = mode_str; + + rc = optlen; +out: + *len = optlen; + return rc; +} + +/** + * aa_getpeercon - get the confinement of the socket's peer (other end) + * @fd: socket to get peer confinement for + * @con: pointer to allocated buffer with the confinement string + * @mode: if non-NULL and a mode is present, will point to mode string in @con + * + * Returns: length of confinement data including null termination or -1 on error + * + * Guarantees that @con and @mode are null terminated. The length returned + * is for all data including both @con and @mode, and maybe > than strlen(@con) + * even if @mode is NULL + * + * Caller is responsible for freeing the buffer returned in @con. @mode is + * always contained within @con's buffer and so NEVER do free(@mode) + */ +int aa_getpeercon(int fd, char **con, char **mode) +{ + int rc, last_size, size = INITIAL_GUESS_SIZE; + char *buffer = NULL; + + if (!con) { + errno = EINVAL; + return -1; + } + + do { + last_size = size; + buffer = realloc(buffer, size); + if (!buffer) + return -1; + memset(buffer, 0, size); + + rc = aa_getpeercon_raw(fd, buffer, &size, mode); + /* size should contain actual size needed if errno == ERANGE */ + } while (rc == -1 && errno == ERANGE && size > last_size); + + if (rc == -1) { + free(buffer); + *con = NULL; + if (mode) + *mode = NULL; + size = -1; + } else + *con = buffer; + + return size; +} + +static pthread_once_t aafs_access_control = PTHREAD_ONCE_INIT; +static char *aafs_access = NULL; + +static void aafs_access_init_once(void) +{ + char *aafs; + int ret; + + ret = aa_find_mountpoint(&aafs); + if (ret < 0) + return; + + ret = asprintf(&aafs_access, "%s/.access", aafs); + if (ret < 0) + aafs_access = NULL; + + free(aafs); +} + +/* "allow 0x00000000\ndeny 0x00000000\naudit 0x00000000\nquiet 0x00000000\n" */ +#define QUERY_LABEL_REPLY_LEN 67 + +/** + * aa_query_label - query the access(es) of a label + * @mask: permission bits to query + * @query: binary query string, must be offset by AA_QUERY_CMD_LABEL_SIZE + * @size: size of the query string must include AA_QUERY_CMD_LABEL_SIZE + * @allowed: upon successful return, will be 1 if query is allowed and 0 if not + * @audited: upon successful return, will be 1 if query should be audited and 0 + * if not + * + * Returns: 0 on success else -1 and sets errno. If -1 is returned and errno is + * ENOENT, the subject label in the query string is unknown to the + * kernel. + */ +int query_label(uint32_t mask, char *query, size_t size, int *allowed, + int *audited) +{ + char buf[QUERY_LABEL_REPLY_LEN]; + uint32_t allow, deny, audit, quiet; + int fd, ret, saved; + + if (!mask || size <= AA_QUERY_CMD_LABEL_SIZE) { + errno = EINVAL; + return -1; + } + + ret = pthread_once(&aafs_access_control, aafs_access_init_once); + if (ret) { + errno = EINVAL; + return -1; + } else if (!aafs_access) { + errno = ENOMEM; + return -1; + } + + fd = open(aafs_access, O_RDWR); + if (fd == -1) { + if (errno == ENOENT) + errno = EPROTONOSUPPORT; + return -1; + } + + memcpy(query, AA_QUERY_CMD_LABEL, AA_QUERY_CMD_LABEL_SIZE); + errno = 0; + ret = write(fd, query, size); + if (ret != size) { + if (ret >= 0) + errno = EPROTO; + /* IMPORTANT: This is the only valid error path that can have + * errno set to ENOENT. It indicates that the subject label + * could not be found by the kernel. + */ + (void)close(fd); + return -1; + } + + ret = read(fd, buf, QUERY_LABEL_REPLY_LEN); + saved = errno; + (void)close(fd); + errno = saved; + if (ret != QUERY_LABEL_REPLY_LEN) { + errno = EPROTO; + return -1; + } + + ret = sscanf(buf, "allow 0x%8" SCNx32 "\n" + "deny 0x%8" SCNx32 "\n" + "audit 0x%8" SCNx32 "\n" + "quiet 0x%8" SCNx32 "\n", + &allow, &deny, &audit, &quiet); + if (ret != 4) { + errno = EPROTONOSUPPORT; + return -1; + } + + *allowed = mask & ~(allow & ~deny) ? 0 : 1; + if (!(*allowed)) + audit = 0xFFFFFFFF; + *audited = mask & ~(audit & ~quiet) ? 0 : 1; + + return 0; +} + +/* export multiple aa_query_label symbols to compensate for downstream + * releases with differing symbol versions. */ +extern typeof((query_label)) __aa_query_label __attribute__((alias ("query_label"))); +symbol_version(__aa_query_label, aa_query_label, APPARMOR_1.1); +default_symbol_version(query_label, aa_query_label, APPARMOR_2.9); diff --git a/libraries/libapparmor/src/kernel_interface.c b/libraries/libapparmor/src/kernel_interface.c deleted file mode 100644 index e3ef04a..0000000 --- a/libraries/libapparmor/src/kernel_interface.c +++ /dev/null @@ -1,778 +0,0 @@ -/* - * Copyright (c) 2003-2008 Novell, Inc. (All rights reserved) - * Copyright 2009-2010 Canonical Ltd. - * - * The libapparmor library is licensed under the terms of the GNU - * Lesser General Public License, version 2.1. Please see the file - * COPYING.LGPL. - * - * This library 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 Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - */ - -#include <stdlib.h> -#include <stdio.h> -#include <string.h> -#include <unistd.h> -#include <sys/types.h> -#include <sys/stat.h> -#include <sys/syscall.h> -#include <sys/socket.h> -#include <fcntl.h> -#include <errno.h> -#include <limits.h> -#include <stdarg.h> -#include <mntent.h> -#include <inttypes.h> -#include <pthread.h> - -#include <sys/apparmor.h> - -/* some non-Linux systems do not define a static value */ -#ifndef PATH_MAX -# define PATH_MAX 4096 -#endif - -#define symbol_version(real, name, version) \ - __asm__ (".symver " #real "," #name "@" #version) -#define default_symbol_version(real, name, version) \ - __asm__ (".symver " #real "," #name "@@" #version) - -/** - * aa_find_mountpoint - find where the apparmor interface filesystem is mounted - * @mnt: returns buffer with the mountpoint string - * - * Returns: 0 on success else -1 on error - * - * NOTE: this function only supports versions of apparmor using securityfs - */ -int aa_find_mountpoint(char **mnt) -{ - struct stat statbuf; - struct mntent *mntpt; - FILE *mntfile; - int rc = -1; - - if (!mnt) { - errno = EINVAL; - return -1; - } - - mntfile = setmntent("/proc/mounts", "r"); - if (!mntfile) - return -1; - - while ((mntpt = getmntent(mntfile))) { - char *proposed = NULL; - if (strcmp(mntpt->mnt_type, "securityfs") != 0) - continue; - - if (asprintf(&proposed, "%s/apparmor", mntpt->mnt_dir) < 0) - /* ENOMEM */ - break; - - if (stat(proposed, &statbuf) == 0) { - *mnt = proposed; - rc = 0; - break; - } - free(proposed); - } - endmntent(mntfile); - if (rc == -1) - errno = ENOENT; - return rc; -} - -/** - * aa_is_enabled - determine if apparmor is enabled - * - * Returns: 1 if enabled else reason it is not, or 0 on error - * - * ENOSYS - no indication apparmor is present in the system - * ENOENT - enabled but interface could not be found - * ECANCELED - disabled at boot - * ENOMEM - out of memory - */ -int aa_is_enabled(void) -{ - int serrno, fd, rc, size; - char buffer[2]; - char *mnt; - - /* if the interface mountpoint is available apparmor is enabled */ - rc = aa_find_mountpoint(&mnt); - if (rc == 0) { - free(mnt); - return 1; - } - - /* determine why the interface mountpoint isn't available */ - fd = open("/sys/module/apparmor/parameters/enabled", O_RDONLY); - if (fd == -1) { - if (errno == ENOENT) - errno = ENOSYS; - return 0; - } - - size = read(fd, &buffer, 2); - serrno = errno; - close(fd); - errno = serrno; - - if (size > 0) { - if (buffer[0] == 'Y') - errno = ENOENT; - else - errno = ECANCELED; - } - return 0; -} - -static inline pid_t aa_gettid(void) -{ -#ifdef SYS_gettid - return syscall(SYS_gettid); -#else - return getpid(); -#endif -} - -static char *procattr_path(pid_t pid, const char *attr) -{ - char *path = NULL; - if (asprintf(&path, "/proc/%d/attr/%s", pid, attr) > 0) - return path; - return NULL; -} - -/** - * parse_confinement_mode - get the mode from the confinement string - * @con: the confinement string - * @size: size of the confinement string - * - * Modifies con to NUL-terminate the label string and the mode string. - * - * Returns: a pointer to the NUL-terminated mode inside the confinement string - * or NULL if the mode was not found - */ -static char *parse_confinement_mode(char *con, int size) -{ - if (strcmp(con, "unconfined") != 0 && - size > 4 && con[size - 2] == ')') { - int pos = size - 3; - - while (pos > 0 && !(con[pos] == ' ' && con[pos + 1] == '(')) - pos--; - if (pos > 0) { - con[pos] = 0; /* overwrite ' ' */ - con[size - 2] = 0; /* overwrite trailing ) */ - return &con[pos + 2]; /* skip '(' */ - } - } - return NULL; -} - -/** - * aa_getprocattr_raw - get the contents of @attr for @tid into @buf - * @tid: tid of task to query - * @attr: which /proc/<tid>/attr/<attr> to query - * @buf: buffer to store the result in - * @len: size of the buffer - * @mode: if non-NULL and a mode is present, will point to mode string in @buf - * - * Returns: size of data read or -1 on error, and sets errno - */ -int aa_getprocattr_raw(pid_t tid, const char *attr, char *buf, int len, - char **mode) -{ - int rc = -1; - int fd, ret; - char *tmp = NULL; - char *mode_str; - int size = 0; - - if (!buf || len <= 0) { - errno = EINVAL; - goto out; - } - - tmp = procattr_path(tid, attr); - if (!tmp) - goto out; - - fd = open(tmp, O_RDONLY); - free(tmp); - if (fd == -1) { - goto out; - } - - tmp = buf; - do { - ret = read(fd, tmp, len); - if (ret <= 0) - break; - tmp += ret; - size += ret; - len -= ret; - if (len < 0) { - errno = ERANGE; - goto out2; - } - } while (ret > 0); - - if (ret < 0) { - int saved; - if (ret != -1) { - errno = EPROTO; - } - saved = errno; - (void)close(fd); - errno = saved; - goto out; - } else if (size > 0 && buf[size - 1] != 0) { - /* check for null termination */ - if (buf[size - 1] == '\n') { - buf[size - 1] = 0; - } else if (len == 0) { - errno = ERANGE; - goto out2; - } else { - buf[size] = 0; - size++; - } - - mode_str = parse_confinement_mode(buf, size); - if (mode) - *mode = mode_str; - } - rc = size; - -out2: - (void)close(fd); -out: - return rc; -} - -#define INITIAL_GUESS_SIZE 128 - -/** - * aa_getprocattr - get the contents of @attr for @tid into @buf - * @tid: tid of task to query - * @attr: which /proc/<tid>/attr/<attr> to query - * @con: allocated buffer the result is stored in - * @mode: if non-NULL and a mode is present, will point to mode string in @con - * - * Returns: size of data read or -1 on error, and sets errno - * - * Guarantees that @con and @mode are null terminated. The length returned - * is for all data including both @con and @mode, and maybe > than strlen(@con) - * even if @mode is NULL - * - * Caller is responsible for freeing the buffer returned in @con. @mode is - * always contained within @con's buffer and so NEVER do free(@mode) - */ -int aa_getprocattr(pid_t tid, const char *attr, char **con, char **mode) -{ - int rc, size = INITIAL_GUESS_SIZE/2; - char *buffer = NULL; - - if (!con) { - errno = EINVAL; - return -1; - } - - do { - size <<= 1; - buffer = realloc(buffer, size); - if (!buffer) - return -1; - memset(buffer, 0, size); - - rc = aa_getprocattr_raw(tid, attr, buffer, size, mode); - } while (rc == -1 && errno == ERANGE); - - if (rc == -1) { - free(buffer); - *con = NULL; - if (mode) - *mode = NULL; - } else - *con = buffer; - - return rc; -} - -static int setprocattr(pid_t tid, const char *attr, const char *buf, int len) -{ - int rc = -1; - int fd, ret; - char *ctl = NULL; - - if (!buf) { - errno = EINVAL; - goto out; - } - - ctl = procattr_path(tid, attr); - if (!ctl) - goto out; - - fd = open(ctl, O_WRONLY); - if (fd == -1) { - goto out; - } - - ret = write(fd, buf, len); - if (ret != len) { - int saved; - if (ret != -1) { - errno = EPROTO; - } - saved = errno; - (void)close(fd); - errno = saved; - goto out; - } - - rc = 0; - (void)close(fd); - -out: - if (ctl) { - free(ctl); - } - return rc; -} - -int aa_change_hat(const char *subprofile, unsigned long token) -{ - int rc = -1; - int len = 0; - char *buf = NULL; - const char *fmt = "changehat %016lx^%s"; - - /* both may not be null */ - if (!(token || subprofile)) { - errno = EINVAL; - goto out; - } - - if (subprofile && strnlen(subprofile, PATH_MAX + 1) > PATH_MAX) { - errno = EPROTO; - goto out; - } - - len = asprintf(&buf, fmt, token, subprofile ? subprofile : ""); - if (len < 0) { - goto out; - } - - rc = setprocattr(aa_gettid(), "current", buf, len); -out: - if (buf) { - /* clear local copy of magic token before freeing */ - memset(buf, '\0', len); - free(buf); - } - return rc; -} - -/* original change_hat interface */ -int __change_hat(char *subprofile, unsigned int token) -{ - return aa_change_hat(subprofile, (unsigned long) token); -} - -int aa_change_profile(const char *profile) -{ - char *buf = NULL; - int len; - int rc; - - if (!profile) { - errno = EINVAL; - return -1; - } - - len = asprintf(&buf, "changeprofile %s", profile); - if (len < 0) - return -1; - - rc = setprocattr(aa_gettid(), "current", buf, len); - - free(buf); - return rc; -} - -int aa_change_onexec(const char *profile) -{ - char *buf = NULL; - int len; - int rc; - - if (!profile) { - errno = EINVAL; - return -1; - } - - len = asprintf(&buf, "exec %s", profile); - if (len < 0) - return -1; - - rc = setprocattr(aa_gettid(), "exec", buf, len); - - free(buf); - return rc; -} - -/* create an alias for the old change_hat@IMMUNIX_1.0 symbol */ -extern typeof((__change_hat)) __old_change_hat __attribute__((alias ("__change_hat"))); -symbol_version(__old_change_hat, change_hat, IMMUNIX_1.0); -default_symbol_version(__change_hat, change_hat, APPARMOR_1.0); - - -int aa_change_hatv(const char *subprofiles[], unsigned long token) -{ - int size, totallen = 0, hatcount = 0; - int rc = -1; - const char **hats; - char *pos, *buf = NULL; - const char *cmd = "changehat"; - - /* both may not be null */ - if (!token && !(subprofiles && *subprofiles)) { - errno = EINVAL; - goto out; - } - - /* validate hat lengths and while we are at it count how many and - * mem required */ - if (subprofiles) { - for (hats = subprofiles; *hats; hats++) { - int len = strnlen(*hats, PATH_MAX + 1); - if (len > PATH_MAX) { - errno = EPROTO; - goto out; - } - totallen += len + 1; - hatcount++; - } - } - - /* allocate size of cmd + space + token + ^ + vector of hats */ - size = strlen(cmd) + 18 + totallen + 1; - buf = malloc(size); - if (!buf) { - goto out; - } - - /* setup command string which is of the form - * changehat <token>^hat1\0hat2\0hat3\0..\0 - */ - sprintf(buf, "%s %016lx^", cmd, token); - pos = buf + strlen(buf); - if (subprofiles) { - for (hats = subprofiles; *hats; hats++) { - strcpy(pos, *hats); - pos += strlen(*hats) + 1; - } - } else - /* step pos past trailing \0 */ - pos++; - - rc = setprocattr(aa_gettid(), "current", buf, pos - buf); - -out: - if (buf) { - /* clear local copy of magic token before freeing */ - memset(buf, '\0', size); - free(buf); - } - - return rc; -} - -/** - * change_hat_vargs - change_hatv but passing the hats as fn arguments - * @token: the magic token - * @nhat: the number of hats being passed in the arguments - * ...: a argument list of const char * being passed - * - * change_hat_vargs can be called directly but it is meant to be called - * through its macro wrapper of the same name. Which automatically - * fills in the nhats arguments based on the number of parameters - * passed. - * to call change_hat_vargs direction do - * (change_hat_vargs)(token, nhats, hat1, hat2...) - */ -int (aa_change_hat_vargs)(unsigned long token, int nhats, ...) -{ - va_list ap; - const char *argv[nhats+1]; - int i; - - va_start(ap, nhats); - for (i = 0; i < nhats ; i++) { - argv[i] = va_arg(ap, char *); - } - argv[nhats] = NULL; - va_end(ap); - return aa_change_hatv(argv, token); -} - -/** - * aa_gettaskcon - get the confinement for task @target in an allocated buffer - * @target: task to query - * @con: pointer to returned buffer with the confinement string - * @mode: if non-NULL and a mode is present, will point to mode string in @con - * - * Returns: length of confinement data or -1 on error and sets errno - * - * Guarantees that @con and @mode are null terminated. The length returned - * is for all data including both @con and @mode, and maybe > than strlen(@con) - * even if @mode is NULL - * - * Caller is responsible for freeing the buffer returned in @con. @mode is - * always contained within @con's buffer and so NEVER do free(@mode) - */ -int aa_gettaskcon(pid_t target, char **con, char **mode) -{ - return aa_getprocattr(target, "current", con, mode); -} - -/** - * aa_getcon - get the confinement for current task in an allocated buffer - * @con: pointer to return buffer with the confinement if successful - * @mode: if non-NULL and a mode is present, will point to mode string in @con - * - * Returns: length of confinement data or -1 on error and sets errno - * - * Guarantees that @con and @mode are null terminated. The length returned - * is for all data including both @con and @mode, and may > than strlen(@con) - * even if @mode is NULL - * - * Caller is responsible for freeing the buffer returned in @con. @mode is - * always contained within @con's buffer and so NEVER do free(@mode) - */ -int aa_getcon(char **con, char **mode) -{ - return aa_gettaskcon(aa_gettid(), con, mode); -} - - -#ifndef SO_PEERSEC -#define SO_PEERSEC 31 -#endif - -/** - * aa_getpeercon_raw - get the confinement of the socket's peer (other end) - * @fd: socket to get peer confinement for - * @buf: buffer to store the result in - * @len: initially contains size of the buffer, returns size of data read - * @mode: if non-NULL and a mode is present, will point to mode string in @buf - * - * Returns: length of confinement data including null termination or -1 on error - * if errno == ERANGE then @len will hold the size needed - */ -int aa_getpeercon_raw(int fd, char *buf, int *len, char **mode) -{ - socklen_t optlen = *len; - char *mode_str; - int rc; - - if (optlen <= 0 || buf == NULL) { - errno = EINVAL; - return -1; - } - - rc = getsockopt(fd, SOL_SOCKET, SO_PEERSEC, buf, &optlen); - if (rc == -1 || optlen <= 0) - goto out; - - /* check for null termination */ - if (buf[optlen - 1] != 0) { - if (optlen < *len) { - buf[optlen] = 0; - optlen++; - } else { - /* buf needs to be bigger by 1 */ - rc = -1; - errno = ERANGE; - optlen++; - goto out; - } - } - - mode_str = parse_confinement_mode(buf, optlen); - if (mode) - *mode = mode_str; - - rc = optlen; -out: - *len = optlen; - return rc; -} - -/** - * aa_getpeercon - get the confinement of the socket's peer (other end) - * @fd: socket to get peer confinement for - * @con: pointer to allocated buffer with the confinement string - * @mode: if non-NULL and a mode is present, will point to mode string in @con - * - * Returns: length of confinement data including null termination or -1 on error - * - * Guarantees that @con and @mode are null terminated. The length returned - * is for all data including both @con and @mode, and maybe > than strlen(@con) - * even if @mode is NULL - * - * Caller is responsible for freeing the buffer returned in @con. @mode is - * always contained within @con's buffer and so NEVER do free(@mode) - */ -int aa_getpeercon(int fd, char **con, char **mode) -{ - int rc, last_size, size = INITIAL_GUESS_SIZE; - char *buffer = NULL; - - if (!con) { - errno = EINVAL; - return -1; - } - - do { - last_size = size; - buffer = realloc(buffer, size); - if (!buffer) - return -1; - memset(buffer, 0, size); - - rc = aa_getpeercon_raw(fd, buffer, &size, mode); - /* size should contain actual size needed if errno == ERANGE */ - } while (rc == -1 && errno == ERANGE && size > last_size); - - if (rc == -1) { - free(buffer); - *con = NULL; - if (mode) - *mode = NULL; - size = -1; - } else - *con = buffer; - - return size; -} - -static pthread_once_t aafs_access_control = PTHREAD_ONCE_INIT; -static char *aafs_access = NULL; - -static void aafs_access_init_once(void) -{ - char *aafs; - int ret; - - ret = aa_find_mountpoint(&aafs); - if (ret < 0) - return; - - ret = asprintf(&aafs_access, "%s/.access", aafs); - if (ret < 0) - aafs_access = NULL; - - free(aafs); -} - -/* "allow 0x00000000\ndeny 0x00000000\naudit 0x00000000\nquiet 0x00000000\n" */ -#define QUERY_LABEL_REPLY_LEN 67 - -/** - * aa_query_label - query the access(es) of a label - * @mask: permission bits to query - * @query: binary query string, must be offset by AA_QUERY_CMD_LABEL_SIZE - * @size: size of the query string must include AA_QUERY_CMD_LABEL_SIZE - * @allowed: upon successful return, will be 1 if query is allowed and 0 if not - * @audited: upon successful return, will be 1 if query should be audited and 0 - * if not - * - * Returns: 0 on success else -1 and sets errno. If -1 is returned and errno is - * ENOENT, the subject label in the query string is unknown to the - * kernel. - */ -int query_label(uint32_t mask, char *query, size_t size, int *allowed, - int *audited) -{ - char buf[QUERY_LABEL_REPLY_LEN]; - uint32_t allow, deny, audit, quiet; - int fd, ret, saved; - - if (!mask || size <= AA_QUERY_CMD_LABEL_SIZE) { - errno = EINVAL; - return -1; - } - - ret = pthread_once(&aafs_access_control, aafs_access_init_once); - if (ret) { - errno = EINVAL; - return -1; - } else if (!aafs_access) { - errno = ENOMEM; - return -1; - } - - fd = open(aafs_access, O_RDWR); - if (fd == -1) { - if (errno == ENOENT) - errno = EPROTONOSUPPORT; - return -1; - } - - memcpy(query, AA_QUERY_CMD_LABEL, AA_QUERY_CMD_LABEL_SIZE); - errno = 0; - ret = write(fd, query, size); - if (ret != size) { - if (ret >= 0) - errno = EPROTO; - /* IMPORTANT: This is the only valid error path that can have - * errno set to ENOENT. It indicates that the subject label - * could not be found by the kernel. - */ - (void)close(fd); - return -1; - } - - ret = read(fd, buf, QUERY_LABEL_REPLY_LEN); - saved = errno; - (void)close(fd); - errno = saved; - if (ret != QUERY_LABEL_REPLY_LEN) { - errno = EPROTO; - return -1; - } - - ret = sscanf(buf, "allow 0x%8" SCNx32 "\n" - "deny 0x%8" SCNx32 "\n" - "audit 0x%8" SCNx32 "\n" - "quiet 0x%8" SCNx32 "\n", - &allow, &deny, &audit, &quiet); - if (ret != 4) { - errno = EPROTONOSUPPORT; - return -1; - } - - *allowed = mask & ~(allow & ~deny) ? 0 : 1; - if (!(*allowed)) - audit = 0xFFFFFFFF; - *audited = mask & ~(audit & ~quiet) ? 0 : 1; - - return 0; -} - -/* export multiple aa_query_label symbols to compensate for downstream - * releases with differing symbol versions. */ -extern typeof((query_label)) __aa_query_label __attribute__((alias ("query_label"))); -symbol_version(__aa_query_label, aa_query_label, APPARMOR_1.1); -default_symbol_version(query_label, aa_query_label, APPARMOR_2.9); -- 2.1.0 -- AppArmor mailing list [email protected] Modify settings or unsubscribe at: https://lists.ubuntu.com/mailman/listinfo/apparmor
