Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to update an already created Wi-Fi configuration (or "UID XXX does not have permission to update [Wi-Fi] configuration error")?

I am developing an app which manages the Wi-Fi connections. My scenario is as follows: Let's say, the entire building has a Wi-Fi network named “testing-tls”. My app should be able to connect to only selected access points (based on BSSID or MAC ID). We use TLS authentication mechanism to verify the user (Custom CA Certificates).

I am able to establish a connection through the app, but failing when I try to connect to a different access point (different BSSID). Even though I created the Wi-Fi configuration programmatically, I am unable to update configuration after a first successful connection. I have tested my app in Oreo and Marshmallow. But, I am facing problems in Oreo (Not sure about Nougat). I am beginning to wonder if it is even possible to update the configuration once it is created.

These are the steps I am following:

1) Create a WifiConfiguration object

private WifiConfiguration createWifiConfiguration() {
    WifiConfiguration config = new WifiConfiguration();
    config.SSID = "\"testing-tls\"";
    config.priority = 1;
    config.status = WifiConfiguration.Status.ENABLED;
    config.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.WPA_EAP;
    config.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.IEEE8021X);
    config.enterpriseConfig.setIdentity(identityName);
    config.enterpriseConfig.setEapMethod(WifiEnterpriseConfig.Eap.TLS);

    PKCS12ParseInfo parseInfo;
    try {
        parseInfo = CertificateUtils.parsePKCS12Certificate(
                       certificateFilePath, identityPassword);
        if (parseInfo != null) {
            config.enterpriseConfig.setClientKeyEntry(parseInfo.getPrivateKey(),
                           parseInfo.getCertificate());
            return config;
        }
        return null;
    } catch (KeyStoreException | NoSuchAlgorithmException | IOException |
                   CertificateException | UnrecoverableKeyException | KeyManagementException e1) {
        Timber.e("WifiMonitorService, Fail to parse the input certificate: %s", e1.toString());
        Toast.makeText(this, "Error occurred", Toast.LENGTH_SHORT).show();
        return null;
    }
}

2) Try to establish a connection

private void establishWifiConnection(String result) {
    Timber.d("WifiMonitorService, establishing WifiConnection");

    WifiConfiguration configuration = createWifiConfiguration();
    if (configuration != null) {
        // result contains a mac id - 00:45:69:c5:34:f2
        configuration.BSSID = result;

        int networkId = wifiManager.addNetwork(configuration);
        if (networkId == -1) {
            networkId = getExistingNetworkId(wifiSsid);
            // Add a new configuration to the db
            if (networkId == -1) {
                Timber.e("Couldn't add network with SSID");
                Toast.makeText(this, "Wifi configuration error", Toast.LENGTH_SHORT).show();
                return;
            }
        }
        Timber.i("WifiMonitorService, # addNetwork returned: %d", networkId);
        wifiManager.saveConfiguration();
        wifiManager.enableNetwork(networkId, true);
        wifiManager.reassociate();
    } else {
        Toast.makeText(this, "Wifi conf Error occurred", Toast.LENGTH_SHORT).show();
    }
}

3) Get Exiting network id if present

private int getExistingNetworkId(String ssid) {
    List<WifiConfiguration> configuredNetworks  = 
    wifiManager.getConfiguredNetworks();
    if (configuredNetworks != null) {
        for (WifiConfiguration existingConfig : configuredNetworks) {
            if (existingConfig.SSID.equals("\"testing-tls\"")) {
                return existingConfig.networkId;
            }
        }
    }
    return -1;
}

Manifest Permissions are as follows:

<uses-permission android:name="android.permission.CAMERA" />
<uses-permission android:name="android.permission.VIBRATE" />
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
<uses-permission android:name="android.permission.CHANGE_WIFI_STATE" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.CHANGE_NETWORK_STATE" />
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.WAKE_LOCK" />

<uses-permission android:name="android.permission.OVERRIDE_WIFI_CONFIG" />
<uses-permission android:name="android.permission.NETWORK_SETTINGS" />

<uses-feature android:name="android.hardware.wifi" />
<uses-feature android:name="android.hardware.camera" />

<permission
   android:name="android.permission.INTERACT_ACROSS_USERS"
   android:protectionLevel="signature" />

