diff options
3 files changed, 137 insertions, 19 deletions
diff --git a/services/core/java/com/android/server/net/watchlist/HarmfulCrcs.java b/services/core/java/com/android/server/net/watchlist/HarmfulCrcs.java new file mode 100644 index 000000000000..65a4b23225c2 --- /dev/null +++ b/services/core/java/com/android/server/net/watchlist/HarmfulCrcs.java @@ -0,0 +1,63 @@ +/* + * Copyright (C) 2017 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.net.watchlist; + +import com.android.internal.util.HexDump; + +import java.io.FileDescriptor; +import java.io.PrintWriter; +import java.util.Collections; +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +/** + * Helper class to store a set of harmful CRC32s in memory. + * TODO: Optimize memory usage using int array with binary search. + */ +class HarmfulCrcs { + + private final Set<Integer> mCrcSet; + + HarmfulCrcs(List<byte[]> digests) { + final HashSet<Integer> crcSet = new HashSet<>(); + final int size = digests.size(); + for (int i = 0; i < size; i++) { + byte[] bytes = digests.get(i); + if (bytes.length <= 4) { + int crc = 0; + for (byte b : bytes) { + // Remember byte is signed + crc = (crc << 8) | (b & 0xff); + } + crcSet.add(crc); + } + } + mCrcSet = Collections.unmodifiableSet(crcSet); + } + + public boolean contains(int crc) { + return mCrcSet.contains(crc); + } + + public void dump(FileDescriptor fd, PrintWriter pw, String[] args) { + for (int crc : mCrcSet) { + pw.println(HexDump.toHexString(crc)); + } + pw.println(""); + } +} diff --git a/services/core/java/com/android/server/net/watchlist/WatchlistConfig.java b/services/core/java/com/android/server/net/watchlist/WatchlistConfig.java index 8352ca60e362..7d06de335e84 100644 --- a/services/core/java/com/android/server/net/watchlist/WatchlistConfig.java +++ b/services/core/java/com/android/server/net/watchlist/WatchlistConfig.java @@ -18,7 +18,6 @@ package com.android.server.net.watchlist; import android.annotation.Nullable; import android.os.FileUtils; -import android.util.AtomicFile; import android.util.Log; import android.util.Slog; import android.util.Xml; @@ -66,11 +65,11 @@ class WatchlistConfig { } private static class CrcShaDigests { - final HarmfulDigests crc32Digests; - final HarmfulDigests sha256Digests; + public final HarmfulCrcs crc32s; + public final HarmfulDigests sha256Digests; - public CrcShaDigests(HarmfulDigests crc32Digests, HarmfulDigests sha256Digests) { - this.crc32Digests = crc32Digests; + CrcShaDigests(HarmfulCrcs crc32s, HarmfulDigests sha256Digests) { + this.crc32s = crc32s; this.sha256Digests = sha256Digests; } } @@ -140,9 +139,9 @@ class WatchlistConfig { } } parser.require(XmlPullParser.END_TAG, null, XmlTags.WATCHLIST_CONFIG); - mDomainDigests = new CrcShaDigests(new HarmfulDigests(crc32DomainList), + mDomainDigests = new CrcShaDigests(new HarmfulCrcs(crc32DomainList), new HarmfulDigests(sha256DomainList)); - mIpDigests = new CrcShaDigests(new HarmfulDigests(crc32IpList), + mIpDigests = new CrcShaDigests(new HarmfulCrcs(crc32IpList), new HarmfulDigests(sha256IpList)); Log.i(TAG, "Reload watchlist done"); } catch (IllegalStateException | NullPointerException | NumberFormatException | @@ -171,8 +170,8 @@ class WatchlistConfig { return false; } // First it does a quick CRC32 check. - final byte[] crc32 = getCrc32(domain); - if (!domainDigests.crc32Digests.contains(crc32)) { + final int crc32 = getCrc32(domain); + if (!domainDigests.crc32s.contains(crc32)) { return false; } // Now we do a slow SHA256 check. @@ -187,8 +186,8 @@ class WatchlistConfig { return false; } // First it does a quick CRC32 check. - final byte[] crc32 = getCrc32(ip); - if (!ipDigests.crc32Digests.contains(crc32)) { + final int crc32 = getCrc32(ip); + if (!ipDigests.crc32s.contains(crc32)) { return false; } // Now we do a slow SHA256 check. @@ -198,15 +197,11 @@ class WatchlistConfig { /** Get CRC32 of a string - * - * TODO: Review if we should use CRC32 or other algorithms */ - private byte[] getCrc32(String str) { + private int getCrc32(String str) { final CRC32 crc = new CRC32(); crc.update(str.getBytes()); - final long tmp = crc.getValue(); - return new byte[]{(byte) (tmp >> 24 & 255), (byte) (tmp >> 16 & 255), - (byte) (tmp >> 8 & 255), (byte) (tmp & 255)}; + return (int) crc.getValue(); } /** Get SHA256 of a string */ @@ -279,7 +274,7 @@ class WatchlistConfig { pw.println("Domain CRC32 digest list:"); // mDomainDigests won't go from non-null to null so it's safe if (mDomainDigests != null) { - mDomainDigests.crc32Digests.dump(fd, pw, args); + mDomainDigests.crc32s.dump(fd, pw, args); } pw.println("Domain SHA256 digest list:"); if (mDomainDigests != null) { @@ -288,7 +283,7 @@ class WatchlistConfig { pw.println("Ip CRC32 digest list:"); // mIpDigests won't go from non-null to null so it's safe if (mIpDigests != null) { - mIpDigests.crc32Digests.dump(fd, pw, args); + mIpDigests.crc32s.dump(fd, pw, args); } pw.println("Ip SHA256 digest list:"); if (mIpDigests != null) { diff --git a/services/tests/servicestests/src/com/android/server/net/watchlist/HarmfulCrcsTests.java b/services/tests/servicestests/src/com/android/server/net/watchlist/HarmfulCrcsTests.java new file mode 100644 index 000000000000..23f128115ea0 --- /dev/null +++ b/services/tests/servicestests/src/com/android/server/net/watchlist/HarmfulCrcsTests.java @@ -0,0 +1,60 @@ +/* + * Copyright (C) 2017 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.net.watchlist; + +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +import androidx.test.filters.SmallTest; +import androidx.test.runner.AndroidJUnit4; + +import com.android.internal.util.HexDump; + +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; + +import java.util.Arrays; + +/** + * runtest frameworks-services -c com.android.server.net.watchlist.HarmfulCrcTests + */ +@RunWith(AndroidJUnit4.class) +@SmallTest +public class HarmfulCrcsTests { + + private static final byte[] TEST_DIGEST = HexDump.hexStringToByteArray("AABBCCDD"); + + @Before + public void setUp() throws Exception { + } + + @After + public void tearDown() throws Exception { + } + + @Test + public void testHarmfulCrcs_setAndContains() throws Exception { + HarmfulCrcs harmfulCrcs = new HarmfulCrcs( + Arrays.asList(new byte[][] {TEST_DIGEST})); + assertTrue(harmfulCrcs.contains(0xaabbccdd)); + assertFalse(harmfulCrcs.contains(0xbbbbbbbb)); + assertFalse(harmfulCrcs.contains(0x01020304)); + assertFalse(harmfulCrcs.contains(0xddccbbaa)); + } +} |