searchusermenu
  • 发布文章
  • 消息中心
点赞
收藏
评论
分享
原创

Android权限管理和鉴权过程

2023-08-21 03:05:21
30
0

Android系统权限是建立在框架层上的一套权限解析分配和鉴权流程,其主要数据结构和校验流程主要在pms(包管理服务)中实现。

简单理解系统权限机制主要分为权限解析、权限分配、鉴权这三个主要内容。

Sdk版本大于等于23后,新增了动态权限管理,让Android系统权限管理更加灵活和自主。

下面来分析下Android 5.1上面的权限管理和鉴权过程。

权限的数据结构

涉及到系统权限的数据结构如上图所示,分别来分析每个数据结构的内容和作用。

Ø BasePermission

系统权限的基本表示单元是BasePermission,Settings中维护了一个总的权限映射表mPermissions,所有的权限都会添加到mPermissions列表中,其中key是权限的名字,value是具体的BasePermission实例。

[/frameworks/base/services/core/java/com/android/server/pm/Settings.java]

// Mapping from permission names to info about them.
final ArrayMap<String, BasePermission> mPermissions =  new ArrayMap<String, BasePermission>();

  我们都知道绝大部分权限已经在系统中定义好,如READ_EXTERNAL_STORAGE读写外部存储权限、READ_PHONE_STATE读取手机状态信息权限等,这些权限的定义都在framework-res.apk中,在扫描framework-res.apk过程中解析并添加到mPermissions映射表中。系统权限的定义:

[/frameworks/base/core/res/AndroidManifest.xml]

系统中绝大部分的权限定义都在frameworks/base/services/core/res/AndroidManifest.xml中,最终会打包进framework-res.apk中并经过解析保存到Settings的mPermissions映射表中。

Ø PackageParser.Permission

PackageParser.Permission在上面分析PackageParser解析apk过程中有提及过,解析apk的AndroidManifest.xml文件中的标签后得到的权限表示。

PackageParser.Permission中包含一个对应的PermissionInfo。

Ø PermissionInfo

权限信息的表示,其中包含权限等级的定义(NORMAL, DANGER, SIGNERATURE),另外实现了序列化,用户于进程间通信。每个BasePermission实例中包含一个PermissionInfo的实例。

以上三个类结构是系统权限部分的定义,下面四个类结构和pkg强相关,表示pkg的权限状态。

Ø GrantedPermissions

表示一个pkg已经赋予的权限,类里面定义了一个字符串列表grantedPermissions保存pkg已经被赋予的所有权限。

[/frameworks/base/services/core/java/com/android/server/pm/GrantedPermissions.java]

class GrantedPermissions {
    int pkgFlags; 
    ArraySet<String> grantedPermissions = new ArraySet<String>();

Ø PackageSettingBase

PackageSettingBase继承了GrantedPermissions类并添加更多和 pkg相关的信息,是一个pkg信息的基本表示类。PackageSettingBase保存为了如pkg的codePath, resourcePath, signature等信息,同时PackageSettingBase是GrantedPermissions的子类,因为也包含了pkg被赋予的权限列表。

Ø PackageSetting

PackageSetting继承了PackageSettingBase类,并新增如PackageParser.Package和SharedUserSetting。那么以后只要获得一个pkg的PackageSetting实例,就可以获得对应的SharedUserSetting实例,通过这个SharedUserSetting实例来获得与其sharedUser的其他pkg信息。同时PackageSetting是GrantedPermissions的子类,那么也就意味着只要拿到pkg的PackageSetting实例就可以知道pkg已经被赋予了哪些权限。

Ø SharedUserSetting

SharedUserSetting继承了GrantedPermissions,类里面保存了一个packages的列表,表示这个列表中的所有应用共享该uid。同时共享这个uid所赋予的所有权限。

权限管理
我们知道在pms构造方法中扫描apk后,创建对应的PackageSetting实例并赋予给pkg的mExtras,以后我们就可以通过pkg.mExtras获得相应的PackageSetting实例。一开始扫描过程中,PackageSetting实例中保存的grantedPermissions列表为空,权限的赋予会在pms构造方法的结束部分,通过调用updatePermissionsLPw方法遍历所有已经安装的pkg,一个一个的根据其请求的权限来确定是否赋予。

首先来看看updatePermissionsLPw方法:

[/frameworks/base/services/core/java/com/android/server/pm/PackageManagerService.java]

