[apparmor] [PATCH RFC] Add profile-based libapparmor query interface
Tyler Hicks
tyhicks at canonical.com
Thu Mar 28 01:30:36 UTC 2013
On 2013-03-05 22:44:35, Tyler Hicks wrote:
> I've got an initial libapparmor patch to complement the kernel query
> interface patch that I recently sent out to the list. It is functional
> but it is quite ugly so I'm looking for suggestions on how we want this
> to look since there's not really a libapparmor precedence for an
> interface like this.
>
> * I made this dead really as far as what information can be extracted
> from the interface. The application using the interface will simply
> know whether it should allow the action and whether it should audit
> the action. My thoughts are that we can add a more complex interface
> later when we need it. For D-Bus, I think this simple of an interface
> is sufficient.
>
> * I reused JJ's design from the aa_has_perm() function where the
> application needs to allocate a query buffer of AA_SOME_HEADER_SIZE +
> query size and then the query needs to be offset by
> AA_SOME_HEADER_SIZE bytes in the buffer. Then, the libapparmor
> function fills in the header and doesn't have to do any extra
> allocations or copies. Certainly good from an efficiency point of
> view, but not extremely user friendly. Something to worry about or
> not?
>
> * Setting two int return parameters to indicate allow and audit isn't a
> final solution. I was thinking that I'd return a special return code
> that will indicate error or allow and audit statuses. Then define some
> simple macros (aa_query_profile_err(rc), aa_query_profile_allow(rc),
> aa_query_profile_audit(rc)) in apparmor.h that can be used to
> translate the return code. I'm open to other suggestions...
>
> * Feel free to comment on any other parts of the patch, as well.
>
> ---
>
> Description: Add profile-based libapparmor query interface
> Wrap the apparmorfs profile query interface with a very simple libapparmor
> interface. This function takes a permission mask and query string consisting
> of a profile name and a DFA match string separated by a NUL char. It sets two
> output parameters indicating whether the action should be allowed and if the
> action should be audited.
> .
> The allowed and audited output parameters take into account deny and quiet
> permission masks returned in the kernel query. Additionally, the audited
> ouput parameter takes into account whether the action is to be allowed or
> not. If not, audited is set to true as long as there was no specific quiet
> rules for the queried permission.
> .
> The function requires a static char array to be allocated and initialized to
> the path of the apparmorfs .access file the first time it is called.
> Otherwise, aa_find_mountpoint() would need to be called for every query which
> would be inefficient. pthread_once() is used to ensure that aa_query_profile()
> is thread-safe while the char array is being allocated and initialized.
> Author: Tyler Hicks <tyhicks at canonical.com>
> Index: apparmor-2.8.0/libraries/libapparmor/src/kernel_interface.c
> ===================================================================
It turns out that this patch had some nasty bugs that I've managed to
chase down. Details below.
> --- apparmor-2.8.0.orig/libraries/libapparmor/src/kernel_interface.c 2013-03-05 16:45:24.471541316 -0800
> +++ apparmor-2.8.0/libraries/libapparmor/src/kernel_interface.c 2013-03-05 19:43:18.524352019 -0800
> @@ -28,6 +28,8 @@
> #include <limits.h>
> #include <stdarg.h>
> #include <mntent.h>
> +#include <inttypes.h>
> +#include <pthread.h>
>
> #include "apparmor.h"
>
> @@ -677,3 +679,92 @@
> {
> return aa_task_has_perm(aa_gettid(), mask, query, 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);
> +}
> +
> +/**
> + * aa_query_profile - test if the profile being enforced allows access to query
> + * @mask: permission bits to query
> + * @query: binary query string, must be offset by AA_QUERY_CMD_PROFILE_SIZE
> + * @size: size of the query string must include AA_QUERY_CMD_PROFILE_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
> + */
> +int aa_query_profile(uint32_t mask, char *query, size_t size,
> + int *allowed, int *audited)
> +{
> + char buf[128];
> + uint32_t allow, deny, audit, quiet;
> + int fd, ret, saved;
> +
> + if (size <= AA_QUERY_CMD_PROFILE_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)
> + return -1;
> +
> + memcpy(query, AA_QUERY_CMD_PROFILE, AA_QUERY_CMD_PROFILE_SIZE);
> + errno = 0;
> + ret = write(fd, query, size);
> + if (ret != size) {
> + if (ret >= 0)
> + errno = EPROTO;
> + return -1;
> + }
> +
> + ret = read(fd, buf, sizeof(buf));
> + saved = errno;
> + (void)close(fd);
> + errno = saved;
> + if (ret < 0)
It would be good to make sure that we read all of the data that we
expect to. I'll make a #define with the length that we expect to read
from the kernel and make sure that we get at least that many bytes from
the read().
I'll also use that #define to set the size of buf[].
> + return -1;
> +
> + ret = sscanf(buf, "allow 0x%8" SCNu32 "\n"
> + "deny 0x%8" SCNu32 "\n"
> + "audit 0x%8" SCNu32 "\n"
> + "quiet 0x%8" SCNu32 "\n",
These should all be SCNx32 since we're reading in hex representations.
SCNu32 results in the wrong values. :/
> + &allow, &deny, &audit, &quiet);
> + if (ret != 4) {
> + errno = EPROTONOSUPPORT;
> + return -1;
> + }
> +
> + *allowed = mask & (allow & ~deny) ? 1 : 0;
This is wrong. mask could contain the same thing as, or a subset of,
(allow & ~deny) and *allowed will properly be set to 1. However, mask
could contain a single matching bit along with any other invalid bits
and *allowed will still be set to 1.
After making those changes, we need to make sure that aa_query_profile()
is not called with a mask value of 0 or *allowed will be set to 1.
> + if (!(*allowed))
> + audit = 0xFFFFFFFF;
> + *audited = mask & (audit & ~quiet) ? 1 : 0;
Same problem here as above.
Patch to follow...
Tyler
> +
> + return 0;
> +}
> Index: apparmor-2.8.0/libraries/libapparmor/src/apparmor.h
> ===================================================================
> --- apparmor-2.8.0.orig/libraries/libapparmor/src/apparmor.h 2013-03-05 16:45:24.471541316 -0800
> +++ apparmor-2.8.0/libraries/libapparmor/src/apparmor.h 2013-03-05 19:26:18.528327777 -0800
> @@ -87,6 +87,10 @@
> size_t size);
> extern int aa_has_perm(uint32_t mask, char *query, size_t size);
>
> +#define AA_QUERY_CMD_PROFILE "profile\0"
> +#define AA_QUERY_CMD_PROFILE_SIZE 8
> +extern int aa_query_profile(uint32_t mask, char *query, size_t size,
> + int *allow, int *audit);
>
> #define __macroarg_counter(Y...) __macroarg_count1 ( , ##Y)
> #define __macroarg_count1(Y...) __macroarg_count2 (Y, 16,15,14,13,12,11,10,9,8,7,6,5,4,3,2,1,0)
> Index: apparmor-2.8.0/libraries/libapparmor/src/libapparmor.map
> ===================================================================
> --- apparmor-2.8.0.orig/libraries/libapparmor/src/libapparmor.map 2013-03-05 16:45:24.471541316 -0800
> +++ apparmor-2.8.0/libraries/libapparmor/src/libapparmor.map 2013-03-05 16:45:24.459535316 -0800
> @@ -39,6 +39,7 @@
> global:
> aa_task_has_perm;
> aa_has_perm;
> + aa_query_profile;
> local:
> *;
> } APPARMOR_1.1;
> Index: apparmor-2.8.0/libraries/libapparmor/swig/SWIG/libapparmor.i
> ===================================================================
> --- apparmor-2.8.0.orig/libraries/libapparmor/swig/SWIG/libapparmor.i 2013-03-05 16:45:24.471541316 -0800
> +++ apparmor-2.8.0/libraries/libapparmor/swig/SWIG/libapparmor.i 2013-03-05 16:45:24.463537315 -0800
> @@ -30,3 +30,5 @@
> extern int aa_task_has_perm(pid_t task, uint32_t mask, const char *query,
> size_t size);
> extern int aa_has_perm(uint32_t mask, const char *query, size_t size);
> +extern int aa_query_profile(uint32_t mask, char *query, size_t size,
> + int *allow, int *audit);
> Index: apparmor-2.8.0/libraries/libapparmor/src/Makefile.am
> ===================================================================
> --- apparmor-2.8.0.orig/libraries/libapparmor/src/Makefile.am 2013-03-05 16:45:24.411511315 -0800
> +++ apparmor-2.8.0/libraries/libapparmor/src/Makefile.am 2013-03-05 19:55:23.144369240 -0800
> @@ -24,7 +24,7 @@
> noinst_HEADERS = grammar.h parser.h scanner.h af_protos.h
>
> libapparmor_la_SOURCES = grammar.y libaalogparse.c kernel_interface.c scanner.c
> -libapparmor_la_LDFLAGS = -version-info 1:2:0 -XCClinker -dynamic \
> +libapparmor_la_LDFLAGS = -version-info 1:2:0 -XCClinker -dynamic -pthread \
> -Wl,--version-script=$(top_srcdir)/src/libapparmor.map -Wl,-soname=libapparmor.so.1
>
> libimmunix_la_SOURCES = kernel_interface.c libimmunix_warning.c
>
> --
> AppArmor mailing list
> AppArmor at lists.ubuntu.com
> Modify settings or unsubscribe at: https://lists.ubuntu.com/mailman/listinfo/apparmor
-------------- next part --------------
A non-text attachment was scrubbed...
Name: signature.asc
Type: application/pgp-signature
Size: 836 bytes
Desc: Digital signature
URL: <https://lists.ubuntu.com/archives/apparmor/attachments/20130327/17f3e397/attachment-0001.pgp>
More information about the AppArmor
mailing list