Introduction
Today, I will continue to look at security at a level slightly above the core. In the second part, we will look at where system.img, userdata.img and cache.img come from, as well as how security is provided in the Native user space.
Anyone interested, welcome!
List of articles
Here are links to my articles from this topic:
- Basics of security of the Android operating system. Core level
- Basics of security of the Android operating system. Native user space, part 1
- Basics of security of the Android operating system. Native user space, part 2
- Basics of security of the Android operating system. Security at the Application Framework level. Binder ipc
What is meant by Native user space
Native user space refers to all user-space components that run outside the Dalvik Virtual Machine and that are not part of the Linux kernel. Native user space are executable files compiled for a specific architecture. These include executable files that are launched from the init script automatically or in the event of an event, toolbox utilities, as well as some executable files that the user can run from under the shell.
')
Start
As I mentioned in the first part, the Android kernel is based on the Linux kernel. As with all Linux systems, the access control is at the heart of Android security. Those. each resource (for example, a file) contains meta-information about who created this file - owner (owner) - and to which main group (owner group) belongs the owner (owner). Each process runs on behalf of a user. Each user has a main group. In addition, he may be a member of other groups. Thus, if you attach information to each resource (in rwxrwxrwx format) about who can read / write / execute a resource (for example, a file), then you can control access to this file. For example, you can assign permissions to a file: what the owner (owner) of this file can do with this file; what can users who are members of the owner group do; what everyone else can do.
Here you can read more about this.
But Android has some differences. First, initially, Android is an operating system for phones that are known to belong to very personal things and which we don’t like to give into the wrong hands. That is, it was conceived as an operating system with only one user. Therefore, it was decided to use different Linux users for security (for each application - a separate user, as I already told in the first article). Secondly, in Android, some user (users) and their UIDs (identifiers) were hard-coded into the system, which causes a lot of complaints from people related to security (although I honestly don’t really understand why this approach is criticized). We have already seen these users in the
system / core / include / private / android_filesystem_config.h file. For example,
root has the identifier
0 , and
system -
1000 .
As I have already noted, the process is launched on behalf of the same user (UID) as the process that starts this new process, i.e. UID (calling_process) == UID (called_process). The first process that runs on Android,
init, is run as root (UID == 0). Thus, in theory, all processes should also be started on behalf of the same users. So it probably would have been. But, first, processes running on behalf of a privileged user (as well as those who possess certain capabilities) can change their UID to a less privileged one. And secondly, in Android, when starting the daemons in the
init.rc script, you can also specify with which user privileges and which groups to start this process.
... service console /system/bin/sh class core console disabled user shell group log ... service servicemanager /system/bin/servicemanager class core user system group system critical onrestart restart zygote onrestart restart media onrestart restart surfaceflinger onrestart restart drm ... service media /system/bin/mediaserver class main user media group audio camera inet net_bt net_bt_admin net_bw_acct drmrpc ioprio rt 4 ...
All processes that will be started through these daemons will no longer have root privileges.
System, data and cache
I announced this topic so many times that it was possible to think that it is very complicated and confusing. In fact, it is not.
System.img ,
userdata.img and
cache.img are what is obtained by compiling the Android operating system. That is, as a result of the assembly of the system, these three files are obtained, which we write to our device.
But the most important thing is not this. Due to the fact that in the Android system, the user name and UID of system users are hard-coded, already at the compilation stage, we can determine the access rights of various system users to various directories in these images. These permissions are specified in the
system / core / include / private / android_filesystem_config.h file , which we already discussed in the first article. Access rights are defined separately for directories (android_dirs []) and separately for files (android_files []) as follows:
... struct fs_path_config { unsigned mode; unsigned uid; unsigned gid; uint64_t capabilities; const char *prefix; }; static const struct fs_path_config android_dirs[] = { { 00770, AID_SYSTEM, AID_CACHE, 0, "cache" }, { 00771, AID_SYSTEM, AID_SYSTEM, 0, "data/app" }, { 00771, AID_SYSTEM, AID_SYSTEM, 0, "data/app-private" }, { 00771, AID_SYSTEM, AID_SYSTEM, 0, "data/dalvik-cache" }, { 00771, AID_SYSTEM, AID_SYSTEM, 0, "data/data" }, { 00771, AID_SHELL, AID_SHELL, 0, "data/local/tmp" }, { 00771, AID_SHELL, AID_SHELL, 0, "data/local" }, { 01771, AID_SYSTEM, AID_MISC, 0, "data/misc" }, { 00770, AID_DHCP, AID_DHCP, 0, "data/misc/dhcp" }, { 00775, AID_MEDIA_RW, AID_MEDIA_RW, 0, "data/media" }, { 00775, AID_MEDIA_RW, AID_MEDIA_RW, 0, "data/media/Music" }, { 00771, AID_SYSTEM, AID_SYSTEM, 0, "data" }, { 00750, AID_ROOT, AID_SHELL, 0, "sbin" }, { 00755, AID_ROOT, AID_SHELL, 0, "system/bin" }, { 00755, AID_ROOT, AID_SHELL, 0, "system/vendor" }, { 00755, AID_ROOT, AID_SHELL, 0, "system/xbin" }, { 00755, AID_ROOT, AID_ROOT, 0, "system/etc/ppp" }, { 00777, AID_ROOT, AID_ROOT, 0, "sdcard" }, { 00755, AID_ROOT, AID_ROOT, 0, 0 }, }; static const struct fs_path_config android_files[] = { { 00440, AID_ROOT, AID_SHELL, 0, "system/etc/init.goldfish.rc" }, { 00550, AID_ROOT, AID_SHELL, 0, "system/etc/init.goldfish.sh" }, { 00440, AID_ROOT, AID_SHELL, 0, "system/etc/init.trout.rc" }, { 00550, AID_ROOT, AID_SHELL, 0, "system/etc/init.ril" }, { 00550, AID_ROOT, AID_SHELL, 0, "system/etc/init.testmenu" }, { 00550, AID_DHCP, AID_SHELL, 0, "system/etc/dhcpcd/dhcpcd-run-hooks" }, { 00440, AID_BLUETOOTH, AID_BLUETOOTH, 0, "system/etc/dbus.conf" }, { 00444, AID_RADIO, AID_AUDIO, 0, "system/etc/AudioPara4.csv" }, { 00555, AID_ROOT, AID_ROOT, 0, "system/etc/ppp/*" }, { 00555, AID_ROOT, AID_ROOT, 0, "system/etc/rc.*" }, { 00644, AID_SYSTEM, AID_SYSTEM, 0, "data/app/*" }, { 00644, AID_MEDIA_RW, AID_MEDIA_RW, 0, "data/media/*" }, { 00644, AID_SYSTEM, AID_SYSTEM, 0, "data/app-private/*" }, { 00644, AID_APP, AID_APP, 0, "data/data/*" }, { 00755, AID_ROOT, AID_ROOT, 0, "system/bin/ping" }, { 02750, AID_ROOT, AID_INET, 0, "system/bin/netcfg" }, { 06755, AID_ROOT, AID_ROOT, 0, "system/xbin/su" }, { 06755, AID_ROOT, AID_ROOT, 0, "system/xbin/librank" }, { 06755, AID_ROOT, AID_ROOT, 0, "system/xbin/procrank" }, { 06755, AID_ROOT, AID_ROOT, 0, "system/xbin/procmem" }, { 06755, AID_ROOT, AID_ROOT, 0, "system/xbin/tcpdump" }, { 04770, AID_ROOT, AID_RADIO, 0, "system/bin/pppd-ril" }, { 00750, AID_ROOT, AID_SHELL, (1 << CAP_SETUID) | (1 << CAP_SETGID), "system/bin/run-as" }, { 00755, AID_ROOT, AID_SHELL, 0, "system/bin/*" }, { 00755, AID_ROOT, AID_ROOT, 0, "system/lib/valgrind/*" }, { 00755, AID_ROOT, AID_SHELL, 0, "system/xbin/*" }, { 00755, AID_ROOT, AID_SHELL, 0, "system/vendor/bin/*" }, { 00750, AID_ROOT, AID_SHELL, 0, "sbin/*" }, { 00755, AID_ROOT, AID_ROOT, 0, "bin/*" }, { 00750, AID_ROOT, AID_SHELL, 0, "init*" }, { 00750, AID_ROOT, AID_SHELL, 0, "charger*" }, { 00750, AID_ROOT, AID_SHELL, 0, "sbin/fs_mgr" }, { 00640, AID_ROOT, AID_SHELL, 0, "fstab.*" }, { 00644, AID_ROOT, AID_ROOT, 0, 0 }, }; ...
And the
static inline function
void fs_config (const char * path, int dir, unsigned * uid, unsigned * gid, unsigned * mode, uint64_t * capabilities) , which is further defined in this file, is responsible for setting the owner, owner group, capabilities and access rights . This function is called during
image build .
In general, everything should be more or less clear, except for setting the
access rights flags (setuid and setgid) for some files (for example, for “system / xbin / su”, the access rights are defined as 06755, where the first 6 means that the setting user ID (4) and setting flag group ID (2)). Setting these flags means that the user can elevate the rights of the process being started to the level of owner (owner) of the file or owner group. In the case of Android, each application is a user with its own UID and GID. Thus, by default, if you run any native executable from your application, it is executed with the same UID and GID as the application that caused it. Installing these permission flags allows you to perform native executable with the rights of the owner. In our case, the owner is AID_ROOT (root). This happens as follows
system / extras / su / su.c :
int main(int argc, char **argv) { ... int uid, gid, myuid; ... if(setgid(gid) || setuid(uid)) { ... } ... }
Those. the
setuid and
setgid functions are called. In this case, if these functions are completed successfully, the process starts on behalf of the owner and owner group of the file. In our example, this process gets the superuser rights, i.e. he can do whatever he wants :) Such anarchy is not always justified, so the concept of
capabilities was introduced in Linux. So, the “run-as” application does not need all the superuser rights, it only needs to be able to change its identifier in order to launch applications on behalf of various users. By the way, the capabilities seem to have appeared recently - in Android 2.3.x I have not met them.
Security
In the case of privileged programs (such as, su), it is necessary to limit the range of applications that these programs can call. Otherwise, any application can get superuser rights. Therefore, very often, UID checks are built into such programs:
... #include <private/android_filesystem_config.h> ... int main(int argc, char **argv) { struct passwd *pw; int uid, gid, myuid; /* Until we have something better, only root and the shell can use su. */ myuid = getuid(); if (myuid != AID_ROOT && myuid != AID_SHELL) { fprintf(stderr,"su: uid %d not allowed to su\n", myuid); return 1; } ... }
Those. the program first checks on whose behalf the calling process is running using the
getuid () function. And then compares these values ​​with the values ​​that are hard-coded into the system. In this case, only processes running on behalf of
the system and
root users are allowed to use
su .
Conclusion
In this article we have finished to understand how security is provided at the level of the Native user space. In the following articles, I plan to make out how permission (permissions) work, but due to the large current load I don’t know when to start writing them. As always, I will be very happy with additions and corrections.
PS Invited to prepare a report on
DevConf @ mobi What do you think, will the audience be interested in a report on the security of the Android operating system at a conference that is more focused on application developers?
Links
- "Embedded Android" by Karim Yaghmour
- Android Security Underpinnings by Marko Gargenta
- Linux Users and Groups
- Image configuration
- Suid
- Overview of capabilities