    private void updatePermissionsLPw(String changingPkg,PackageParser.Package pkgInfo, int flags) {
        // Make sure there are no dangling permission trees.
        // 更新permission trees列表,去除无效的permission tree
        // Make sure all dynamic permissions have been assigned to a package,
        // and make sure there are no dangling permissions.
        // 更新permission列表,去除无效无主的permission定义
        if ((flags&UPDATE_PERMISSIONS_ALL) != 0) {
            for (PackageParser.Package pkg : mPackages.values()) {
                if (pkg != pkgInfo) {
                    grantPermissionsLPw(pkg, (flags&UPDATE_PERMISSIONS_REPLACE_ALL) != 0,
                            changingPkg);
                }
            }
        }
        if (pkgInfo != null) {
            grantPermissionsLPw(pkgInfo, (flags&UPDATE_PERMISSIONS_REPLACE_PKG) != 0, changingPkg);
        }
    }

updatePermissionsLPw方法中主要做两个事情,检查无效的permissiontree和permission定义,另外一个就是根据传入的flags是否带有UPDATE_PERMISSIONS_ALL来遍历mPackages包列表来更新所有安装包的权限信息。重点分析grantPermissionsLPw方法。

[/frameworks/base/services/core/java/com/android/server/pm/PackageManagerService.java]

 
     private void grantPermissionsLPw(PackageParser.Package pkg, boolean replace, String packageOfInterest) {        
final PackageSetting ps = (PackageSetting) pkg.mExtras; 
        final GrantedPermissions gp = ps.sharedUser != null ? ps.sharedUser : ps;
              ArraySet<String> origPermissions = gp.grantedPermissions;
        boolean changedPermission = false;  
        /* 获得解析pkg中请求的权限数量,循环一个一个去决定是否分配给该应用或者sharedUserId */     
        final int N = pkg.requestedPermissions.size();
        for (int i=0; i<N; i++) {
            final String name = pkg.requestedPermissions.get(i);
            final boolean required = pkg.requestedPermissionsRequired.get(i);
            final BasePermission bp = mSettings.mPermissions.get(name);
            /* 检查应用申请的这个权限是否存在有效,如果无效,忽略 */
            if (bp == null || bp.packageSetting == null) {
                if (packageOfInterest == null || packageOfInterest.equals(pkg.packageName)) {
                    Slog.w(TAG, "Unknown permission " + name  + " in package " + pkg.packageName);
                }
                continue;
            }
            final String perm = bp.name;
            boolean allowed;
            boolean allowedSig = false;
            final int level = bp.protectionLevel & PermissionInfo.PROTECTION_MASK_BASE;
            if (level == PermissionInfo.PROTECTION_NORMAL || level == PermissionInfo.PROTECTION_DANGEROUS) {
                /* 权限等级为NORMAL和DANGER的权限,只要申请就分配 */
                allowed = (required || origPermissions.contains(perm)
                        || (isSystemApp(ps) && !isUpdatedSystemApp(ps)));
            } else if (bp.packageSetting == null) {
                /* 无头的权限,直接拒绝 */
                allowed = false;
            } else if (level == PermissionInfo.PROTECTION_SIGNATURE) {
/* 权限等级要求签名的,检查该pkg签名是否和platform apk签名一致,一致则返回 */
                allowed = grantSignaturePermission(perm, pkg, bp, origPermissions);
                if (allowed) { allowedSig = true; }
            } else {
                allowed = false;
            }
            if (allowed) {                
                if (allowed) {
                    if (!gp.grantedPermissions.contains(perm)) {
                        /* 权限允许赋予后,如果pkg已获得权限列表中没有,则添加 */
                        changedPermission = true;
                        gp.grantedPermissions.add(perm);
                        gp.gids = appendInts(gp.gids, bp.gids);
                    } 
                } 
            } else {
            /* 权限不允许 */
                if (gp.grantedPermissions.remove(perm)) {
/* 如果已获取权限列表中存在去除的该权限,说明apk版本有变化 */
                    changedPermission = true;
                    gp.gids = removeInts(gp.gids, bp.gids);
                }
            }
        }
   } 
 

grantPermissionsLPw方法中实现了5.1权限赋予的流程。主要流程首先获得pkg的PackageSettings实例,上面我们知道pkg的PackageSettings实例中包含了应用已经获得的权限列表grantedPermissions。第一次开机扫描应用的过程中,这个列表为空。通过遍历包解析后得到的requestPermissions列表的权限的等级来判断是否授权,如果请求的权限授权成功后,就会保存进入grantedPermissions列表中。

grantedPermissions列表会根据应用的更新而更新。

权限管理的基本流程如上。

鉴权过程
一般客户端调用checkPermission方法鉴权的流程如下:

①Context.java--->public int checkPermission(String permission, int pid, int uid)

②ActivityManagerService.java--->public int checkPermission(String permission, int pid, int uid)

③ActivityManagerService.java---> checkComponentPermission

④ActivityManager.java--->checkComponentPermission

Root用户(uid=0)和System用户(uid=1000)直接鉴权通过。

⑤PackageManagerService.java--->checkUidPermission

查看pms的checkUidPermission方法:

[/frameworks/base/services/core/java/com/android/server/pm/PackageManagerService.java]

