From Linux Man Pages
change_hat - change to or from a "hat" within a AppArmor profile
SYNOPSIS
#include <sys/apparmor.h>
int change_hat (char *subprofile, unsigned int magic_token);
Link with -lapparmor when compiling.
DESCRIPTION
An AppArmor profile applies to an executable program; if a portion of the program needs different access permis-
sions than other portions, the program can "change hats" to a different role, also known as a subprofile. To
change into a new hat, it calls the change_hat() function to do so. It passes in a pointer to the subprofile
which it wants to change into, and a 32bit magic_token. The magic_token is used to return out of the subprofile
at a later time.
If a program wants to return out of the current subprofile to the original profile, it calls change_hat() with a
pointer to NULL as the subprofile, and the original magic_token value. If the magic_token does not match the
original magic_token passed into the kernel when the program entered the subprofile, the change back to the orig-
inal profile will not happen, and the current task will be killed. If the magic_token matches the original
token, then the process will change back to the original profile.
If the program wants to change to a subprofile that it can never change back out of, the application should call
change_hat() with a magic_token of 0.
As both read(2) and write(2) are mediated, a file must be listed in a subprofile definition if the file is to be
accessed while the process is in a "hat".
RETURN VALUE
On success zero is returned. On error, -1 is returned, and errno(3) is set appropriately.
ERRORS
EINVAL
The apparmor kernel module is not loaded or the communication via the /proc/*/attr/current file did not con-
form to protocol.
ENOMEM
Insufficient kernel memory was available.
EPERM
The calling application is not confined by apparmor.
ECHILD
The application's profile has no hats defined for it.
EACCES
The specified subprofile does not exist in this profile or the process tried to change another process's
domain.
EXAMPLE
The following code examples shows simple, if contrived, uses of change_hat(); a typical use of change_hat() will
separate privileged portions of a process from unprivileged portions of a process, such as keeping unauthenti-
cated network traffic handling separate from authenticated network traffic handling in OpenSSH or executing user-
supplied CGI scripts in apache.
The use of random(3) is simply illustrative. Use of /dev/urandom is recommended.
First, a simple high-level overview of change_hat() use:
void foo (void) {
int magic_token;
/* get a random magic token value
from our huge entropy pool */
magic_token = random_function();
/* change into the subprofile while
* we do stuff we don't trust */
change_hat ("stuff_we_dont_trust", magic_token);
/* Go do stuff we don't trust -- this is all
* done in *this* process space, no separate
* fork()/exec()'s are done. */
interpret_perl_stuff(stuff_from_user);
/* now change back to our original profile */
change_hat (NULL, magic_token);
}
Second, an example to show that files not listed in a subprofile ("hat") aren't accessible after a change_hat()
call:
#include <stdlib.h>
#include <string.h>
#include <sys/apparmor.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdio.h>
#include <unistd.h>
int main(int argc, char *argv[]) {
int fd;
int tok;
char buf[10];
/* random() is a poor choice */
tok = random();
/* open /etc/passwd outside of any hat */
if ((fd=open("/etc/passwd", O_RDONLY)) < 0)
perror("Failure opening /etc/passwd");
/* confirm for ourselves that we can really read /etc/passwd */
memset(&buf, 0, 10);
if (read(fd, &buf, 10) == -1) {
perror("Failure reading /etc/passwd pre-hat");
_exit(1);
}
buf[9] = '\0';
printf("/etc/passwd: %s\n", buf);
/* change hat to the "hat" subprofile, which should not have
* read access to /etc/passwd -- even though we have a valid
* file descriptor at the time of the change_hat() call. */
if (change_hat("hat", tok)) {
perror("Failure changing hat -- aborting");
_exit(1);
}
/* confirm that we cannot read /etc/passwd */
lseek(fd,0,SEEK_SET);
memset(&buf, 0, 10);
if (read(fd, &buf, 10) == -1)
perror("Failure reading /etc/passwd post-hat");
buf[9] = '\0';
printf("/etc/passwd: %s\n", buf);
return 0;
}
This code example requires the following profile to be loaded with apparmor_parser(8):
/tmp/ch {
/etc/ld.so.cache mr,
/etc/locale/** r,
/etc/localtime r,
/usr/share/locale/** r,
/usr/share/zoneinfo/** r,
/usr/lib/locale/** mr,
/usr/lib/gconv/*.so mr,
/usr/lib/gconv/gconv-modules* mr,
/lib/ld-*.so* mrix,
/lib/libc*.so* mr,
/lib/libapparmor*.so* mr,
/dev/pts/* rw,
/tmp/ch mr,
/etc/passwd r,
^hat {
/dev/pts/* rw,
}
}
The output when run:
$ /tmp/ch
/etc/passwd: root:x:0:
Failure reading /etc/passwd post-hat: Permission denied
/etc/passwd:
$
BUGS
None known. If you find any, please report them to bugzilla at <http://bugzilla.novell.com>. Note that
change_hat(2) provides no memory barriers between different areas of a program; if address space separation is
required, then separate processes should be used.
RELATED
apparmor(7), apparmor.d(5), apparmor_parser(8), and <http://forge.novell.com/modules/xfmod/project/?apparmor>.
CATEGORY