Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit b4ebf52f authored by Liz Prucka's avatar Liz Prucka
Browse files

[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
parent 5f89fa38
Loading
Loading
Loading
Loading
+13 −1
Original line number Diff line number Diff line
@@ -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;
    }

+5 −0
Original line number Diff line number Diff line
@@ -17,6 +17,11 @@
package com.android.server.security.intrusiondetection;

public interface DataSource {
    /**
     * Initialize the data source.
     */
    boolean initialize();

    /**
     * Enable the data collection.
     */
+42 −0
Original line number Diff line number Diff line
/*
 * 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;
    }
}
+134 −0
Original line number Diff line number Diff line
/*
 * 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());
    }
}
+21 −7
Original line number Diff line number Diff line
@@ -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();
    }