Error: I always get UID 10189 does not have permission to update configuration error in Oreo

2018-12-28 12:23:44.571 1320-1847/? E/WifiConfigManager: UID 10189 does not have permission to update configuration "testing-tls"WPA_EAP
2018-12-28 12:23:44.571 1320-1847/? I/WifiStateMachine: connectToUserSelectNetwork Allowing uid 10189 with insufficient permissions to connect=1

Investigation

After digging through the source code, I found the implementation of the addOrUpdateNetwork method in WifiConfigManager class.

The implementation addOrUpdateNetwork, in tag android_8.0.0_r21 (Build number OPD1.170816.010) is as follows:

  • First check if we already have a network with the provided network id or configKey
  • If no existing network found, validate the configuration, and add.
  • If existing network found, update the network configuration. Before that, check whether app has necesssary permissions to update the network.

AddOrUpdateNetwork internally calls a function called canModifyNetwork:

/**
 * Checks if |uid| has permission to modify the provided configuration.
 *
 * @param config         WifiConfiguration object corresponding to the network to be modified.
 * @param uid            UID of the app requesting the modification.
 * @param ignoreLockdown Ignore the configuration lockdown checks for connection attempts.
 */
private boolean canModifyNetwork(WifiConfiguration config, int uid, boolean ignoreLockdown) {
    // Passpoint configurations are generated and managed by PasspointManager. They can be
    // added by either PasspointNetworkEvaluator (for auto connection) or Settings app
    // (for manual connection), and need to be removed once the connection is completed.
    // Since it is "owned" by us, so always allow us to modify them.
    if (config.isPasspoint() && uid == Process.WIFI_UID) {
        return true;
    }
    // EAP-SIM/AKA/AKA' network needs framework to update the anonymous identity provided
    // by authenticator back to the WifiConfiguration object.
    // Since it is "owned" by us, so always allow us to modify them.
    if (config.enterpriseConfig != null
            && uid == Process.WIFI_UID
            && TelephonyUtil.isSimEapMethod(config.enterpriseConfig.getEapMethod())) {
        return true;
    }
    final DevicePolicyManagerInternal dpmi = LocalServices.getService(
            DevicePolicyManagerInternal.class);
    final boolean isUidDeviceOwner = dpmi != null && dpmi.isActiveAdminWithPolicy(uid,
            DeviceAdminInfo.USES_POLICY_DEVICE_OWNER);
    // If |uid| corresponds to the device owner, allow all modifications.
    if (isUidDeviceOwner) {
        return true;
    }
    final boolean isCreator = (config.creatorUid == uid);
    // Check if the |uid| holds the |NETWORK_SETTINGS| permission if the caller asks us to
    // bypass the lockdown checks.
    if (ignoreLockdown) {
        return mWifiPermissionsUtil.checkNetworkSettingsPermission(uid);
    }
    // Check if device has DPM capability. If it has and |dpmi| is still null, then we
    // treat this case with suspicion and bail out.
    if (mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_DEVICE_ADMIN)
            && dpmi == null) {
        Log.w(TAG, "Error retrieving DPMI service.");
        return false;
    }
    // WiFi config lockdown related logic. At this point we know uid is NOT a Device Owner.
    final boolean isConfigEligibleForLockdown = dpmi != null && dpmi.isActiveAdminWithPolicy(
            config.creatorUid, DeviceAdminInfo.USES_POLICY_DEVICE_OWNER);
    if (!isConfigEligibleForLockdown) {
        return isCreator || mWifiPermissionsUtil.checkNetworkSettingsPermission(uid);
    }
    final ContentResolver resolver = mContext.getContentResolver();
    final boolean isLockdownFeatureEnabled = Settings.Global.getInt(resolver,
            Settings.Global.WIFI_DEVICE_OWNER_CONFIGS_LOCKDOWN, 0) != 0;
    return !isLockdownFeatureEnabled
            && mWifiPermissionsUtil.checkNetworkSettingsPermission(uid);
}

As far I can see, only the following uids have access to modify network configurations.

  1. System app
  2. Device owner
  3. Creator (It is failing for some reason)

