---
name: "aa_change_hat"
section: "2"
platform: "linux"
package: "apparmor"
version: "4.1.8-2"
release: "Arch Linux 2026-11-01"
platforms: ["linux"]
url: "https://new.carta.tech/aa_change_hat"
---

# aa\_change\_hat(2)

change to or from a "hat" within a AppArmor profile

## NAME

aa\_change\_hat  - change to or from a "hat" within a AppArmor profile

## SYNOPSIS

**#include \<sys/apparmor.h\>**

**int aa\_change\_hat (char \*subprofile, unsigned long magic\_token);**

**int aa\_change\_hatv (char \*subprofiles\[\], unsigned long magic\_token);**

**int aa\_change\_hat\_vargs (unsigned long 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 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 *subprofile* which it wants to change into, and a 64bit *magic\_token*.  The *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 *subprofile* that the application wants to change into.  A pointer to the name of the *subprofile* is passed along with the *magic\_token*.  If the profile is not present the call will fail with the appropriate error.

The **aa\_change\_hatv()** function allows passing a *NULL* terminated vector of pointers to *subprofile* names which will be tried in order.  The first *subprofile* in the vector that exists will be transitioned to and if none of the *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 *magic\_token* it takes an arbitrary number of pointers to *subprofile* names.  Similar to [**execl**(3)](/execl/3.md), **aa\_change\_hat\_vargs()** assembles the list of *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 *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.

As both [**read**(2)](/read/2.md) and [**write**(2)](/write/2.md) 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)](/errno/3.md) is set appropriately.

## ERRORS

- **EINVAL**

  The apparmor kernel module is not loaded or the communication via the */proc/\*/attr/current* file did not conform to protocol.

- **ENOMEM**

  Insufficient kernel memory was available.

- **EPERM**

  The calling application is not confined by apparmor, the specified *subprofile* is not a *hat profile*, the task is being ptraced and the tracing task does not have permission to trace the specified *subprofile* or the no\_new\_privs execution bit is enabled.

- **ECHILD**

  The application's profile has no hats defined for it.

- **ENOENT**

  The specified *subprofile* does not exist in this profile but other hats are defined.

- **EACCES**

  The specified magic token did not match, and permissions to change to the specified *subprofile* has been denied. This will in most situations also result in the task being killed, to prevent brute force attacks.

## 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)](/random/3.md) is simply illustrative. Use of */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)](/apparmor_parser/8.md):

```
 /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 at \<https://gitlab.com/apparmor/apparmor/-/issues\>. Note that [**aa\_change\_hat**(2)](/aa_change_hat/2.md) provides no memory barriers between different areas of a program; if address space separation is required, then separate processes should be used.

## SEE ALSO

[**apparmor**(7)](/apparmor/7.md), [**apparmor.d**(5)](/apparmor.d/5.md), [**apparmor\_parser**(8)](/apparmor_parser/8.md), [**aa\_change\_profile**(2)](/aa_change_profile/2.md), [**aa\_getcon**(2)](/aa_getcon/2.md) and \<https://wiki.apparmor.net\>.
