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
/* some non-Linux systems do not define a static value */
36
# define PATH_MAX 4096
39
#define symbol_version(real, name, version) \
40
__asm__ (".symver " #real "," #name "@" #version)
41
#define default_symbol_version(real, name, version) \
42
__asm__ (".symver " #real "," #name "@@" #version)
45
* aa_find_mountpoint - find where the apparmor interface filesystem is mounted
46
* @mnt: returns buffer with the mountpoint string
48
* Returns: 0 on success else -1 on error
50
* NOTE: this function only supports versions of apparmor using securityfs
52
int aa_find_mountpoint(char **mnt)
64
mntfile = setmntent("/proc/mounts", "r");
68
while ((mntpt = getmntent(mntfile))) {
69
char *proposed = NULL;
70
if (strcmp(mntpt->mnt_type, "securityfs") != 0)
73
if (asprintf(&proposed, "%s/apparmor", mntpt->mnt_dir) < 0)
77
if (stat(proposed, &statbuf) == 0) {
91
* aa_is_enabled - determine if apparmor is enabled
93
* Returns: 1 if enabled else reason it is not, or 0 on error
95
* ENOSYS - no indication apparmor is present in the system
96
* ENOENT - enabled but interface could not be found
97
* ECANCELED - disabled at boot
98
* ENOMEM - out of memory
100
int aa_is_enabled(void)
102
int serrno, fd, rc, size;
106
/* if the interface mountpoint is available apparmor is enabled */
107
rc = aa_find_mountpoint(&mnt);
113
/* determine why the interface mountpoint isn't available */
114
fd = open("/sys/module/apparmor/parameters/enabled", O_RDONLY);
121
size = read(fd, &buffer, 2);
127
if (buffer[0] == 'Y')
135
static inline pid_t aa_gettid(void)
138
return syscall(SYS_gettid);
144
static char *procattr_path(pid_t pid, const char *attr)
147
if (asprintf(&path, "/proc/%d/attr/%s", pid, attr) > 0)
153
* parse_confinement_mode - get the mode from the confinement string
154
* @con: the confinement string
155
* @size: size of the confinement string
157
* Modifies con to NUL-terminate the label string and the mode string.
159
* Returns: a pointer to the NUL-terminated mode inside the confinement string
160
* or NULL if the mode was not found
162
static char *parse_confinement_mode(char *con, int size)
164
if (strcmp(con, "unconfined") != 0 &&
165
size > 4 && con[size - 2] == ')') {
168
while (pos > 0 && !(con[pos] == ' ' && con[pos + 1] == '('))
171
con[pos] = 0; /* overwrite ' ' */
172
con[size - 2] = 0; /* overwrite trailing ) */
173
return &con[pos + 2]; /* skip '(' */
180
* aa_getprocattr_raw - get the contents of @attr for @tid into @buf
181
* @tid: tid of task to query
182
* @attr: which /proc/<tid>/attr/<attr> to query
183
* @buf: buffer to store the result in
184
* @len: size of the buffer
185
* @mode: if set will point to mode string within buffer if it is present
187
* Returns: size of data read or -1 on error, and sets errno
189
int aa_getprocattr_raw(pid_t tid, const char *attr, char *buf, int len,
197
if (!buf || len <= 0) {
202
tmp = procattr_path(tid, attr);
206
fd = open(tmp, O_RDONLY);
214
ret = read(fd, tmp, len);
235
} else if (size > 0 && buf[size - 1] != 0) {
236
/* check for null termination */
237
if (buf[size - 1] == '\n') {
239
} else if (len == 0) {
248
*mode = parse_confinement_mode(buf, size);
258
#define INITIAL_GUESS_SIZE 128
261
* aa_getprocattr - get the contents of @attr for @tid into @buf
262
* @tid: tid of task to query
263
* @attr: which /proc/<tid>/attr/<attr> to query
264
* @buf: allocated buffer the result is stored in
265
* @mode: if set will point to mode string within buffer if it is present
267
* Returns: size of data read or -1 on error, and sets errno
269
int aa_getprocattr(pid_t tid, const char *attr, char **buf, char **mode)
271
int rc, size = INITIAL_GUESS_SIZE/2;
281
buffer = realloc(buffer, size);
284
memset(buffer, 0, size);
286
rc = aa_getprocattr_raw(tid, attr, buffer, size, mode);
287
} while (rc == -1 && errno == ERANGE);
299
static int setprocattr(pid_t tid, const char *attr, const char *buf, int len)
310
ctl = procattr_path(tid, attr);
314
fd = open(ctl, O_WRONLY);
319
ret = write(fd, buf, len);
341
int aa_change_hat(const char *subprofile, unsigned long token)
346
const char *fmt = "changehat %016x^%s";
348
/* both may not be null */
349
if (!(token || subprofile)) {
354
if (subprofile && strnlen(subprofile, PATH_MAX + 1) > PATH_MAX) {
359
len = asprintf(&buf, fmt, token, subprofile ? subprofile : "");
364
rc = setprocattr(aa_gettid(), "current", buf, len);
367
/* clear local copy of magic token before freeing */
368
memset(buf, '\0', len);
374
/* original change_hat interface */
375
int __change_hat(char *subprofile, unsigned int token)
377
return aa_change_hat(subprofile, (unsigned long) token);
380
int aa_change_profile(const char *profile)
391
len = asprintf(&buf, "changeprofile %s", profile);
395
rc = setprocattr(aa_gettid(), "current", buf, len);
401
int aa_change_onexec(const char *profile)
412
len = asprintf(&buf, "exec %s", profile);
416
rc = setprocattr(aa_gettid(), "exec", buf, len);
422
/* create an alias for the old change_hat@IMMUNIX_1.0 symbol */
423
extern typeof((__change_hat)) __old_change_hat __attribute__((alias ("__change_hat")));
424
symbol_version(__old_change_hat, change_hat, IMMUNIX_1.0);
425
default_symbol_version(__change_hat, change_hat, APPARMOR_1.0);
428
int aa_change_hatv(const char *subprofiles[], unsigned long token)
430
int size, totallen = 0, hatcount = 0;
433
char *pos, *buf = NULL;
434
const char *cmd = "changehat";
436
/* both may not be null */
437
if (!token && !(subprofiles && *subprofiles)) {
442
/* validate hat lengths and while we are at it count how many and
445
for (hats = subprofiles; *hats; hats++) {
446
int len = strnlen(*hats, PATH_MAX + 1);
447
if (len > PATH_MAX) {
456
/* allocate size of cmd + space + token + ^ + vector of hats */
457
size = strlen(cmd) + 18 + totallen + 1;
463
/* setup command string which is of the form
464
* changehat <token>^hat1\0hat2\0hat3\0..\0
466
sprintf(buf, "%s %016lx^", cmd, token);
467
pos = buf + strlen(buf);
469
for (hats = subprofiles; *hats; hats++) {
471
pos += strlen(*hats) + 1;
474
/* step pos past trailing \0 */
477
rc = setprocattr(aa_gettid(), "current", buf, pos - buf);
481
/* clear local copy of magic token before freeing */
482
memset(buf, '\0', size);
490
* change_hat_vargs - change_hatv but passing the hats as fn arguments
491
* @token: the magic token
492
* @nhat: the number of hats being passed in the arguments
493
* ...: a argument list of const char * being passed
495
* change_hat_vargs can be called directly but it is meant to be called
496
* through its macro wrapper of the same name. Which automatically
497
* fills in the nhats arguments based on the number of parameters
499
* to call change_hat_vargs direction do
500
* (change_hat_vargs)(token, nhats, hat1, hat2...)
502
int (aa_change_hat_vargs)(unsigned long token, int nhats, ...)
505
const char *argv[nhats+1];
509
for (i = 0; i < nhats ; i++) {
510
argv[i] = va_arg(ap, char *);
514
return aa_change_hatv(argv, token);
518
* aa_gettaskcon - get the confinement for task @target in an allocated buffer
519
* @target: task to query
520
* @con: pointer to returned buffer with the confinement string
521
* @mode: if provided will point to the mode string in @con if present
523
* Returns: length of confinement data or -1 on error and sets errno
525
* Guarentees that @con and @mode are null terminated. The length returned
526
* is for all data including both @con and @mode, and maybe > than strlen(@con)
527
* even if @mode is NULL
529
* Caller is responsible for freeing the buffer returned in @con. @mode is
530
* always contained within @con's buffer and so NEVER do free(@mode)
532
int aa_gettaskcon(pid_t target, char **con, char **mode)
534
return aa_getprocattr(target, "current", con, mode);
538
* aa_getcon - get the confinement for current task in an allocated buffer
539
* @con: pointer to return buffer with the confinement if successful
540
* @mode: if provided will point to the mode string in @con if present
542
* Returns: length of confinement data or -1 on error and sets errno
544
* Guarentees that @con and @mode are null terminated. The length returned
545
* is for all data including both @con and @mode, and may > than strlen(@con)
546
* even if @mode is NULL
548
* Caller is responsible for freeing the buffer returned in @con. @mode is
549
* always contained within @con's buffer and so NEVER do free(@mode)
551
int aa_getcon(char **con, char **mode)
553
return aa_gettaskcon(aa_gettid(), con, mode);
558
#define SO_PEERSEC 31
562
* aa_getpeercon_raw - get the confinement of the socket's peer (other end)
563
* @fd: socket to get peer confinement for
564
* @buf: buffer to store the result in
565
* @len: initially contains size of the buffer, returns size of data read
566
* @mode: if set will point to mode string within buffer if it is present
568
* Returns: length of confinement data including null termination or -1 on error
569
* if errno == ERANGE then @len will hold the size needed
571
int aa_getpeercon_raw(int fd, char *buf, int *len, char **mode)
573
socklen_t optlen = *len;
577
if (optlen <= 0 || buf == NULL) {
582
rc = getsockopt(fd, SOL_SOCKET, SO_PEERSEC, buf, &optlen);
583
if (rc == -1 || optlen <= 0)
586
/* check for null termination */
587
if (buf[optlen - 1] != 0) {
592
/* buf needs to be bigger by 1 */
600
mode_str = parse_confinement_mode(buf, optlen);
611
* aa_getpeercon - get the confinement of the socket's peer (other end)
612
* @fd: socket to get peer confinement for
613
* @con: pointer to allocated buffer with the confinement string
614
* @mode: if provided will point to the mode string in @con if present
616
* Returns: length of confinement data including null termination or -1 on error
618
* Caller is responsible for freeing the buffer returned.
620
int aa_getpeercon(int fd, char **con, char **mode)
622
int rc, last_size, size = INITIAL_GUESS_SIZE;
632
buffer = realloc(buffer, size);
635
memset(buffer, 0, size);
637
rc = aa_getpeercon_raw(fd, buffer, &size, mode);
638
/* size should contain actual size needed if errno == ERANGE */
639
} while (rc == -1 && errno == ERANGE && size > last_size);