2
* Copyright (c) 2003-2008 Novell, Inc. (All rights reserved)
3
* Copyright 2009-2010 Canonical Ltd.
5
* The libapparmor library is licensed under the terms of the GNU
6
* Lesser General Public License, version 2.1. Please see the file
9
* This library is distributed in the hope that it will be useful,
10
* but WITHOUT ANY WARRANTY; without even the implied warranty of
11
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12
* GNU Lesser General Public License for more details.
14
* You should have received a copy of the GNU Lesser General Public License
15
* along with this program. If not, see <http://www.gnu.org/licenses/>.
22
#include <sys/types.h>
24
#include <sys/syscall.h>
25
#include <sys/socket.h>
34
#include <sys/apparmor.h>
37
/* some non-Linux systems do not define a static value */
39
# define PATH_MAX 4096
42
#define symbol_version(real, name, version) \
43
__asm__ (".symver " #real "," #name "@" #version)
44
#define default_symbol_version(real, name, version) \
45
__asm__ (".symver " #real "," #name "@@" #version)
47
#define UNCONFINED "unconfined"
48
#define UNCONFINED_SIZE strlen(UNCONFINED)
51
* aa_find_mountpoint - find where the apparmor interface filesystem is mounted
52
* @mnt: returns buffer with the mountpoint string
54
* Returns: 0 on success else -1 on error
56
* NOTE: this function only supports versions of apparmor using securityfs
58
int aa_find_mountpoint(char **mnt)
70
mntfile = setmntent("/proc/mounts", "r");
74
while ((mntpt = getmntent(mntfile))) {
75
char *proposed = NULL;
76
if (strcmp(mntpt->mnt_type, "securityfs") != 0)
79
if (asprintf(&proposed, "%s/apparmor", mntpt->mnt_dir) < 0)
83
if (stat(proposed, &statbuf) == 0) {
97
* aa_is_enabled - determine if apparmor is enabled
99
* Returns: 1 if enabled else reason it is not, or 0 on error
101
* ENOSYS - no indication apparmor is present in the system
102
* ENOENT - enabled but interface could not be found
103
* ECANCELED - disabled at boot
104
* ENOMEM - out of memory
106
int aa_is_enabled(void)
108
int serrno, fd, rc, size;
112
/* if the interface mountpoint is available apparmor is enabled */
113
rc = aa_find_mountpoint(&mnt);
119
/* determine why the interface mountpoint isn't available */
120
fd = open("/sys/module/apparmor/parameters/enabled", O_RDONLY);
127
size = read(fd, &buffer, 2);
133
if (buffer[0] == 'Y')
141
static inline pid_t aa_gettid(void)
144
return syscall(SYS_gettid);
150
static char *procattr_path(pid_t pid, const char *attr)
153
if (asprintf(&path, "/proc/%d/attr/%s", pid, attr) > 0)
159
* parse_unconfined - check for the unconfined label
160
* @con: the confinement context
161
* @size: size of the confinement context (not including the NUL terminator)
163
* Returns: True if the con is the unconfined label or false otherwise
165
static bool parse_unconfined(char *con, int size)
167
return size == UNCONFINED_SIZE &&
168
strncmp(con, UNCONFINED, UNCONFINED_SIZE) == 0;
172
* splitcon - split the confinement context into a label and mode
173
* @con: the confinement context
174
* @size: size of the confinement context (not including the NUL terminator)
175
* @strip_newline: true if a trailing newline character should be stripped
176
* @mode: if non-NULL and a mode is present, will point to mode string in @con
179
* Modifies the @con string to split it into separate label and mode strings.
180
* If @strip_newline is true and @con contains a single trailing newline, it
181
* will be stripped on success (it will not be stripped on error). The @mode
182
* argument is optional. If @mode is NULL, @con will still be split between the
183
* label and mode (if present) but @mode will not be set.
185
* Returns: a pointer to the label string or NULL on error
187
static char *splitcon(char *con, int size, bool strip_newline, char **mode)
190
char *mode_str = NULL;
191
char *newline = NULL;
196
if (strip_newline && con[size - 1] == '\n') {
197
newline = &con[size - 1];
201
if (parse_unconfined(con, size)) {
206
if (size > 3 && con[size - 1] == ')') {
209
while (pos > 0 && !(con[pos] == ' ' && con[pos + 1] == '('))
212
con[pos] = 0; /* overwrite ' ' */
213
con[size - 1] = 0; /* overwrite trailing ) */
214
mode_str = &con[pos + 2]; /* skip '(' */
219
if (label && strip_newline && newline)
220
*newline = 0; /* overwrite '\n', if requested, on success */
227
* aa_splitcon - split the confinement context into a label and mode
228
* @con: the confinement context
229
* @mode: if non-NULL and a mode is present, will point to mode string in @con
232
* Modifies the @con string to split it into separate label and mode strings. A
233
* single trailing newline character will be stripped from @con, if found. The
234
* @mode argument is optional. If @mode is NULL, @con will still be split
235
* between the label and mode (if present) but @mode will not be set.
237
* Returns: a pointer to the label string or NULL on error
239
char *aa_splitcon(char *con, char **mode)
241
return splitcon(con, strlen(con), true, mode);
245
* aa_getprocattr_raw - get the contents of @attr for @tid into @buf
246
* @tid: tid of task to query
247
* @attr: which /proc/<tid>/attr/<attr> to query
248
* @buf: buffer to store the result in
249
* @len: size of the buffer
250
* @mode: if non-NULL and a mode is present, will point to mode string in @buf
252
* Returns: size of data read or -1 on error, and sets errno
254
int aa_getprocattr_raw(pid_t tid, const char *attr, char *buf, int len,
262
if (!buf || len <= 0) {
267
tmp = procattr_path(tid, attr);
271
fd = open(tmp, O_RDONLY);
279
ret = read(fd, tmp, len);
300
} else if (size > 0 && buf[size - 1] != 0) {
301
/* check for null termination */
302
if (buf[size - 1] != '\n') {
312
if (splitcon(buf, size, true, mode) != buf) {
325
#define INITIAL_GUESS_SIZE 128
328
* aa_getprocattr - get the contents of @attr for @tid into @label and @mode
329
* @tid: tid of task to query
330
* @attr: which /proc/<tid>/attr/<attr> to query
331
* @label: allocated buffer the label is stored in
332
* @mode: if non-NULL and a mode is present, will point to mode string in @label
334
* Returns: size of data read or -1 on error, and sets errno
336
* Guarantees that @label and @mode are null terminated. The length returned
337
* is for all data including both @label and @mode, and maybe > than
338
* strlen(@label) even if @mode is NULL
340
* Caller is responsible for freeing the buffer returned in @label. @mode is
341
* always contained within @label's buffer and so NEVER do free(@mode)
343
int aa_getprocattr(pid_t tid, const char *attr, char **label, char **mode)
345
int rc, size = INITIAL_GUESS_SIZE/2;
357
tmp = realloc(buffer, size);
363
memset(buffer, 0, size);
365
rc = aa_getprocattr_raw(tid, attr, buffer, size, mode);
366
} while (rc == -1 && errno == ERANGE);
379
static int setprocattr(pid_t tid, const char *attr, const char *buf, int len)
390
ctl = procattr_path(tid, attr);
394
fd = open(ctl, O_WRONLY);
399
ret = write(fd, buf, len);
421
int aa_change_hat(const char *subprofile, unsigned long token)
426
const char *fmt = "changehat %016lx^%s";
428
/* both may not be null */
429
if (!(token || subprofile)) {
434
if (subprofile && strnlen(subprofile, PATH_MAX + 1) > PATH_MAX) {
439
len = asprintf(&buf, fmt, token, subprofile ? subprofile : "");
444
rc = setprocattr(aa_gettid(), "current", buf, len);
447
/* clear local copy of magic token before freeing */
448
memset(buf, '\0', len);
454
/* original change_hat interface */
455
int __change_hat(char *subprofile, unsigned int token)
457
return aa_change_hat(subprofile, (unsigned long) token);
460
int aa_change_profile(const char *profile)
471
len = asprintf(&buf, "changeprofile %s", profile);
475
rc = setprocattr(aa_gettid(), "current", buf, len);
481
int aa_change_onexec(const char *profile)
492
len = asprintf(&buf, "exec %s", profile);
496
rc = setprocattr(aa_gettid(), "exec", buf, len);
502
/* create an alias for the old change_hat@IMMUNIX_1.0 symbol */
503
extern typeof((__change_hat)) __old_change_hat __attribute__((alias ("__change_hat")));
504
symbol_version(__old_change_hat, change_hat, IMMUNIX_1.0);
505
default_symbol_version(__change_hat, change_hat, APPARMOR_1.0);
508
int aa_change_hatv(const char *subprofiles[], unsigned long token)
510
int size, totallen = 0, hatcount = 0;
513
char *pos, *buf = NULL;
514
const char *cmd = "changehat";
516
/* both may not be null */
517
if (!token && !(subprofiles && *subprofiles)) {
522
/* validate hat lengths and while we are at it count how many and
525
for (hats = subprofiles; *hats; hats++) {
526
int len = strnlen(*hats, PATH_MAX + 1);
527
if (len > PATH_MAX) {
536
/* allocate size of cmd + space + token + ^ + vector of hats */
537
size = strlen(cmd) + 18 + totallen + 1;
543
/* setup command string which is of the form
544
* changehat <token>^hat1\0hat2\0hat3\0..\0
546
sprintf(buf, "%s %016lx^", cmd, token);
547
pos = buf + strlen(buf);
549
for (hats = subprofiles; *hats; hats++) {
551
pos += strlen(*hats) + 1;
554
/* step pos past trailing \0 */
557
rc = setprocattr(aa_gettid(), "current", buf, pos - buf);
561
/* clear local copy of magic token before freeing */
562
memset(buf, '\0', size);
570
* change_hat_vargs - change_hatv but passing the hats as fn arguments
571
* @token: the magic token
572
* @nhat: the number of hats being passed in the arguments
573
* ...: a argument list of const char * being passed
575
* change_hat_vargs can be called directly but it is meant to be called
576
* through its macro wrapper of the same name. Which automatically
577
* fills in the nhats arguments based on the number of parameters
579
* to call change_hat_vargs direction do
580
* (change_hat_vargs)(token, nhats, hat1, hat2...)
582
int (aa_change_hat_vargs)(unsigned long token, int nhats, ...)
585
const char *argv[nhats+1];
589
for (i = 0; i < nhats ; i++) {
590
argv[i] = va_arg(ap, char *);
594
return aa_change_hatv(argv, token);
597
int aa_stack_profile(const char *profile)
608
len = asprintf(&buf, "stack %s", profile);
612
rc = setprocattr(aa_gettid(), "current", buf, len);
618
int aa_stack_onexec(const char *profile)
629
len = asprintf(&buf, "stack %s", profile);
633
rc = setprocattr(aa_gettid(), "exec", buf, len);
640
* aa_gettaskcon - get the confinement context for task @target in an allocated buffer
641
* @target: task to query
642
* @label: pointer to returned buffer with the label
643
* @mode: if non-NULL and a mode is present, will point to mode string in @label
645
* Returns: length of confinement context or -1 on error and sets errno
647
* Guarantees that @label and @mode are null terminated. The length returned
648
* is for all data including both @label and @mode, and maybe > than
649
* strlen(@label) even if @mode is NULL
651
* Caller is responsible for freeing the buffer returned in @label. @mode is
652
* always contained within @label's buffer and so NEVER do free(@mode)
654
int aa_gettaskcon(pid_t target, char **label, char **mode)
656
return aa_getprocattr(target, "current", label, mode);
660
* aa_getcon - get the confinement context for current task in an allocated buffer
661
* @label: pointer to return buffer with the label if successful
662
* @mode: if non-NULL and a mode is present, will point to mode string in @label
664
* Returns: length of confinement context or -1 on error and sets errno
666
* Guarantees that @label and @mode are null terminated. The length returned
667
* is for all data including both @label and @mode, and may > than
668
* strlen(@label) even if @mode is NULL
670
* Caller is responsible for freeing the buffer returned in @label. @mode is
671
* always contained within @label's buffer and so NEVER do free(@mode)
673
int aa_getcon(char **label, char **mode)
675
return aa_gettaskcon(aa_gettid(), label, mode);
680
#define SO_PEERSEC 31
684
* aa_getpeercon_raw - get the confinement context of the socket's peer (other end)
685
* @fd: socket to get peer confinement context for
686
* @buf: buffer to store the result in
687
* @len: initially contains size of the buffer, returns size of data read
688
* @mode: if non-NULL and a mode is present, will point to mode string in @buf
690
* Returns: length of confinement context including null termination or -1 on
691
* error if errno == ERANGE then @len will hold the size needed
693
int aa_getpeercon_raw(int fd, char *buf, int *len, char **mode)
695
socklen_t optlen = *len;
698
if (optlen <= 0 || buf == NULL) {
703
rc = getsockopt(fd, SOL_SOCKET, SO_PEERSEC, buf, &optlen);
704
if (rc == -1 || optlen <= 0)
707
/* check for null termination */
708
if (buf[optlen - 1] != 0) {
713
/* buf needs to be bigger by 1 */
721
if (splitcon(buf, optlen - 1, false, mode) != buf) {
734
* aa_getpeercon - get the confinement context of the socket's peer (other end)
735
* @fd: socket to get peer confinement context for
736
* @label: pointer to allocated buffer with the label
737
* @mode: if non-NULL and a mode is present, will point to mode string in @label
739
* Returns: length of confinement context including null termination or -1 on error
741
* Guarantees that @label and @mode are null terminated. The length returned
742
* is for all data including both @label and @mode, and maybe > than
743
* strlen(@label) even if @mode is NULL
745
* Caller is responsible for freeing the buffer returned in @label. @mode is
746
* always contained within @label's buffer and so NEVER do free(@mode)
748
int aa_getpeercon(int fd, char **label, char **mode)
750
int rc, last_size, size = INITIAL_GUESS_SIZE;
762
tmp = realloc(buffer, size);
768
memset(buffer, 0, size);
770
rc = aa_getpeercon_raw(fd, buffer, &size, mode);
771
/* size should contain actual size needed if errno == ERANGE */
772
} while (rc == -1 && errno == ERANGE && size > last_size);
786
static pthread_once_t aafs_access_control = PTHREAD_ONCE_INIT;
787
static char *aafs_access = NULL;
789
static void aafs_access_init_once(void)
794
ret = aa_find_mountpoint(&aafs);
798
ret = asprintf(&aafs_access, "%s/.access", aafs);
805
/* "allow 0x00000000\ndeny 0x00000000\naudit 0x00000000\nquiet 0x00000000\n" */
806
#define QUERY_LABEL_REPLY_LEN 67
809
* aa_query_label - query the access(es) of a label
810
* @mask: permission bits to query
811
* @query: binary query string, must be offset by AA_QUERY_CMD_LABEL_SIZE
812
* @size: size of the query string must include AA_QUERY_CMD_LABEL_SIZE
813
* @allowed: upon successful return, will be 1 if query is allowed and 0 if not
814
* @audited: upon successful return, will be 1 if query should be audited and 0
817
* Returns: 0 on success else -1 and sets errno. If -1 is returned and errno is
818
* ENOENT, the subject label in the query string is unknown to the
821
int query_label(uint32_t mask, char *query, size_t size, int *allowed,
824
char buf[QUERY_LABEL_REPLY_LEN];
825
uint32_t allow, deny, audit, quiet;
828
if (!mask || size <= AA_QUERY_CMD_LABEL_SIZE) {
833
ret = pthread_once(&aafs_access_control, aafs_access_init_once);
837
} else if (!aafs_access) {
842
fd = open(aafs_access, O_RDWR);
845
errno = EPROTONOSUPPORT;
849
memcpy(query, AA_QUERY_CMD_LABEL, AA_QUERY_CMD_LABEL_SIZE);
851
ret = write(fd, query, size);
855
/* IMPORTANT: This is the only valid error path that can have
856
* errno set to ENOENT. It indicates that the subject label
857
* could not be found by the kernel.
863
ret = read(fd, buf, QUERY_LABEL_REPLY_LEN);
867
if (ret != QUERY_LABEL_REPLY_LEN) {
872
ret = sscanf(buf, "allow 0x%8" SCNx32 "\n"
873
"deny 0x%8" SCNx32 "\n"
874
"audit 0x%8" SCNx32 "\n"
875
"quiet 0x%8" SCNx32 "\n",
876
&allow, &deny, &audit, &quiet);
878
errno = EPROTONOSUPPORT;
882
*allowed = mask & ~(allow & ~deny) ? 0 : 1;
885
*audited = mask & ~(audit & ~quiet) ? 0 : 1;
890
/* export multiple aa_query_label symbols to compensate for downstream
891
* releases with differing symbol versions. */
892
extern typeof((query_label)) __aa_query_label __attribute__((alias ("query_label")));
893
symbol_version(__aa_query_label, aa_query_label, APPARMOR_1.1);
894
default_symbol_version(query_label, aa_query_label, APPARMOR_2.9);
898
* aa_query_file_path_len - query access permissions for a file @path
899
* @mask: permission bits to query
900
* @label: apparmor label
901
* @label_len: length of @label (does not include any terminating nul byte)
902
* @path: file path to query permissions for
903
* @path_len: length of @path (does not include any terminating nul byte)
904
* @allowed: upon successful return, will be 1 if query is allowed and 0 if not
905
* @audited: upon successful return, will be 1 if query should be audited and 0
908
* Returns: 0 on success else -1 and sets errno. If -1 is returned and errno is
909
* ENOENT, the subject label in the query string is unknown to the
912
int aa_query_file_path_len(uint32_t mask, const char *label, size_t label_len,
913
const char *path, size_t path_len, int *allowed,
916
autofree char *query = NULL;
918
/* + 1 for null separator */
919
size_t size = AA_QUERY_CMD_LABEL_SIZE + label_len + 1 + path_len;
920
query = malloc(size + 1);
923
memcpy(query + AA_QUERY_CMD_LABEL_SIZE, label, label_len);
925
query[AA_QUERY_CMD_LABEL_SIZE + label_len] = 0;
926
query[AA_QUERY_CMD_LABEL_SIZE + label_len + 1] = AA_CLASS_FILE;
927
memcpy(query + AA_QUERY_CMD_LABEL_SIZE + label_len + 2, path, path_len);
928
return aa_query_label(mask, query, size , allowed, audited);
932
* aa_query_file_path - query access permissions for a file @path
933
* @mask: permission bits to query
934
* @label: apparmor label
935
* @path: file path to query permissions for
936
* @allowed: upon successful return, will be 1 if query is allowed and 0 if not
937
* @audited: upon successful return, will be 1 if query should be audited and 0
940
* Returns: 0 on success else -1 and sets errno. If -1 is returned and errno is
941
* ENOENT, the subject label in the query string is unknown to the
944
int aa_query_file_path(uint32_t mask, const char *label, const char *path,
945
int *allowed, int *audited)
947
return aa_query_file_path_len(mask, label, strlen(label), path,
948
strlen(path), allowed, audited);
952
* aa_query_link_path_len - query access permissions for a hard link @link
953
* @label: apparmor label
954
* @label_len: length of @label (does not include any terminating nul byte)
955
* @target: file path that hard link will point to
956
* @target_len: length of @target (does not include any terminating nul byte)
957
* @link: file path of hard link
958
* @link_len: length of @link (does not include any terminating nul byte)
959
* @allowed: upon successful return, will be 1 if query is allowed and 0 if not
960
* @audited: upon successful return, will be 1 if query should be audited and 0
963
* Returns: 0 on success else -1 and sets errno. If -1 is returned and errno is
964
* ENOENT, the subject label in the query string is unknown to the
967
int aa_query_link_path_len(const char *label, size_t label_len,
968
const char *target, size_t target_len,
969
const char *link, size_t link_len,
970
int *allowed, int *audited)
972
autofree char *query = NULL;
974
/* + 1 for null separators */
975
size_t size = AA_QUERY_CMD_LABEL_SIZE + label_len + 1 + target_len +
977
size_t pos = AA_QUERY_CMD_LABEL_SIZE;
979
query = malloc(size);
982
memcpy(query + pos, label, label_len);
986
query[++pos] = AA_CLASS_FILE;
987
memcpy(query + pos + 1, link, link_len);
988
/* The kernel does the query in two parts we could similate this
989
* doing the following, however as long as policy is compiled
990
* correctly this isn't requied, and it requires and extra round
991
* trip to the kernel and adds a race on policy replacement between
994
int rc = aa_query_label(AA_MAY_LINK, query, size, allowed, audited);
1000
memcpy(query + pos + 1, target, target_len);
1001
return aa_query_label(AA_MAY_LINK, query, size, allowed, audited);
1005
* aa_query_link_path - query access permissions for a hard link @link
1006
* @label: apparmor label
1007
* @target: file path that hard link will point to
1008
* @link: file path of hard link
1009
* @allowed: upon successful return, will be 1 if query is allowed and 0 if not
1010
* @audited: upon successful return, will be 1 if query should be audited and 0
1013
* Returns: 0 on success else -1 and sets errno. If -1 is returned and errno is
1014
* ENOENT, the subject label in the query string is unknown to the
1017
int aa_query_link_path(const char *label, const char *target, const char *link,
1018
int *allowed, int *audited)
1020
return aa_query_link_path_len(label, strlen(label), target,
1021
strlen(target), link, strlen(link),