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

Commit 4b7c2bb6 authored by Ted Bauer's avatar Ted Bauer
Browse files

For aconfig flag writes, check write permission

Flag: com.android.providers.settings.check_root_and_read_only
Bug: 342636474
Test: m
Change-Id: I422b076bf8e44bbaa1185822ef99dbb4ff588084
parent acdbc9c1
Loading
Loading
Loading
Loading
+1 −0
Original line number Original line Diff line number Diff line
@@ -32,6 +32,7 @@ android_library {
        "unsupportedappusage",
        "unsupportedappusage",
    ],
    ],
    static_libs: [
    static_libs: [
        "aconfig_device_paths_java",
        "aconfig_new_storage_flags_lib",
        "aconfig_new_storage_flags_lib",
        "aconfigd_java_utils",
        "aconfigd_java_utils",
        "aconfig_demo_flags_java_lib",
        "aconfig_demo_flags_java_lib",
+108 −14
Original line number Original line Diff line number Diff line
@@ -20,8 +20,10 @@ import static android.provider.Settings.Config.SYNC_DISABLED_MODE_NONE;
import static android.provider.Settings.Config.SYNC_DISABLED_MODE_PERSISTENT;
import static android.provider.Settings.Config.SYNC_DISABLED_MODE_PERSISTENT;
import static android.provider.Settings.Config.SYNC_DISABLED_MODE_UNTIL_REBOOT;
import static android.provider.Settings.Config.SYNC_DISABLED_MODE_UNTIL_REBOOT;


import android.aconfig.Aconfig.parsed_flag;
import android.aconfig.DeviceProtos;
import android.aconfig.Aconfig.parsed_flags;
import android.aconfig.nano.Aconfig;
import android.aconfig.nano.Aconfig.parsed_flag;
import android.aconfig.nano.Aconfig.parsed_flags;
import android.annotation.SuppressLint;
import android.annotation.SuppressLint;
import android.app.ActivityManager;
import android.app.ActivityManager;
import android.content.AttributionSource;
import android.content.AttributionSource;
@@ -42,7 +44,6 @@ import android.provider.Settings.Config.SyncDisabledMode;
import android.provider.UpdatableDeviceConfigServiceReadiness;
import android.provider.UpdatableDeviceConfigServiceReadiness;
import android.util.Slog;
import android.util.Slog;


import com.android.internal.pm.pkg.component.AconfigFlags;
import com.android.internal.util.FastPrintWriter;
import com.android.internal.util.FastPrintWriter;


import java.io.File;
import java.io.File;
@@ -136,11 +137,8 @@ public final class DeviceConfigService extends Binder {
                    continue;
                    continue;
                }
                }


                for (parsed_flag flag : parsedFlags.getParsedFlagList()) {
                for (parsed_flag flag : parsedFlags.parsedFlag) {
                    String namespace = flag.getNamespace();
                    nameSet.add(flag.namespace + "/" + flag.package_ + "." + flag.name);
                    String packageName = flag.getPackage();
                    String name = flag.getName();
                    nameSet.add(namespace + "/" + packageName + "." + name);
                }
                }
            }
            }
        } catch (IOException e) {
        } catch (IOException e) {
@@ -169,6 +167,7 @@ public final class DeviceConfigService extends Binder {


    static final class MyShellCommand extends ShellCommand {
    static final class MyShellCommand extends ShellCommand {
        final SettingsProvider mProvider;
        final SettingsProvider mProvider;
        private HashMap<String, parsed_flag> mAconfigParsedFlags;


        enum CommandVerb {
        enum CommandVerb {
            GET,
            GET,
@@ -186,6 +185,51 @@ public final class DeviceConfigService extends Binder {


        MyShellCommand(SettingsProvider provider) {
        MyShellCommand(SettingsProvider provider) {
            mProvider = provider;
            mProvider = provider;

            if (Flags.checkRootAndReadOnly()) {
                List<parsed_flag> parsedFlags;
                try {
                    parsedFlags = DeviceProtos.loadAndParseFlagProtos();
                } catch (IOException e) {
                    throw new IllegalStateException("failed to parse aconfig protos");
                }

                mAconfigParsedFlags = new HashMap();
                for (parsed_flag flag : parsedFlags) {
                    mAconfigParsedFlags.put(flag.package_ + "." + flag.name, flag);
                }
            }
        }

        /**
         * Return true if a flag is aconfig.
         */
        private boolean isAconfigFlag(String name) {
            return mAconfigParsedFlags.get(name) != null;
        }

        /**
         * Return true if a flag is both aconfig and read-only.
         *
         * @return true if a flag is both aconfig and read-only
         */
        private boolean isReadOnly(String name) {
            parsed_flag flag = mAconfigParsedFlags.get(name);
            if (flag != null) {
                if (flag.permission == Aconfig.READ_ONLY) {
                    return true;
                }
            }
            return false;
        }

        /**
         * Return true if the calling process is root.
         *
         * @return true if a flag is aconfig, and the calling process is root
         */
        private boolean isRoot() {
            return Binder.getCallingUid() == Process.ROOT_UID;
        }
        }


      public static HashMap<String, String> getAllFlags(IContentProvider provider) {
      public static HashMap<String, String> getAllFlags(IContentProvider provider) {
@@ -414,21 +458,71 @@ public final class DeviceConfigService extends Binder {
                    pout.println(DeviceConfig.getProperty(namespace, key));
                    pout.println(DeviceConfig.getProperty(namespace, key));
                    break;
                    break;
                case PUT:
                case PUT:
                    if (Flags.checkRootAndReadOnly()) {
                        if (isAconfigFlag(key)) {
                            if (!isRoot()) {
                                pout.println("Error: must be root to write aconfig flag");
                                break;
                            }

                            if (isReadOnly(key)) {
                                pout.println("Error: cannot write read-only flag");
                                break;
                            }
                        }
                    }

                    DeviceConfig.setProperty(namespace, key, value, makeDefault);
                    DeviceConfig.setProperty(namespace, key, value, makeDefault);
                    break;
                    break;
                case OVERRIDE:
                case OVERRIDE:
                    AconfigFlags.Permission permission =
                    if (Flags.checkRootAndReadOnly()) {
                            (new AconfigFlags()).getFlagPermission(key);
                        if (isAconfigFlag(key)) {
                    if (permission == AconfigFlags.Permission.READ_ONLY) {
                            if (!isRoot()) {
                        pout.println("cannot override read-only flag " + key);
                                pout.println("Error: must be root to write aconfig flag");
                    } else {
                                break;
                        DeviceConfig.setLocalOverride(namespace, key, value);
                            }

                            if (isReadOnly(key)) {
                                pout.println("Error: cannot write read-only flag");
                                break;
                            }
                        }
                        }
                    }

                    DeviceConfig.setLocalOverride(namespace, key, value);
                    break;
                    break;
                case CLEAR_OVERRIDE:
                case CLEAR_OVERRIDE:
                    if (Flags.checkRootAndReadOnly()) {
                        if (isAconfigFlag(key)) {
                            if (!isRoot()) {
                                pout.println("Error: must be root to write aconfig flag");
                                break;
                            }

                            if (isReadOnly(key)) {
                                pout.println("Error: cannot write read-only flag");
                                break;
                            }
                        }
                    }

                    DeviceConfig.clearLocalOverride(namespace, key);
                    DeviceConfig.clearLocalOverride(namespace, key);
                    break;
                    break;
                case DELETE:
                case DELETE:
                    if (Flags.checkRootAndReadOnly()) {
                        if (isAconfigFlag(key)) {
                            if (!isRoot()) {
                                pout.println("Error: must be root to write aconfig flag");
                                break;
                            }

                            if (isReadOnly(key)) {
                                pout.println("Error: cannot write read-only flag");
                                break;
                            }
                        }
                    }

                    pout.println(delete(iprovider, namespace, key)
                    pout.println(delete(iprovider, namespace, key)
                            ? "Successfully deleted " + key + " from " + namespace
                            ? "Successfully deleted " + key + " from " + namespace
                            : "Failed to delete " + key + " from " + namespace);
                            : "Failed to delete " + key + " from " + namespace);
+11 −0
Original line number Original line Diff line number Diff line
@@ -70,3 +70,14 @@ flag {
        purpose: PURPOSE_BUGFIX
        purpose: PURPOSE_BUGFIX
    }
    }
}
}

flag {
    name: "check_root_and_read_only"
    namespace: "core_experiments_team_internal"
    description: "Check root and aconfig flag permissions in adb shell device_config commands."
    bug: "342636474"
    is_fixed_read_only: true
    metadata {
        purpose: PURPOSE_BUGFIX
    }
}