    @Override
    public int checkUidPermission(String permName, int uid) {
        synchronized (mPackages) {
            Object obj = mSettings.getUserIdLPr(UserHandle.getAppId(uid));
            if (obj != null) {
                GrantedPermissions gp = (GrantedPermissions)obj;
                if (gp.grantedPermissions.contains(permName)) {
                    return PackageManager.PERMISSION_GRANTED;
                }
            } else {
                ArraySet<String> perms = mSystemPermissions.get(uid);
                if (perms != null && perms.contains(permName)) {
                    return PackageManager.PERMISSION_GRANTED;
                }
            }
        }
        return PackageManager.PERMISSION_DENIED;
    }

发现checkUidPermission方法中调用Settings的getUserIdLPr获得一个Object实例,这个Object实例就是对应uid的pkgSettings实例,里面包含grantedPermissions信息。最后查询grantedPermissions列表中是否包含所要检查的权限即可判断该uid是否具有该权限。

继续分析Settings的getUserIdLPr方法:

[/frameworks/base/services/core/java/com/android/server/pm/Settings.java]

  public Object getUserIdLPr(int uid) {
        if (uid >= Process.FIRST_APPLICATION_UID) {
            final int N = mUserIds.size();
            final int index = uid - Process.FIRST_APPLICATION_UID;
            return index < N ? mUserIds.get(index) : null;
        } else {
            return mOtherUserIds.get(uid);
        }
  }

这个方法首先判断检查的uid是否是应用的uid(10000以上),如果是,通过uid来查询mUserIds表来获得对应uid的pkgSettings实例(mUserIds就是uid和pkgSettings的映射表)。通过进一步分析,mUserIds列表是在手机开机后读取packages.xml缓存生成的。

鉴权流程2:

① 根据PMS解析的申请权限数据,对应用添加gid

② 启动应用进程时,指定所有对应的gid

举例:android.permission.WRITE_EXTERNAL_STORAGE

① 应用在AndroidManifest.xml中申请android.permission.WRITE_EXTERNAL_STORAGE

② 安装后,PMS解析该应用,并为该应用添加sdcard_rw的组ID。

③ 启动该应用进程时,使用上面解析的gid。

0条评论
0 / 1000
hi_long
15文章数
0粉丝数
hi_long
15 文章 | 0 粉丝
hi_long
15文章数
0粉丝数
hi_long
15 文章 | 0 粉丝
原创

Android权限管理和鉴权过程

2023-08-21 03:05:21
30
0

Android系统权限是建立在框架层上的一套权限解析分配和鉴权流程,其主要数据结构和校验流程主要在pms(包管理服务)中实现。

简单理解系统权限机制主要分为权限解析、权限分配、鉴权这三个主要内容。

Sdk版本大于等于23后,新增了动态权限管理,让Android系统权限管理更加灵活和自主。

下面来分析下Android 5.1上面的权限管理和鉴权过程。

权限的数据结构

涉及到系统权限的数据结构如上图所示,分别来分析每个数据结构的内容和作用。

Ø BasePermission

系统权限的基本表示单元是BasePermission,Settings中维护了一个总的权限映射表mPermissions,所有的权限都会添加到mPermissions列表中,其中key是权限的名字,value是具体的BasePermission实例。

[/frameworks/base/services/core/java/com/android/server/pm/Settings.java]

// Mapping from permission names to info about them.
final ArrayMap<String, BasePermission> mPermissions =  new ArrayMap<String, BasePermission>();