I am getting the same behaviour in these two phones.

  1. Pixel 2 (Oreo 8.0.0)
  2. Samsung J8 (Oreo 8.0.0)

Additionally, Samsung J8 always shows this warning:

CertificateException: java.security.cert.CertPathValidatorException: Trust anchor for certificate path not found

Question:

  • How can an already created Wi-Fi configuration be updated programmatically?
    • Is it even possible to update the Wi-Fi configuration once it is created in the Wi-Fi's internal database?
    • Is it mandatory to disconnect Wi-Fi before updating or enabling the configuration?
like image 892
Sarweshkumar C R Avatar asked Dec 28 '18 09:12

Sarweshkumar C R


1 Answers

After digging the source code, finally got answers to my questions.

Question: Is it possible to update the configurations once it is created?

Answer: Yes, Android os allows you to update the configuration created from your application. When I called wifiManager.addNetwork(), in the log window the following statements were printed.

2019-01-04 12:23:16.168 1328-3114/? I/addOrUpdateNetwork:  uid = 10190 SSID "testing-tls" nid=-1
2019-01-04 12:23:16.169 1328-1851/? V/WifiConfigManager: Adding/Updating network testing-tls
2019-01-04 12:23:16.193 1328-1851/? D/WifiConfigManager: addOrUpdateNetworkInternal: added/updated config. netId=6 configKey="testing-tls"WPA_EAP uid=10190 name=in.ac.iisc.wifimonitoring vendorAP=false hiddenSSID=false autoReconnect=1
2019-01-04 12:23:16.204 1328-1851/? D/WifiConfigStore: Writing to stores completed in 7 ms.
2019-01-04 12:23:16.205 1328-1851/? D/WifiIssueDetector: report htime=2019-01-04_12:23:16 time=1546584796205 rid=105 callBy=in.ac.iisc.wifimonitoring apiName=addOrUpdateNetwork netid=6 callUid=in.ac.iisc.wifimonitoring
2019-01-04 12:23:16.206 15873-15873/in.ac.iisc.wifimonitoring I/WifiMonitorService: WifiMonitorService, #addNetwork returned: 6

Question: what is "UID 10189 does not have permission to update configuration error" in Oreo?

Answer: After updating the configuration, we have to call wifimanager.enableNetwork() method to establish a connection to the desired access point. Workflow of EnableNetwork() is as follows.

  1. WifiManager.enableNetwork() internally calls SyncEnableNetwork() method of WifiStateMachine class.

WifiStateMachine is the core class which tracks the state of Wifi connectivity. All event handling and all changes in connectivity state are initiated in this class.

  1. SyncEnableNetwork() method sends CMD_ENABLE_NETWORK message to ConnectModeState class.

  2. If disableOthers is true, call connectToUserSelectNetwork() method and passes networkId, calling UID and force reconnect [always false - hardcoded value] as arguments.

  3. If an app does not have all the necessary permissions to update the configuration [uses checkAndUpdateLastUid() method in WifiConfigManager class - returns true only for system settings/sysui app] or if enabling of a network is failed, the following statements will be printed.

2018-12-28 12:23:44.571 1320-1847/? E/WifiConfigManager: UID 10189 does not have permission to update configuration "testing-tls"WPA_EAP
2018-12-28 12:23:44.571 1320-1847/? I/WifiStateMachine: connectToUserSelectNetwork Allowing uid 10189 with insufficient permissions to connect=1

Note: checkAndUpdateLastUid() method has been renamed to updateLastConnectUid() in Android Pie. They have slightly modified its functionality as well.

For more information, please refer to the below diagram [I am no good in drawing flowcharts. Please bear with me or suggest if any changes required].

enable network

Question 3: Is it mandatory to disconnect wifi before updating or enabling the configuration?

Answer: OS triggers a connection/reconnection to a network under the following conditions:

  1. Selected network id must be different from currently connected network id.
  2. If forceReconnect argument is true, Android prepares for reconnection [True only for system settings/sysui app].

Since developer apps do not have the ability to a force connection, we should disconnect the wifi in order to connect/reconnect to the network after updating the configuration.

Hope this will help others.

like image 60
Sarweshkumar C R Avatar answered Nov 20 '22 00:11

Sarweshkumar C R