1 /* 2 * Copyright (C) 2013 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.android.server.updates; 18 19 import android.content.Context; 20 import android.content.Intent; 21 import android.os.FileUtils; 22 import android.os.SELinux; 23 import android.os.SystemProperties; 24 import android.provider.Settings; 25 import android.util.Base64; 26 import android.util.Slog; 27 28 import java.io.BufferedInputStream; 29 import java.io.File; 30 import java.io.FileInputStream; 31 import java.io.IOException; 32 33 import libcore.io.ErrnoException; 34 import libcore.io.IoUtils; 35 import libcore.io.Libcore; 36 37 public class SELinuxPolicyInstallReceiver extends ConfigUpdateInstallReceiver { 38 39 private static final String TAG = "SELinuxPolicyInstallReceiver"; 40 41 private static final String sepolicyPath = "sepolicy"; 42 private static final String fileContextsPath = "file_contexts"; 43 private static final String propertyContextsPath = "property_contexts"; 44 private static final String seappContextsPath = "seapp_contexts"; 45 46 public SELinuxPolicyInstallReceiver() { 47 super("/data/security/bundle", "sepolicy_bundle", "metadata/", "version"); 48 } 49 50 private void backupContexts(File contexts) { 51 new File(contexts, seappContextsPath).renameTo( 52 new File(contexts, seappContextsPath + "_backup")); 53 54 new File(contexts, propertyContextsPath).renameTo( 55 new File(contexts, propertyContextsPath + "_backup")); 56 57 new File(contexts, fileContextsPath).renameTo( 58 new File(contexts, fileContextsPath + "_backup")); 59 60 new File(contexts, sepolicyPath).renameTo( 61 new File(contexts, sepolicyPath + "_backup")); 62 } 63 64 private void copyUpdate(File contexts) { 65 new File(updateDir, seappContextsPath).renameTo(new File(contexts, seappContextsPath)); 66 new File(updateDir, propertyContextsPath).renameTo(new File(contexts, propertyContextsPath)); 67 new File(updateDir, fileContextsPath).renameTo(new File(contexts, fileContextsPath)); 68 new File(updateDir, sepolicyPath).renameTo(new File(contexts, sepolicyPath)); 69 } 70 71 private int readInt(BufferedInputStream reader) throws IOException { 72 int value = 0; 73 for (int i=0; i < 4; i++) { 74 value = (value << 8) | reader.read(); 75 } 76 return value; 77 } 78 79 private int[] readChunkLengths(BufferedInputStream bundle) throws IOException { 80 int[] chunks = new int[4]; 81 chunks[0] = readInt(bundle); 82 chunks[1] = readInt(bundle); 83 chunks[2] = readInt(bundle); 84 chunks[3] = readInt(bundle); 85 return chunks; 86 } 87 88 private void installFile(File destination, BufferedInputStream stream, int length) 89 throws IOException { 90 byte[] chunk = new byte[length]; 91 stream.read(chunk, 0, length); 92 writeUpdate(updateDir, destination, Base64.decode(chunk, Base64.DEFAULT)); 93 } 94 95 private void unpackBundle() throws IOException { 96 BufferedInputStream stream = new BufferedInputStream(new FileInputStream(updateContent)); 97 try { 98 int[] chunkLengths = readChunkLengths(stream); 99 installFile(new File(updateDir, seappContextsPath), stream, chunkLengths[0]); 100 installFile(new File(updateDir, propertyContextsPath), stream, chunkLengths[1]); 101 installFile(new File(updateDir, fileContextsPath), stream, chunkLengths[2]); 102 installFile(new File(updateDir, sepolicyPath), stream, chunkLengths[3]); 103 } finally { 104 IoUtils.closeQuietly(stream); 105 } 106 } 107 108 private void applyUpdate() throws IOException, ErrnoException { 109 Slog.i(TAG, "Applying SELinux policy"); 110 File contexts = new File(updateDir.getParentFile(), "contexts"); 111 File current = new File(updateDir.getParentFile(), "current"); 112 File update = new File(updateDir.getParentFile(), "update"); 113 File tmp = new File(updateDir.getParentFile(), "tmp"); 114 if (current.exists()) { 115 Libcore.os.symlink(updateDir.getPath(), update.getPath()); 116 Libcore.os.rename(update.getPath(), current.getPath()); 117 } else { 118 Libcore.os.symlink(updateDir.getPath(), current.getPath()); 119 } 120 contexts.mkdirs(); 121 backupContexts(contexts); 122 copyUpdate(contexts); 123 Libcore.os.symlink(contexts.getPath(), tmp.getPath()); 124 Libcore.os.rename(tmp.getPath(), current.getPath()); 125 SystemProperties.set("selinux.reload_policy", "1"); 126 } 127 128 private void setEnforcingMode(Context context) { 129 String mode = Settings.Global.getString(context.getContentResolver(), 130 Settings.Global.SELINUX_STATUS); 131 if ("1".equals(mode)) { 132 Slog.i(TAG, "Setting enforcing mode"); 133 SystemProperties.set("persist.selinux.enforcing", mode); 134 } else if ("0".equals(mode)) { 135 Slog.i(TAG, "Tried to set permissive mode, ignoring"); 136 } else { 137 Slog.e(TAG, "Got invalid enforcing mode: " + mode); 138 } 139 } 140 141 @Override 142 protected void postInstall(Context context, Intent intent) { 143 try { 144 unpackBundle(); 145 applyUpdate(); 146 setEnforcingMode(context); 147 } catch (IllegalArgumentException e) { 148 Slog.e(TAG, "SELinux policy update malformed: ", e); 149 } catch (IOException e) { 150 Slog.e(TAG, "Could not update selinux policy: ", e); 151 } catch (ErrnoException e) { 152 Slog.e(TAG, "Could not update selinux policy: ", e); 153 } 154 } 155 } 156