  我们都知道绝大部分权限已经在系统中定义好,如READ_EXTERNAL_STORAGE读写外部存储权限、READ_PHONE_STATE读取手机状态信息权限等,这些权限的定义都在framework-res.apk中,在扫描framework-res.apk过程中解析并添加到mPermissions映射表中。系统权限的定义:

[/frameworks/base/core/res/AndroidManifest.xml]

系统中绝大部分的权限定义都在frameworks/base/services/core/res/AndroidManifest.xml中,最终会打包进framework-res.apk中并经过解析保存到Settings的mPermissions映射表中。

Ø PackageParser.Permission

PackageParser.Permission在上面分析PackageParser解析apk过程中有提及过,解析apk的AndroidManifest.xml文件中的标签后得到的权限表示。

PackageParser.Permission中包含一个对应的PermissionInfo。

Ø PermissionInfo

权限信息的表示,其中包含权限等级的定义(NORMAL, DANGER, SIGNERATURE),另外实现了序列化,用户于进程间通信。每个BasePermission实例中包含一个PermissionInfo的实例。

以上三个类结构是系统权限部分的定义,下面四个类结构和pkg强相关,表示pkg的权限状态。

Ø GrantedPermissions

表示一个pkg已经赋予的权限,类里面定义了一个字符串列表grantedPermissions保存pkg已经被赋予的所有权限。

[/frameworks/base/services/core/java/com/android/server/pm/GrantedPermissions.java]

class GrantedPermissions {
    int pkgFlags; 
    ArraySet<String> grantedPermissions = new ArraySet<String>();

Ø PackageSettingBase

PackageSettingBase继承了GrantedPermissions类并添加更多和 pkg相关的信息,是一个pkg信息的基本表示类。PackageSettingBase保存为了如pkg的codePath, resourcePath, signature等信息,同时PackageSettingBase是GrantedPermissions的子类,因为也包含了pkg被赋予的权限列表。

Ø PackageSetting

PackageSetting继承了PackageSettingBase类,并新增如PackageParser.Package和SharedUserSetting。那么以后只要获得一个pkg的PackageSetting实例,就可以获得对应的SharedUserSetting实例,通过这个SharedUserSetting实例来获得与其sharedUser的其他pkg信息。同时PackageSetting是GrantedPermissions的子类,那么也就意味着只要拿到pkg的PackageSetting实例就可以知道pkg已经被赋予了哪些权限。

Ø SharedUserSetting

SharedUserSetting继承了GrantedPermissions,类里面保存了一个packages的列表,表示这个列表中的所有应用共享该uid。同时共享这个uid所赋予的所有权限。

权限管理
我们知道在pms构造方法中扫描apk后,创建对应的PackageSetting实例并赋予给pkg的mExtras,以后我们就可以通过pkg.mExtras获得相应的PackageSetting实例。一开始扫描过程中,PackageSetting实例中保存的grantedPermissions列表为空,权限的赋予会在pms构造方法的结束部分,通过调用updatePermissionsLPw方法遍历所有已经安装的pkg,一个一个的根据其请求的权限来确定是否赋予。

首先来看看updatePermissionsLPw方法:

[/frameworks/base/services/core/java/com/android/server/pm/PackageManagerService.java]

