[Android General] mirfatif:
Permissions, Root User and Linux Capabilities: Linux vs. Android​

Linux makes use of file permissions (DAC, MAC) and file / process capabilities to restrict access to system resources such as files on a certain filesystem. SELinux and AppArmor are commonly used Mandatory Access Control systems. Discretionary Access Controls include classic UNIX RWX file modes, owner/group or UID/GID, Extended Attributes and Access Control Lists. XATTR and ACL are rarely used filesystem related attributes. DAC and Linux Capabilities are closely related. Filesystem capabilities are of three categories: Inheritable, Permitted and Effective, while processes have two additional types: Ambient and Bounding. A process with complete set of effective capabilities is said to have root privileges. Root user can bypass all permission checks imposed by kernel. So the kernel checks for effective UID and effective process capablities for permission checks.

Android users are not allowed to have admin privileges by default. Rooting is the process of gaining these privileges with user ID 0 and all Linux capabilities, which are dropped for common human users with nonzero user ID's. A user is basically a set of privileges/capabilities identified by a number (UID). All processes running under a user have same privileges. These privileges can be dropped and elevated depending on some conditions.

In Linux boot process, kernel starts "init" process with all capabilities i.e. as root. Subsequently different processes are started with different required capabilities/privileges. On completion of boot process when reaching a human user shell (at login), effective capabilities are dropped to minimal but bounding set is maintained so that effective capabilities can be elevated later on when required using some method such as libcap. So, the privileged tasks can be performed in two ways:

Using setuid

An executable binary with suid bit set (a DAC file attrib set by command 'chmod u+s') will always run as root user. Setuid flag (set-user-id root) is equivalent to setting file's permitted and effective capabilities to all capabilities. CAP_SYS_ADMIN alone is almost near to all capabilities. Linux programs "su" and "sudo" makes use of "setuid" to gain effective UID 0 for temporarily performing some task with elevated privileges.

Using capabilities

To start a process with refined elevated privilege and not fully root (on per user base), set appropriate file capabilities on executable binaries and start processes at login with appropriate capabilities using PAM (making use of libcap). File capabilities are extended attribs set by command 'setcap' e.g. 'setcap cap_net_admin+ep' and 'setcap cap_net_raw+ep'. Check links (1, 2, 3, 4) to see how a new process's capabilities depend on executable file's capabilities and parent process's capabilities.

A brief description is being given here.

Effective UID is used for most access checks, and is the owner UID for files created by that process

File system user ID (FSUID) equals EUID unless (NFS, SDCARDFS etc.) filesystem is mounted with fsuid= option

An unprivileged process (non-root) can change its EUID only to either its SUID or RUID. If EUID=0, it can be changed to anything.

Saved UID is the priveleged UID saved by a process running with elevated privileges before it temporarily lowers its privileges

After executing unpriveleged task, saved-UID is restored back

If a set-user-ID program is executed, saved-UID is the owner UID of file. Otherwise saved-UID = RUID

Real UID (RUID) is the UID of user starting the program. It controls which user can send signals to other program

An unprivileged process (non-root) can signal another process only if the sender’s RUID or EUID matches the receiver's RUID or SUID

A set UID (set-user-ID) program can switch between the UID of the user who invoked it (RUID), and the owner of the executable i.e. can change effective UID (EUID)

set-user-ID bit is ignored if prctl: NO_NEW_PRIVS operation is performed on calling process, or if filesystem is mounted with "nosuid", or if process is being ptrace'd (strace)

Same is for setgid

fI/fP : File's Inheritable/Permitted capabilities
fE : File's Effective capabilities bit
pI : Process's initial Inheritable capabilities; not generally preserved when running as a non-root user
X : Process's initial Bounding set; a security mechanism; inherited from parent; init begins with full set
Can be dropped by a thread by prctl: PR_CAPBSET_DROP operation, if process has CAP_SETPCAP in pE; cannot be gained once dropped
Can be read using prctl: PR_CAPBSET_READ
pP : Process's initial permitted capabilities; if dropped, can't be reacquired without executing a privileged program binary
pI'/pP'/pE' : Process's new Inheritable/Permitted/Effective capabilities
pA : Ambient capabilities; preserved across execution of a program that is not privileged
Can be raised by prctl: PR_CAP_AMBIENT_RAISE operation, but overridden by setting SECBIT_NO_CAP_AMBIENT_RAISE on calling process

How capabilities are preserved when switching user (su; using setuid or setresuid):

If one/more of EUID/RUID/SUID is 0, and all 3 become non-zero , then pP', pE' and pA' are cleared

pP' and pE' can be preserved by setting SECBIT_KEEP_CAPS, but flag is cleared on every execve

If EUID is 0, and becomes non-zero, then pE' is cleared

If EUID is non-zero, and becomes 0, then pE'=pP

Setting SECBIT_NO_SETUID_FIXUP preserves all capabilities in pP', pE' and pA' when EUID changes

