1 /* 2 * Copyright (C) 2018 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.signedconfig; 18 19 import android.content.Context; 20 import android.content.pm.ApplicationInfo; 21 import android.os.Build; 22 import android.provider.Settings; 23 import android.util.ArrayMap; 24 import android.util.ArraySet; 25 import android.util.Slog; 26 import android.util.StatsLog; 27 28 import java.security.GeneralSecurityException; 29 import java.util.Arrays; 30 import java.util.Collections; 31 import java.util.Map; 32 import java.util.Set; 33 34 class GlobalSettingsConfigApplicator { 35 36 private static final String TAG = "SignedConfig"; 37 38 private static final Set<String> ALLOWED_KEYS = Collections.unmodifiableSet(new ArraySet<>( 39 Arrays.asList( 40 Settings.Global.HIDDEN_API_POLICY, 41 Settings.Global.HIDDEN_API_BLACKLIST_EXEMPTIONS 42 ))); 43 44 private static final Map<String, String> HIDDEN_API_POLICY_KEY_MAP = makeMap( 45 "DEFAULT", String.valueOf(ApplicationInfo.HIDDEN_API_ENFORCEMENT_DEFAULT), 46 "DISABLED", String.valueOf(ApplicationInfo.HIDDEN_API_ENFORCEMENT_DISABLED), 47 "JUST_WARN", String.valueOf(ApplicationInfo.HIDDEN_API_ENFORCEMENT_JUST_WARN), 48 "ENABLED", String.valueOf(ApplicationInfo.HIDDEN_API_ENFORCEMENT_ENABLED) 49 ); 50 51 private static final Map<String, Map<String, String>> KEY_VALUE_MAPPERS = makeMap( 52 Settings.Global.HIDDEN_API_POLICY, HIDDEN_API_POLICY_KEY_MAP 53 ); 54 55 private static <K, V> Map<K, V> makeMap(Object... keyValuePairs) { 56 if (keyValuePairs.length % 2 != 0) { 57 throw new IllegalArgumentException(); 58 } 59 final int len = keyValuePairs.length / 2; 60 ArrayMap<K, V> m = new ArrayMap<>(len); 61 for (int i = 0; i < len; ++i) { 62 m.put((K) keyValuePairs[i * 2], (V) keyValuePairs[(i * 2) + 1]); 63 } 64 return Collections.unmodifiableMap(m); 65 66 } 67 68 private final Context mContext; 69 private final String mSourcePackage; 70 private final SignedConfigEvent mEvent; 71 private final SignatureVerifier mVerifier; 72 73 GlobalSettingsConfigApplicator(Context context, String sourcePackage, SignedConfigEvent event) { 74 mContext = context; 75 mSourcePackage = sourcePackage; 76 mEvent = event; 77 mVerifier = new SignatureVerifier(mEvent); 78 } 79 80 private boolean checkSignature(String data, String signature) { 81 try { 82 return mVerifier.verifySignature(data, signature); 83 } catch (GeneralSecurityException e) { 84 Slog.e(TAG, "Failed to verify signature", e); 85 mEvent.status = StatsLog.SIGNED_CONFIG_REPORTED__STATUS__SECURITY_EXCEPTION; 86 return false; 87 } 88 } 89 90 private int getCurrentConfigVersion() { 91 return Settings.Global.getInt(mContext.getContentResolver(), 92 Settings.Global.SIGNED_CONFIG_VERSION, 0); 93 } 94 95 private void updateCurrentConfig(int version, Map<String, String> values) { 96 for (Map.Entry<String, String> e: values.entrySet()) { 97 Settings.Global.putString( 98 mContext.getContentResolver(), 99 e.getKey(), 100 e.getValue()); 101 } 102 Settings.Global.putInt( 103 mContext.getContentResolver(), Settings.Global.SIGNED_CONFIG_VERSION, version); 104 } 105 106 107 void applyConfig(String configStr, String signature) { 108 if (!checkSignature(configStr, signature)) { 109 Slog.e(TAG, "Signature check on global settings in package " + mSourcePackage 110 + " failed; ignoring"); 111 return; 112 } 113 SignedConfig config; 114 try { 115 config = SignedConfig.parse(configStr, ALLOWED_KEYS, KEY_VALUE_MAPPERS); 116 mEvent.version = config.version; 117 } catch (InvalidConfigException e) { 118 Slog.e(TAG, "Failed to parse global settings from package " + mSourcePackage, e); 119 mEvent.status = StatsLog.SIGNED_CONFIG_REPORTED__STATUS__INVALID_CONFIG; 120 return; 121 } 122 int currentVersion = getCurrentConfigVersion(); 123 if (currentVersion >= config.version) { 124 Slog.i(TAG, "Global settings from package " + mSourcePackage 125 + " is older than existing: " + config.version + "<=" + currentVersion); 126 mEvent.status = StatsLog.SIGNED_CONFIG_REPORTED__STATUS__OLD_CONFIG; 127 return; 128 } 129 // We have new config! 130 Slog.i(TAG, "Got new global settings from package " + mSourcePackage + ": version " 131 + config.version + " replacing existing version " + currentVersion); 132 SignedConfig.PerSdkConfig matchedConfig = 133 config.getMatchingConfig(Build.VERSION.SDK_INT); 134 if (matchedConfig == null) { 135 Slog.i(TAG, "Settings is not applicable to current SDK version; ignoring"); 136 mEvent.status = StatsLog.SIGNED_CONFIG_REPORTED__STATUS__NOT_APPLICABLE; 137 return; 138 } 139 140 Slog.i(TAG, "Updating global settings to version " + config.version); 141 updateCurrentConfig(config.version, matchedConfig.values); 142 mEvent.status = StatsLog.SIGNED_CONFIG_REPORTED__STATUS__APPLIED; 143 } 144 } 145