diff options
| author | 2024-11-03 15:23:42 +0000 | |
|---|---|---|
| committer | 2024-11-22 16:14:03 +0000 | |
| commit | b4ebf52f1f12bca778a1bd7e51ef010bc6eec1e2 (patch) | |
| tree | 0e1dfa311a98657ab20ec774d728b2b4aecf1aef | |
| parent | 5f89fa38e3ba65d1d6704e84fba5e6e0cc90fce9 (diff) | |
[ID] Add Network Logging
Added an AdminReceiver to receive DeviceAdmin callbacks.
NetworkLogSource receives NetworkLog callbacks from the admin
receiver, and returns data to the DataAggregator.
The network log source architecture is documented at
go/forensic-datasource-docs.
Added a permission check in SecurityLog initialization to prevent
silent failures.
Bug: 365994454
Test: atest IntrusionDetectionServiceTest
Flag: android.security.afl_api
Ignore-AOSP-First: security feature
Change-Id: I4a5eaa4b6f4e2ce244f61250f39fb66bc0550326
5 files changed, 215 insertions, 8 deletions
diff --git a/services/core/java/com/android/server/security/intrusiondetection/DataAggregator.java b/services/core/java/com/android/server/security/intrusiondetection/DataAggregator.java index 06e9dcdcbedd..0ea88e8523f0 100644 --- a/services/core/java/com/android/server/security/intrusiondetection/DataAggregator.java +++ b/services/core/java/com/android/server/security/intrusiondetection/DataAggregator.java @@ -36,6 +36,9 @@ public class DataAggregator { private static final int MSG_DISABLE = 2; private static final int STORED_EVENTS_SIZE_LIMIT = 1024; + private static final IntrusionDetectionAdminReceiver ADMIN_RECEIVER = + new IntrusionDetectionAdminReceiver(); + private final IntrusionDetectionService mIntrusionDetectionService; private final ArrayList<DataSource> mDataSources; @@ -60,10 +63,19 @@ public class DataAggregator { * Initialize DataSources * @return Whether the initialization succeeds. */ - // TODO: Add the corresponding data sources public boolean initialize() { SecurityLogSource securityLogSource = new SecurityLogSource(mContext, this); mDataSources.add(securityLogSource); + + NetworkLogSource networkLogSource = new NetworkLogSource(mContext, this); + ADMIN_RECEIVER.setNetworkLogEventCallback(networkLogSource); + mDataSources.add(networkLogSource); + + for (DataSource ds : mDataSources) { + if (!ds.initialize()) { + return false; + } + } return true; } diff --git a/services/core/java/com/android/server/security/intrusiondetection/DataSource.java b/services/core/java/com/android/server/security/intrusiondetection/DataSource.java index 0bc448245b76..61fac46be82d 100644 --- a/services/core/java/com/android/server/security/intrusiondetection/DataSource.java +++ b/services/core/java/com/android/server/security/intrusiondetection/DataSource.java @@ -18,6 +18,11 @@ package com.android.server.security.intrusiondetection; public interface DataSource { /** + * Initialize the data source. + */ + boolean initialize(); + + /** * Enable the data collection. */ void enable(); diff --git a/services/core/java/com/android/server/security/intrusiondetection/IntrusionDetectionAdminReceiver.java b/services/core/java/com/android/server/security/intrusiondetection/IntrusionDetectionAdminReceiver.java new file mode 100644 index 000000000000..dba7374fe02a --- /dev/null +++ b/services/core/java/com/android/server/security/intrusiondetection/IntrusionDetectionAdminReceiver.java @@ -0,0 +1,42 @@ +/* + * Copyright (C) 2024 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.security.intrusiondetection; + +import android.app.admin.DeviceAdminReceiver; +import android.content.Context; +import android.content.Intent; +import android.util.Slog; + +public class IntrusionDetectionAdminReceiver extends DeviceAdminReceiver { + private static final String TAG = "IntrusionDetectionAdminReceiver"; + + private static NetworkLogSource sNetworkLogSource; + + @Override + public void onNetworkLogsAvailable( + Context context, Intent intent, long batchToken, int networkLogsCount) { + if (sNetworkLogSource != null) { + sNetworkLogSource.onNetworkLogsAvailable(batchToken); + } else { + Slog.w(TAG, "Network log receiver is not initialized"); + } + } + + public void setNetworkLogEventCallback(NetworkLogSource networkLogSource) { + sNetworkLogSource = networkLogSource; + } +} diff --git a/services/core/java/com/android/server/security/intrusiondetection/NetworkLogSource.java b/services/core/java/com/android/server/security/intrusiondetection/NetworkLogSource.java new file mode 100644 index 000000000000..1c93d3f9c6a1 --- /dev/null +++ b/services/core/java/com/android/server/security/intrusiondetection/NetworkLogSource.java @@ -0,0 +1,134 @@ +/* + * Copyright (C) 2024 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.security.intrusiondetection; + +import android.app.admin.ConnectEvent; +import android.app.admin.DevicePolicyManager; +import android.app.admin.DnsEvent; +import android.app.admin.NetworkEvent; +import android.content.ComponentName; +import android.content.Context; +import android.security.intrusiondetection.IntrusionDetectionEvent; +import android.util.Slog; + +import java.util.List; +import java.util.stream.Collectors; + +public class NetworkLogSource implements DataSource { + + private static final String TAG = "IntrusionDetectionEvent NetworkLogSource"; + + private DevicePolicyManager mDpm; + private ComponentName mAdmin; + private DataAggregator mDataAggregator; + + public NetworkLogSource(Context context, DataAggregator dataAggregator) { + mDataAggregator = dataAggregator; + mDpm = context.getSystemService(DevicePolicyManager.class); + mAdmin = new ComponentName(context, IntrusionDetectionAdminReceiver.class); + } + + @Override + public boolean initialize() { + try { + if (!mDpm.isAdminActive(mAdmin)) { + Slog.e(TAG, "Admin " + mAdmin.flattenToString() + "is not active admin"); + return false; + } + } catch (SecurityException e) { + Slog.e(TAG, "Security exception in initialize: ", e); + return false; + } + return true; + } + + @Override + public void enable() { + enableNetworkLog(); + } + + @Override + public void disable() { + disableNetworkLog(); + } + + private void enableNetworkLog() { + if (!isNetworkLogEnabled()) { + mDpm.setNetworkLoggingEnabled(mAdmin, true); + } + } + + private void disableNetworkLog() { + if (isNetworkLogEnabled()) { + mDpm.setNetworkLoggingEnabled(mAdmin, false); + } + } + + private boolean isNetworkLogEnabled() { + return mDpm.isNetworkLoggingEnabled(mAdmin); + } + + /** + * Retrieve network logs when onNetworkLogsAvailable callback is received. + * + * @param batchToken The token representing the current batch of network logs. + */ + public void onNetworkLogsAvailable(long batchToken) { + List<NetworkEvent> events; + try { + events = mDpm.retrieveNetworkLogs(mAdmin, batchToken); + } catch (SecurityException e) { + Slog.e( + TAG, + "Admin " + + mAdmin.flattenToString() + + "does not have permission to retrieve network logs", + e); + return; + } + if (events == null) { + if (!isNetworkLogEnabled()) { + Slog.w(TAG, "Network logging is disabled"); + } else { + Slog.e(TAG, "Invalid batch token: " + batchToken); + } + return; + } + + List<IntrusionDetectionEvent> intrusionDetectionEvents = + events.stream() + .filter(event -> event != null) + .map(event -> toIntrusionDetectionEvent(event)) + .collect(Collectors.toList()); + mDataAggregator.addBatchData(intrusionDetectionEvents); + } + + private IntrusionDetectionEvent toIntrusionDetectionEvent(NetworkEvent event) { + if (event instanceof DnsEvent) { + DnsEvent dnsEvent = (DnsEvent) event; + return new IntrusionDetectionEvent(dnsEvent); + } else if (event instanceof ConnectEvent) { + ConnectEvent connectEvent = (ConnectEvent) event; + return new IntrusionDetectionEvent(connectEvent); + } + throw new IllegalArgumentException( + "Invalid event type with ID: " + + event.getId() + + "from package: " + + event.getPackageName()); + } +} diff --git a/services/core/java/com/android/server/security/intrusiondetection/SecurityLogSource.java b/services/core/java/com/android/server/security/intrusiondetection/SecurityLogSource.java index 226f9d879cab..c5f736e383b2 100644 --- a/services/core/java/com/android/server/security/intrusiondetection/SecurityLogSource.java +++ b/services/core/java/com/android/server/security/intrusiondetection/SecurityLogSource.java @@ -22,6 +22,7 @@ import android.app.admin.DevicePolicyManager; import android.app.admin.SecurityLog.SecurityEvent; import android.content.Context; import android.security.intrusiondetection.IntrusionDetectionEvent; +import android.util.Slog; import java.util.List; import java.util.concurrent.Executor; @@ -33,7 +34,7 @@ public class SecurityLogSource implements DataSource { private static final String TAG = "IntrusionDetection SecurityLogSource"; - private SecurityEventCallback mEventCallback = new SecurityEventCallback(); + private SecurityEventCallback mEventCallback; private DevicePolicyManager mDpm; private Executor mExecutor; private DataAggregator mDataAggregator; @@ -42,9 +43,26 @@ public class SecurityLogSource implements DataSource { mDataAggregator = dataAggregator; mDpm = context.getSystemService(DevicePolicyManager.class); mExecutor = Executors.newSingleThreadExecutor(); + } + + @Override + public boolean initialize() { + // Confirm caller is system and the device is managed. Otherwise logs will + // be redacted. + try { + if (!mDpm.isDeviceManaged()) { + Slog.e(TAG, "Caller does not have device owner permissions"); + return false; + } + } catch (SecurityException e) { + Slog.e(TAG, "Security exception in initialize: ", e); + return false; + } mEventCallback = new SecurityEventCallback(); + return true; } + @Override @RequiresPermission(permission.MANAGE_DEVICE_POLICY_AUDIT_LOGGING) public void enable() { @@ -72,12 +90,8 @@ public class SecurityLogSource implements DataSource { } } - /** - * Check if security audit logging is enabled for the caller. - * - * @return Whether security audit logging is enabled. - */ - public boolean isAuditLogEnabled() { + @RequiresPermission(permission.MANAGE_DEVICE_POLICY_AUDIT_LOGGING) + private boolean isAuditLogEnabled() { return mDpm.isAuditLogEnabled(); } |