    private void updatePermissionsLPw(String changingPkg,PackageParser.Package pkgInfo, int flags) {
        // Make sure there are no dangling permission trees.
        // 更新permission trees列表,去除无效的permission tree
        // Make sure all dynamic permissions have been assigned to a package,
        // and make sure there are no dangling permissions.
        // 更新permission列表,去除无效无主的permission定义
        if ((flags&UPDATE_PERMISSIONS_ALL) != 0) {
            for (PackageParser.Package pkg : mPackages.values()) {
                if (pkg != pkgInfo) {
                    grantPermissionsLPw(pkg, (flags&UPDATE_PERMISSIONS_REPLACE_ALL) != 0,
                            changingPkg);
                }
            }
        }
        if (pkgInfo != null) {
            grantPermissionsLPw(pkgInfo, (flags&UPDATE_PERMISSIONS_REPLACE_PKG) != 0, changingPkg);
        }
    }

updatePermissionsLPw方法中主要做两个事情,检查无效的permissiontree和permission定义,另外一个就是根据传入的flags是否带有UPDATE_PERMISSIONS_ALL来遍历mPackages包列表来更新所有安装包的权限信息。重点分析grantPermissionsLPw方法。

[/frameworks/base/services/core/java/com/android/server/pm/PackageManagerService.java]

 
     private void grantPermissionsLPw(PackageParser.Package pkg, boolean replace, String packageOfInterest) {        
final PackageSetting ps = (PackageSetting) pkg.mExtras; 
        final GrantedPermissions gp = ps.sharedUser != null ? ps.sharedUser : ps;
              ArraySet<String> origPermissions = gp.grantedPermissions;
        boolean changedPermission = false;  
        /* 获得解析pkg中请求的权限数量,循环一个一个去决定是否分配给该应用或者sharedUserId */     
        final int N = pkg.requestedPermissions.size();
        for (int i=0; i<N; i++) {
            final String name = pkg.requestedPermissions.get(i);
            final boolean required = pkg.requestedPermissionsRequired.get(i);
            final BasePermission bp = mSettings.mPermissions.get(name);
            /* 检查应用申请的这个权限是否存在有效,如果无效,忽略 */
            if (bp == null || bp.packageSetting == null) {
                if (packageOfInterest == null || packageOfInterest.equals(pkg.packageName)) {
                    Slog.w(TAG, "Unknown permission " + name  + " in package " + pkg.packageName);
                }
                continue;
            }
            final String perm = bp.name;
            boolean allowed;
            boolean allowedSig = false;
            final int level = bp.protectionLevel & PermissionInfo.PROTECTION_MASK_BASE;
            if (level == PermissionInfo.PROTECTION_NORMAL || level == PermissionInfo.PROTECTION_DANGEROUS) {
                /* 权限等级为NORMAL和DANGER的权限,只要申请就分配 */
                allowed = (required || origPermissions.contains(perm)
                        || (isSystemApp(ps) && !isUpdatedSystemApp(ps)));
            } else if (bp.packageSetting == null) {
                /* 无头的权限,直接拒绝 */
                allowed = false;
            } else if (level == PermissionInfo.PROTECTION_SIGNATURE) {
/* 权限等级要求签名的,检查该pkg签名是否和platform apk签名一致,一致则返回 */
                allowed = grantSignaturePermission(perm, pkg, bp, origPermissions);
                if (allowed) { allowedSig = true; }
            } else {
                allowed = false;
            }
            if (allowed) {                
                if (allowed) {
                    if (!gp.grantedPermissions.contains(perm)) {
                        /* 权限允许赋予后,如果pkg已获得权限列表中没有,则添加 */
                        changedPermission = true;
                        gp.grantedPermissions.add(perm);
                        gp.gids = appendInts(gp.gids, bp.gids);
                    } 
                } 
            } else {
            /* 权限不允许 */
                if (gp.grantedPermissions.remove(perm)) {
/* 如果已获取权限列表中存在去除的该权限,说明apk版本有变化 */
                    changedPermission = true;
                    gp.gids = removeInts(gp.gids, bp.gids);
                }
            }
        }
   } 
 

grantPermissionsLPw方法中实现了5.1权限赋予的流程。主要流程首先获得pkg的PackageSettings实例,上面我们知道pkg的PackageSettings实例中包含了应用已经获得的权限列表grantedPermissions。第一次开机扫描应用的过程中,这个列表为空。通过遍历包解析后得到的requestPermissions列表的权限的等级来判断是否授权,如果请求的权限授权成功后,就会保存进入grantedPermissions列表中。

grantedPermissions列表会根据应用的更新而更新。

权限管理的基本流程如上。

鉴权过程
一般客户端调用checkPermission方法鉴权的流程如下:

①Context.java--->public int checkPermission(String permission, int pid, int uid)

②ActivityManagerService.java--->public int checkPermission(String permission, int pid, int uid)

③ActivityManagerService.java---> checkComponentPermission

④ActivityManager.java--->checkComponentPermission

Root用户(uid=0)和System用户(uid=1000)直接鉴权通过。

⑤PackageManagerService.java--->checkUidPermission

查看pms的checkUidPermission方法:

[/frameworks/base/services/core/java/com/android/server/pm/PackageManagerService.java]

