1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262
|
# This publication is intellectual property of Novell Inc. and Canonical
# Ltd. 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, Canonical Ltd, 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
# and Canonical Ltd. essentially adhere 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.
#
=pod
=head1 NAME
aa_change_hat - change to or from a "hat" within a AppArmor profile
=head1 SYNOPSIS
B<#include E<lt>sys/apparmor.hE<gt>>
B<int aa_change_hat (char *subprofile, unsigned long magic_token);>
B<int aa_change_hatv (char *subprofiles[], unsigned long magic_token);>
B<int aa_change_hat_vargs (unsigned long 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 one of the family of change_hat
functions to do so. It passes in a pointer to the I<subprofile> which it
wants to change into, and a 64bit I<magic_token>. The I<magic_token>
is used to return out of the subprofile at a later time.
The aa_change_hat() function allows specifying the name of a single
I<subprofile> that the application wants to change into. A pointer to the
name of the I<subprofile> is passed along with the I<magic_token>. If the
profile is not present the call will fail with the appropriate error.
The aa_change_hatv() function allows passing a I<NULL> terminated vector
of pointers to I<subprofile> names which will be tried in order. The
first I<subprofile> in the vector that exists will be transitioned to
and if none of the I<subprofiles> exist the call will fail with the
appropriate error.
The aa_change_hat_vargs() function is a convenience wrapper for the
aa_change_hatv() function. After the I<magic_token> it takes an arbitrary
number of pointers to I<subprofile> names. Similar to execl(3),
aa_change_hat_vargs() assembles the list of I<subprofile> names into a
vector and calls aa_change_hatv().
If a program wants to return out of the current subprofile to the
original profile, it calls aa_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.
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, the specified
I<subprofile> is not a I<hat profile>, the task is being ptraced and the
tracing task does not have permission to trace the specified I<subprofile> or the no_new_privs execution bit is
enabled.
=item B<ECHILD>
The application's profile has no hats defined for it.
=item B<ENOENT>
The specified I<subprofile> does not exist in this profile but other hats
are defined.
=item B<EACCES>
The specified magic token did not match, and permissions to change to
the specified I<subprofile> has been denied. This will in most situations
also result in the task being killed, to prevent brute force attacks.
=back
=head1 EXAMPLE
The following code examples shows simple, if contrived, uses of
aa_change_hat(); a typical use of aa_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 aa_change_hat() use:
void foo (void) {
unsigned long 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 */
aa_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 */
aa_change_hat(NULL, magic_token);
}
Second, an example to show that files not listed in a subprofile ("hat")
aren't accessible after an aa_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;
unsigned long 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 aa_change_hat() call. */
if (aa_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 at
L<https://gitlab.com/apparmor/apparmor/-/issues>. Note that
aa_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), aa_change_profile(2),
aa_getcon(2) and
L<https://wiki.apparmor.net>.
=cut
|