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.parsing.component.ParsedProcess;
22 import android.service.pm.PackageServiceDumpProto;
23 import android.util.ArrayMap;
24 import android.util.ArraySet;
25 import android.util.proto.ProtoOutputStream;
26 
27 import com.android.internal.util.ArrayUtils;
28 import com.android.server.pm.parsing.pkg.AndroidPackage;
29 import com.android.server.utils.SnapshotCache;
30 
31 import libcore.util.EmptyArray;
32 
33 import java.util.ArrayList;
34 import java.util.List;
35 import java.util.Map;
36 
37 /**
38  * Settings data for a particular shared user ID we know about.
39  */
40 public final class SharedUserSetting extends SettingBase {
41     final String name;
42 
43     int userId;
44 
45     // flags that are associated with this uid, regardless of any package flags
46     int uidFlags;
47     int uidPrivateFlags;
48 
49     // The lowest targetSdkVersion of all apps in the sharedUserSetting, used to assign seinfo so
50     // that all apps within the sharedUser run in the same selinux context.
51     int seInfoTargetSdkVersion;
52 
53     final ArraySet<PackageSetting> packages;
54 
55     final PackageSignatures signatures = new PackageSignatures();
56     Boolean signaturesChanged;
57 
58     final ArrayMap<String, ParsedProcess> processes;
59 
60     /**
61      * Snapshot support.
62      */
63     private final SnapshotCache<SharedUserSetting> mSnapshot;
64 
makeCache()65     private SnapshotCache<SharedUserSetting> makeCache() {
66         return new SnapshotCache<SharedUserSetting>(this, this) {
67             @Override
68             public SharedUserSetting createSnapshot() {
69                 return new SharedUserSetting(mSource);
70             }};
71     }
72 
73     SharedUserSetting(String _name, int _pkgFlags, int _pkgPrivateFlags) {
74         super(_pkgFlags, _pkgPrivateFlags);
75         uidFlags =  _pkgFlags;
76         uidPrivateFlags = _pkgPrivateFlags;
77         name = _name;
78         seInfoTargetSdkVersion = android.os.Build.VERSION_CODES.CUR_DEVELOPMENT;
79         packages = new ArraySet<>();
80         processes = new ArrayMap<>();
81         mSnapshot = makeCache();
82     }
83 
84     // The copy constructor is used to create a snapshot
85     private SharedUserSetting(SharedUserSetting orig) {
86         super(orig);
87         name = orig.name;
88         uidFlags = orig.uidFlags;
89         uidPrivateFlags = orig.uidPrivateFlags;
90         packages = new ArraySet(orig.packages);
91         // A PackageParser.SigningDetails seems to consist solely of final attributes, so
92         // it is safe to copy the reference.
93         signatures.mSigningDetails = orig.signatures.mSigningDetails;
94         signaturesChanged = orig.signaturesChanged;
95         processes = new ArrayMap(orig.processes);
96         mSnapshot = new SnapshotCache.Sealed();
97     }
98 
99     /**
100      * Return a read-only snapshot of this object.
101      */
102     public SharedUserSetting snapshot() {
103         return mSnapshot.snapshot();
104     }
105 
106     @Override
107     public String toString() {
108         return "SharedUserSetting{" + Integer.toHexString(System.identityHashCode(this)) + " "
109                 + name + "/" + userId + "}";
110     }
111 
112     public void dumpDebug(ProtoOutputStream proto, long fieldId) {
113         long token = proto.start(fieldId);
114         proto.write(PackageServiceDumpProto.SharedUserProto.UID, userId);
115         proto.write(PackageServiceDumpProto.SharedUserProto.NAME, name);
116         proto.end(token);
117     }
118 
119     void addProcesses(Map<String, ParsedProcess> newProcs) {
120         if (newProcs != null) {
121             final int numProcs = newProcs.size();
122             for (String key : newProcs.keySet()) {
123                 ParsedProcess newProc = newProcs.get(key);
124                 ParsedProcess proc = processes.get(newProc.getName());
125                 if (proc == null) {
126                     proc = new ParsedProcess(newProc);
127                     processes.put(newProc.getName(), proc);
128                 } else {
129                     proc.addStateFrom(newProc);
130                 }
131             }
132             onChanged();
133         }
134     }
135 
136     boolean removePackage(PackageSetting packageSetting) {
137         if (!packages.remove(packageSetting)) {
138             return false;
139         }
140         // recalculate the pkgFlags for this shared user if needed
141         if ((this.pkgFlags & packageSetting.pkgFlags) != 0) {
142             int aggregatedFlags = uidFlags;
143             for (PackageSetting ps : packages) {
144                 aggregatedFlags |= ps.pkgFlags;
145             }
146             setFlags(aggregatedFlags);
147         }
148         if ((this.pkgPrivateFlags & packageSetting.pkgPrivateFlags) != 0) {
149             int aggregatedPrivateFlags = uidPrivateFlags;
150             for (PackageSetting ps : packages) {
151                 aggregatedPrivateFlags |= ps.pkgPrivateFlags;
152             }
153             setPrivateFlags(aggregatedPrivateFlags);
154         }
155         // recalculate processes.
156         updateProcesses();
157         onChanged();
158         return true;
159     }
160 
161     void addPackage(PackageSetting packageSetting) {
162         // If this is the first package added to this shared user, temporarily (until next boot) use
163         // its targetSdkVersion when assigning seInfo for the shared user.
164         if ((packages.size() == 0) && (packageSetting.pkg != null)) {
165             seInfoTargetSdkVersion = packageSetting.pkg.getTargetSdkVersion();
166         }
167         if (packages.add(packageSetting)) {
168             setFlags(this.pkgFlags | packageSetting.pkgFlags);
169             setPrivateFlags(this.pkgPrivateFlags | packageSetting.pkgPrivateFlags);
170             onChanged();
171         }
172         if (packageSetting.pkg != null) {
173             addProcesses(packageSetting.pkg.getProcesses());
174         }
175     }
176 
177     public @Nullable List<AndroidPackage> getPackages() {
178         if (packages == null || packages.size() == 0) {
179             return null;
180         }
181         final ArrayList<AndroidPackage> pkgList = new ArrayList<>(packages.size());
182         for (PackageSetting ps : packages) {
183             if ((ps == null) || (ps.pkg == null)) {
184                 continue;
185             }
186             pkgList.add(ps.pkg);
187         }
188         return pkgList;
189     }
190 
191     public boolean isPrivileged() {
192         return (this.pkgPrivateFlags & ApplicationInfo.PRIVATE_FLAG_PRIVILEGED) != 0;
193     }
194 
195     /**
196      * Determine the targetSdkVersion for a sharedUser and update pkg.applicationInfo.seInfo
197      * to ensure that all apps within the sharedUser share an SELinux domain. Use the lowest
198      * targetSdkVersion of all apps within the shared user, which corresponds to the least
199      * restrictive selinux domain.
200      */
201     public void fixSeInfoLocked() {
202         if (packages == null || packages.size() == 0) {
203             return;
204         }
205         for (PackageSetting ps : packages) {
206             if ((ps == null) || (ps.pkg == null)) {
207                 continue;
208             }
209             if (ps.pkg.getTargetSdkVersion() < seInfoTargetSdkVersion) {
210                 seInfoTargetSdkVersion = ps.pkg.getTargetSdkVersion();
211                 onChanged();
212             }
213         }
214 
215         for (PackageSetting ps : packages) {
216             if ((ps == null) || (ps.pkg == null)) {
217                 continue;
218             }
219             final boolean isPrivileged = isPrivileged() | ps.pkg.isPrivileged();
220             ps.getPkgState().setOverrideSeInfo(SELinuxMMAC.getSeInfo(ps.pkg, isPrivileged,
221                     seInfoTargetSdkVersion));
222             onChanged();
223         }
224     }
225 
226     /**
227      * Update tracked data about processes based on all known packages in the shared user ID.
228      */
229     public void updateProcesses() {
230         processes.clear();
231         for (int i = packages.size() - 1; i >= 0; i--) {
232             final AndroidPackage pkg = packages.valueAt(i).pkg;
233             if (pkg != null) {
234                 addProcesses(pkg.getProcesses());
235             }
236         }
237     }
238 
239     /** Returns userIds which doesn't have any packages with this sharedUserId */
240     public int[] getNotInstalledUserIds() {
241         int[] excludedUserIds = null;
242         for (PackageSetting ps : packages) {
243             final int[] userIds = ps.getNotInstalledUserIds();
244             if (excludedUserIds == null) {
245                 excludedUserIds = userIds;
246             } else {
247                 for (int userId : excludedUserIds) {
248                     if (!ArrayUtils.contains(userIds, userId)) {
249                         excludedUserIds = ArrayUtils.removeInt(excludedUserIds, userId);
250                     }
251                 }
252             }
253         }
254         return excludedUserIds == null ? EmptyArray.INT : excludedUserIds;
255     }
256 
257     /** Updates all fields in this shared user setting from another. */
258     public SharedUserSetting updateFrom(SharedUserSetting sharedUser) {
259         copyFrom(sharedUser);
260         this.userId = sharedUser.userId;
261         this.uidFlags = sharedUser.uidFlags;
262         this.uidPrivateFlags = sharedUser.uidPrivateFlags;
263         this.seInfoTargetSdkVersion = sharedUser.seInfoTargetSdkVersion;
264         this.packages.clear();
265         this.packages.addAll(sharedUser.packages);
266         this.signaturesChanged = sharedUser.signaturesChanged;
267         if (sharedUser.processes != null) {
268             final int numProcs = sharedUser.processes.size();
269             this.processes.clear();
270             this.processes.ensureCapacity(numProcs);
271             for (int i = 0; i < numProcs; i++) {
272                 ParsedProcess proc =
273                         new ParsedProcess(sharedUser.processes.valueAt(i));
274                 this.processes.put(proc.getName(), proc);
275             }
276         } else {
277             this.processes.clear();
278         }
279         onChanged();
280         return this;
281     }
282 }
283