change_hat  - change to or from a "hat" or subprofile within an AppArmor profile
#name
NAME
#synopsis
SYNOPSIS
#description
DESCRIPTION
#return_value
RETURN VALUE
#errors
ERRORS
#example
EXAMPLE
#bugs
BUGS
#see_also
SEE ALSO
NAME
change_hat  - change to or from a ``hat'' or subprofile within an AppArmor profile
SYNOPSIS
#include <sys/apparmor.h>
int change_hat (char *subdomain, unsigned int magic_token);
DESCRIPTION
A profile applies to an executable program, but sometimes a portion of the program
requires different access permissions than other portions, which is when the program can
``change hats'' to a different role, also known as a subprofile. To change
into a new hat, the program calls the
change_hat()
function. It passes
in a pointer to the
subprofile
that 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 to the original profile
from the subprofile, it will call
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
original 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
ENOMEM
Insufficient kernel memory was available.
EACCES
The
magic_token
passed in was 0, which is not a valid value for
the
magic_token
, or the specified
subprofile
does not exist in
this profile.
EFAULT
An internal error occurred.
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 unauthenticated 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 subdomain */
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/immunix.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
subdomain_parser(8):
/tmp/ch {
#default entries; most required by __canary_death_handler()
/dev/log                       w ,
/etc/ld.so.cache               r ,
/etc/locale/**                 r ,
/etc/localtime                 r ,
/usr/share/locale/**           r ,
/usr/share/zoneinfo/**         r ,
/usr/lib/locale/**             r ,
/usr/lib/gconv/*.so            r ,
/usr/lib/gconv/gconv-modules*  r ,
#entries specific to this application
/lib/ld-*.so* rx ,
/lib/libc*.so* r ,
/etc/passwd    r ,
/dev/pts/*     rw,
/tmp/ch        r ,
}
/tmp/ch^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://bugs.wirex.com
http://bugs.wirex.com
.
SEE ALSO
change_hat  - change to or from a "hat" or subprofile within an AppArmor profile
