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

Commit d0ac50bc authored by Benedict Wong's avatar Benedict Wong Committed by Gerrit Code Review
Browse files

Merge "[ipsec-qtaguid] Tag sockets upon creation of encap sockets"

parents c5212b66 babe5d73
Loading
Loading
Loading
Loading
+7 −0
Original line number Diff line number Diff line
@@ -253,6 +253,13 @@ package android.net {
    field public static final int INVALID_SECURITY_PARAMETER_INDEX = 0; // 0x0
  }

  public class TrafficStats {
    method public static long getLoopbackRxBytes();
    method public static long getLoopbackRxPackets();
    method public static long getLoopbackTxBytes();
    method public static long getLoopbackTxPackets();
  }

}

package android.os {
+27 −0
Original line number Diff line number Diff line
@@ -19,6 +19,7 @@ package android.net;
import android.annotation.RequiresPermission;
import android.annotation.SuppressLint;
import android.annotation.SystemApi;
import android.annotation.TestApi;
import android.app.DownloadManager;
import android.app.backup.BackupManager;
import android.app.usage.NetworkStatsManager;
@@ -154,6 +155,8 @@ public class TrafficStats {

    private static Object sProfilingLock = new Object();

    private static final String LOOPBACK_IFACE = "lo";

    /**
     * Set active tag to use when accounting {@link Socket} traffic originating
     * from the current thread. Only one active tag per thread is supported.
@@ -542,6 +545,30 @@ public class TrafficStats {
        return nativeGetIfaceStat(iface, TYPE_RX_BYTES);
    }

    /** {@hide} */
    @TestApi
    public static long getLoopbackTxPackets() {
        return nativeGetIfaceStat(LOOPBACK_IFACE, TYPE_TX_PACKETS);
    }

    /** {@hide} */
    @TestApi
    public static long getLoopbackRxPackets() {
        return nativeGetIfaceStat(LOOPBACK_IFACE, TYPE_RX_PACKETS);
    }

    /** {@hide} */
    @TestApi
    public static long getLoopbackTxBytes() {
        return nativeGetIfaceStat(LOOPBACK_IFACE, TYPE_TX_BYTES);
    }

    /** {@hide} */
    @TestApi
    public static long getLoopbackRxBytes() {
        return nativeGetIfaceStat(LOOPBACK_IFACE, TYPE_RX_BYTES);
    }

    /**
     * Return number of packets transmitted since device boot. Counts packets
     * across all network interfaces, and always increases monotonically since
+40 −1
Original line number Diff line number Diff line
@@ -34,6 +34,7 @@ import android.net.IpSecTransform;
import android.net.IpSecTransformResponse;
import android.net.IpSecUdpEncapResponse;
import android.net.NetworkUtils;
import android.net.TrafficStats;
import android.net.util.NetdService;
import android.os.Binder;
import android.os.IBinder;
@@ -120,6 +121,7 @@ public class IpSecService extends IIpSecService.Stub {
    }

    private final IpSecServiceConfiguration mSrvConfig;
    final UidFdTagger mUidFdTagger;

    /**
     * Interface for user-reference and kernel-resource cleanup.
@@ -762,8 +764,23 @@ public class IpSecService extends IIpSecService.Stub {
    /** @hide */
    @VisibleForTesting
    public IpSecService(Context context, IpSecServiceConfiguration config) {
        this(context, config, (fd, uid) ->  {
            try{
                TrafficStats.setThreadStatsUid(uid);
                TrafficStats.tagFileDescriptor(fd);
            } finally {
                TrafficStats.clearThreadStatsUid();
            }
        });
    }

    /** @hide */
    @VisibleForTesting
    public IpSecService(
            Context context, IpSecServiceConfiguration config, UidFdTagger uidFdTagger) {
        mContext = context;
        mSrvConfig = config;
        mUidFdTagger = uidFdTagger;
    }

    public void systemReady() {
@@ -924,6 +941,26 @@ public class IpSecService extends IIpSecService.Stub {
        throw new IOException("Failed " + MAX_PORT_BIND_ATTEMPTS + " attempts to bind to a port");
    }

    /**
     * Functional interface to do traffic tagging of given sockets to UIDs.
     *
     * <p>Specifically used by openUdpEncapsulationSocket to ensure data usage on the UDP encap
     * sockets are billed to the UID that the UDP encap socket was created on behalf of.
     *
     * <p>Separate class so that the socket tagging logic can be mocked; TrafficStats uses static
     * methods that cannot be easily mocked/tested.
     */
    @VisibleForTesting
    public interface UidFdTagger {
        /**
         * Sets socket tag to assign all traffic to the provided UID.
         *
         * <p>Since the socket is created on behalf of an unprivileged application, all traffic
         * should be accounted to the UID of the unprivileged application.
         */
        public void tag(FileDescriptor fd, int uid) throws IOException;
    }

    /**
     * Open a socket via the system server and bind it to the specified port (random if port=0).
     * This will return a PFD to the user that represent a bound UDP socket. The system server will
@@ -939,7 +976,8 @@ public class IpSecService extends IIpSecService.Stub {
        }
        checkNotNull(binder, "Null Binder passed to openUdpEncapsulationSocket");

        UserRecord userRecord = mUserResourceTracker.getUserRecord(Binder.getCallingUid());
        int callingUid = Binder.getCallingUid();
        UserRecord userRecord = mUserResourceTracker.getUserRecord(callingUid);
        int resourceId = mNextResourceId.getAndIncrement();
        FileDescriptor sockFd = null;
        try {
@@ -948,6 +986,7 @@ public class IpSecService extends IIpSecService.Stub {
            }

            sockFd = Os.socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
            mUidFdTagger.tag(sockFd, callingUid);

            if (port != 0) {
                Log.v(TAG, "Binding to port " + port);
+64 −0
Original line number Diff line number Diff line
@@ -27,6 +27,7 @@ import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
import static org.mockito.Matchers.anyInt;
import static org.mockito.Matchers.anyString;
import static org.mockito.Matchers.argThat;
import static org.mockito.Matchers.eq;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
@@ -40,10 +41,14 @@ import android.net.IpSecTransform;
import android.net.IpSecUdpEncapResponse;
import android.os.Binder;
import android.os.ParcelFileDescriptor;
import android.os.Process;
import android.support.test.filters.SmallTest;
import android.support.test.runner.AndroidJUnit4;
import android.system.ErrnoException;
import android.system.Os;
import android.system.StructStat;

import dalvik.system.SocketTagger;

import java.io.FileDescriptor;
import java.net.InetAddress;
@@ -56,6 +61,7 @@ import java.util.List;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.ArgumentMatcher;

/** Unit tests for {@link IpSecService}. */
@SmallTest
@@ -411,4 +417,62 @@ public class IpSecServiceTest {
            mIpSecService.releaseSecurityParameterIndex(spiResp.resourceId);
        }
    }

    @Test
    public void testUidFdtagger() throws Exception {
        SocketTagger actualSocketTagger = SocketTagger.get();

        try {
            FileDescriptor sockFd = Os.socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);

            // Has to be done after socket creation because BlockGuardOS calls tag on new sockets
            SocketTagger mockSocketTagger = mock(SocketTagger.class);
            SocketTagger.set(mockSocketTagger);

            mIpSecService.mUidFdTagger.tag(sockFd, Process.LAST_APPLICATION_UID);
            verify(mockSocketTagger).tag(eq(sockFd));
        } finally {
            SocketTagger.set(actualSocketTagger);
        }
    }

    /**
     * Checks if two file descriptors point to the same file.
     *
     * <p>According to stat.h documentation, the correct way to check for equivalent or duplicated
     * file descriptors is to check their inode and device. These two entries uniquely identify any
     * file.
     */
    private boolean fileDescriptorsEqual(FileDescriptor fd1, FileDescriptor fd2) {
        try {
            StructStat fd1Stat = Os.fstat(fd1);
            StructStat fd2Stat = Os.fstat(fd2);

            return fd1Stat.st_ino == fd2Stat.st_ino && fd1Stat.st_dev == fd2Stat.st_dev;
        } catch (ErrnoException e) {
            return false;
        }
    }

    @Test
    public void testOpenUdpEncapSocketTagsSocket() throws Exception {
        IpSecService.UidFdTagger mockTagger = mock(IpSecService.UidFdTagger.class);
        IpSecService testIpSecService =
                new IpSecService(mMockContext, mMockIpSecSrvConfig, mockTagger);

        IpSecUdpEncapResponse udpEncapResp =
                testIpSecService.openUdpEncapsulationSocket(0, new Binder());
        assertNotNull(udpEncapResp);
        assertEquals(IpSecManager.Status.OK, udpEncapResp.status);

        FileDescriptor sockFd = udpEncapResp.fileDescriptor.getFileDescriptor();
        ArgumentMatcher<FileDescriptor> fdMatcher =
                (argFd) -> {
                    return fileDescriptorsEqual(sockFd, argFd);
                };
        verify(mockTagger).tag(argThat(fdMatcher), eq(Os.getuid()));

        testIpSecService.closeUdpEncapsulationSocket(udpEncapResp.resourceId);
        udpEncapResp.fileDescriptor.close();
    }
}