# Access control in Linux ## CS 3710 === ## Motivation: sandboxing --- ## Sandboxing <div class="container container-center"> <div class="col"> A _**sandbox**_ is an isolated environment that an application executes in that protects it from damaging the system outside of the sandbox. </div> <div class="col"> <figure> <img src="../../img/auth/sandbox.webp"> <figcaption> *Source: [StackOverflow](https://stackoverflow.com/a/2126214)* </figcaption> </figure> </div> </div> --- ## Sandboxing Sandboxes are prevalent throughout security. If an application is isolated sufficiently well, you can have high confidence that even if a vulnerability is discovered, it cannot be easily exploited. _**Example:**_ Chromium is well-known for having strong sandboxing; you can see some of the mechanisms it uses at `chrome://sandbox` <figure> <img src="../../img/auth/chrome_sandbox.webp"style="max-height: 25vh;"> <figcaption> </figcaption> </figure> notes: Chromium sandbox FAQ: https://chromium.googlesource.com/chromium/src/+/HEAD/docs/design/sandbox_faq.md Chromium sandboxing docs: https://chromium.googlesource.com/chromium/src/+/HEAD/docs/design/sandbox.md --- ## Some warnings ⚠️ _**Warning:**_ operating system-level privilege control mechanisms are great, but often aren't enough in complex applications. <div class="fragment semi-fade-out" data-fragment-index=0> - They can be great for stopping or mitigating things like remote code execution, _but_ </div> <div class="fragment" data-fragment-index=0> - Some data and privileges specific to the application cannot easily be captured by the operating system's abstractions. </div> <figure> <img src="../../img/auth/firefox_passwords.webp"style="max-height: 15vh;"> <figcaption> *Source: [Mozilla](https://support.mozilla.org/en-US/kb/use-primary-password-protect-stored-logins)* </figcaption> </figure> --- ## Some warnings ⚠️ _**Warning:**_ for extremely high-risk applications you may also want some deeper protection mechanisms, including virtualization and potentially even hardware-level mechanisms. <figure> <img src="../../img/malware/vx_underground.png"style="max-height: 30vh;"> <figcaption> *Source: [vx-underground](https://www.vx-underground.org/)* </figcaption> </figure> === ## Unix Discretionary Access Control model --- ## Users and groups <div class="fragment semi-fade-out" data-fragment-index=0> Unix-based operating system (including Linux, macOS, FreeBSD, etc.) have _**users**_ and _**groups**_. Each user has a user ID (UID) and each group has a group ID (GID). Users can be in several different groups, although one is specified as their "primary" group. </div> <div class="fragment" data-fragment-index=0> ```bash # Print your UID id -u # Print the ID of all groups that you're in id -G # Each line of /etc/passwd contains a user, their UID, their # primary group GID, home directory, and some additional info grep "^$(id -un)" /etc/passwd # Each line of /etc/group contains a group and all users that # are in that group grep "^$(id -gn)" /etc/group ``` </div> --- ## The `root` user In this system, the root user (the user with UID 0) is a special administrative user. _**sudo**_ is a common tool for gating access to the root user and is widely used in Unix-based OSes. <div class="container container-center"> <div class="col"> <figure> <img src="../../img/auth/sudo_logo.png"style="height: 15vh;"> <figcaption> *Source: [Sudo Project](https://sudo.ws)* </figcaption> </figure> </div> <div class="col"> <figure> <img src="../../img/auth/sudo_sandwich_xkcd.png"style="height: 25vh;"> <figcaption> *Source: [XKCD #149](https://xkcd.com/149/)* </figcaption> </figure> </div> </div> --- ## File permissions under DAC Each file and directory has an *owner* (a user) and a group. The permissions that a user has over that file usually varies based on whether they're the owner of the file, in the group that owns the file, or neither. <figure> <img src="../../img/auth/linux_dac_1.png"> <figcaption> </figcaption> </figure> --- ## File permissions under DAC The most common permissions you'll see are *read* (`r`), *write* (`w`), and *execute* (`x`). *Example:* the owner of this file has `rwx` permissions, while all other users have `rx` permissions. ```bash $ ls -l /tmp/myfile -rwxr-xr-x 1 student student 0 Nov 1 12:53 /tmp/myfile ``` <figure> <img src="../../img/auth/linux_dac_2.png"> <figcaption> </figcaption> </figure> --- ## File and directory permissions <div class="fragment semi-fade-out" data-fragment-index=0> For a file, these permissions mean: - `r`: can read the file - `w`: can write the file - `x`: can execute the file </div> <div class="fragment" data-fragment-index=0> For a directory, these permissions mean: - `r`: can list files in the directory - `w`: can create and delete files in the directory - `x`: can read files in the directory </div> --- ## Changing permissions: `chmod`, `setfacl` The owner of the file is allowed to choose what permissions are assigned to the file. ```bash # Give all users (`a`) read permission to a file chmod a+r myfile # Add read permissions for the user (`u`) and the group (`g`) that own the # file, remove write permissions for all other users (`o`). chmod ug+r,o-w myfile # Set the permissions to rwx for the file owner, while setting permissions # to --- for everyone else chmod u=rwx,go= myfile # Permissions can also be set as an octal (base-8) mask, where the first # bit is read, the second bit is write, and the third bit is execute. The # following command gives rwx permisions to the owner of the file, and r-x # permissions to all other users. chmod 755 myfile ``` --- ## Changing ownership: `chown` The root user (technically, anyone with the `CAP_CHOWN` capability) can also change the owner of a file. ```bash # Change the owner of a file to "student" chown student myfile # Change the group of a file to "cs3710" (the owner of a file can do this # without CAP_CHOWN if they're in the group that the file is being # assigned to.) chown :csuva myfile # Change both the owner and group of a file chown student:csuva myfile ``` === ## Capabilities --- ## `root` is dangerous! Traditionally, the root user is allowed to do whatever they want. At the same time, you have to become the root user to perform a lot of common operations. <div class="container"> <div class="col"> <figure> <img src="../../img/auth/dangerous_roots_1.jpg"style="height: 30vh;"> <figcaption> </figcaption> </figure> </div> <div class="col"> <figure> <img src="../../img/auth/dangerous_roots_2.jpg"style="height: 30vh;"> <figcaption> </figcaption> </figure> </div> </div> --- ## Constrained root We will discuss two general ways this problem gets solved in practice: <div class="fragment fade-in-then-semi-out" data-fragment-index=0> _**Idea 1:**_ we parcel out the permissions that the root user has into discrete units, and only give a user some of those permissions (*capabilities*) </div> <div class="fragment fade-in" data-fragment-index=1> _**Idea 2:**_ we allow processes to obtain a "constrained root" that behaves a lot like the root user, but whose privileges are constrained by a security policy (*Linux Security Modules*) </div> --- ## Capabilities <div class="fragment semi-fade-out" data-fragment-index=0> Starting in Linux kernel version 2.2, the root user's privileges were separated into discrete units known as _**capabilities**_. The root user has all of these capabilities by default. </div> <div class="fragment fade-in-then-semi-out" data-fragment-index=0> Each process has its own capability set, with capabilities falling into categories like *"effective"*, *"permitted"*, and *"inheritable"*. </div> <div class="fragment" data-fragment-index=1> _**Example:**_ by default, the `ping` program runs with `CAP_NET_RAW` as one of its "permitted" capabilities. ```bash $ ps -eo pid,comm | grep ping 15270 ping $ /usr/sbin/getpcaps 15270 15270: cap_net_raw=p ``` </div> notes: The capability categories determine which capabilities are active or able to be activated, which are preserved when a new process is created, and so on. --- ## Capabilities You can see the full list of capabilities with `man 7 capabilities`. Some examples: <div class="fragment semi-fade-out" data-fragment-index=0> - `CAP_DAC_OVERRIDE`: override the normal DAC permission checks that occur when you try to read / write / execute a file </div> <div class="fragment fade-in-then-semi-out" data-fragment-index=0> - `CAP_CHOWN`: change the owner or group of a file arbitrarily </div> <div class="fragment fade-in-then-semi-out" data-fragment-index=1> - `CAP_NET_BIND_SERVICE`: listen on a port $< 1024$ </div> <div class="fragment fade-in" data-fragment-index=2> - `CAP_NET_ADMIN`: allows various network administration-related operations, e.g. setting up firewalls, modifying network interfaces, etc. </div> --- ## Changing capabilities There are various utilities that allow you to view and modify the capabilities of a process: <pre> <code class="bash" data-trim data-line-numbers="1-18|1-8|10-11|13-18" data-fragment-index="1"> # setcap allows you to modify an executable file so that when it's # run, it's run with the specified capabilities. # Ex: add CAP_NET_ADMIN to the effective and inheritable capabilities # of /bin/program sudo setcap cap_net_admin+ei /bin/myprogram # getcap shows capabilities that have been set for a file /usr/sbin/getcap /bin/myprogram # Print the capability set of the executing process /usr/sbin/capsh --print # Run `nft list ruleset` as user `nobody` by giving CAP_NET_ADMIN # privileges. sudo capsh \ --caps='cap_net_admin+eip cap_setpcap,cap_setuid,cap_setgid+ep' \ --keep=1 --user=nobody --addamb='cap_net_admin' -- \ -c 'nft list ruleset' </pre> </code> === ## Linux Security Modules --- ## Linux Security Modules _**Linux Security Modules (LSMs)**_ take a different approach. They implement a *mandatory access control (MAC)* policy that constrain the actions that a user can take to only those that are specified by the policy. <figure> <img src="../../img/auth/lsm_paper.png"style="max-height: 30vh;"> <figcaption> </figcaption> </figure> --- ## Linux Security Modules <div class="fragment semi-fade-out" data-fragment-index=0> _**Advantages:**_ the protections implemented by LSMs are extremely strong! _Default deny:_ privileges that aren't explicitly granted by the policy are denied _Constrained root:_ an attacker can attain root and not be able to do much with it, thanks to the LSM policy. </div> <div class="fragment fade-in" data-fragment-index=0> _**Disadvantages:**_ LSMs can be notoriously difficult to work with, and creating a comprehensive policy can be non-trivial. </div> --- ## Linux Security Modules The Linux kernel supports a handful of different LSMs but only two are used fairly widely: SELinux and AppArmor. <div class="container"> <div class="col r-stack"> <div class="fragment fade-out" data-fragment-index=0> SELinux is the LSM used by RHEL and related Linux distributions (CentOS, Fedora, Rocky, etc.) </div> <div class="fragment fade-in" data-fragment-index=0> AppArmor is used by OpenSUSE and Debian-family distributions (e.g. Ubuntu) </div> </div> <div class="col image-background r-stack"> <div class="fragment fade-out" data-fragment-index=0> <figure> <img src="../../img/auth/selinux.svg"style="height: 20vh;"> <figcaption> </figcaption> </figure> </div> <div class="fragment fade-in" data-fragment-index=0> <figure> <img src="../../img/auth/apparmor.svg"style="height: 20vh;"> <figcaption> </figcaption> </figure> </div> </div> </div> --- ## SELinux <div class="container container-center"> <div class="col"> _**SELinux**_ implements its own form of *role-based access control (RBAC)*, which in turn enables mandatory access control. </div> <div class="col"> <figure> <img src="../../img/auth/selinux_rbac.png"class="image-background"> <figcaption> *Source: [Gentoo Linux wiki](https://wiki.gentoo.org/wiki/SELinux/Role-based_access_control)* </figcaption> </figure> </div> </div> --- ## SELinux In SELinux, each user is mapped to an "SELinux user"... ```bash [kernelmethod@fedora ~]$ sudo semanage login -l Login Name SELinux User ... __default__ user_u ... kernelmethod staff_u ... root unconfined_u ... ``` --- ## SELinux ... which is able to operate in one of several roles... ```bash [kernelmethod@fedora ~]$ sudo semanage user -l SELinux User ... SELinux Roles guest_u ... guest_r root ... staff_r sysadm_r system_r unconfined_r staff_u ... staff_r sysadm_r system_r unconfined_r sysadm_u ... sysadm_r system_u ... system_r unconfined_r unconfined_u ... system_r unconfined_r user_u ... user_r xguest_u ... xguest_r ``` --- ## SELinux ... which determines the domain (or "context") spawned processes are allowed to be in. The process's context determines what permissions it has. <pre> <code class="bash" data-trim data-line-numbers="1-9|1-2|4-5|7-9" data-fragment-index="1"> [kernelmethod@fedora ~]$ id -Z staff_u:staff_r:staff_t:s0-s0:c0.c1023 [kernelmethod@fedora ~]$ ls -Z /usr/bin/sudo system_u:object_r:sudo_exec_t:s0 /usr/bin/sudo [kernelmethod@fedora ~]$ sudo sesearch --allow -c file \ -t sudo_exec_t -ds -s staff_t allow staff_t sudo_exec_t:file entrypoint; </code> </pre> --- ## SELinux <div class="fragment semi-fade-out" data-fragment-index=0> _**Advantages:**_ - Flexible, and allows you to maintain very fine-grained access control over a number of different resources - Older and has better support in Linux tooling </div> <div class="fragment" data-fragment-index=0> _**Disadvantages:**_ - Extremely painful to write and manage custom policies - Difficult to simply enable and disable at will </div> --- ## AppArmor AppArmor is *profile*-centric. You write an AppArmor profile and load it into the kernel, and then execute programs under that profile. <figure> <img src="../../img/auth/apparmor_access_control.webp"class="image-background"style="max-height: 50vh;"> <figcaption> </figcaption> </figure> --- ## AppArmor profile syntax Profiles are structured as follows: ```text # Named profile profile my_profile { ... } # Alternatively, you can designate a profile to automatically # be applied to a given program /usr/bin/my_program { ... } ``` --- ## AppArmor profile syntax Within the profile, you detail the permissions that processes running with that profile are allowed to have: <pre> <code class="text" data-trim data-line-numbers="|1-2|4-7|9-13|15-18"> # Allow process to read /etc/passwd /etc/passwd r, # Allow to read any file in /etc/profile.d # # * = can substitute for any number of characters, excepting '/' /etc/profile.d/* r, # Allow process to read or write any file or directory in /tmp that # it owns # # ** = can substitute for any number of characters, including '/' owner /tmp/** rw, # Allow process to read and execute any file in /usr/bin/ # ix = "inherit execute" -- new process will inherit the same # AppArmor profile /usr/bin/* rix, </code> </pre> --- ## AppArmor <div class="fragment semi-fade-out" data-fragment-index=0> _**Example:**_ this AppArmor profile allows you to read and execute arbitrary files, but makes it impossible to write files. <pre> <code class="text" data-trim data-line-numbers="5,7-8"> # /etc/apparmor.d/example.profile # This profile makes it possible to read or execute any file, but # prevents writing anything. profile myprofile { # /** = "any file or directory under /" /** rix, } </code> </pre> </div> <div class="fragment" data-fragment-index=0> <pre> <code class="text" data-trim data-line-numbers="|1|3-4|6-7"> $ sudo apparmor_parser -r /etc/apparmor.d/example.profile $ sudo aa-exec -p myprofile -- head -n1 /etc/shadow root:!:19226:0:99999:7::: $ sudo aa-exec -p myprofile -- touch /tmp/test touch: cannot touch '/tmp/test': Permission denied </code> </pre> </div> --- ## AppArmor profiles _**Example:**_ this profile gets automatically applied when the program `/bin/mybash` is executed. <pre> <code class="text" data-trim data-line-numbers="2,4-5,7-9,13,16-17"> # /etc/apparmor.d/bin.mybash #include <tunables/global> /bin/mybash { #include <abstractions/base> /bin/* rix, /usr/bin/* rix, /usr/local/bin/* rix, # From `man 5 apparmor.d`: w = write, k = lock, l = link, # m = allow executable mapping owner @{HOME}/** rwlkmix, # Don't allow the user to modify their authorized_keys file deny @{HOME}/.ssh/authorized_keys w, } </code> </pre> --- ## AppArmor demo notes: To demonstrate file locking, run the following in two terminals under the restricted shell: - `flock -n -x file.lock vim test.txt` --- ## AppArmor <div class="fragment semi-fade-out" data-fragment-index=0> _**Advantages:**_ - Much easier to define custom security policies - Profiles have an easy-to-understand syntax </div> <div class="fragment" data-fragment-index=0> _**Disadvantages:**_ - Somewhat younger and not as well-supported in common Linux tools - Documentation and user guides are lacking </div> === ## Other access control mechanisms --- ## Single Sign-On <div class="container container-center"> <div class="col"> _**Single-Sign On (SSO)**_ is a mechanism that allows a user to authenticate themselves to several independent services by only needing to log in to one. </div> <div class="col"> <figure> <img src="../../img/auth/okta_sso.png"style="max-height: 40vh;"> <figcaption> *Source: [Okta](https://www.okta.com/blog/2021/02/single-sign-on-sso/)* </figcaption> </figure> </div> </div> --- ## Single Sign-On <div class="fragment semi-fade-out" data-fragment-index=0> SSO has a number of different security benefits: - _**Credential management:**_ it's significantly easier to manage usernames and passwords and change the amount of authority given to users. - _**Centralization of access:**_ single point where you can monitor different attempts to gain access to services on a network. </div> <div class="fragment" data-fragment-index=0> Conversely, SSO can also be a target for attackers since a compromised SSO provider can give access to many different services. </div> <figure> <img src="../../img/auth/Shibboleth_logo.png"style="max-height: 15vh;"> <figcaption> </figcaption> </figure> --- ## cgroups and namespaces <div class="fragment semi-fade-out" data-fragment-index=0> _**Cgroups**_ are a kernel feature that constrain what resources (e.g. CPU, memory, etc.) a process has access to _**Namespaces**_ isolate the view that a process has of the system (including processes, ports, etc.) </div> <div class="fragment" data-fragment-index=0> They are commonly used in tandem to implement _**containers**_. </div> <figure> <img src="../../img/auth/docker_logo.svg"style="max-height: 10vh;"> <figcaption> </figcaption> </figure> --- ## cgroups and namespaces <div class="fragment semi-fade-out" data-fragment-index=0> For example, the following command starts a Docker container running Debian Linux: ```text $ docker run --rm -it debian:stable bash root@5a40ea4cc6a6:/# ``` </div> <div class="fragment" data-fragment-index=0> You can do a lot of different things inside of this container, e.g.: ```text # Dangerous -- *never* run this on a real system unless you're absolutely # sure about what you're doing root@5a40ea4cc6a6:/# rm -rf --no-preserve-root / ``` and for the most part* you avoid the risk of damaging the host machine. (\* *Exceptions may apply*) --- ## seccomp On Linux, processes can run in a "securing computing" mode that limits which syscalls a program is allowed to make. <figure> <img src="../../img/auth/libseccomp.webp"class="image-background"style="padding: 20px;"> <figcaption> </figcaption> </figure> --- ## seccomp <div class="fragment semi-fade-out" data-fragment-index=0> Recall from our discussion of rootkits and Linux kernel modules: _**System calls**_ (aka _**syscalls**_) are the interface that programs use to work with the operating system kernel. System calls are used for many different things: creating processes, performing I/O, watching for system events, etc. </div> <div class="fragment" data-fragment-index=0> The Linux kernel currently supports ~350-400 syscalls (depending on what hardware you're on). </div> --- ## seccomp <div class="fragment semi-fade-out" data-fragment-index=0> When a process enters Secure Computing mode, it limits the syscalls it performs to just a small allowed subset. </div> <div class="fragment" data-fragment-index=0> In the strictest mode, this constrains the process to just the `read`, `write`, and `_exit` syscalls. In other words: the process is allowed to work with files it's already opened, and it's allowed to exit, and that's it! </div> <figure> <img src="../../img/auth/chromium_seccomp_sandbox.webp"> <figcaption> </figcaption> </figure> --- ## Pluggable Authentication Modules (PAM) _**Pluggable Authentication Modules (PAM)**_ are an easy(ish) way to incorporate various bits of common functionality into the authentication and authorization process. <figure> <img src="../../img/auth/pam_cooking_spray.webp"style="max-height: 30vh;"> <figcaption> </figcaption> </figure> --- ## Pluggable Authentication Modules (PAM) <div class="fragment semi-fade-out" data-fragment-index=0> There are a million different programs you can use to log in as a user and perform some action on Linux. </div> <div class="fragment" data-fragment-index=0> PAM provides a common way to configure the way users gain access and privileges with those programs. </div> <figure> <img src="../../img/auth/pam_connections.drawio.svg"class="image-background"style="padding: 1em; max-height: 30vh;"> <figcaption> </figcaption> </figure> --- ## Pluggable Authentication Modules (PAM) <div class="fragment semi-fade-out" data-fragment-index=0> **Example:** you automatically want to give a user a certain capability when they login. For instance, you want to create a `firewalladmin` user with `CAP_NET_ADMIN`. </div> <div class="fragment" data-fragment-index=0> **Solution:** use `pam_cap`, which allows you to set a user's capabilities when they login. </div> --- ## Pluggable Authentication Modules (PAM) <div class="fragment semi-fade-out" data-fragment-index=0> **Example:** you want to be able to record what somebody types in when they login to your machine as the root user. </div> <div class="fragment" data-fragment-index=0> _**Solution:**_ use the `pam_tty_audit` PAM module for each of these programs. This module gets automatically loaded whenever somebody logs in through any of these as root and records their session. </div>