1 /*
2  * Copyright 2020 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 package com.android.server.blob;
17 
18 import static android.app.blob.XmlTags.ATTR_CERTIFICATE;
19 import static android.app.blob.XmlTags.ATTR_PACKAGE;
20 import static android.app.blob.XmlTags.ATTR_TYPE;
21 import static android.app.blob.XmlTags.TAG_ALLOWED_PACKAGE;
22 
23 import android.annotation.IntDef;
24 import android.annotation.NonNull;
25 import android.content.Context;
26 import android.content.pm.PackageManager;
27 import android.content.pm.PackageManagerInternal;
28 import android.os.Binder;
29 import android.os.UserHandle;
30 import android.util.ArraySet;
31 import android.util.Base64;
32 import android.util.DebugUtils;
33 import android.util.IndentingPrintWriter;
34 
35 import com.android.internal.util.XmlUtils;
36 import com.android.server.LocalServices;
37 
38 import org.xmlpull.v1.XmlPullParser;
39 import org.xmlpull.v1.XmlPullParserException;
40 import org.xmlpull.v1.XmlSerializer;
41 
42 import java.io.IOException;
43 import java.lang.annotation.Retention;
44 import java.lang.annotation.RetentionPolicy;
45 import java.util.Arrays;
46 import java.util.Objects;
47 
48 /**
49  * Class for representing how a blob can be shared.
50  *
51  * Note that this class is not thread-safe, callers need to take care of synchronizing access.
52  */
53 class BlobAccessMode {
54     @Retention(RetentionPolicy.SOURCE)
55     @IntDef(flag = true, value = {
56             ACCESS_TYPE_PRIVATE,
57             ACCESS_TYPE_PUBLIC,
58             ACCESS_TYPE_SAME_SIGNATURE,
59             ACCESS_TYPE_ALLOWLIST,
60     })
61     @interface AccessType {}
62     public static final int ACCESS_TYPE_PRIVATE = 1 << 0;
63     public static final int ACCESS_TYPE_PUBLIC = 1 << 1;
64     public static final int ACCESS_TYPE_SAME_SIGNATURE = 1 << 2;
65     public static final int ACCESS_TYPE_ALLOWLIST = 1 << 3;
66 
67     private int mAccessType = ACCESS_TYPE_PRIVATE;
68 
69     private final ArraySet<PackageIdentifier> mAllowedPackages = new ArraySet<>();
70 
allow(BlobAccessMode other)71     void allow(BlobAccessMode other) {
72         if ((other.mAccessType & ACCESS_TYPE_ALLOWLIST) != 0) {
73             mAllowedPackages.addAll(other.mAllowedPackages);
74         }
75         mAccessType |= other.mAccessType;
76     }
77 
allowPublicAccess()78     void allowPublicAccess() {
79         mAccessType |= ACCESS_TYPE_PUBLIC;
80     }
81 
allowSameSignatureAccess()82     void allowSameSignatureAccess() {
83         mAccessType |= ACCESS_TYPE_SAME_SIGNATURE;
84     }
85 
allowPackageAccess(@onNull String packageName, @NonNull byte[] certificate)86     void allowPackageAccess(@NonNull String packageName, @NonNull byte[] certificate) {
87         mAccessType |= ACCESS_TYPE_ALLOWLIST;
88         mAllowedPackages.add(PackageIdentifier.create(packageName, certificate));
89     }
90 
isPublicAccessAllowed()91     boolean isPublicAccessAllowed() {
92         return (mAccessType & ACCESS_TYPE_PUBLIC) != 0;
93     }
94 
isSameSignatureAccessAllowed()95     boolean isSameSignatureAccessAllowed() {
96         return (mAccessType & ACCESS_TYPE_SAME_SIGNATURE) != 0;
97     }
98 
isPackageAccessAllowed(@onNull String packageName, @NonNull byte[] certificate)99     boolean isPackageAccessAllowed(@NonNull String packageName, @NonNull byte[] certificate) {
100         if ((mAccessType & ACCESS_TYPE_ALLOWLIST) == 0) {
101             return false;
102         }
103         return mAllowedPackages.contains(PackageIdentifier.create(packageName, certificate));
104     }
105 
isAccessAllowedForCaller(Context context, @NonNull String callingPackage, int callingUid, int committerUid)106     boolean isAccessAllowedForCaller(Context context,
107             @NonNull String callingPackage, int callingUid, int committerUid) {
108         if ((mAccessType & ACCESS_TYPE_PUBLIC) != 0) {
109             return true;
110         }
111 
112         if ((mAccessType & ACCESS_TYPE_SAME_SIGNATURE) != 0) {
113             if (checkSignatures(callingUid, committerUid)) {
114                 return true;
115             }
116         }
117 
118         if ((mAccessType & ACCESS_TYPE_ALLOWLIST) != 0) {
119             final UserHandle callingUser = UserHandle.of(UserHandle.getUserId(callingUid));
120             final PackageManager pm =
121                     context.createContextAsUser(callingUser, 0 /* flags */).getPackageManager();
122             for (int i = 0; i < mAllowedPackages.size(); ++i) {
123                 final PackageIdentifier packageIdentifier = mAllowedPackages.valueAt(i);
124                 if (packageIdentifier.packageName.equals(callingPackage)
125                         && pm.hasSigningCertificate(callingPackage, packageIdentifier.certificate,
126                                 PackageManager.CERT_INPUT_SHA256)) {
127                     return true;
128                 }
129             }
130         }
131 
132         return false;
133     }
134 
135     /**
136      * Compare signatures for two packages of different users.
137      */
checkSignatures(int uid1, int uid2)138     private boolean checkSignatures(int uid1, int uid2) {
139         final long token = Binder.clearCallingIdentity();
140         try {
141             return LocalServices.getService(PackageManagerInternal.class)
142                     .checkUidSignaturesForAllUsers(uid1, uid2) == PackageManager.SIGNATURE_MATCH;
143         } finally {
144             Binder.restoreCallingIdentity(token);
145         }
146     }
147 
getAccessType()148     int getAccessType() {
149         return mAccessType;
150     }
151 
getAllowedPackagesCount()152     int getAllowedPackagesCount() {
153         return mAllowedPackages.size();
154     }
155 
dump(IndentingPrintWriter fout)156     void dump(IndentingPrintWriter fout) {
157         fout.println("accessType: " + DebugUtils.flagsToString(
158                 BlobAccessMode.class, "ACCESS_TYPE_", mAccessType));
159         fout.print("Explicitly allowed pkgs:");
160         if (mAllowedPackages.isEmpty()) {
161             fout.println(" (Empty)");
162         } else {
163             fout.increaseIndent();
164             for (int i = 0, count = mAllowedPackages.size(); i < count; ++i) {
165                 fout.println(mAllowedPackages.valueAt(i).toString());
166             }
167             fout.decreaseIndent();
168         }
169     }
170 
writeToXml(@onNull XmlSerializer out)171     void writeToXml(@NonNull XmlSerializer out) throws IOException {
172         XmlUtils.writeIntAttribute(out, ATTR_TYPE, mAccessType);
173         for (int i = 0, count = mAllowedPackages.size(); i < count; ++i) {
174             out.startTag(null, TAG_ALLOWED_PACKAGE);
175             final PackageIdentifier packageIdentifier = mAllowedPackages.valueAt(i);
176             XmlUtils.writeStringAttribute(out, ATTR_PACKAGE, packageIdentifier.packageName);
177             XmlUtils.writeByteArrayAttribute(out, ATTR_CERTIFICATE, packageIdentifier.certificate);
178             out.endTag(null, TAG_ALLOWED_PACKAGE);
179         }
180     }
181 
182     @NonNull
createFromXml(@onNull XmlPullParser in)183     static BlobAccessMode createFromXml(@NonNull XmlPullParser in)
184             throws IOException, XmlPullParserException {
185         final BlobAccessMode blobAccessMode = new BlobAccessMode();
186 
187         final int accessType = XmlUtils.readIntAttribute(in, ATTR_TYPE);
188         blobAccessMode.mAccessType = accessType;
189 
190         final int depth = in.getDepth();
191         while (XmlUtils.nextElementWithin(in, depth)) {
192             if (TAG_ALLOWED_PACKAGE.equals(in.getName())) {
193                 final String packageName = XmlUtils.readStringAttribute(in, ATTR_PACKAGE);
194                 final byte[] certificate = XmlUtils.readByteArrayAttribute(in, ATTR_CERTIFICATE);
195                 blobAccessMode.allowPackageAccess(packageName, certificate);
196             }
197         }
198         return blobAccessMode;
199     }
200 
201     private static final class PackageIdentifier {
202         public final String packageName;
203         public final byte[] certificate;
204 
PackageIdentifier(@onNull String packageName, @NonNull byte[] certificate)205         private PackageIdentifier(@NonNull String packageName, @NonNull byte[] certificate) {
206             this.packageName = packageName;
207             this.certificate = certificate;
208         }
209 
create(@onNull String packageName, @NonNull byte[] certificate)210         public static PackageIdentifier create(@NonNull String packageName,
211                 @NonNull byte[] certificate) {
212             return new PackageIdentifier(packageName, certificate);
213         }
214 
215         @Override
equals(Object obj)216         public boolean equals(Object obj) {
217             if (this == obj) {
218                 return true;
219             }
220             if (obj == null || !(obj instanceof PackageIdentifier)) {
221                 return false;
222             }
223             final PackageIdentifier other = (PackageIdentifier) obj;
224             return this.packageName.equals(other.packageName)
225                     && Arrays.equals(this.certificate, other.certificate);
226         }
227 
228         @Override
hashCode()229         public int hashCode() {
230             return Objects.hash(packageName, Arrays.hashCode(certificate));
231         }
232 
233         @Override
toString()234         public String toString() {
235             return "[" + packageName + ", "
236                     + Base64.encodeToString(certificate, Base64.NO_WRAP) + "]";
237         }
238     }
239 }
240