Android权限及chown失败原因查找

原文: http://blog.csdn.net/plato_f/article/details/18793049

 

文章将描述在Android2.3.7中移植wifi所碰到的一个chown失败问题,并记录问题分析的过程。

注意文章中引用了《UNIX环境高级编程》

1:发现问题

启动Android 的wifi硬件的时候,system_server进程将调用hardware层所提供的wifi_start_supplicant(), 该函数将会调用ensure_config_file_exists()来确认配置文件。

如/data/misc/wifi/wpa_supplicant.conf中的文件不存在,将会新建一个并“COPY”/system/etc/wifi/wpa_supplicant.conf文件中的内容。完成后将会对新建的文件进行chown的系统调用,这时候错误就发生了E/WifiHW  (   92): Error changing group ownership of /data/misc/wifi/wpa_supplicant.conf  to 1010: Operation not permitted。

 

2:分析问题

1):遇到这类问题第一时间就是google了,国外的报出了问题,但没有决定。国内的解决了,但方法令人吐血,不是直接手动copy文件就是直接chown代码去掉。哎,本着学习的心态继续下去。

 

2):网上实在找不到解决办法,哪从0开始吧。首先看了《UNIX环境高级编程》4.11chown的说明。大致的意思就是在_POSIX_CHOWN_RESTRICTED起用作的情况下:

(1):只有超级用户进程能更改该文件的用户ID。

(2):若满足下列条件,一个非超级用户进程可以更改该文件的的组ID.

(a):进程拥有此文件(其有效用户ID等于该文件的用户ID).

(b):参数owner等于文件的用户ID,参数group等于进程的有效组ID或进程的添加组ID之一。

好了当前进程system_server的有效用户ID是system,不是超级用户,而chown(SUPP_CONFIG_FILE, AID_SYSTEM, AID_WIFI),也只是想改变文件的组ID。哪么新建出的文件的uID,跟gID又是什么呢?这将决定到条件(b)是否成立。

另外_POSIX_CHOWN_RESTRICTED是否定义,可用pathconf或fpathconf函数查询。

 

3):新建文件(使用open,creat)的用户ID为进程的有效用户ID。而组ID呢,这个有点复杂,在《UNIX环境高级编程》4.6提到新建文件组ID的问题。POSIX.1允许选择下列之一作为新文件的组ID,

(a):新文件的组ID可以是进程的有效组ID.

(b):新文件的组ID可以是它所有目录的组ID.

如果按照(b)来继承目录组的ID的话,新建出的文件其组ID将是WIFI,哪么参数group等于进程的有效组ID的条件将成立。可到现在,在实际的Android下验证,新建出的文件其gid总是其进程的有效组ID. 而到现在我还不清楚Android是属于SVR4还是4.3+BSD还是都不属于,如果知道朋友同学可以留一个言吗。

 

4):在没有办法改变新文件的组ID的情况下,只有寄望于进程的添加组ID了。好吧,在此之前我也不清楚这些ID哪些ID的,但这些ID又很重要,权限就是这些东西决定的,硬着头皮看吧。

(1):与进程有关的ID有6个或者更多。

(a):实际用户ID(real user id) ,实际组ID(real group id)---这两个字段在登录时取自口令文件中的登录项,通常,在一个登录会话期间这些值并不改变,但超级用户(privileged user)进程有方法改变它他,例如setuid,setgid等。

(b):有效用户ID(effective user id) ,有效组ID(effective group id)及添加组ID--这几组ID组定了进程对文件的访问权限,通常,有效用户ID等于实际用户ID,有效组ID等于实际组ID。将与文件许可权位结合讲述其作用。

(c):保存设置-用户ID(saved user id), 保存设置-组ID(saved group id)--大概是作为有效用户/组ID的副本使用。_POSIX_SAVED_IDS控制着这个ID的可选。

 (2): 与文件有关的存取许可权。

可通过函数stat(),fstat(),或者lstat获取到文件的有关信息,这类函数第二个参数是指针,函数调用成功将返回一个struct stat的结构体。其中的成员st_mode不仅包含了文件类型的信息,还包含文件存取许可权,st_mode是32位。        

每个文件有9个存取许可权位,可分成三类,chmod命令或者函数可修改这些位。

(a):S_IRUSR(用户-读), S_IWUSR(用户-写), S_IXUSR(用户-执行)

(b):S_IRGRP(-读), S_IWGRP(-写), S_IXGRP(组-执行)

(c):S_IROTH(其他-读), S_IWOTH(其他-写), S_IXOTH(其他-执行)

另外struct stat中的成员st_uid表示了文件的所有者,st_gid表示了文件组所有者。

(3):进程每次打开,创建或删除一个文件时,内核将进行文件许可权测试,测试可能涉及文件所有者ID(st_uid,及st_gid), 以及进程的有效ID及进程的添加ID(用户/组)。

(a):超级用户进程,有效用户ID是0,允许存取。

(b):进程的effective uid等于文件所有者id(进程拥有该文件),相应文件存取许可位S_IRUSR/S_IWUSR被设置,则允许存取,否则拒绝。

(c):进程的effective gid或者添加组id等于文件组的ID, 相应文件存取许可位S_IRGRP/S_IWGRP被设置,则允许存取,否则拒绝。

