mirror of
https://gitlab.com/apparmor/apparmor.git
synced 2025-03-05 17:01:00 +01:00
233 lines
6.9 KiB
Text
233 lines
6.9 KiB
Text
# $Id$
|
|
# This publication is intellectual property of Novell Inc. Its contents
|
|
# can be duplicated, either in part or in whole, provided that a copyright
|
|
# label is visibly located on each copy.
|
|
#
|
|
# All information found in this book has been compiled with utmost
|
|
# attention to detail. However, this does not guarantee complete accuracy.
|
|
# Neither SUSE LINUX GmbH, the authors, nor the translators shall be held
|
|
# liable for possible errors or the consequences thereof.
|
|
#
|
|
# Many of the software and hardware descriptions cited in this book
|
|
# are registered trademarks. All trade names are subject to copyright
|
|
# restrictions and may be registered trade marks. SUSE LINUX GmbH
|
|
# essentially adheres to the manufacturer's spelling.
|
|
#
|
|
# Names of products and trademarks appearing in this book (with or without
|
|
# specific notation) are likewise subject to trademark and trade protection
|
|
# laws and may thus fall under copyright restrictions.
|
|
#
|
|
# Please direct suggestions and comments to apparmor-general@forge.novell.com.
|
|
|
|
|
|
=pod
|
|
|
|
=head1 NAME
|
|
|
|
change_hat - change to or from a "hat" within a AppArmor profile
|
|
|
|
=head1 SYNOPSIS
|
|
|
|
B<#include E<lt>sys/apparmor.hE<gt>>
|
|
|
|
B<int change_hat (char *subprofile, unsigned int magic_token);>
|
|
|
|
Link with B<-lapparmor> when compiling.
|
|
|
|
=head1 DESCRIPTION
|
|
|
|
An AppArmor profile applies to an executable program; if a portion of
|
|
the program needs different access permissions 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 I<subprofile> which it wants to
|
|
change into, and a 32bit I<magic_token>. The I<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 I<subprofile>, and the original I<magic_token> value. If the
|
|
I<magic_token> does not match the original I<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 I<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
|
|
I<magic_token> of I<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".
|
|
|
|
=head1 RETURN VALUE
|
|
|
|
On success zero is returned. On error, -1 is returned, and
|
|
errno(3) is set appropriately.
|
|
|
|
=head1 ERRORS
|
|
|
|
=over 4
|
|
|
|
=item B<EINVAL>
|
|
|
|
The apparmor kernel module is not loaded or the communication via the
|
|
F</proc/*/attr/current> file did not conform to protocol.
|
|
|
|
=item B<ENOMEM>
|
|
|
|
Insufficient kernel memory was available.
|
|
|
|
=item B<EPERM>
|
|
|
|
The calling application is not confined by apparmor.
|
|
|
|
=item B<ECHILD>
|
|
|
|
The application's profile has no hats defined for it.
|
|
|
|
=item B<EACCES>
|
|
|
|
The specified I<subprofile> does not exist in this profile or the
|
|
process tried to change another process's domain.
|
|
|
|
=back
|
|
|
|
=head1 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 F</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:
|
|
$
|
|
|
|
|
|
=head1 BUGS
|
|
|
|
None known. If you find any, please report them to bugzilla at
|
|
L<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.
|
|
|
|
=head1 SEE ALSO
|
|
|
|
apparmor(7), apparmor.d(5), apparmor_parser(8), and
|
|
L<http://forge.novell.com/modules/xfmod/project/?apparmor>.
|
|
|
|
=cut
|