How capabilities are preserved when not exec'ing or fork'ing a process (using "capset" syscall as libcap / "pam_cap" module do):

Any task can remove capabilities from pE, pP, or pI at any time

If a task has a capability in pP,

It can add that capability to pI' masked by X

It can add that capability to pP'

It can add that capability to pE masked by pP'

If a task has CAP_SETPCAP, it can add any capability to pI, and it can remove capabilities from X

How capabilities are preserved when fork'ing a process:

All capabilities are preserved

How capabilities are preserved when exec'ing a process:

pA' = (file caps or setuid or setgid ? 0 : pA)

pA' = (pP' & pI') i.e. a capability is automatically dropped from pA' if that is dropped from pP' or pI'

Preserved when executing non-priveleged programs, cleared otherwise

pP' = (X & fP) | (pI & fI) | pA'

Cleared if calling process is non-root user (and if pA is empty?)

For dumb binaries, if X masks out some capabilities in fP, execution fails (to ensure that dumb binaries aren't executed with less than required privileges)

If file is set-user-ID-root or calling process has EUID 0, and X is full set, then pP' is also full set

If calling process has RUID 0, and X is full set, then pP' is also full set

pI' = pI (for root user)

pI' = (pI & pP) if user doesn't have CAP_SETPCAP in pE, so not preserved when running as a non-root user (i.e. with no pP and no pE)

pE' = (fE ? pP' : pA')

If fE bit is set for a capability, that capability in pP' (acquired from fP or fI) is also added to pE', otherwise ambient capabilities are assigned to the effective set

It means fI or fP won't appear in pE' if fE isn't set (for capability-dumb binaries)

Cleared if calling process is non-root user (and if pA is empty?)

If file is set-user-ID-root or calling process has EUID 0, and X is full set, then pE' is also full set

X is unchanged unless changed by capset

Privileged program/binary file is a program that has set-user-ID or set-group-ID bits or has any file capabilities set

Setting file capabilities requires CAP_SETFCAP

File capabilities are ignored if prctl: NO_NEW_PRIVS operation is performed on calling process, or if filesystem is mounted with "nosuid", or if process is being ptrace'd (strace)

A capability-dumb binary is a program that has file capabilities (even if empty capability set), but has not been converted to use the libcap API to manipulate its capabilities. Kernel consider a binary dumb if fE bit is set

A file with set-user-ID-root is considered to have full set of fI and fP, and fE bit set, same is considered if calling process has RUID or EUID 0

This can be overridden by setting SECBIT_NOROOT on calling process

File capabilities take precedence over setuid (even if empty capability set), though EUID and SUID becomes 0 if file is set-user-ID-root. But to make setuid effective for gaining all capabilities, execute 'setcap -r' or 'setfattr -x security.capability' on binary to remove xattr from file completely.

SECBIT Flags:

Can be locked to prevent further changes

Can be modified and retrieved using the prctl: PR_SET_SECUREBITS and PR_GET_SECUREBITS operations. CAP_SETPCAP is required for modification

Are preseved when fork'ing and execve'ing, except SECBIT_KEEP_CAPS is cleared on execve

Android restricts all of its apps (Java virtual machines) from gaining elevated privileges (by ignoring setuid and file capabilities) by setting PR_SET_NO_NEW_PRIVS as explained here and here. So the apps cannot run a binary with set-user-ID-root as root process because they (users/processes) don't have CAP_SETGID in Bounding Set (X).

Additionally a special patch CONFIG_ANDROID_PARANOID_NETWORK was applied to Linux kernel which maps certain capabilities to certain groups. For instance, user without CAP_NET_RAW capability cannot access internet. But Android allows users belonging to group AID_INET (3003) to create network sockets. However it's not applicable to raw sockets, commonly used by ping for echoing. Android's ping binary creates UDP sockets (uses SOCK_DGRAM in place of SOCK_RAW) in order to work without any special privileges (i.e. without CAP_NET_RAW).

So when using Linux on Android device, we need to add the user to group inet 3003 using /etc/group which is read at login by PAM and login programs (su, login, ssh etc.).

On Linux user login, PAM modules set process (login shell usually) capabilities according to its user / groups (uid, gid, secondary groups) as set in login files (/etc/{passwd,group} etc.) and PAM modules (such as pam_cap.so) can be configured to set desired capabilities for a user/group. Login files also let assign a name for a UID/GID to remember easily. But there is no login mechanism on Android and hence no pam/libcap to set required processes capabilities at runtime. However ambient capabilities (along with Permitted, Effective and Inheritable) for boot processes/services can be set from init *.rc files or filesystem capabilities can be set on binaries while building ROM.

In addition to Linux's access control mechanisms, Android enforces it's own manifest permissions control to installed apps within its Java framework. However, as explained above, some of the permissions are associated with UIDs and GIDs to enforce filesystem access control at kernel level. A common example is permission android.permission.INTERNET associated with group inet (GID 3003), necessarily required to create network sockets i.e. to access internet. Further reading: Android's permissions mapping with UIDs/GIDs.