Commits

robert craig  committed daf40f5

Add code to allow mmac policy updates.

New code allows eops.xml and mac_permissions.xml
updates via the buildbundle route b/c of new tools
under external/sepolicy. New code hooks into the
ConfigUpdateInstallReceiver are then used by to
deliver the new policy bundles.

  • Participants
  • Parent commits 0b0f3fa

Comments (0)

Files changed (6)

File res/values/strings.xml

     <!-- Update Categories -->
     <string name="update_category_title">Update Options</string>
     <string name="config_update_title">SELinux Policy</string>
+    <string name="config_update_mmac_title">Middleware Policy</string>
     <string name="config_kernel_reload_title">Reload Kernel Policies</string>
-    <string name="config_mmac_reload_title">Reload MMAC Policies</string>
+    <string name="config_mmac_install_reload_title">Reload Install MAC Policy</string>
+    <string name="config_eop_reload_title">Reload Eops Policy</string>
     <string name="selinux_policy_reload">Reload Selinux Policies</string>
+    <string name="mmac_policy_reload">Reload MMAC Policies</string>
 
 </resources>

File res/xml/config_update_fragment.xml

                 android:title="@string/config_kernel_reload_title"
                 android:key="key_kernel_reload" />
 
+<!--
     <Preference android:id="@+id/mmac_config_reload"
                 android:title="@string/config_mmac_reload_title"
                 android:key="key_mmac_reload" />
-
+-->
     </PreferenceCategory>
 
 </PreferenceScreen>

File res/xml/config_update_mmac_fragment.xml

+<?xml version="1.0" encoding="utf-8"?>
+<PreferenceScreen
+    xmlns:android="http://schemas.android.com/apk/res/android">
+
+  <PreferenceCategory
+      android:title="@string/mmac_policy_reload">
+
+    <Preference android:id="@+id/mac_perms_config_reload"
+                android:title="@string/config_mmac_install_reload_title"
+                android:key="key_mac_perms_reload" />
+
+    <Preference android:id="@+id/eop_config_reload"
+                android:title="@string/config_eop_reload_title"
+                android:key="key_eops_reload" />
+
+  </PreferenceCategory>
+
+</PreferenceScreen>

File res/xml/enabled_headers.xml

         android:fragment="com.android.seandroid_admin.ConfigUpdateFragment"
         android:title="@string/config_update_title" />
 
+    <header android:id="@+id/config_update_mmac"
+        android:fragment="com.android.seandroid_admin.ConfigUpdateMmacFragment"
+        android:title="@string/config_update_mmac_title" />
+
 </preference-headers>

File src/com/android/seandroid_admin/ConfigUpdateFragment.java

 
     // Preference elements to toggle policy values.
     private Preference mReloadSELinux;
-    private Preference mReloadMmac;
     private CheckBoxPreference mEnforcingCheckbox;
 
     // Keys from xml layout. Refer to preference elements.
             }
         });
 
-        mReloadMmac = getPreferenceScreen().findPreference(KEY_RELOAD_MMAC);
-        mReloadMmac.setOnPreferenceClickListener(new OnPreferenceClickListener() {
-            @Override
-            public boolean onPreferenceClick(Preference preference) {
-                return true;
-            }
-        });
-
         String mes = "Load from: ";
         if (cacheDir == null || extDir == null) {
             String where = (extDir == null) ? "sdcard" : "cache";
             mes += mZipFile.getPath();
         }
         mReloadSELinux.setSummary(mes);