(d):相应文件其他用户存取许可权S_IROTH/S_IWOTH被设置,则允许存取,否则拒绝。

(4):st_mode还有这么两个特殊的标志位S_ISUID,S_ISGID

(a):设置-用户-ID(set-user-ID),当执行此文件时进程的有效用户ID设置为文件的所有者(st_uid)。

(b):设置-组-ID(set-group-ID),当执行此文件时进程的有效组ID设置为文件的组所有者(st_gid)。 

这两个标志可在超级用户下设定, 例如:chmod u+s xxx.c 文件的S_ISUID将置位,进程的有效用户ID设置为文件所有者ID,所以其存取权限将依据(3).(b)。

chmod +s xxx.c  文件的S_ISUID及S_ISGID将置位。chmod g+s xxx.c  文件的S_ISGID将置位。

另外直接使用数字表示,S_ISUID对就是4,S_ISGID对就是2, 例如xxx.c存取许可权原来为0666,现在要增加S_ISUID, chmod 4666xxx.c 。

好吧,进程与文件,权限控制就说到这里。Android在进行权限控制的时候大部份是在代码编写的时候就已设定好,并用是利用进程的添加组id方式来获取文件的存取权限。我所见到有三个途径。

 

5)Android权限-我所接触过的途径。

(1):在启动脚本init.rc或者init_XXX.rc.主要使用者是一个标准bin,或者第三方库。

例如:

service media /system/bin/mediaserver
user media
group system audio camera graphics inet net_bt net_bt_admin net_raw

media进程的uid及gid都为media(AID_MEDIA 1013),其添加组id包含system audio camera graphics inet net_bt net_bt_admin net_raw.

也就是说如果某个进程,其uid/gid是添加组id之一,哪么就会根据4).(3)中的描述的内核将进行文件许可权测试。

(2):Android中一些系统级的服务,直接使作函数setgroups()设置添加组id。

例如zygote进程启动的system_server,在zygoteInit.java里startSystemServer(),有一个参数--setgroups=1001,1002,1003,1004,1005,1006,1007,1008,1009,1010,1018,3001,3002,3003。它最终是通过调用setgroups()来设置添加组id。setgroups()的设置可通过getgroups()系统调用查询。

(3):Android apk通过AndroidManifest.xml中声明权限。

这个途径,开发Android应用程序的同学应该比我更懂。

当安装应用程序时,Android就会分配一个UID,GID及添加组ID,所以在安装应用程序的时候,Android就会分析AndroidManifest.xml中的内容,同时屏幕上的窗口里就可以看到应用程序的权限等说明。

例如应用程序需要有WIFI权限,就需要在其AndroidManifest.xml中增加如下内容:

<uses-permisson android:name="android.permission.WIFI" />

在安装apk的时候,会解析这个AndroidManifest.xml,把相应的信息解析保存下来。最终调用dalvik/vm/native/dalvik_system_Zygote.c中的forkAndSpecializeCommon()函数中的setgid()设置gid,setuid()设置uid,以及setgroupsIntarray()函数设置添加组ID。而setgroupsIntarray()函数其他也是调用setgroups()系统调用来完成设置添加组id。

下面的一篇文章中说得更清楚些,更有代码级的分析:

http://www.2cto.com/kf/201204/127684.html

 

6)chown系统调用代码分析。

通过添加getgroups,增加打印查询了添加组id,system_server的进程其添加组ID确实有WIFI这个组,从理论上说chown应该成功,为什么还是失败了???

只有硬着头皮分析chown的代码了。

在内核中,fs/open.c SYSCALL_DEFINE3(chown, const char __user *, filename, uid_t, user, gid_t, group)就是chown的原码。

其中在Attr.c中notify_change()函数中调用

 if (inode->i_op->setattr)
  error = inode->i_op->setattr(dentry, attr);

每个文件系统都注册了其inode_operations。所以最终将调用fs/nfs/inode.c中的nfs_setattr(),其过程就是和nfs service主机的通信,所以chown的成功与否还是取决于nfs service主机。

而我的nfs service主机是vmware下的ubuntu12,尝试过通过/etc/exports修改nfs service的权限/opt/android-rootfs  *(rw,insecure,sync,no_wdelay,no_root_squash),但发现所开放的权限已经够了。

在同事的提醒下,突然想通了。其实nfs service端对文件的操作也必需按照POSIX的标准。

也就是说我的ubuntu下也应该有一个WIFI(1010)的用户组,并且SYSTEM(1000)这个用户必须属于WIFI这个用户组。

按照这个指导思想,我在ubuntu下创建了WIFI这个用户组,并把uid=1000的这个用户加入到WIFI的组。

groupadd -g 1010 wifi               创建一个gid=1010,名字叫wifi的一个用户组

gpasswd -a abc wifi                   把abc用户添加到wifi用户组,注意abc的uid=1000,这里是对应上android里的AID_SYSTEM用户。

 

3:结论

结论就是ubuntu12的nfs  service的问题造成了chown失败,也就是说明了为什么有外国人说换成了sdcard下跑这个wifi的代码就没有这个问题。

Ubuntu与Android对用户及用户组的管理不一致造成了这个问题。

另外通过个分析,更懂了:出现问题,第一时间应该自己查看原码。

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值