diff options
5 files changed, 122 insertions, 16 deletions
diff --git a/cmds/statsd/src/atoms.proto b/cmds/statsd/src/atoms.proto index 6267ac2f2257..f9828a2b5302 100644 --- a/cmds/statsd/src/atoms.proto +++ b/cmds/statsd/src/atoms.proto @@ -181,6 +181,7 @@ message Atom { DocsUISearchTypeReported docs_ui_search_type_reported = 120; DataStallEvent data_stall_event = 121; RescuePartyResetReported rescue_party_reset_reported = 122; + SignedConfigReported signed_config_reported = 123; } // Pulled events will start at field 10000. @@ -3859,3 +3860,44 @@ message RescuePartyResetReported { // The rescue level of this reset. A value of 0 indicates missing or unknown level information. optional int32 rescue_level = 1; } + +/** + * Logs when signed config is received from an APK, and if that config was applied successfully. + * Logged from: + * frameworks/base/services/core/java/com/android/server/signedconfig/SignedConfigService.java + */ +message SignedConfigReported { + enum Type { + UNKNOWN_TYPE = 0; + GLOBAL_SETTINGS = 1; + } + optional Type type = 1; + + // The final status of the signed config received. + enum Status { + UNKNOWN_STATUS = 0; + APPLIED = 1; + BASE64_FAILURE_CONFIG = 2; + BASE64_FAILURE_SIGNATURE = 3; + SECURITY_EXCEPTION = 4; + INVALID_CONFIG = 5; + OLD_CONFIG = 6; + SIGNATURE_CHECK_FAILED = 7; + NOT_APPLICABLE = 8; + } + optional Status status = 2; + + // The version of the signed config processed. + optional int32 version = 3; + + // The package name that the config was extracted from. + optional string from_package = 4; + + enum Key { + NO_KEY = 0; + DEBUG = 1; + PRODUCTION = 2; + } + // Which key was used to verify the config. + optional Key verified_with = 5; +} diff --git a/services/core/java/com/android/server/signedconfig/GlobalSettingsConfigApplicator.java b/services/core/java/com/android/server/signedconfig/GlobalSettingsConfigApplicator.java index 438c303de6cc..d77cf900a0a9 100644 --- a/services/core/java/com/android/server/signedconfig/GlobalSettingsConfigApplicator.java +++ b/services/core/java/com/android/server/signedconfig/GlobalSettingsConfigApplicator.java @@ -23,6 +23,7 @@ import android.provider.Settings; import android.util.ArrayMap; import android.util.ArraySet; import android.util.Slog; +import android.util.StatsLog; import java.security.GeneralSecurityException; import java.util.Arrays; @@ -66,12 +67,14 @@ class GlobalSettingsConfigApplicator { private final Context mContext; private final String mSourcePackage; + private final SignedConfigEvent mEvent; private final SignatureVerifier mVerifier; - GlobalSettingsConfigApplicator(Context context, String sourcePackage) { + GlobalSettingsConfigApplicator(Context context, String sourcePackage, SignedConfigEvent event) { mContext = context; mSourcePackage = sourcePackage; - mVerifier = new SignatureVerifier(); + mEvent = event; + mVerifier = new SignatureVerifier(mEvent); } private boolean checkSignature(String data, String signature) { @@ -79,6 +82,7 @@ class GlobalSettingsConfigApplicator { return mVerifier.verifySignature(data, signature); } catch (GeneralSecurityException e) { Slog.e(TAG, "Failed to verify signature", e); + mEvent.status = StatsLog.SIGNED_CONFIG_REPORTED__STATUS__SECURITY_EXCEPTION; return false; } } @@ -109,14 +113,17 @@ class GlobalSettingsConfigApplicator { SignedConfig config; try { config = SignedConfig.parse(configStr, ALLOWED_KEYS, KEY_VALUE_MAPPERS); + mEvent.version = config.version; } catch (InvalidConfigException e) { Slog.e(TAG, "Failed to parse global settings from package " + mSourcePackage, e); + mEvent.status = StatsLog.SIGNED_CONFIG_REPORTED__STATUS__INVALID_CONFIG; return; } int currentVersion = getCurrentConfigVersion(); if (currentVersion >= config.version) { Slog.i(TAG, "Global settings from package " + mSourcePackage + " is older than existing: " + config.version + "<=" + currentVersion); + mEvent.status = StatsLog.SIGNED_CONFIG_REPORTED__STATUS__OLD_CONFIG; return; } // We have new config! @@ -126,10 +133,12 @@ class GlobalSettingsConfigApplicator { config.getMatchingConfig(Build.VERSION.SDK_INT); if (matchedConfig == null) { Slog.i(TAG, "Settings is not applicable to current SDK version; ignoring"); + mEvent.status = StatsLog.SIGNED_CONFIG_REPORTED__STATUS__NOT_APPLICABLE; return; } Slog.i(TAG, "Updating global settings to version " + config.version); updateCurrentConfig(config.version, matchedConfig.values); + mEvent.status = StatsLog.SIGNED_CONFIG_REPORTED__STATUS__APPLIED; } } diff --git a/services/core/java/com/android/server/signedconfig/SignatureVerifier.java b/services/core/java/com/android/server/signedconfig/SignatureVerifier.java index 944db84acc71..5ba57b50c439 100644 --- a/services/core/java/com/android/server/signedconfig/SignatureVerifier.java +++ b/services/core/java/com/android/server/signedconfig/SignatureVerifier.java @@ -18,6 +18,7 @@ package com.android.server.signedconfig; import android.os.Build; import android.util.Slog; +import android.util.StatsLog; import java.nio.charset.StandardCharsets; import java.security.InvalidKeyException; @@ -43,9 +44,11 @@ public class SignatureVerifier { "MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEaAn2XVifsLTHg616nTsOMVmlhBoECGbTEBTKKvdd2hO60" + "pj1pnU8SMkhYfaNxZuKgw9LNvOwlFwStboIYeZ3lQ=="; + private final SignedConfigEvent mEvent; private final PublicKey mDebugKey; - public SignatureVerifier() { + public SignatureVerifier(SignedConfigEvent event) { + mEvent = event; mDebugKey = createKey(DEBUG_KEY); } @@ -80,6 +83,7 @@ public class SignatureVerifier { try { signature = Base64.getDecoder().decode(base64Signature); } catch (IllegalArgumentException e) { + mEvent.status = StatsLog.SIGNED_CONFIG_REPORTED__STATUS__BASE64_FAILURE_SIGNATURE; Slog.e(TAG, "Failed to base64 decode signature"); return false; } @@ -94,6 +98,7 @@ public class SignatureVerifier { verifier.update(data); if (verifier.verify(signature)) { Slog.i(TAG, "Verified config using debug key"); + mEvent.verifiedWith = StatsLog.SIGNED_CONFIG_REPORTED__VERIFIED_WITH__DEBUG; return true; } else { if (DBG) Slog.i(TAG, "Config verification failed using debug key"); @@ -104,6 +109,7 @@ public class SignatureVerifier { } // TODO verify production key. Slog.w(TAG, "NO PRODUCTION KEY YET, FAILING VERIFICATION"); + mEvent.status = StatsLog.SIGNED_CONFIG_REPORTED__STATUS__SIGNATURE_CHECK_FAILED; return false; } } diff --git a/services/core/java/com/android/server/signedconfig/SignedConfigEvent.java b/services/core/java/com/android/server/signedconfig/SignedConfigEvent.java new file mode 100644 index 000000000000..2f2062c6f2ee --- /dev/null +++ b/services/core/java/com/android/server/signedconfig/SignedConfigEvent.java @@ -0,0 +1,39 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.server.signedconfig; + +import android.util.StatsLog; + +/** + * Helper class to allow a SignedConfigReported event to be built up in stages. + */ +public class SignedConfigEvent { + + public int type = StatsLog.SIGNED_CONFIG_REPORTED__TYPE__UNKNOWN_TYPE; + public int status = StatsLog.SIGNED_CONFIG_REPORTED__STATUS__UNKNOWN_STATUS; + public int version = 0; + public String fromPackage = null; + public int verifiedWith = StatsLog.SIGNED_CONFIG_REPORTED__VERIFIED_WITH__NO_KEY; + + /** + * Write this event to statslog. + */ + public void send() { + StatsLog.write(StatsLog.SIGNED_CONFIG_REPORTED, + type, status, version, fromPackage, verifiedWith); + } + +} diff --git a/services/core/java/com/android/server/signedconfig/SignedConfigService.java b/services/core/java/com/android/server/signedconfig/SignedConfigService.java index 6bcee1413fa8..dc39542dc29f 100644 --- a/services/core/java/com/android/server/signedconfig/SignedConfigService.java +++ b/services/core/java/com/android/server/signedconfig/SignedConfigService.java @@ -26,6 +26,7 @@ import android.content.pm.PackageManagerInternal; import android.net.Uri; import android.os.Bundle; import android.util.Slog; +import android.util.StatsLog; import com.android.server.LocalServices; @@ -82,21 +83,30 @@ public class SignedConfigService { } if (metaData.containsKey(KEY_GLOBAL_SETTINGS) && metaData.containsKey(KEY_GLOBAL_SETTINGS_SIGNATURE)) { - String config = metaData.getString(KEY_GLOBAL_SETTINGS); - String signature = metaData.getString(KEY_GLOBAL_SETTINGS_SIGNATURE); + SignedConfigEvent event = new SignedConfigEvent(); try { - // Base64 encoding is standard (not URL safe) encoding: RFC4648 - config = new String(Base64.getDecoder().decode(config), StandardCharsets.UTF_8); - } catch (IllegalArgumentException iae) { - Slog.e(TAG, "Failed to base64 decode global settings config from " + packageName); - return; + event.type = StatsLog.SIGNED_CONFIG_REPORTED__TYPE__GLOBAL_SETTINGS; + event.fromPackage = packageName; + String config = metaData.getString(KEY_GLOBAL_SETTINGS); + String signature = metaData.getString(KEY_GLOBAL_SETTINGS_SIGNATURE); + try { + // Base64 encoding is standard (not URL safe) encoding: RFC4648 + config = new String(Base64.getDecoder().decode(config), StandardCharsets.UTF_8); + } catch (IllegalArgumentException iae) { + Slog.e(TAG, "Failed to base64 decode global settings config from " + + packageName); + event.status = StatsLog.SIGNED_CONFIG_REPORTED__STATUS__BASE64_FAILURE_CONFIG; + return; + } + if (DBG) { + Slog.d(TAG, "Got global settings config: " + config); + Slog.d(TAG, "Got global settings signature: " + signature); + } + new GlobalSettingsConfigApplicator(mContext, packageName, event).applyConfig( + config, signature); + } finally { + event.send(); } - if (DBG) { - Slog.d(TAG, "Got global settings config: " + config); - Slog.d(TAG, "Got global settings signature: " + signature); - } - new GlobalSettingsConfigApplicator(mContext, packageName).applyConfig( - config, signature); } else { if (DBG) Slog.d(TAG, "Package has no global settings config/signature."); } |