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