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 17package com.android.server.updates; 18 19import android.content.Context; 20import android.content.Intent; 21import android.os.SystemProperties; 22import android.system.ErrnoException; 23import android.system.Os; 24import android.util.Base64; 25import android.util.Slog; 26 27import java.io.BufferedInputStream; 28import java.io.File; 29import java.io.FileInputStream; 30import java.io.IOException; 31 32import libcore.io.IoUtils; 33 34public class SELinuxPolicyInstallReceiver extends ConfigUpdateInstallReceiver { 35 36 private static final String TAG = "SELinuxPolicyInstallReceiver"; 37 38 private static final String sepolicyPath = "sepolicy"; 39 private static final String fileContextsPath = "file_contexts.bin"; 40 private static final String propertyContextsPath = "property_contexts"; 41 private static final String seappContextsPath = "seapp_contexts"; 42 private static final String versionPath = "selinux_version"; 43 private static final String macPermissionsPath = "mac_permissions.xml"; 44 private static final String serviceContextsPath = "service_contexts"; 45 46 public SELinuxPolicyInstallReceiver() { 47 super("/data/security/bundle", "sepolicy_bundle", "metadata/", "version"); 48 } 49 50 private int readInt(BufferedInputStream reader) throws IOException { 51 int value = 0; 52 for (int i=0; i < 4; i++) { 53 value = (value << 8) | reader.read(); 54 } 55 return value; 56 } 57 58 private int[] readChunkLengths(BufferedInputStream bundle) throws IOException { 59 int[] chunks = new int[7]; 60 chunks[0] = readInt(bundle); 61 chunks[1] = readInt(bundle); 62 chunks[2] = readInt(bundle); 63 chunks[3] = readInt(bundle); 64 chunks[4] = readInt(bundle); 65 chunks[5] = readInt(bundle); 66 chunks[6] = readInt(bundle); 67 return chunks; 68 } 69 70 private void installFile(File destination, BufferedInputStream stream, int length) 71 throws IOException { 72 byte[] chunk = new byte[length]; 73 stream.read(chunk, 0, length); 74 writeUpdate(updateDir, destination, Base64.decode(chunk, Base64.DEFAULT)); 75 } 76 77 private void deleteRecursive(File fileOrDirectory) { 78 if (fileOrDirectory.isDirectory()) 79 for (File child : fileOrDirectory.listFiles()) 80 deleteRecursive(child); 81 fileOrDirectory.delete(); 82 } 83 84 private void unpackBundle() throws IOException { 85 BufferedInputStream stream = new BufferedInputStream(new FileInputStream(updateContent)); 86 File tmp = new File(updateDir.getParentFile(), "tmp"); 87 try { 88 int[] chunkLengths = readChunkLengths(stream); 89 deleteRecursive(tmp); 90 tmp.mkdirs(); 91 installFile(new File(tmp, versionPath), stream, chunkLengths[0]); 92 installFile(new File(tmp, macPermissionsPath), stream, chunkLengths[1]); 93 installFile(new File(tmp, seappContextsPath), stream, chunkLengths[2]); 94 installFile(new File(tmp, propertyContextsPath), stream, chunkLengths[3]); 95 installFile(new File(tmp, fileContextsPath), stream, chunkLengths[4]); 96 installFile(new File(tmp, sepolicyPath), stream, chunkLengths[5]); 97 installFile(new File(tmp, serviceContextsPath), stream, chunkLengths[6]); 98 } finally { 99 IoUtils.closeQuietly(stream); 100 } 101 } 102 103 private void applyUpdate() throws IOException, ErrnoException { 104 Slog.i(TAG, "Applying SELinux policy"); 105 File backup = new File(updateDir.getParentFile(), "backup"); 106 File current = new File(updateDir.getParentFile(), "current"); 107 File tmp = new File(updateDir.getParentFile(), "tmp"); 108 if (current.exists()) { 109 deleteRecursive(backup); 110 Os.rename(current.getPath(), backup.getPath()); 111 } 112 try { 113 Os.rename(tmp.getPath(), current.getPath()); 114 SystemProperties.set("selinux.reload_policy", "1"); 115 } catch (ErrnoException e) { 116 Slog.e(TAG, "Could not update selinux policy: ", e); 117 if (backup.exists()) { 118 Os.rename(backup.getPath(), current.getPath()); 119 } 120 } 121 } 122 123 @Override 124 protected void postInstall(Context context, Intent intent) { 125 try { 126 unpackBundle(); 127 applyUpdate(); 128 } catch (IllegalArgumentException e) { 129 Slog.e(TAG, "SELinux policy update malformed: ", e); 130 } catch (IOException e) { 131 Slog.e(TAG, "Could not update selinux policy: ", e); 132 } catch (ErrnoException e) { 133 Slog.e(TAG, "Could not update selinux policy: ", e); 134 } 135 } 136} 137