本文共 8162 字,大约阅读时间需要 27 分钟。
Android 权限控制代码分析
前在文章介绍过android系统管理层次: ,这里就核心代码分析一下
android系统充分利用了linux的用户权限管理方法,所以如果需要移植到其它系统,这一块也是一个相当不小的工作量。那么android系统到底是如何使用这些的有利因素呢?
首先需要知道linux权限的两个基本知识:
1、 一个用户可以属于多个组.
2、 一个文件只能属于某个组。
这里主要是在AndroidManifest.xml中声明权限,主要是通过在AndroidManifest.xml中显示地声明应用程序需要的权限,防止应用程序错误的使用服务,不恰当访问资源。
Android中每种权限都用一个独立的标签表示.如:
<uses-permission android:name="android.permission.WAKE_LOCK" /> <uses-permission android:name="android.permission.INTERNET" /> <uses-permission android:name="android.permission.READ_PHONE_STATE" /> <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
当在安装(Install) 应用程序时,Android就会给予一个UID。这个UID可连结到该应用程序的 AndroidManifest.xml档案的内容。所User在安装你的应用程序时,在屏幕上的窗口里可以检视这个 AndroidManifest.xml档案的内容。在检视时,用户会看到你对应用程序的目的、权限等说明。当你接受这支程序的意图、权限说明之后,Android就安装它,并给它一个UID。万一在你的应用程序执行期间有越轨(企图做出非权限范围)的行为时,用户将会得到Android的警告讯息。
下面是两个安装程序安装时的界面
这两个应用其实代码是一样的,唯一的不同就是它们的AndroidManifest.xml,图2的AndroidManifest.xml中多了如下内容:
<uses-permission android:name="android.permission.RECORD_AUDIO" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
即需要使用存储设备和录音设备,在安装的时候,就会提示用户它需要的权限。Android里面是怎么去控制的呢?
在安装apk的时候,会解析这个AndroidManifest.xml,把相应的信息保存起来。
代码路径:frameworks\base\core\java\android\content\pm\PackageParser.java
最终调用的是private Package parsePackage(
Resources res, XmlResourceParser parser, int flags, String[] outError)
这个函数,在里面对AndroidManifest.xml进行了解析,其中就有
private Package parsePackage( Resources res, XmlResourceParser parser, int flags, String[] outError) throws XmlPullParserException, IOException {
...
String tagName = parser.getName(); if (tagName.equals("application")) { ... } else if (tagName.equals("permission-group")) { if (parsePermissionGroup(pkg, res, parser, attrs, outError) == null) { return null; } } else if (tagName.equals("permission")) { if (parsePermission(pkg, res, parser, attrs, outError) == null) { return null; } } else if (tagName.equals("permission-tree")) { if (parsePermissionTree(pkg, res, parser, attrs, outError) == null) { return null; } } else if (tagName.equals("uses-permission")) { sa = res.obtainAttributes(attrs, com.android.internal.R.styleable.AndroidManifestUsesPermission); // Note: don't allow this value to be a reference to a resource // that may change. String name = sa.getNonResourceString( com.android.internal.R.styleable.AndroidManifestUsesPermission_name); sa.recycle(); if (name != null && !pkg.requestedPermissions.contains(name)) { pkg.requestedPermissions.add(name.intern()); } XmlUtils.skipCurrentTag(parser); }
...
}
这里对它使用的权限进行了解析。
这里保存的都是"android.permission.WRITE_EXTERNAL_STORAGE"这样的字符串,在解析完后,会调用grantPermissionsLP函数获取对应的group_id,
代码路径:frameworks\base\services\java\com\android\server\PackageManagerService.java
private void grantPermissionsLP(PackageParser.Package pkg, boolean replace) { ... if (allowed) { if (!gp.grantedPermissions.contains(perm)) { changedPermission = true; gp.grantedPermissions.add(perm); gp.gids = appendInts(gp.gids, bp.gids); } else if (!ps.haveGids) { gp.gids = appendInts(gp.gids, bp.gids); } } else { Slog.w(TAG, "Not granting permission " + perm + " to package " + pkg.packageName + " because it was previously installed without"); } ... }这里把相应的组都保存到了gids中。
当应用程序启动的过程中会调用
private final void startProcessLocked(ProcessRecord app,
String hostingType, String hostingNameStr)
代码路径:frameworks\base\services\java\com\android\server\am\ActivityManagerService.java
private final void startProcessLocked(ProcessRecord app, String hostingType, String hostingNameStr) { ... try { int uid = app.info.uid; int[] gids = null; try { gids = mContext.getPackageManager().getPackageGids( app.info.packageName); } catch (PackageManager.NameNotFoundException e) { Slog.w(TAG, "Unable to retrieve gids", e); } ... int pid = Process.start("android.app.ActivityThread", mSimpleProcessManagement ? app.processName : null, uid, uid, gids, debugFlags, null); } 这里就是获取前面保存的gids,再后面调用创建了一个新的进程,这里传的参数就有gids
创建新进程利用jni最终调用 forkAndSpecializeCommon 函数 (路径:\dalvik\vm\native\dalvik_system_Zygote.c)
static pid_t forkAndSpecializeCommon(const u4* args, bool isSystemServer) { pid_t pid; uid_t uid = (uid_t) args[0]; gid_t gid = (gid_t) args[1]; ArrayObject* gids = (ArrayObject *)args[2]; u4 debugFlags = args[3]; ArrayObject *rlimits = (ArrayObject *)args[4]; int64_t permittedCapabilities, effectiveCapabilities; ... pid = fork(); if (pid == 0) { int err; /* The child process */ err = setgroupsIntarray(gids); if (err < 0) { LOGE("cannot setgroups(): %s", strerror(errno)); dvmAbort(); } err = setrlimitsFromArray(rlimits); if (err < 0) { LOGE("cannot setrlimit(): %s", strerror(errno)); dvmAbort(); } err = setgid(gid); if (err < 0) { LOGE("cannot setgid(%d): %s", gid, strerror(errno)); dvmAbort(); } err = setuid(uid); if (err < 0) { LOGE("cannot setuid(%d): %s", uid, strerror(errno)); dvmAbort(); } ... } 我们看到在子进程里调用setgroupsIntarray设置该进程所属的组,这样它就拥有了该组的权限。也通过setgid及setuid决定了应用程序的uid及gid值。
举个例子:
我们新建一Android工程,读取其应用的uid/gid值,贴入如下代码:
try{
java.lang.Process process = Runtime.getRuntime().exec("id");
InputStream input = process.getInputStream();
byte[] bytes = new byte[1204];
int len;
while((len = (input.read(bytes))) > 0)
{
System.out.print(new String(bytes, 0, len));
}
input.close();
这里运行运行linux的id命令的代码,id命令的功能是输出当前用户的uid,主要的group id和所在的group
在其AndroidManifest.xml添加如下权限:
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
运行后看log里面有:
这里面就有groups=1015
再看下android_filesystem_config.h里面
代码路径:
system\core\include\private
有如下代码
#define AID_ROOT 0 /* traditional unix root user */ #define AID_SYSTEM 1000 /* system server */ #define AID_RADIO 1001 /* telephony subsystem, RIL */ #define AID_BLUETOOTH 1002 /* bluetooth subsystem */ #define AID_GRAPHICS 1003 /* graphics devices */...
#define AID_WIFI 1010 /* wifi subsystem */ #define AID_ADB 1011 /* android debug bridge (adbd) */ #define AID_INSTALL 1012 /* group for installing packages */ #define AID_MEDIA 1013 /* mediaserver process */ #define AID_DHCP 1014 /* dhcp client */ #define AID_SDCARD_RW 1015 /* external storage write access */
再看下platform.xml
路径:frameworks\base\data\etc
有如下配置
<permission name="android.permission.WRITE_EXTERNAL_STORAGE" >
<group gid="sdcard_rw" />
</permission>
这样就把android.permission.WRITE_EXTERNAL_STORAGE 、"sdcard_rw"、以及 1015组关联起来了
我们再看下当sd卡挂载上去后目录的权限:
ls -l
drwxr-xr-x root system 1970-01-01 08:00 obb
drwxr-xr-x root system 1970-01-01 08:00 asec
drwx------ root root 1970-01-01 08:00 secure
d---rwxr-x system sdcard_rw 2012-03-29 17:11 sdcard
可以看到对于sd卡,组用户具有读写权限,而我们的应用也加入了这个组,这样它就可以操作sdcard了。
当一个应用需要操作sdcard而没有在AndroidManifest.xml添加相应的权限时,就不能成功完成。
以下是同一个程序,一个有在AndroidManifest.xml添加WRITE_EXTERNAL_STORAGE权限,一个没有,它们的对比,可以看到由于没有权限程序运行异常了。
对于管理权限的xml文件补充说明一下:
源代码中权限文件位于: frameworks\base\data\etc 下面,镜像生成在 system\etc\permissions\platform.xml中
文件权限读取点:
readPermissions @ PackageManagerService.java
void readPermissions() { // Read permissions from .../etc/permission directory. File libraryDir = new File(Environment.getRootDirectory(), "etc/permissions"); if (!libraryDir.exists() || !libraryDir.isDirectory()) { Slog.w(TAG, "No directory " + libraryDir + ", skipping"); return; } if (!libraryDir.canRead()) { Slog.w(TAG, "Directory " + libraryDir + " cannot be read"); return; } // Iterate over the files in the directory and scan .xml files for (File f : libraryDir.listFiles()) { // We'll read platform.xml last if (f.getPath().endsWith("etc/permissions/platform.xml")) { continue; } if (!f.getPath().endsWith(".xml")) { Slog.i(TAG, "Non-xml file " + f + " in " + libraryDir + " directory, ignoring"); continue; } if (!f.canRead()) { Slog.w(TAG, "Permissions library file " + f + " cannot be read"); continue; } readPermissionsFromXml(f); } // Read permissions from .../etc/permissions/platform.xml last so it will take precedence final File permFile = new File(Environment.getRootDirectory(), "etc/permissions/platform.xml"); readPermissionsFromXml(permFile); }
platform.xml 的重要性在于:
<!-- This file is used to define the mappings between lower-level system user and group IDs and the higher-level permission names managed by the platform. Be VERY careful when editing this file! Mistakes made here can open big security holes. -->
转载地址:http://vhdcb.baihongyu.com/