1 /*
2  * Copyright (C) 2011 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.android.server.pm;
18 
19 import android.annotation.Nullable;
20 import android.content.pm.ApplicationInfo;
21 import android.content.pm.PackageParser;
22 import android.service.pm.PackageServiceDumpProto;
23 import android.util.ArraySet;
24 import android.util.proto.ProtoOutputStream;
25 
26 import com.android.internal.util.ArrayUtils;
27 
28 import libcore.util.EmptyArray;
29 
30 import java.util.ArrayList;
31 import java.util.List;
32 
33 /**
34  * Settings data for a particular shared user ID we know about.
35  */
36 public final class SharedUserSetting extends SettingBase {
37     final String name;
38 
39     int userId;
40 
41     // flags that are associated with this uid, regardless of any package flags
42     int uidFlags;
43     int uidPrivateFlags;
44 
45     // The lowest targetSdkVersion of all apps in the sharedUserSetting, used to assign seinfo so
46     // that all apps within the sharedUser run in the same selinux context.
47     int seInfoTargetSdkVersion;
48 
49     final ArraySet<PackageSetting> packages = new ArraySet<PackageSetting>();
50 
51     final PackageSignatures signatures = new PackageSignatures();
52     Boolean signaturesChanged;
53 
SharedUserSetting(String _name, int _pkgFlags, int _pkgPrivateFlags)54     SharedUserSetting(String _name, int _pkgFlags, int _pkgPrivateFlags) {
55         super(_pkgFlags, _pkgPrivateFlags);
56         uidFlags =  _pkgFlags;
57         uidPrivateFlags = _pkgPrivateFlags;
58         name = _name;
59         seInfoTargetSdkVersion = android.os.Build.VERSION_CODES.CUR_DEVELOPMENT;
60     }
61 
62     @Override
toString()63     public String toString() {
64         return "SharedUserSetting{" + Integer.toHexString(System.identityHashCode(this)) + " "
65                 + name + "/" + userId + "}";
66     }
67 
writeToProto(ProtoOutputStream proto, long fieldId)68     public void writeToProto(ProtoOutputStream proto, long fieldId) {
69         long token = proto.start(fieldId);
70         proto.write(PackageServiceDumpProto.SharedUserProto.UID, userId);
71         proto.write(PackageServiceDumpProto.SharedUserProto.NAME, name);
72         proto.end(token);
73     }
74 
removePackage(PackageSetting packageSetting)75     boolean removePackage(PackageSetting packageSetting) {
76         if (!packages.remove(packageSetting)) {
77             return false;
78         }
79         // recalculate the pkgFlags for this shared user if needed
80         if ((this.pkgFlags & packageSetting.pkgFlags) != 0) {
81             int aggregatedFlags = uidFlags;
82             for (PackageSetting ps : packages) {
83                 aggregatedFlags |= ps.pkgFlags;
84             }
85             setFlags(aggregatedFlags);
86         }
87         if ((this.pkgPrivateFlags & packageSetting.pkgPrivateFlags) != 0) {
88             int aggregatedPrivateFlags = uidPrivateFlags;
89             for (PackageSetting ps : packages) {
90                 aggregatedPrivateFlags |= ps.pkgPrivateFlags;
91             }
92             setPrivateFlags(aggregatedPrivateFlags);
93         }
94         return true;
95     }
96 
addPackage(PackageSetting packageSetting)97     void addPackage(PackageSetting packageSetting) {
98         // If this is the first package added to this shared user, temporarily (until next boot) use
99         // its targetSdkVersion when assigning seInfo for the shared user.
100         if ((packages.size() == 0) && (packageSetting.pkg != null)) {
101             seInfoTargetSdkVersion = packageSetting.pkg.applicationInfo.targetSdkVersion;
102         }
103         if (packages.add(packageSetting)) {
104             setFlags(this.pkgFlags | packageSetting.pkgFlags);
105             setPrivateFlags(this.pkgPrivateFlags | packageSetting.pkgPrivateFlags);
106         }
107     }
108 
getPackages()109     public @Nullable List<PackageParser.Package> getPackages() {
110         if (packages == null || packages.size() == 0) {
111             return null;
112         }
113         final ArrayList<PackageParser.Package> pkgList = new ArrayList<>(packages.size());
114         for (PackageSetting ps : packages) {
115             if ((ps == null) || (ps.pkg == null)) {
116                 continue;
117             }
118             pkgList.add(ps.pkg);
119         }
120         return pkgList;
121     }
122 
isPrivileged()123     public boolean isPrivileged() {
124         return (this.pkgPrivateFlags & ApplicationInfo.PRIVATE_FLAG_PRIVILEGED) != 0;
125     }
126 
127     /**
128      * Determine the targetSdkVersion for a sharedUser and update pkg.applicationInfo.seInfo
129      * to ensure that all apps within the sharedUser share an SELinux domain. Use the lowest
130      * targetSdkVersion of all apps within the shared user, which corresponds to the least
131      * restrictive selinux domain.
132      */
fixSeInfoLocked()133     public void fixSeInfoLocked() {
134         final List<PackageParser.Package> pkgList = getPackages();
135         if (pkgList == null || pkgList.size() == 0) {
136             return;
137         }
138 
139         for (PackageParser.Package pkg : pkgList) {
140             if (pkg.applicationInfo.targetSdkVersion < seInfoTargetSdkVersion) {
141                 seInfoTargetSdkVersion = pkg.applicationInfo.targetSdkVersion;
142             }
143         }
144         for (PackageParser.Package pkg : pkgList) {
145             final boolean isPrivileged = isPrivileged() | pkg.isPrivileged();
146             pkg.applicationInfo.seInfo = SELinuxMMAC.getSeInfo(pkg, isPrivileged,
147                 seInfoTargetSdkVersion);
148         }
149     }
150 
151     /** Returns userIds which doesn't have any packages with this sharedUserId */
getNotInstalledUserIds()152     public int[] getNotInstalledUserIds() {
153         int[] excludedUserIds = null;
154         for (PackageSetting ps : packages) {
155             final int[] userIds = ps.getNotInstalledUserIds();
156             if (excludedUserIds == null) {
157                 excludedUserIds = userIds;
158             } else {
159                 for (int userId : excludedUserIds) {
160                     if (!ArrayUtils.contains(userIds, userId)) {
161                         excludedUserIds = ArrayUtils.removeInt(excludedUserIds, userId);
162                     }
163                 }
164             }
165         }
166         return excludedUserIds == null ? EmptyArray.INT : excludedUserIds;
167     }
168 
169     /** Updates all fields in this shared user setting from another. */
updateFrom(SharedUserSetting sharedUser)170     public SharedUserSetting updateFrom(SharedUserSetting sharedUser) {
171         copyFrom(sharedUser);
172         this.userId = sharedUser.userId;
173         this.uidFlags = sharedUser.uidFlags;
174         this.uidPrivateFlags = sharedUser.uidPrivateFlags;
175         this.seInfoTargetSdkVersion = sharedUser.seInfoTargetSdkVersion;
176         this.packages.clear();
177         this.packages.addAll(sharedUser.packages);
178         this.signaturesChanged = sharedUser.signaturesChanged;
179         return this;
180     }
181 }
182