ext4_crypt_init_extensions.cpp revision f9768752cbf6ebac97214a021dc288c7b6592f1e
1#define TAG "ext4_utils" 2 3#include "ext4_crypt.h" 4 5#include <string> 6#include <fstream> 7#include <iomanip> 8#include <sstream> 9 10#include <errno.h> 11#include <sys/mount.h> 12#include <sys/stat.h> 13 14#include <cutils/klog.h> 15#include <cutils/properties.h> 16#include <cutils/sockets.h> 17 18// ext4enc:TODO Include structure from somewhere sensible 19// MUST be in sync with ext4_crypto.c in kernel 20#define EXT4_MAX_KEY_SIZE 76 21struct ext4_encryption_key { 22 uint32_t mode; 23 char raw[EXT4_MAX_KEY_SIZE]; 24 uint32_t size; 25}; 26 27static const std::string unencrypted_path = "/unencrypted"; 28static const std::string keyring = "@s"; 29static const std::string arbitrary_sequence_number = "42"; 30 31static key_serial_t device_keyring = -1; 32 33static std::string vold_command(std::string const& command) 34{ 35 KLOG_INFO(TAG, "Running command %s\n", command.c_str()); 36 int sock = socket_local_client("vold", 37 ANDROID_SOCKET_NAMESPACE_RESERVED, 38 SOCK_STREAM); 39 40 if (sock < 0) { 41 KLOG_INFO(TAG, "Cannot open vold, failing command\n"); 42 return ""; 43 } 44 45 class CloseSocket 46 { 47 int sock_; 48 public: 49 CloseSocket(int sock) : sock_(sock) {} 50 ~CloseSocket() { close(sock_); } 51 }; 52 53 CloseSocket cs(sock); 54 55 // Use arbitrary sequence number. This should only be used when the 56 // framework is down, so this is (mostly) OK. 57 std::string actual_command = arbitrary_sequence_number + " " + command; 58 if (write(sock, actual_command.c_str(), actual_command.size() + 1) < 0) { 59 KLOG_ERROR(TAG, "Cannot write command\n"); 60 return ""; 61 } 62 63 while (1) { 64 struct timeval to; 65 to.tv_sec = 10; 66 to.tv_usec = 0; 67 68 fd_set read_fds; 69 FD_ZERO(&read_fds); 70 FD_SET(sock, &read_fds); 71 72 int rc = select(sock + 1, &read_fds, NULL, NULL, &to); 73 if (rc < 0) { 74 KLOG_ERROR(TAG, "Error in select %s\n", strerror(errno)); 75 return ""; 76 } else if (!rc) { 77 KLOG_ERROR(TAG, "Timeout\n"); 78 return ""; 79 } else if (FD_ISSET(sock, &read_fds)) { 80 char buffer[4096]; 81 memset(buffer, 0, sizeof(buffer)); 82 rc = read(sock, buffer, sizeof(buffer)); 83 if (rc <= 0) { 84 if (rc == 0) { 85 KLOG_ERROR(TAG, "Lost connection to Vold - did it crash?\n"); 86 } else { 87 KLOG_ERROR(TAG, "Error reading data (%s)\n", strerror(errno)); 88 } 89 return ""; 90 } 91 92 // We don't truly know that this is the correct result. However, 93 // since this will only be used when the framework is down, 94 // it should be OK unless someone is running vdc at the same time. 95 // Worst case we force a reboot in the very rare synchronization 96 // error 97 return std::string(buffer, rc); 98 } 99 } 100} 101 102int e4crypt_create_device_key(const char* dir, 103 int ensure_dir_exists(const char*)) 104{ 105 // Make sure folder exists. Use make_dir to set selinux permissions. 106 KLOG_INFO(TAG, "Creating test device key\n"); 107 std::string path = std::string() + dir + unencrypted_path; 108 if (ensure_dir_exists(path.c_str())) { 109 KLOG_ERROR(TAG, "Failed to create %s with error %s\n", 110 path.c_str(), strerror(errno)); 111 return -1; 112 } 113 114 // Open key if it exists 115 std::string key_path = path + "/key"; 116 std::ifstream key(key_path.c_str(), std::ifstream::binary); 117 118 if (!key.good()) { 119 // Create new key if it doesn't 120 std::ofstream new_key(key_path.c_str(), std::ofstream::binary); 121 if (!new_key) { 122 KLOG_ERROR(TAG, "Failed to open %s\n", key_path.c_str()); 123 return -1; 124 } 125 126 std::ifstream urandom("/dev/urandom", std::ifstream::binary); 127 if (!urandom) { 128 KLOG_ERROR(TAG, "Failed to open /dev/urandom\n"); 129 return -1; 130 } 131 132 char key_material[32]; 133 urandom.read(key_material, 32); 134 if (!urandom) { 135 KLOG_ERROR(TAG, "Failed to read random bytes\n"); 136 return -1; 137 } 138 139 new_key.write(key_material, 32); 140 if (!new_key) { 141 KLOG_ERROR(TAG, "Failed to write key material"); 142 return -1; 143 } 144 } 145 146 remove((std::string(dir) + "/ref").c_str()); 147 return 0; 148} 149 150int e4crypt_install_keyring() 151{ 152 device_keyring = add_key("keyring", 153 "e4crypt", 154 0, 155 0, 156 KEY_SPEC_SESSION_KEYRING); 157 158 if (device_keyring == -1) { 159 KLOG_ERROR(TAG, "Failed to create keyring\n"); 160 return -1; 161 } 162 163 KLOG_INFO(TAG, "Keyring created wth id %d in process %d\n", device_keyring, getpid()); 164 165 // ext4enc:TODO set correct permissions 166 long result = keyctl_setperm(device_keyring, 0x3f3f3f3f); 167 if (result) { 168 KLOG_ERROR(TAG, "KEYCTL_SETPERM failed with error %ld\n", result); 169 return -1; 170 } 171 172 return 0; 173} 174 175int e4crypt_install_key(const char* dir) 176{ 177 std::string path = std::string() + dir + unencrypted_path; 178 179 // Open key if it exists 180 std::string key_path = path + "/key"; 181 std::ifstream key(key_path.c_str(), std::ifstream::binary); 182 if (!key.good()) { 183 KLOG_ERROR(TAG, "Failed to open key %s\n", key_path.c_str()); 184 return -1; 185 } 186 187 char keyblob[256]; 188 key.read(keyblob, sizeof(keyblob)); 189 std::streamsize keyblob_size = key.gcount(); 190 if (keyblob_size <= 0) { 191 KLOG_ERROR(TAG, "Failed to read key data\n"); 192 return -1; 193 } 194 195 // Get password to decrypt as needed 196 if (e4crypt_non_default_key(dir)) { 197 std::string result = vold_command("cryptfs getpw"); 198 // result is either 199 // 200 0 -1 200 // or 201 // 200 0 {{sensitive}} 0001020304 202 // where 0001020304 is hex encoding of password 203 std::istringstream i(result); 204 std::string bit; 205 i >> bit; 206 if (bit != "200") { 207 KLOG_ERROR(TAG, "Expecting 200\n"); 208 return -1; 209 } 210 211 i >> bit; 212 if (bit != arbitrary_sequence_number) { 213 KLOG_ERROR(TAG, "Expecting %s\n", arbitrary_sequence_number.c_str()); 214 return -1; 215 } 216 217 i >> bit; 218 if (bit != "{{sensitive}}") { 219 KLOG_INFO(TAG, "Not encrypted\n"); 220 return -1; 221 } 222 223 i >> bit; 224 } 225 226 // Add key to keyring 227 ext4_encryption_key ext4_key = {0, {0}, 0}; 228 memcpy(ext4_key.raw, keyblob, keyblob_size); 229 ext4_key.size = keyblob_size; 230 231 // ext4enc:TODO Use better reference not 1234567890 232 key_serial_t key_id = add_key("logon", "ext4-key:1234567890", 233 (void*)&ext4_key, sizeof(ext4_key), 234 device_keyring); 235 236 if (key_id == -1) { 237 KLOG_ERROR(TAG, "Failed to insert key into keyring with error %s\n", 238 strerror(errno)); 239 return -1; 240 } 241 242 KLOG_INFO(TAG, "Added key %d to keyring %d in process %d\n", 243 key_id, device_keyring, getpid()); 244 245 // ext4enc:TODO set correct permissions 246 long result = keyctl_setperm(key_id, 0x3f3f3f3f); 247 if (result) { 248 KLOG_ERROR(TAG, "KEYCTL_SETPERM failed with error %ld\n", result); 249 return -1; 250 } 251 252 // Save reference to key so we can set policy later 253 std::ofstream(path + "/ref") << "ext4-key:1234567890"; 254 return 0; 255} 256 257int e4crypt_set_directory_policy(const char* dir) 258{ 259 // Only set policy on first level /data directories 260 // ext4enc:TODO don't hard code /data/ 261 if (!dir || strncmp(dir, "/data/", 6) || strchr(dir + 6, '/')) { 262 return 0; 263 } 264 265 std::ifstream ref_file("/data/unencrypted/ref"); 266 if (!ref_file) { 267 KLOG_ERROR(TAG, "Cannot open key reference file\n"); 268 return -1; 269 } 270 271 std::string ref; 272 std::getline(ref_file, ref); 273 std::string policy = std::string() + keyring + "." + ref; 274 KLOG_INFO(TAG, "Setting policy %s\n", policy.c_str()); 275 if (do_policy_set(dir, policy.c_str())) { 276 KLOG_ERROR(TAG, "Setting policy on %s failed!", dir); 277 return -1; 278 } 279 280 return 0; 281} 282