android sdcard 权限管理策略研究
来源:互联网 发布:287团淘宝兼职是真的吗 编辑:程序博客网 时间:2024/06/05 04:57
自从android4.4 以来,第三方应用程序是不能再随便的访问sdcard了,从开发者的角度而言,研究一下android系统到底是怎么样来实现这样的控制还是比较有价值的。
首先分析下现状,现在已知,对于内部存储系统android的控制策略还是没有多大改变的,内部sdcard还是可以在申请了android.permission.WRITE_EXTERNAL_STORAGE
这个权限后随便访问的,而对于外置sdcard就没那么方便。网络上一般的说法是只有内置app才可以访问,其他的app就只能限定/data/data/Package_name/ 的目录下。
那具体这个差别是怎么实现的呢?
首先sdcard的权限管理是fuse 即用户空间文件系统(Filesystem in Userspace)来实现的,一旦使用了fuse,文件的访问权限就不在是单纯的Linux的ACL机制,不能光凭借文件的umask来判断谁具有读写权限了。以5.0为例:
drwxrwx--- root sdcard_r 2014-01-01 07:47 Alarms
drwxrwx--x root sdcard_r 2014-01-01 07:47 Android
drwxrwx--- root sdcard_r 2014-01-02 06:35 DCIM
drwxrwx--- root sdcard_r 2014-01-01 07:47 Download
即使app的gid加入了sdcard_r甚至sdcard_rw也是不能访问这些目录了。原因就是这个fuse的机制。本质上android的fuse并不是正统的fuse,而是fuse代码的重写,以sdcard的deamo的形式存在在android系统中,这个是google说的。
那么找到sdcard的deamo的实现就可以找到线索了。在android源码system/core/sdcard中可以找sdcard.c这么个文件。它最终被编译成为sdcard可执行程序,这就是所谓的sdcard的守护进程。查看它的代码可以发现它在初始化以后就跑了一个for (;;)的循环,一直从/dev/fuse这个设备中读取请求,然后交给handle_fuse_request去处理。从Android的整体设计原则来猜想,这又是一个CS的典型结构,即只有这个进程可以进行真正意义上的读写操作,上层的对于sdcard的io操作,实际上是通过/dev/fuse交给这个守护进程来处理的。
class late_start
service fuse_sdcard1 /system/bin/sdcard -u 1023 -g 1023 -w 1023 -d /mnt/media_rw/sdcard1 /storage/sdcard1
class late_start
-u 1023 应该是uid
-g 1023 应该是gid
-l /data/media /mnt/shell/emulated 这个表示的什么要看代码实现了,代码里前一个是source_path 后一个dest_path, dest_path挂载了/dev/fuse。
根据经验来猜测,上层针对 /mnt/shell/emulated的操作本质上是对/data/media 的操作,但是中间夹杂着fuse的处理,必须由sdcard这个程序来决定真正的操作。
drwxrwx--- media_rw media_rw 2014-01-01 07:47 media
可以发现这个目录的uid和gid都是media_rw, 而 # ps -c sdcard
USER PID PPID VSIZE RSS CPU WCHAN PC NAME
所有对与虚拟sdcard的操作都通过fuse的设计,经kernel从/dev/fuse又给/system/bin/sdcard 这个进程,由它来正真的handle请求。 Ok,大概的逻辑就是这样了,详细的实现还是要从代码中去学习。
问题回到原来的,为什么内置sdcard可以随便访问,而外置不行呢。答案还在代码中,查看sdcard.c中对于创建文件的处理,
其中check_caller_access_to_name 是关键,因为他会返回EACCES!
注释写的比较清楚就不多分析了,重点是has_rw ,这个谁决定
has_rw = get_caller_has_rw_locked(fuse, hdr);
get_caller_has_rw_locked的实现
hashmapContainsKey这个方法,看appid是不是在appid_with_rw 这个map中,那这个map是哪里来的呢?通过追踪可以发现时通过/data/system/packages.list这个文件解析得出的。
这段代码在解析packages.list中的每一行,如果有gid = fuse->write_gid的那就会被加到这个Hashmap中了。
com.android.providers.downloads.ui 10005 0 /data/data/com.android.providers.downloads.ui default 1028,1015,1023,1024,2001,3003,3007
com.android.pacprocessor 10040 0 /data/data/com.android.pacprocessor platform 3003
com.android.certinstaller 10024 0 /data/data/com.android.certinstaller platform none
com.android.speechrecorder 10050 0 /data/data/com.android.speechrecorder default none
android 1000 0 /data/system platform 1028,1015,3002,3001,3003
那fuse->write_gid 是什么呢?
gid_t write_gid = AID_SDCARD_RW; 这是初始化时候的值,AID_SDCARD_RW 在system/core/include/private/android_filesystem_config.h中有定义, 1015正是sdcard_rw的group id。
但是,但是,还记得init.rc里面的 -w吗?
service fuse_sdcard1 /system/bin/sdcard -u 1023 -g 1023 -w 1023 -d /mnt/media_rw/sdcard1 /storage/sdcard1
这个-w的值就覆盖了write_gid , 1023是什么gid?
#define AID_MEDIA_RW 1023
ok,正是media_rw的group id。
AID_SDCARD_RW 和 AID_MEDIA_RW 有什么差别,怎么才能加入这个对应group呢?
android权限管理机制,加permission呗。那来看看对应的permission是怎么声明的。
要加入sdcard_rw组需要加android.permission.WRITE_EXTERNAL_STORAGE,
要加入media_rw组需要加android.permission.WRITE_MEDIA_STORAGE。
Ok,真相大白了! WRITE_EXTERNAL_STORAGE的protectionLevel 仅仅是dangerous, 而WRITE_MEDIA_STORAGE的protectionLevel 是signature|system, 这就是为什么第三方应用程序不能随便访问sdcard的原因!
首先分析下现状,现在已知,对于内部存储系统android的控制策略还是没有多大改变的,内部sdcard还是可以在申请了android.permission.WRITE_EXTERNAL_STORAGE
这个权限后随便访问的,而对于外置sdcard就没那么方便。网络上一般的说法是只有内置app才可以访问,其他的app就只能限定/data/data/Package_name/ 的目录下。
那具体这个差别是怎么实现的呢?
首先sdcard的权限管理是fuse 即用户空间文件系统(Filesystem in Userspace)来实现的,一旦使用了fuse,文件的访问权限就不在是单纯的Linux的ACL机制,不能光凭借文件的umask来判断谁具有读写权限了。以5.0为例:
drwxrwx--- root sdcard_r 2014-01-01 07:47 Alarms
drwxrwx--x root sdcard_r 2014-01-01 07:47 Android
drwxrwx--- root sdcard_r 2014-01-02 06:35 DCIM
drwxrwx--- root sdcard_r 2014-01-01 07:47 Download
即使app的gid加入了sdcard_r甚至sdcard_rw也是不能访问这些目录了。原因就是这个fuse的机制。本质上android的fuse并不是正统的fuse,而是fuse代码的重写,以sdcard的deamo的形式存在在android系统中,这个是google说的。
那么找到sdcard的deamo的实现就可以找到线索了。在android源码system/core/sdcard中可以找sdcard.c这么个文件。它最终被编译成为sdcard可执行程序,这就是所谓的sdcard的守护进程。查看它的代码可以发现它在初始化以后就跑了一个for (;;)的循环,一直从/dev/fuse这个设备中读取请求,然后交给handle_fuse_request去处理。从Android的整体设计原则来猜想,这又是一个CS的典型结构,即只有这个进程可以进行真正意义上的读写操作,上层的对于sdcard的io操作,实际上是通过/dev/fuse交给这个守护进程来处理的。
事实上是这样吗。来看下sdcard是谁启动的:
-------------------------------------------------------------------------------------------------------------------------------------------------------------
service sdcard /system/bin/sdcard -u 1023 -g 1023 -l /data/media /mnt/shell/emulatedclass late_start
service fuse_sdcard1 /system/bin/sdcard -u 1023 -g 1023 -w 1023 -d /mnt/media_rw/sdcard1 /storage/sdcard1
class late_start
disabled
-----------------------------------------------------------------------------------------------------------------------------------------------------------
-u 1023 应该是uid
-g 1023 应该是gid
-l /data/media /mnt/shell/emulated 这个表示的什么要看代码实现了,代码里前一个是source_path 后一个dest_path, dest_path挂载了/dev/fuse。
根据经验来猜测,上层针对 /mnt/shell/emulated的操作本质上是对/data/media 的操作,但是中间夹杂着fuse的处理,必须由sdcard这个程序来决定真正的操作。
drwxrwx--- media_rw media_rw 2014-01-01 07:47 media
可以发现这个目录的uid和gid都是media_rw, 而 # ps -c sdcard
USER PID PPID VSIZE RSS CPU WCHAN PC NAME
media_rw 2098 1 16232 1120 4 ffffffff 9b64f1d4 S /system/bin/sdcard
所有对与虚拟sdcard的操作都通过fuse的设计,经kernel从/dev/fuse又给/system/bin/sdcard 这个进程,由它来正真的handle请求。 Ok,大概的逻辑就是这样了,详细的实现还是要从代码中去学习。
问题回到原来的,为什么内置sdcard可以随便访问,而外置不行呢。答案还在代码中,查看sdcard.c中对于创建文件的处理,
static int handle_mknod(struct fuse* fuse, struct fuse_handler* handler, const struct fuse_in_header* hdr, const struct fuse_mknod_in* req, const char* name){ bool has_rw; struct node* parent_node; char parent_path[PATH_MAX]; char child_path[PATH_MAX]; const char* actual_name; pthread_mutex_lock(&fuse->lock); has_rw = get_caller_has_rw_locked(fuse, hdr); parent_node = lookup_node_and_path_by_id_locked(fuse, hdr->nodeid, parent_path, sizeof(parent_path)); TRACE("[%d] MKNOD %s 0%o @ %"PRIx64" (%s)\n", handler->token, name, req->mode, hdr->nodeid, parent_node ? parent_node->name : "?"); pthread_mutex_unlock(&fuse->lock); if (!parent_node || !(actual_name = find_file_within(parent_path, name, child_path, sizeof(child_path), 1))) { return -ENOENT; } if (!check_caller_access_to_name(fuse, hdr, parent_node, name, W_OK, has_rw)) { return -EACCES; } __u32 mode = (req->mode & (~0777)) | 0664; if (mknod(child_path, mode, req->rdev) < 0) { return -errno; } return fuse_reply_entry(fuse, hdr->unique, parent_node, name, actual_name, child_path);}
其中check_caller_access_to_name 是关键,因为他会返回EACCES!
static bool check_caller_access_to_name(struct fuse* fuse, const struct fuse_in_header *hdr, const struct node* parent_node, const char* name, int mode, bool has_rw) { /* Always block security-sensitive files at root */ if (parent_node && parent_node->perm == PERM_ROOT) { if (!strcasecmp(name, "autorun.inf") || !strcasecmp(name, ".android_secure") || !strcasecmp(name, "android_secure")) { return false; } } /* No additional permissions enforcement */ if (fuse->derive == DERIVE_NONE) { return true; } /* Root always has access; access for any other UIDs should always * be controlled through packages.list. */ if (hdr->uid == 0) { return true; } /* If asking to write, verify that caller either owns the * parent or holds sdcard_rw. */ if (mode & W_OK) { if (parent_node && hdr->uid == parent_node->uid) { return true; } return has_rw; } /* No extra permissions to enforce */ return true;}
注释写的比较清楚就不多分析了,重点是has_rw ,这个谁决定
has_rw = get_caller_has_rw_locked(fuse, hdr);
get_caller_has_rw_locked的实现
/* Return if the calling UID holds sdcard_rw. */ static bool get_caller_has_rw_locked(struct fuse* fuse, const struct fuse_in_header *hdr) { /* No additional permissions enforcement */ if (fuse->derive == DERIVE_NONE) { return true; } appid_t appid = multiuser_get_app_id(hdr->uid); return hashmapContainsKey(fuse->appid_with_rw, (void*) (uintptr_t) appid); }
hashmapContainsKey这个方法,看appid是不是在appid_with_rw 这个map中,那这个map是哪里来的呢?通过追踪可以发现时通过/data/system/packages.list这个文件解析得出的。
if (strtoul(token, NULL, 10) == fuse->write_gid) { hashmapPut(fuse->appid_with_rw, (void*) (uintptr_t) appid, (void*) (uintptr_t) 1); break;}
这段代码在解析packages.list中的每一行,如果有gid = fuse->write_gid的那就会被加到这个Hashmap中了。
com.android.providers.downloads.ui 10005 0 /data/data/com.android.providers.downloads.ui default 1028,1015,1023,1024,2001,3003,3007
com.android.pacprocessor 10040 0 /data/data/com.android.pacprocessor platform 3003
com.android.certinstaller 10024 0 /data/data/com.android.certinstaller platform none
com.android.speechrecorder 10050 0 /data/data/com.android.speechrecorder default none
android 1000 0 /data/system platform 1028,1015,3002,3001,3003
那fuse->write_gid 是什么呢?
gid_t write_gid = AID_SDCARD_RW; 这是初始化时候的值,AID_SDCARD_RW 在system/core/include/private/android_filesystem_config.h中有定义, 1015正是sdcard_rw的group id。
但是,但是,还记得init.rc里面的 -w吗?
service fuse_sdcard1 /system/bin/sdcard -u 1023 -g 1023 -w 1023 -d /mnt/media_rw/sdcard1 /storage/sdcard1
这个-w的值就覆盖了write_gid , 1023是什么gid?
#define AID_MEDIA_RW 1023
ok,正是media_rw的group id。
AID_SDCARD_RW 和 AID_MEDIA_RW 有什么差别,怎么才能加入这个对应group呢?
android权限管理机制,加permission呗。那来看看对应的permission是怎么声明的。
<permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" android:permissionGroup="android.permission-group.STORAGE" android:label="@string/permlab_sdcardWrite" android:description="@string/permdesc_sdcardWrite" android:protectionLevel="dangerous" /> <!-- @SystemApi Allows an application to write to internal media storage @hide --> <permission android:name="android.permission.WRITE_MEDIA_STORAGE" android:permissionGroup="android.permission-group.STORAGE" android:label="@string/permlab_mediaStorageWrite" android:description="@string/permdesc_mediaStorageWrite" android:protectionLevel="signature|system" />
要加入sdcard_rw组需要加android.permission.WRITE_EXTERNAL_STORAGE,
要加入media_rw组需要加android.permission.WRITE_MEDIA_STORAGE。
Ok,真相大白了! WRITE_EXTERNAL_STORAGE的protectionLevel 仅仅是dangerous, 而WRITE_MEDIA_STORAGE的protectionLevel 是signature|system, 这就是为什么第三方应用程序不能随便访问sdcard的原因!
0 0
- android sdcard 权限管理策略研究
- android sdcard的研究
- Android SdCard写入权限
- android 模拟器 sdcard权限修改
- 更改Android sdcard访问权限
- android 模拟器 sdcard权限修改
- android 模拟器 sdcard权限修改
- android sdcard访问权限问题
- 权限管理研究
- android sdcard 写入数据权限的问题
- 关于android读写sdcard的权限问题
- 更改android模拟器sdcard文件夹的权限
- Android sdcard读写权限问题之一
- Android 外置SDCard读写权限总结
- android 权限策略
- RBAC权限管理 的研究
- 【Shiro权限管理】12.Shiro认证策略
- 【编程语言】Android--如何创建sdcard,向sdcard里面传送图片,更改sd卡权限
- Android自定义Toast
- 发工资之贪心 hdu2021
- 博客搬家至博客园
- lightoj-1422 - Halloween Costumes 解题报告 区间dp
- 如何知道一个edittext失去焦点
- android sdcard 权限管理策略研究
- TextView 添加Onclick 无效
- Hadoop学习笔记———《Mac OS X 下hadoop2.6.0安装教程》
- 拒绝访问。 Failed to open the WinNT service manager..."错误
- leetcode-172 Factorial Trailing Zeroes
- 选择处事
- linux mmap详解
- Storm实战常见问题及解决方案
- BZOJ 3128 Usaco2013 Open Figure Eight