.\" -*- mode: troff; coding: utf-8 -*- .\" Automatically generated by Pod::Man 5.01 (Pod::Simple 3.43) .\" .\" Standard preamble: .\" ======================================================================== .de Sp \" Vertical space (when we can't use .PP) .if t .sp .5v .if n .sp .. .de Vb \" Begin verbatim text .ft CW .nf .ne \\$1 .. .de Ve \" End verbatim text .ft R .fi .. .\" \*(C` and \*(C' are quotes in nroff, nothing in troff, for use with C<>. .ie n \{\ . ds C` "" . ds C' "" 'br\} .el\{\ . ds C` . ds C' 'br\} .\" .\" Escape single quotes in literal strings from groff's Unicode transform. .ie \n(.g .ds Aq \(aq .el .ds Aq ' .\" .\" If the F register is >0, we'll generate index entries on stderr for .\" titles (.TH), headers (.SH), subsections (.SS), items (.Ip), and index .\" entries marked with X<> in POD. Of course, you'll have to process the .\" output yourself in some meaningful fashion. .\" .\" Avoid warning from groff about undefined register 'F'. .de IX .. .nr rF 0 .if \n(.g .if rF .nr rF 1 .if (\n(rF:(\n(.g==0)) \{\ . if \nF \{\ . de IX . tm Index:\\$1\t\\n%\t"\\$2" .. . if !\nF==2 \{\ . nr % 0 . nr F 2 . \} . \} .\} .rr rF .\" ======================================================================== .\" .IX Title "AA_CHANGE_HAT 2" .TH AA_CHANGE_HAT 2 2024-02-28 "AppArmor 3.0.13" AppArmor .\" For nroff, turn off justification. Always turn off hyphenation; it makes .\" way too many mistakes in technical documents. .if n .ad l .nh .SH NAME aa_change_hat \- change to or from a "hat" within a AppArmor profile .SH SYNOPSIS .IX Header "SYNOPSIS" \&\fB#include \fR .PP \&\fBint aa_change_hat (char *subprofile, unsigned long magic_token);\fR .PP \&\fBint aa_change_hatv (char *subprofiles[], unsigned long magic_token);\fR .PP \&\fBint aa_change_hat_vargs (unsigned long magic_token, ...);\fR .PP Link with \fB\-lapparmor\fR when compiling. .SH DESCRIPTION .IX Header "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. .PP 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 \fIsubprofile\fR which it wants to change into, and a 64bit \fImagic_token\fR. The \fImagic_token\fR is used to return out of the subprofile at a later time. .PP The \fBaa_change_hat()\fR function allows specifying the name of a single \&\fIsubprofile\fR that the application wants to change into. A pointer to the name of the \fIsubprofile\fR is passed along with the \fImagic_token\fR. If the profile is not present the call will fail with the appropriate error. .PP The \fBaa_change_hatv()\fR function allows passing a \fINULL\fR terminated vector of pointers to \fIsubprofile\fR names which will be tried in order. The first \fIsubprofile\fR in the vector that exists will be transitioned to and if none of the \fIsubprofiles\fR exist the call will fail with the appropriate error. .PP The \fBaa_change_hat_vargs()\fR function is a convenience wrapper for the \&\fBaa_change_hatv()\fR function. After the \fImagic_token\fR it takes an arbitrary number of pointers to \fIsubprofile\fR names. Similar to \fBexecl\fR\|(3), \&\fBaa_change_hat_vargs()\fR assembles the list of \fIsubprofile\fR names into a vector and calls \fBaa_change_hatv()\fR. .PP If a program wants to return out of the current subprofile to the original profile, it calls \fBaa_change_hat()\fR with a pointer to NULL as the \fIsubprofile\fR, and the original \fImagic_token\fR value. If the \&\fImagic_token\fR does not match the original \fImagic_token\fR 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 \fImagic_token\fR matches the original token, then the process will change back to the original profile. .PP As both \fBread\fR\|(2) and \fBwrite\fR\|(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". .SH "RETURN VALUE" .IX Header "RETURN VALUE" On success zero is returned. On error, \-1 is returned, and \&\fBerrno\fR\|(3) is set appropriately. .SH ERRORS .IX Header "ERRORS" .IP \fBEINVAL\fR 4 .IX Item "EINVAL" The apparmor kernel module is not loaded or the communication via the \&\fI/proc/*/attr/current\fR file did not conform to protocol. .IP \fBENOMEM\fR 4 .IX Item "ENOMEM" Insufficient kernel memory was available. .IP \fBEPERM\fR 4 .IX Item "EPERM" The calling application is not confined by apparmor, the specified \&\fIsubprofile\fR is not a \fIhat profile\fR, the task is being ptraced and the tracing task does not have permission to trace the specified \fIsubprofile\fR or the no_new_privs execution bit is enabled. .IP \fBECHILD\fR 4 .IX Item "ECHILD" The application's profile has no hats defined for it. .IP \fBENOENT\fR 4 .IX Item "ENOENT" The specified \fIsubprofile\fR does not exist in this profile but other hats are defined. .IP \fBEACCES\fR 4 .IX Item "EACCES" The specified magic token did not match, and permissions to change to the specified \fIsubprofile\fR has been denied. This will in most situations also result in the task being killed, to prevent brute force attacks. .SH EXAMPLE .IX Header "EXAMPLE" The following code examples shows simple, if contrived, uses of \&\fBaa_change_hat()\fR; a typical use of \fBaa_change_hat()\fR 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. .PP The use of \fBrandom\fR\|(3) is simply illustrative. Use of \fI/dev/urandom\fR is recommended. .PP First, a simple high-level overview of \fBaa_change_hat()\fR use: .PP .Vb 2 \& 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\*(Aqt trust */ \& aa_change_hat("stuff_we_dont_trust", magic_token); \& \& /* Go do stuff we don\*(Aqt trust \-\- this is all \& * done in *this* process space, no separate \& * fork()/exec()\*(Aqs are done. */ \& interpret_perl_stuff(stuff_from_user); \& \& /* now change back to our original profile */ \& aa_change_hat(NULL, magic_token); \& } .Ve .PP Second, an example to show that files not listed in a subprofile ("hat") aren't accessible after an \fBaa_change_hat()\fR call: .PP .Vb 8 \& #include \& #include \& #include \& #include \& #include \& #include \& #include \& #include \& \& \& 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] = \*(Aq\e0\*(Aq; \& printf("/etc/passwd: %s\en", 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] = \*(Aq\e0\*(Aq; \& printf("/etc/passwd: %s\en", buf); \& \& return 0; \& } .Ve .PP This code example requires the following profile to be loaded with \&\fBapparmor_parser\fR\|(8): .PP .Vb 9 \& /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, \& } \& } .Ve .PP The output when run: .PP .Vb 5 \& $ /tmp/ch \& /etc/passwd: root:x:0: \& Failure reading /etc/passwd post\-hat: Permission denied \& /etc/passwd: \& $ .Ve .SH BUGS .IX Header "BUGS" None known. If you find any, please report them at . Note that \&\fBaa_change_hat\fR\|(2) provides no memory barriers between different areas of a program; if address space separation is required, then separate processes should be used. .SH "SEE ALSO" .IX Header "SEE ALSO" \&\fBapparmor\fR\|(7), \fBapparmor.d\fR\|(5), \fBapparmor_parser\fR\|(8), \fBaa_change_profile\fR\|(2), \&\fBaa_getcon\fR\|(2) and .