    @Override
    public int checkUidPermission(String permName, int uid) {
        synchronized (mPackages) {
            Object obj = mSettings.getUserIdLPr(UserHandle.getAppId(uid));
            if (obj != null) {
                GrantedPermissions gp = (GrantedPermissions)obj;
                if (gp.grantedPermissions.contains(permName)) {
                    return PackageManager.PERMISSION_GRANTED;
                }
            } else {
                ArraySet<String> perms = mSystemPermissions.get(uid);
                if (perms != null && perms.contains(permName)) {
                    return PackageManager.PERMISSION_GRANTED;
                }
            }
        }
        return PackageManager.PERMISSION_DENIED;
    }

发现checkUidPermission方法中调用Settings的getUserIdLPr获得一个Object实例,这个Object实例就是对应uid的pkgSettings实例,里面包含grantedPermissions信息。最后查询grantedPermissions列表中是否包含所要检查的权限即可判断该uid是否具有该权限。

继续分析Settings的getUserIdLPr方法:

[/frameworks/base/services/core/java/com/android/server/pm/Settings.java]

  public Object getUserIdLPr(int uid) {
        if (uid >= Process.FIRST_APPLICATION_UID) {
            final int N = mUserIds.size();
            final int index = uid - Process.FIRST_APPLICATION_UID;
            return index < N ? mUserIds.get(index) : null;
        } else {
            return mOtherUserIds.get(uid);
        }
  }

这个方法首先判断检查的uid是否是应用的uid(10000以上),如果是,通过uid来查询mUserIds表来获得对应uid的pkgSettings实例(mUserIds就是uid和pkgSettings的映射表)。通过进一步分析,mUserIds列表是在手机开机后读取packages.xml缓存生成的。

鉴权流程2:

① 根据PMS解析的申请权限数据,对应用添加gid

② 启动应用进程时,指定所有对应的gid

举例:android.permission.WRITE_EXTERNAL_STORAGE

① 应用在AndroidManifest.xml中申请android.permission.WRITE_EXTERNAL_STORAGE

② 安装后,PMS解析该应用,并为该应用添加sdcard_rw的组ID。

③ 启动该应用进程时,使用上面解析的gid。

文章来自个人专栏
Android
15 文章 | 1 订阅
0条评论
0 / 1000
请输入你的评论
0
0