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