1 /* 2 * Copyright (C) 2019 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.security; 18 19 import android.annotation.NonNull; 20 import android.annotation.Nullable; 21 import android.app.AppOpsManager; 22 import android.content.Context; 23 import android.content.pm.PackageManager; 24 import android.content.pm.PackageManagerInternal; 25 import android.os.Binder; 26 import android.os.Environment; 27 import android.os.IBinder; 28 import android.os.UserHandle; 29 import android.security.IFileIntegrityService; 30 import android.util.Slog; 31 32 import com.android.internal.security.VerityUtils; 33 import com.android.server.LocalServices; 34 import com.android.server.SystemService; 35 36 import java.io.ByteArrayInputStream; 37 import java.io.File; 38 import java.io.IOException; 39 import java.nio.file.Files; 40 import java.nio.file.Path; 41 import java.security.cert.Certificate; 42 import java.security.cert.CertificateException; 43 import java.security.cert.CertificateFactory; 44 import java.security.cert.X509Certificate; 45 import java.util.ArrayList; 46 import java.util.Collection; 47 48 /** 49 * A {@link SystemService} that provides file integrity related operations. 50 * @hide 51 */ 52 public class FileIntegrityService extends SystemService { 53 private static final String TAG = "FileIntegrityService"; 54 55 private static CertificateFactory sCertFactory; 56 57 private Collection<X509Certificate> mTrustedCertificates = new ArrayList<X509Certificate>(); 58 59 private final IBinder mService = new IFileIntegrityService.Stub() { 60 @Override 61 public boolean isApkVeritySupported() { 62 return VerityUtils.isFsVeritySupported(); 63 } 64 65 @Override 66 public boolean isAppSourceCertificateTrusted(@Nullable byte[] certificateBytes, 67 @NonNull String packageName) { 68 checkCallerPermission(packageName); 69 70 try { 71 if (!VerityUtils.isFsVeritySupported()) { 72 return false; 73 } 74 if (certificateBytes == null) { 75 Slog.w(TAG, "Received a null certificate"); 76 return false; 77 } 78 return mTrustedCertificates.contains(toCertificate(certificateBytes)); 79 } catch (CertificateException e) { 80 Slog.e(TAG, "Failed to convert the certificate: " + e); 81 return false; 82 } 83 } 84 85 private void checkCallerPermission(String packageName) { 86 final int callingUid = Binder.getCallingUid(); 87 final int callingUserId = UserHandle.getUserId(callingUid); 88 final PackageManagerInternal packageManager = 89 LocalServices.getService(PackageManagerInternal.class); 90 final int packageUid = packageManager.getPackageUid( 91 packageName, 0 /*flag*/, callingUserId); 92 if (callingUid != packageUid) { 93 throw new SecurityException( 94 "Calling uid " + callingUid + " does not own package " + packageName); 95 } 96 97 if (getContext().checkCallingPermission(android.Manifest.permission.INSTALL_PACKAGES) 98 == PackageManager.PERMISSION_GRANTED) { 99 return; 100 } 101 102 final AppOpsManager appOpsManager = getContext().getSystemService(AppOpsManager.class); 103 final int mode = appOpsManager.checkOpNoThrow( 104 AppOpsManager.OP_REQUEST_INSTALL_PACKAGES, callingUid, packageName); 105 if (mode != AppOpsManager.MODE_ALLOWED) { 106 throw new SecurityException( 107 "Caller should have INSTALL_PACKAGES or REQUEST_INSTALL_PACKAGES"); 108 } 109 } 110 }; 111 FileIntegrityService(final Context context)112 public FileIntegrityService(final Context context) { 113 super(context); 114 try { 115 sCertFactory = CertificateFactory.getInstance("X.509"); 116 } catch (CertificateException e) { 117 Slog.wtf(TAG, "Cannot get an instance of X.509 certificate factory"); 118 } 119 } 120 121 @Override onStart()122 public void onStart() { 123 loadAllCertificates(); 124 publishBinderService(Context.FILE_INTEGRITY_SERVICE, mService); 125 } 126 loadAllCertificates()127 private void loadAllCertificates() { 128 // A better alternative to load certificates would be to read from .fs-verity kernel 129 // keyring, which fsverity_init loads to during earlier boot time from the same sources 130 // below. But since the read operation from keyring is not provided in kernel, we need to 131 // duplicate the same loading logic here. 132 133 // Load certificates trusted by the device manufacturer. 134 // NB: Directories need to be synced with system/security/fsverity_init/fsverity_init.cpp. 135 final String relativeDir = "etc/security/fsverity"; 136 loadCertificatesFromDirectory(Environment.getRootDirectory().toPath() 137 .resolve(relativeDir)); 138 loadCertificatesFromDirectory(Environment.getProductDirectory().toPath() 139 .resolve(relativeDir)); 140 } 141 loadCertificatesFromDirectory(Path path)142 private void loadCertificatesFromDirectory(Path path) { 143 try { 144 File[] files = path.toFile().listFiles(); 145 if (files == null) { 146 return; 147 } 148 149 for (File cert : files) { 150 byte[] certificateBytes = Files.readAllBytes(cert.toPath()); 151 if (certificateBytes == null) { 152 Slog.w(TAG, "The certificate file is empty, ignoring " + cert); 153 continue; 154 } 155 collectCertificate(certificateBytes); 156 } 157 } catch (IOException e) { 158 Slog.wtf(TAG, "Failed to load fs-verity certificate from " + path, e); 159 } 160 } 161 162 /** 163 * Tries to convert {@code bytes} into an X.509 certificate and store in memory. 164 * Errors need to be surpressed in order fo the next certificates to still be collected. 165 */ collectCertificate(@onNull byte[] bytes)166 private void collectCertificate(@NonNull byte[] bytes) { 167 try { 168 mTrustedCertificates.add(toCertificate(bytes)); 169 } catch (CertificateException e) { 170 Slog.e(TAG, "Invalid certificate, ignored: " + e); 171 } 172 } 173 174 /** 175 * Converts byte array into one X.509 certificate. If multiple certificate is defined, ignore 176 * the rest. The rational is to make it harder to smuggle. 177 */ 178 @NonNull toCertificate(@onNull byte[] bytes)179 private static X509Certificate toCertificate(@NonNull byte[] bytes) 180 throws CertificateException { 181 Certificate certificate = sCertFactory.generateCertificate(new ByteArrayInputStream(bytes)); 182 if (!(certificate instanceof X509Certificate)) { 183 throw new CertificateException("Expected to contain an X.509 certificate"); 184 } 185 return (X509Certificate) certificate; 186 } 187 } 188