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.SystemProperties; 22 import android.provider.Settings; 23 import android.system.ErrnoException; 24 import android.system.Os; 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.IoUtils; 34 35 public class SELinuxPolicyInstallReceiver extends ConfigUpdateInstallReceiver { 36 37 private static final String TAG = "SELinuxPolicyInstallReceiver"; 38 39 private static final String sepolicyPath = "sepolicy"; 40 private static final String fileContextsPath = "file_contexts"; 41 private static final String propertyContextsPath = "property_contexts"; 42 private static final String seappContextsPath = "seapp_contexts"; 43 private static final String versionPath = "selinux_version"; 44 private static final String macPermissionsPath = "mac_permissions.xml"; 45 private static final String serviceContextsPath = "service_contexts"; 46 47 public SELinuxPolicyInstallReceiver() { 48 super("/data/security/bundle", "sepolicy_bundle", "metadata/", "version"); 49 } 50 51 private void backupContexts(File contexts) { 52 new File(contexts, versionPath).renameTo( 53 new File(contexts, versionPath + "_backup")); 54 55 new File(contexts, macPermissionsPath).renameTo( 56 new File(contexts, macPermissionsPath + "_backup")); 57 58 new File(contexts, seappContextsPath).renameTo( 59 new File(contexts, seappContextsPath + "_backup")); 60 61 new File(contexts, propertyContextsPath).renameTo( 62 new File(contexts, propertyContextsPath + "_backup")); 63 64 new File(contexts, fileContextsPath).renameTo( 65 new File(contexts, fileContextsPath + "_backup")); 66 67 new File(contexts, sepolicyPath).renameTo( 68 new File(contexts, sepolicyPath + "_backup")); 69 70 new File(contexts, serviceContextsPath).renameTo( 71 new File(contexts, serviceContextsPath + "_backup")); 72 } 73 74 private void copyUpdate(File contexts) { 75 new File(updateDir, versionPath).renameTo(new File(contexts, versionPath)); 76 new File(updateDir, macPermissionsPath).renameTo(new File(contexts, macPermissionsPath)); 77 new File(updateDir, seappContextsPath).renameTo(new File(contexts, seappContextsPath)); 78 new File(updateDir, propertyContextsPath).renameTo(new File(contexts, propertyContextsPath)); 79 new File(updateDir, fileContextsPath).renameTo(new File(contexts, fileContextsPath)); 80 new File(updateDir, sepolicyPath).renameTo(new File(contexts, sepolicyPath)); 81 new File(updateDir, serviceContextsPath).renameTo(new File(contexts, serviceContextsPath)); 82 } 83 84 private int readInt(BufferedInputStream reader) throws IOException { 85 int value = 0; 86 for (int i=0; i < 4; i++) { 87 value = (value << 8) | reader.read(); 88 } 89 return value; 90 } 91 92 private int[] readChunkLengths(BufferedInputStream bundle) throws IOException { 93 int[] chunks = new int[7]; 94 chunks[0] = readInt(bundle); 95 chunks[1] = readInt(bundle); 96 chunks[2] = readInt(bundle); 97 chunks[3] = readInt(bundle); 98 chunks[4] = readInt(bundle); 99 chunks[5] = readInt(bundle); 100 chunks[6] = readInt(bundle); 101 return chunks; 102 } 103 104 private void installFile(File destination, BufferedInputStream stream, int length) 105 throws IOException { 106 byte[] chunk = new byte[length]; 107 stream.read(chunk, 0, length); 108 writeUpdate(updateDir, destination, Base64.decode(chunk, Base64.DEFAULT)); 109 } 110 111 private void unpackBundle() throws IOException { 112 BufferedInputStream stream = new BufferedInputStream(new FileInputStream(updateContent)); 113 try { 114 int[] chunkLengths = readChunkLengths(stream); 115 installFile(new File(updateDir, versionPath), stream, chunkLengths[0]); 116 installFile(new File(updateDir, macPermissionsPath), stream, chunkLengths[1]); 117 installFile(new File(updateDir, seappContextsPath), stream, chunkLengths[2]); 118 installFile(new File(updateDir, propertyContextsPath), stream, chunkLengths[3]); 119 installFile(new File(updateDir, fileContextsPath), stream, chunkLengths[4]); 120 installFile(new File(updateDir, sepolicyPath), stream, chunkLengths[5]); 121 installFile(new File(updateDir, serviceContextsPath), stream, chunkLengths[6]); 122 } finally { 123 IoUtils.closeQuietly(stream); 124 } 125 } 126 127 private void applyUpdate() throws IOException, ErrnoException { 128 Slog.i(TAG, "Applying SELinux policy"); 129 File contexts = new File(updateDir.getParentFile(), "contexts"); 130 File current = new File(updateDir.getParentFile(), "current"); 131 File update = new File(updateDir.getParentFile(), "update"); 132 File tmp = new File(updateDir.getParentFile(), "tmp"); 133 if (current.exists()) { 134 Os.symlink(updateDir.getPath(), update.getPath()); 135 Os.rename(update.getPath(), current.getPath()); 136 } else { 137 Os.symlink(updateDir.getPath(), current.getPath()); 138 } 139 contexts.mkdirs(); 140 backupContexts(contexts); 141 copyUpdate(contexts); 142 Os.symlink(contexts.getPath(), tmp.getPath()); 143 Os.rename(tmp.getPath(), current.getPath()); 144 SystemProperties.set("selinux.reload_policy", "1"); 145 } 146 147 @Override 148 protected void postInstall(Context context, Intent intent) { 149 try { 150 unpackBundle(); 151 applyUpdate(); 152 } catch (IllegalArgumentException e) { 153 Slog.e(TAG, "SELinux policy update malformed: ", e); 154 } catch (IOException e) { 155 Slog.e(TAG, "Could not update selinux policy: ", e); 156 } catch (ErrnoException e) { 157 Slog.e(TAG, "Could not update selinux policy: ", e); 158 } 159 } 160 } 161