-
-        mReloadMmac.setEnabled(false);
-        mReloadMmac.setSelectable(false);
     }
 
     private void atomicWriteToFile(File dir, File file, byte[] content) throws IOException {

File src/com/android/seandroid_admin/ConfigUpdateMmacFragment.java

+package com.android.seandroid_admin;
+
+import android.app.Activity;
+import android.content.Context;
+import android.content.Intent;
+import android.content.SharedPreferences;
+import android.content.res.AssetManager;
+import android.os.Bundle;
+import android.os.Environment;
+import android.preference.CheckBoxPreference;
+import android.preference.Preference;
+import android.preference.PreferenceFragment;
+import android.preference.Preference.OnPreferenceChangeListener;
+import android.preference.Preference.OnPreferenceClickListener;
+import android.provider.Settings;
+import android.util.Log;
+import android.widget.Toast;
+
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Scanner;
+import java.util.zip.ZipEntry;
+import java.util.zip.ZipInputStream;
+
+import java.io.ByteArrayOutputStream;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.InputStream;
+import java.io.IOException;
+import java.nio.ByteBuffer;
+
+import com.google.common.io.Closeables;
+import com.google.common.io.Files;
+import com.google.common.io.ByteStreams;
+
+import com.android.seandroid_admin.R;
+
+/**
+ * Mock sepolicy updates using new ConfigUpdateInstallReceiver mechanism.
+ * Code will help explore the new ConfigUpdate mechanism regarding mmac
+ * policies. Ideally, you'll want to use the host based build*bundle
+ * tool to first generate the zip file that will contain both the
+ * approved OTA bundle format as well as the metadata file. The zip
+ * is expected to be pushed to /sdcard. In a production environment
+ * you'll clearly want to change the location as well as deal with
+ * other various concurrency issues outlined below.
+ */
+public class ConfigUpdateMmacFragment extends PreferenceFragment implements
+        OnPreferenceChangeListener, OnPreferenceClickListener {
+
+    private static String TAG = "SEAdminConfigUpdateMmacFragment";
+
+    // Connection back out to the main fragment.
+    protected SEAndroidAdminActivity mActivity;
+
+    @Override
+    public void onAttach(Activity activity) {
+        super.onAttach(activity);
+        mActivity = (SEAndroidAdminActivity) activity;
+    }
+
+    @Override
+    public void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+    }
+
+    @Override
+    public void onActivityCreated(Bundle savedInstanceState) {
+        super.onActivityCreated(savedInstanceState);
+
+        addPreferencesFromResource(R.xml.config_update_mmac_fragment);
+
+        new MacInstallConfigUpdate();
+        new EopsConfigUpdate();
+    }
+
+    @Override
+    public void onStart() {
+        super.onStart();
+    }
+
+    @Override
+    public void onResume() {
+        super.onResume();
+    }
+
+    @Override
+    public boolean onPreferenceChange(Preference preference, Object newValue) {
+        return true;
+    }
+
+    @Override
+    public boolean onPreferenceClick(Preference preference) {
+        return true;
+    }
+
+    private class ConfigUpdate {
+
+        private File mBundleFile = null;
+        private File mMetadataFile = null;
+        private File mZipFile = null;
+
+        private final Preference mReload;
+        private final String mKeyReload;
+        private final String mIntent;
+
+        private final String mBundleFilePath;
+        private final String mMetadataFilePath;
+        private final String mBundleZipPath;
+
+        private static final String ZIP_METADATA_ENTRY = "update_bundle_metadata";
+        private static final String ZIP_BUNDLE_ENTRY = "update_bundle";
+
+        private final Map<String, File> mZipMap = new HashMap<String, File>(2);
+
+        ConfigUpdate(String name, String intent) {
+            mKeyReload = "key_" + name + "_reload";
+            mBundleFilePath = name + "_bundle";
+            mMetadataFilePath = name + "_bundle_metadata";
+            mBundleZipPath = name + "_bundle.zip";
+
+            mIntent = "android.intent.action." + intent;
+
+            final File cacheDir = Environment.getDownloadCacheDirectory();
+            if (cacheDir != null) {
+                mBundleFile = new File(cacheDir, mBundleFilePath);
+                mMetadataFile = new File(cacheDir, mMetadataFilePath);
+            } else {
+                Log.e(TAG, "Download cache directory not found. " +
+                      "Policy updates won't work.");
+            }
+
+            mZipMap.put(ZIP_BUNDLE_ENTRY, mBundleFile);
+            mZipMap.put(ZIP_METADATA_ENTRY, mMetadataFile);
+
+            final File extDir = Environment.getExternalStorageDirectory();
+            if (extDir != null) {
+                mZipFile = new File(extDir, mBundleZipPath);
+            } else {
+                Log.e(TAG, "External storage directory not found. " +
+                      "Policy updates won't work.");
+            }
+
+            mReload = getPreferenceScreen().findPreference(mKeyReload);
+            mReload.setOnPreferenceClickListener(new OnPreferenceClickListener() {
+                @Override
+                public boolean onPreferenceClick(Preference preference) {
+                    Log.d(TAG, "Loading of policy bundle requested.");
+
+                    FileInputStream fis = null;
+                    ZipInputStream zis = null;
+                    try {
+                        fis = new FileInputStream(mZipFile);
+                        zis = new ZipInputStream(fis);
+                        ZipEntry ze;
+                        while ((ze = zis.getNextEntry()) != null) {
+                            String name = ze.getName();
+                            if (mZipMap.containsKey(name)) {
+                                File output = mZipMap.get(name);
+                                try {
+                                    ByteArrayOutputStream baos = new ByteArrayOutputStream();
+                                    byte[] data = new byte[1024];
+                                    int n;
+                                    while ((n = zis.read(data)) != -1) {
+                                        baos.write(data, 0, n);
+                                    }
+                                    atomicWriteToFile(cacheDir, output, baos.toByteArray());
+                                } catch (IOException ioex) {
+                                    throw new Exception("Failed extracting policy zip. " + ioex);
+                                } finally {
+                                    zis.closeEntry();
+                                }
+                            }
+                        }
+
+                        // Open the metadata file.
+                        Scanner scan = new Scanner(mMetadataFile);
+                        scan.useDelimiter(":");
+                        String requiredHash = scan.next();
+                        String signature = scan.next();
+                        String version = scan.next();
+
+                        // Build the intent to broadcast.
+                        Intent i = new Intent(mIntent);
+                        i.putExtra("CONTENT_PATH", mBundleFile.getPath());
+                        i.putExtra("REQUIRED_HASH", requiredHash);
+                        i.putExtra("SIGNATURE", signature);
+                        i.putExtra("VERSION", version);
+
+                        Log.d(TAG, mIntent + " intent being broadcast.");
+                        mActivity.sendBroadcast(i);
+
+                    } catch (Exception ex) {
+                        Toast.makeText(mActivity, ex.toString(), Toast.LENGTH_SHORT).show();
+                        mBundleFile.delete();
+                        mMetadataFile.delete();
+                        Log.e(TAG, "Exception loading policy.", ex);
+                    } finally {
+                        if (zis != null) Closeables.closeQuietly(zis);
+                        if (fis != null) Closeables.closeQuietly(fis);
+                    }
+
+                    return true;
+                }
+            });
+
+            String mes = "Load from: ";
+            if (cacheDir == null || extDir == null) {
+                String where = (extDir == null) ? "sdcard" : "cache";
+                mes += "Can't locate " + where + ". Policy location error.";
+                mReload.setEnabled(false);
+                mReload.setSelectable(false);
+            } else {
+                mes += mZipFile.getPath();
+            }
+            mReload.setSummary(mes);
+        }
+
+        private void atomicWriteToFile(File dir, File file, byte[] content) throws IOException {
+            FileOutputStream out = null;
+            File tmp = null;
+            try {
+                tmp = File.createTempFile("journal", "", dir);
+                tmp.setReadable(true, false);
+                out = new FileOutputStream(tmp);
+                out.write(content);
+                out.getFD().sync();
+                if (!tmp.renameTo(file)) {
+                    throw new IOException("Failed to atomically rename " + file.getCanonicalPath());
+                }
+            } finally {
+                if (tmp != null) {
+                    tmp.delete();
+                }
+                Closeables.closeQuietly(out);
+            }
+        }
+    }
+
+    private class MacInstallConfigUpdate extends ConfigUpdate {
+        MacInstallConfigUpdate() {
+            super("mac_perms", "UPDATE_MAC_PERMS");
+        }
+    }
+
+    private class EopsConfigUpdate extends ConfigUpdate {
+        EopsConfigUpdate() {
+            super("eops", "UPDATE_EOPS");
+        }
+    }
+}