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

Commit 309e071b authored by Eran Messeri's avatar Eran Messeri
Browse files

Private DNS connectivity check

Implement connectivity check to DNS-over-TLS servers, checking that the
RFC-defined port on the host is reachable and a TLS handshake can be
performed.

Bug: 112982691
Test: atest com.android.cts.devicepolicy.DeviceOwnerTest#testPrivateDnsPolicy
Change-Id: I1eb4ec201d7e096b969b7bc2bcba271f99de2d2f
parent db58c205
Loading
Loading
Loading
Loading
+4 −1
Original line number Diff line number Diff line
@@ -6663,7 +6663,7 @@ package android.app.admin {
    method public void setDelegatedScopes(android.content.ComponentName, java.lang.String, java.util.List<java.lang.String>);
    method public void setDeviceOwnerLockScreenInfo(android.content.ComponentName, java.lang.CharSequence);
    method public void setEndUserSessionMessage(android.content.ComponentName, java.lang.CharSequence);
    method public void setGlobalPrivateDns(android.content.ComponentName, int, java.lang.String);
    method public int setGlobalPrivateDns(android.content.ComponentName, int, java.lang.String);
    method public void setGlobalSetting(android.content.ComponentName, java.lang.String, java.lang.String);
    method public void setKeepUninstalledPackages(android.content.ComponentName, java.util.List<java.lang.String>);
    method public boolean setKeyPairCertificate(android.content.ComponentName, java.lang.String, java.util.List<java.security.cert.Certificate>, boolean);
@@ -6844,6 +6844,9 @@ package android.app.admin {
    field public static final int PRIVATE_DNS_MODE_OPPORTUNISTIC = 2; // 0x2
    field public static final int PRIVATE_DNS_MODE_PROVIDER_HOSTNAME = 3; // 0x3
    field public static final int PRIVATE_DNS_MODE_UNKNOWN = 0; // 0x0
    field public static final int PRIVATE_DNS_SET_ERROR_FAILURE_SETTING = 2; // 0x2
    field public static final int PRIVATE_DNS_SET_ERROR_HOST_NOT_SERVING = 1; // 0x1
    field public static final int PRIVATE_DNS_SET_SUCCESS = 0; // 0x0
    field public static final int RESET_PASSWORD_DO_NOT_ASK_CREDENTIALS_ON_BOOT = 2; // 0x2
    field public static final int RESET_PASSWORD_REQUIRE_ENTRY = 1; // 0x1
    field public static final int SKIP_SETUP_WIZARD = 1; // 0x1
+56 −3
Original line number Diff line number Diff line
@@ -49,6 +49,8 @@ import android.content.pm.PackageManager.NameNotFoundException;
import android.content.pm.ParceledListSlice;
import android.content.pm.UserInfo;
import android.graphics.Bitmap;
import android.net.NetworkUtils;
import android.net.PrivateDnsConnectivityChecker;
import android.net.ProxyInfo;
import android.net.Uri;
import android.os.Binder;
@@ -79,6 +81,7 @@ import android.security.keystore.StrongBoxUnavailableException;
import android.service.restrictions.RestrictionsReceiver;
import android.telephony.TelephonyManager;
import android.telephony.data.ApnSetting;
import android.text.TextUtils;
import android.util.ArraySet;
import android.util.Log;

@@ -1973,6 +1976,35 @@ public class DevicePolicyManager {
    @Retention(RetentionPolicy.SOURCE)
    public @interface InstallUpdateCallbackErrorConstants {}

    /**
     * The selected mode has been set successfully. If the mode is
     * {@code PRIVATE_DNS_MODE_PROVIDER_HOSTNAME} then it implies the supplied host is valid
     * and reachable.
     */
    public static final int PRIVATE_DNS_SET_SUCCESS = 0;

    /**
     * If the {@code privateDnsHost} provided was of a valid hostname but that host was found
     * to not support DNS-over-TLS.
     */
    public static final int PRIVATE_DNS_SET_ERROR_HOST_NOT_SERVING = 1;

    /**
     * General failure to set the Private DNS mode, not due to one of the reasons listed above.
     */
    public static final int PRIVATE_DNS_SET_ERROR_FAILURE_SETTING = 2;

    /**
     * @hide
     */
    @IntDef(prefix = {"PRIVATE_DNS_SET_"}, value = {
            PRIVATE_DNS_SET_SUCCESS,
            PRIVATE_DNS_SET_ERROR_HOST_NOT_SERVING,
            PRIVATE_DNS_SET_ERROR_FAILURE_SETTING
    })
    @Retention(RetentionPolicy.SOURCE)
    public @interface SetPrivateDnsModeResultConstants {}

    /**
     * Return true if the given administrator component is currently active (enabled) in the system.
     *
@@ -9839,6 +9871,16 @@ public class DevicePolicyManager {
     * Sets the global Private DNS mode and host to be used.
     * May only be called by the device owner.
     *
     * <p>Note that in case a Private DNS resolver is specified, the method is blocking as it
     * will perform a connectivity check to the resolver, to ensure it is valid. Because of that,
     * the method should not be called on any thread that relates to user interaction, such as the
     * UI thread.
     *
     * <p>In case a VPN is used in conjunction with Private DNS resolver, the Private DNS resolver
     * must be reachable both from within and outside the VPN. Otherwise, the device may lose
     * the ability to resolve hostnames as system traffic to the resolver may not go through the
     * VPN.
     *
     * @param admin which {@link DeviceAdminReceiver} this request is associated with.
     * @param mode Which mode to set - either {@code PRIVATE_DNS_MODE_OPPORTUNISTIC} or
     *             {@code PRIVATE_DNS_MODE_PROVIDER_HOSTNAME}.
@@ -9848,6 +9890,9 @@ public class DevicePolicyManager {
     * @param privateDnsHost The hostname of a server that implements DNS over TLS (RFC7858), if
     *                       {@code PRIVATE_DNS_MODE_PROVIDER_HOSTNAME} was specified as the mode,
     *                       null otherwise.
     *
     * @return One of the values in {@link SetPrivateDnsModeResultConstants}.
     *
     * @throws IllegalArgumentException in the following cases: if a {@code privateDnsHost} was
     * provided but the mode was not {@code PRIVATE_DNS_MODE_PROVIDER_HOSTNAME}, if the mode
     * specified was {@code PRIVATE_DNS_MODE_PROVIDER_HOSTNAME} but {@code privateDnsHost} does
@@ -9855,15 +9900,23 @@ public class DevicePolicyManager {
     *
     * @throws SecurityException if the caller is not the device owner.
     */
    public void setGlobalPrivateDns(@NonNull ComponentName admin,
    public int setGlobalPrivateDns(@NonNull ComponentName admin,
            @PrivateDnsMode int mode, @Nullable String privateDnsHost) {
        throwIfParentInstance("setGlobalPrivateDns");

        if (mService == null) {
            return;
            return PRIVATE_DNS_SET_ERROR_FAILURE_SETTING;
        }

        if (mode == PRIVATE_DNS_MODE_PROVIDER_HOSTNAME && !TextUtils.isEmpty(privateDnsHost)
                && NetworkUtils.isWeaklyValidatedHostname(privateDnsHost)) {
            if (!PrivateDnsConnectivityChecker.canConnectToPrivateDnsServer(privateDnsHost)) {
                return PRIVATE_DNS_SET_ERROR_HOST_NOT_SERVING;
            }
        }

        try {
            mService.setGlobalPrivateDns(admin, mode, privateDnsHost);
            return mService.setGlobalPrivateDns(admin, mode, privateDnsHost);
        } catch (RemoteException re) {
            throw re.rethrowFromSystemServer();
        }
+1 −1
Original line number Diff line number Diff line
@@ -415,7 +415,7 @@ interface IDevicePolicyManager {

    boolean isMeteredDataDisabledPackageForUser(in ComponentName admin, String packageName, int userId);

    void setGlobalPrivateDns(in ComponentName admin, int mode, in String privateDnsHost);
    int setGlobalPrivateDns(in ComponentName admin, int mode, in String privateDnsHost);
    int getGlobalPrivateDnsMode(in ComponentName admin);
    String getGlobalPrivateDnsHost(in ComponentName admin);

+64 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2018 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 android.net;

import android.annotation.NonNull;
import android.util.Log;

import java.io.IOException;
import java.net.InetSocketAddress;

import javax.net.SocketFactory;
import javax.net.ssl.SSLSocket;
import javax.net.ssl.SSLSocketFactory;

/**
 * Class for testing connectivity to DNS-over-TLS servers.
 * {@hide}
 */
public class PrivateDnsConnectivityChecker {
    private static final String TAG = "NetworkUtils";

    private static final int PRIVATE_DNS_PORT = 853;
    private static final int CONNECTION_TIMEOUT_MS = 5000;

    private PrivateDnsConnectivityChecker() { }

    /**
     * checks that a provided host can perform a TLS handshake on port 853.
     * @param hostname host to connect to.
     */
    public static boolean canConnectToPrivateDnsServer(@NonNull String hostname) {
        final SocketFactory factory = SSLSocketFactory.getDefault();
        TrafficStats.setThreadStatsTag(TrafficStats.TAG_SYSTEM_APP);

        try (SSLSocket socket = (SSLSocket) factory.createSocket()) {
            socket.setSoTimeout(CONNECTION_TIMEOUT_MS);
            socket.connect(new InetSocketAddress(hostname, PRIVATE_DNS_PORT));
            if (!socket.isConnected()) {
                Log.w(TAG, String.format("Connection to %s failed.", hostname));
                return false;
            }
            socket.startHandshake();
            Log.w(TAG, String.format("TLS handshake to %s succeeded.", hostname));
            return true;
        } catch (IOException e) {
            Log.w(TAG, String.format("TLS handshake to %s failed.", hostname), e);
            return false;
        }
    }
}
+2 −1
Original line number Diff line number Diff line
@@ -81,7 +81,8 @@ abstract class BaseIDevicePolicyManager extends IDevicePolicyManager.Stub {
    }

    @Override
    public void setGlobalPrivateDns(ComponentName who, int mode, String privateDnsHost) {
    public int setGlobalPrivateDns(ComponentName who, int mode, String privateDnsHost) {
        return DevicePolicyManager.PRIVATE_DNS_SET_ERROR_FAILURE_SETTING;
    }

    @Override
Loading