Brian Carlstrom | 5cfee3f | 2011-05-31 01:00:15 -0700 | [diff] [blame] | 1 | /* |
| 2 | * Copyright (C) 2009 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 | #include <stdio.h> |
| 18 | #include <stdint.h> |
| 19 | #include <string.h> |
| 20 | #include <unistd.h> |
| 21 | #include <signal.h> |
| 22 | #include <errno.h> |
| 23 | #include <dirent.h> |
| 24 | #include <fcntl.h> |
| 25 | #include <limits.h> |
| 26 | #include <sys/types.h> |
| 27 | #include <sys/socket.h> |
| 28 | #include <sys/stat.h> |
| 29 | #include <sys/time.h> |
| 30 | #include <arpa/inet.h> |
| 31 | |
| 32 | #include <openssl/aes.h> |
| 33 | #include <openssl/evp.h> |
| 34 | #include <openssl/md5.h> |
| 35 | |
| 36 | #define LOG_TAG "keystore" |
| 37 | #include <cutils/log.h> |
| 38 | #include <cutils/sockets.h> |
| 39 | #include <private/android_filesystem_config.h> |
| 40 | |
| 41 | #include "keystore.h" |
| 42 | |
| 43 | /* KeyStore is a secured storage for key-value pairs. In this implementation, |
| 44 | * each file stores one key-value pair. Keys are encoded in file names, and |
| 45 | * values are encrypted with checksums. The encryption key is protected by a |
| 46 | * user-defined password. To keep things simple, buffers are always larger than |
| 47 | * the maximum space we needed, so boundary checks on buffers are omitted. */ |
| 48 | |
| 49 | #define KEY_SIZE ((NAME_MAX - 15) / 2) |
| 50 | #define VALUE_SIZE 32768 |
| 51 | #define PASSWORD_SIZE VALUE_SIZE |
| 52 | |
| 53 | struct Value { |
| 54 | int length; |
| 55 | uint8_t value[VALUE_SIZE]; |
| 56 | }; |
| 57 | |
| 58 | /* Here is the encoding of keys. This is necessary in order to allow arbitrary |
| 59 | * characters in keys. Characters in [0-~] are not encoded. Others are encoded |
| 60 | * into two bytes. The first byte is one of [+-.] which represents the first |
| 61 | * two bits of the character. The second byte encodes the rest of the bits into |
| 62 | * [0-o]. Therefore in the worst case the length of a key gets doubled. Note |
| 63 | * that Base64 cannot be used here due to the need of prefix match on keys. */ |
| 64 | |
| 65 | static int encode_key(char* out, uid_t uid, const Value* key) { |
| 66 | int n = snprintf(out, NAME_MAX, "%u_", uid); |
| 67 | out += n; |
| 68 | const uint8_t* in = key->value; |
| 69 | int length = key->length; |
| 70 | for (int i = length; i > 0; --i, ++in, ++out) { |
| 71 | if (*in >= '0' && *in <= '~') { |
| 72 | *out = *in; |
| 73 | } else { |
| 74 | *out = '+' + (*in >> 6); |
| 75 | *++out = '0' + (*in & 0x3F); |
| 76 | ++length; |
| 77 | } |
| 78 | } |
| 79 | *out = '\0'; |
| 80 | return n + length; |
| 81 | } |
| 82 | |
| 83 | static int decode_key(uint8_t* out, char* in, int length) { |
| 84 | for (int i = 0; i < length; ++i, ++in, ++out) { |
| 85 | if (*in >= '0' && *in <= '~') { |
| 86 | *out = *in; |
| 87 | } else { |
| 88 | *out = (*in - '+') << 6; |
| 89 | *out |= (*++in - '0') & 0x3F; |
| 90 | --length; |
| 91 | } |
| 92 | } |
| 93 | *out = '\0'; |
| 94 | return length; |
| 95 | } |
| 96 | |
| 97 | static size_t readFully(int fd, uint8_t* data, size_t size) { |
| 98 | size_t remaining = size; |
| 99 | while (remaining > 0) { |
| 100 | ssize_t n = TEMP_FAILURE_RETRY(read(fd, data, size)); |
| 101 | if (n == -1 || n == 0) { |
| 102 | return size-remaining; |
| 103 | } |
| 104 | data += n; |
| 105 | remaining -= n; |
| 106 | } |
| 107 | return size; |
| 108 | } |
| 109 | |
| 110 | static size_t writeFully(int fd, uint8_t* data, size_t size) { |
| 111 | size_t remaining = size; |
| 112 | while (remaining > 0) { |
| 113 | ssize_t n = TEMP_FAILURE_RETRY(write(fd, data, size)); |
| 114 | if (n == -1 || n == 0) { |
| 115 | return size-remaining; |
| 116 | } |
| 117 | data += n; |
| 118 | remaining -= n; |
| 119 | } |
| 120 | return size; |
| 121 | } |
| 122 | |
| 123 | class Entropy { |
| 124 | public: |
| 125 | Entropy() : mRandom(-1) {} |
| 126 | ~Entropy() { |
| 127 | if (mRandom != -1) { |
| 128 | close(mRandom); |
| 129 | } |
| 130 | } |
| 131 | |
| 132 | bool open() { |
| 133 | const char* randomDevice = "/dev/urandom"; |
| 134 | mRandom = ::open(randomDevice, O_RDONLY); |
| 135 | if (mRandom == -1) { |
| 136 | LOGE("open: %s: %s", randomDevice, strerror(errno)); |
| 137 | return false; |
| 138 | } |
| 139 | return true; |
| 140 | } |
| 141 | |
| 142 | bool generate_random_data(uint8_t* data, size_t size) { |
| 143 | return (readFully(mRandom, data, size) == size); |
| 144 | } |
| 145 | |
| 146 | private: |
| 147 | int mRandom; |
| 148 | }; |
| 149 | |
| 150 | /* Here is the file format. There are two parts in blob.value, the secret and |
| 151 | * the description. The secret is stored in ciphertext, and its original size |
| 152 | * can be found in blob.length. The description is stored after the secret in |
| 153 | * plaintext, and its size is specified in blob.info. The total size of the two |
| 154 | * parts must be no more than VALUE_SIZE bytes. The first three bytes of the |
| 155 | * file are reserved for future use and are always set to zero. Fields other |
| 156 | * than blob.info, blob.length, and blob.value are modified by encryptBlob() |
| 157 | * and decryptBlob(). Thus they should not be accessed from outside. */ |
| 158 | |
| 159 | struct __attribute__((packed)) blob { |
| 160 | uint8_t reserved[3]; |
| 161 | uint8_t info; |
| 162 | uint8_t vector[AES_BLOCK_SIZE]; |
| 163 | uint8_t encrypted[0]; |
| 164 | uint8_t digest[MD5_DIGEST_LENGTH]; |
| 165 | uint8_t digested[0]; |
| 166 | int32_t length; // in network byte order when encrypted |
| 167 | uint8_t value[VALUE_SIZE + AES_BLOCK_SIZE]; |
| 168 | }; |
| 169 | |
| 170 | class Blob { |
| 171 | public: |
| 172 | Blob(uint8_t* value, int32_t valueLength, uint8_t* info, uint8_t infoLength) { |
| 173 | mBlob.length = valueLength; |
| 174 | memcpy(mBlob.value, value, valueLength); |
| 175 | |
| 176 | mBlob.info = infoLength; |
| 177 | memcpy(mBlob.value + valueLength, info, infoLength); |
| 178 | } |
| 179 | |
| 180 | Blob(blob b) { |
| 181 | mBlob = b; |
| 182 | } |
| 183 | |
| 184 | Blob() {} |
| 185 | |
| 186 | uint8_t* getValue() { |
| 187 | return mBlob.value; |
| 188 | } |
| 189 | |
| 190 | int32_t getLength() { |
| 191 | return mBlob.length; |
| 192 | } |
| 193 | |
| 194 | uint8_t getInfo() { |
| 195 | return mBlob.info; |
| 196 | } |
| 197 | |
| 198 | ResponseCode encryptBlob(const char* filename, AES_KEY *aes_key, Entropy* entropy) { |
| 199 | if (!entropy->generate_random_data(mBlob.vector, AES_BLOCK_SIZE)) { |
| 200 | return SYSTEM_ERROR; |
| 201 | } |
| 202 | |
| 203 | // data includes the value and the value's length |
| 204 | size_t dataLength = mBlob.length + sizeof(mBlob.length); |
| 205 | // pad data to the AES_BLOCK_SIZE |
| 206 | size_t digestedLength = ((dataLength + AES_BLOCK_SIZE - 1) |
| 207 | / AES_BLOCK_SIZE * AES_BLOCK_SIZE); |
| 208 | // encrypted data includes the digest value |
| 209 | size_t encryptedLength = digestedLength + MD5_DIGEST_LENGTH; |
| 210 | // move info after space for padding |
| 211 | memmove(&mBlob.encrypted[encryptedLength], &mBlob.value[mBlob.length], mBlob.info); |
| 212 | // zero padding area |
| 213 | memset(mBlob.value + mBlob.length, 0, digestedLength - dataLength); |
| 214 | |
| 215 | mBlob.length = htonl(mBlob.length); |
| 216 | MD5(mBlob.digested, digestedLength, mBlob.digest); |
| 217 | |
| 218 | uint8_t vector[AES_BLOCK_SIZE]; |
| 219 | memcpy(vector, mBlob.vector, AES_BLOCK_SIZE); |
| 220 | AES_cbc_encrypt(mBlob.encrypted, mBlob.encrypted, encryptedLength, |
| 221 | aes_key, vector, AES_ENCRYPT); |
| 222 | |
| 223 | memset(mBlob.reserved, 0, sizeof(mBlob.reserved)); |
| 224 | size_t headerLength = (mBlob.encrypted - (uint8_t*) &mBlob); |
| 225 | size_t fileLength = encryptedLength + headerLength + mBlob.info; |
| 226 | |
| 227 | const char* tmpFileName = ".tmp"; |
| 228 | int out = open(tmpFileName, O_WRONLY | O_TRUNC | O_CREAT, S_IRUSR | S_IWUSR); |
| 229 | if (out == -1) { |
| 230 | return SYSTEM_ERROR; |
| 231 | } |
| 232 | size_t writtenBytes = writeFully(out, (uint8_t*) &mBlob, fileLength); |
| 233 | if (close(out) != 0) { |
| 234 | return SYSTEM_ERROR; |
| 235 | } |
| 236 | if (writtenBytes != fileLength) { |
| 237 | unlink(tmpFileName); |
| 238 | return SYSTEM_ERROR; |
| 239 | } |
| 240 | return (rename(tmpFileName, filename) == 0) ? NO_ERROR : SYSTEM_ERROR; |
| 241 | } |
| 242 | |
| 243 | ResponseCode decryptBlob(const char* filename, AES_KEY *aes_key) { |
| 244 | int in = open(filename, O_RDONLY); |
| 245 | if (in == -1) { |
| 246 | return (errno == ENOENT) ? KEY_NOT_FOUND : SYSTEM_ERROR; |
| 247 | } |
| 248 | // fileLength may be less than sizeof(mBlob) since the in |
| 249 | // memory version has extra padding to tolerate rounding up to |
| 250 | // the AES_BLOCK_SIZE |
| 251 | size_t fileLength = readFully(in, (uint8_t*) &mBlob, sizeof(mBlob)); |
| 252 | if (close(in) != 0) { |
| 253 | return SYSTEM_ERROR; |
| 254 | } |
| 255 | size_t headerLength = (mBlob.encrypted - (uint8_t*) &mBlob); |
| 256 | if (fileLength < headerLength) { |
| 257 | return VALUE_CORRUPTED; |
| 258 | } |
| 259 | |
| 260 | ssize_t encryptedLength = fileLength - (headerLength + mBlob.info); |
| 261 | if (encryptedLength < 0 || encryptedLength % AES_BLOCK_SIZE != 0) { |
| 262 | return VALUE_CORRUPTED; |
| 263 | } |
| 264 | AES_cbc_encrypt(mBlob.encrypted, mBlob.encrypted, encryptedLength, aes_key, |
| 265 | mBlob.vector, AES_DECRYPT); |
| 266 | size_t digestedLength = encryptedLength - MD5_DIGEST_LENGTH; |
| 267 | uint8_t computedDigest[MD5_DIGEST_LENGTH]; |
| 268 | MD5(mBlob.digested, digestedLength, computedDigest); |
| 269 | if (memcmp(mBlob.digest, computedDigest, MD5_DIGEST_LENGTH) != 0) { |
| 270 | return VALUE_CORRUPTED; |
| 271 | } |
| 272 | |
| 273 | ssize_t maxValueLength = digestedLength - sizeof(mBlob.length); |
| 274 | mBlob.length = ntohl(mBlob.length); |
| 275 | if (mBlob.length < 0 || mBlob.length > maxValueLength) { |
| 276 | return VALUE_CORRUPTED; |
| 277 | } |
| 278 | if (mBlob.info != 0) { |
| 279 | // move info from after padding to after data |
| 280 | memmove(&mBlob.value[mBlob.length], &mBlob.value[maxValueLength], mBlob.info); |
| 281 | } |
| 282 | return NO_ERROR; |
| 283 | } |
| 284 | |
| 285 | private: |
| 286 | struct blob mBlob; |
| 287 | }; |
| 288 | |
| 289 | class KeyStore { |
| 290 | public: |
| 291 | KeyStore(Entropy* entropy) : mEntropy(entropy), mRetry(MAX_RETRY) { |
| 292 | if (access(MASTER_KEY_FILE, R_OK) == 0) { |
| 293 | setState(STATE_LOCKED); |
| 294 | } else { |
| 295 | setState(STATE_UNINITIALIZED); |
| 296 | } |
| 297 | } |
| 298 | |
| 299 | State getState() { |
| 300 | return mState; |
| 301 | } |
| 302 | |
| 303 | int8_t getRetry() { |
| 304 | return mRetry; |
| 305 | } |
| 306 | |
| 307 | ResponseCode initialize(Value* pw) { |
| 308 | if (!generateMasterKey()) { |
| 309 | return SYSTEM_ERROR; |
| 310 | } |
| 311 | ResponseCode response = writeMasterKey(pw); |
| 312 | if (response != NO_ERROR) { |
| 313 | return response; |
| 314 | } |
| 315 | setupMasterKeys(); |
| 316 | return NO_ERROR; |
| 317 | } |
| 318 | |
| 319 | ResponseCode writeMasterKey(Value* pw) { |
| 320 | uint8_t passwordKey[MASTER_KEY_SIZE_BYTES]; |
| 321 | generateKeyFromPassword(passwordKey, MASTER_KEY_SIZE_BYTES, pw, mSalt); |
| 322 | AES_KEY passwordAesKey; |
| 323 | AES_set_encrypt_key(passwordKey, MASTER_KEY_SIZE_BITS, &passwordAesKey); |
| 324 | Blob masterKeyBlob(mMasterKey, sizeof(mMasterKey), mSalt, sizeof(mSalt)); |
| 325 | return masterKeyBlob.encryptBlob(MASTER_KEY_FILE, &passwordAesKey, mEntropy); |
| 326 | } |
| 327 | |
| 328 | ResponseCode readMasterKey(Value* pw) { |
| 329 | int in = open(MASTER_KEY_FILE, O_RDONLY); |
| 330 | if (in == -1) { |
| 331 | return SYSTEM_ERROR; |
| 332 | } |
| 333 | |
| 334 | // we read the raw blob to just to get the salt to generate |
| 335 | // the AES key, then we create the Blob to use with decryptBlob |
| 336 | blob rawBlob; |
| 337 | size_t length = readFully(in, (uint8_t*) &rawBlob, sizeof(rawBlob)); |
| 338 | if (close(in) != 0) { |
| 339 | return SYSTEM_ERROR; |
| 340 | } |
| 341 | // find salt at EOF if present, otherwise we have an old file |
| 342 | uint8_t* salt; |
| 343 | if (length > SALT_SIZE && rawBlob.info == SALT_SIZE) { |
| 344 | salt = (uint8_t*) &rawBlob + length - SALT_SIZE; |
| 345 | } else { |
| 346 | salt = NULL; |
| 347 | } |
| 348 | uint8_t passwordKey[MASTER_KEY_SIZE_BYTES]; |
| 349 | generateKeyFromPassword(passwordKey, MASTER_KEY_SIZE_BYTES, pw, salt); |
| 350 | AES_KEY passwordAesKey; |
| 351 | AES_set_decrypt_key(passwordKey, MASTER_KEY_SIZE_BITS, &passwordAesKey); |
| 352 | Blob masterKeyBlob(rawBlob); |
| 353 | ResponseCode response = masterKeyBlob.decryptBlob(MASTER_KEY_FILE, &passwordAesKey); |
| 354 | if (response == SYSTEM_ERROR) { |
| 355 | return SYSTEM_ERROR; |
| 356 | } |
| 357 | if (response == NO_ERROR && masterKeyBlob.getLength() == MASTER_KEY_SIZE_BYTES) { |
| 358 | // if salt was missing, generate one and write a new master key file with the salt. |
| 359 | if (salt == NULL) { |
| 360 | if (!generateSalt()) { |
| 361 | return SYSTEM_ERROR; |
| 362 | } |
| 363 | response = writeMasterKey(pw); |
| 364 | } |
| 365 | if (response == NO_ERROR) { |
Brian Carlstrom | ac8a1b2 | 2011-06-23 00:58:19 -0700 | [diff] [blame] | 366 | memcpy(mMasterKey, masterKeyBlob.getValue(), MASTER_KEY_SIZE_BYTES); |
Brian Carlstrom | 5cfee3f | 2011-05-31 01:00:15 -0700 | [diff] [blame] | 367 | setupMasterKeys(); |
| 368 | } |
| 369 | return response; |
| 370 | } |
| 371 | if (mRetry <= 0) { |
| 372 | reset(); |
| 373 | return UNINITIALIZED; |
| 374 | } |
| 375 | --mRetry; |
| 376 | switch (mRetry) { |
| 377 | case 0: return WRONG_PASSWORD_0; |
| 378 | case 1: return WRONG_PASSWORD_1; |
| 379 | case 2: return WRONG_PASSWORD_2; |
| 380 | case 3: return WRONG_PASSWORD_3; |
| 381 | default: return WRONG_PASSWORD_3; |
| 382 | } |
| 383 | } |
| 384 | |
| 385 | bool reset() { |
| 386 | clearMasterKeys(); |
| 387 | setState(STATE_UNINITIALIZED); |
| 388 | |
| 389 | DIR* dir = opendir("."); |
| 390 | struct dirent* file; |
| 391 | |
| 392 | if (!dir) { |
| 393 | return false; |
| 394 | } |
| 395 | while ((file = readdir(dir)) != NULL) { |
Brian Carlstrom | e2afc24 | 2011-06-02 16:21:55 -0700 | [diff] [blame] | 396 | unlink(file->d_name); |
Brian Carlstrom | 5cfee3f | 2011-05-31 01:00:15 -0700 | [diff] [blame] | 397 | } |
| 398 | closedir(dir); |
| 399 | return true; |
| 400 | } |
| 401 | |
| 402 | bool isEmpty() { |
| 403 | DIR* dir = opendir("."); |
| 404 | struct dirent* file; |
| 405 | if (!dir) { |
| 406 | return true; |
| 407 | } |
| 408 | bool result = true; |
| 409 | while ((file = readdir(dir)) != NULL) { |
| 410 | if (isKeyFile(file->d_name)) { |
| 411 | result = false; |
| 412 | break; |
| 413 | } |
| 414 | } |
| 415 | closedir(dir); |
| 416 | return result; |
| 417 | } |
| 418 | |
| 419 | void lock() { |
| 420 | clearMasterKeys(); |
| 421 | setState(STATE_LOCKED); |
| 422 | } |
| 423 | |
| 424 | ResponseCode get(const char* filename, Blob* keyBlob) { |
| 425 | return keyBlob->decryptBlob(filename, &mMasterKeyDecryption); |
| 426 | } |
| 427 | |
| 428 | ResponseCode put(const char* filename, Blob* keyBlob) { |
| 429 | return keyBlob->encryptBlob(filename, &mMasterKeyEncryption, mEntropy); |
| 430 | } |
| 431 | |
| 432 | private: |
| 433 | static const char* MASTER_KEY_FILE; |
| 434 | static const int MASTER_KEY_SIZE_BYTES = 16; |
| 435 | static const int MASTER_KEY_SIZE_BITS = MASTER_KEY_SIZE_BYTES * 8; |
| 436 | |
| 437 | static const int MAX_RETRY = 4; |
| 438 | static const size_t SALT_SIZE = 16; |
| 439 | |
| 440 | Entropy* mEntropy; |
| 441 | |
| 442 | State mState; |
| 443 | int8_t mRetry; |
| 444 | |
| 445 | uint8_t mMasterKey[MASTER_KEY_SIZE_BYTES]; |
| 446 | uint8_t mSalt[SALT_SIZE]; |
| 447 | |
| 448 | AES_KEY mMasterKeyEncryption; |
| 449 | AES_KEY mMasterKeyDecryption; |
| 450 | |
| 451 | void setState(State state) { |
| 452 | mState = state; |
| 453 | if (mState == STATE_NO_ERROR || mState == STATE_UNINITIALIZED) { |
| 454 | mRetry = MAX_RETRY; |
| 455 | } |
| 456 | } |
| 457 | |
| 458 | bool generateSalt() { |
| 459 | return mEntropy->generate_random_data(mSalt, sizeof(mSalt)); |
| 460 | } |
| 461 | |
| 462 | bool generateMasterKey() { |
| 463 | if (!mEntropy->generate_random_data(mMasterKey, sizeof(mMasterKey))) { |
| 464 | return false; |
| 465 | } |
| 466 | if (!generateSalt()) { |
| 467 | return false; |
| 468 | } |
| 469 | return true; |
| 470 | } |
| 471 | |
| 472 | void setupMasterKeys() { |
| 473 | AES_set_encrypt_key(mMasterKey, MASTER_KEY_SIZE_BITS, &mMasterKeyEncryption); |
| 474 | AES_set_decrypt_key(mMasterKey, MASTER_KEY_SIZE_BITS, &mMasterKeyDecryption); |
| 475 | setState(STATE_NO_ERROR); |
| 476 | } |
| 477 | |
| 478 | void clearMasterKeys() { |
| 479 | memset(mMasterKey, 0, sizeof(mMasterKey)); |
| 480 | memset(mSalt, 0, sizeof(mSalt)); |
| 481 | memset(&mMasterKeyEncryption, 0, sizeof(mMasterKeyEncryption)); |
| 482 | memset(&mMasterKeyDecryption, 0, sizeof(mMasterKeyDecryption)); |
| 483 | } |
| 484 | |
| 485 | static void generateKeyFromPassword(uint8_t* key, ssize_t keySize, Value* pw, uint8_t* salt) { |
| 486 | size_t saltSize; |
| 487 | if (salt != NULL) { |
| 488 | saltSize = SALT_SIZE; |
| 489 | } else { |
| 490 | // pre-gingerbread used this hardwired salt, readMasterKey will rewrite these when found |
| 491 | salt = (uint8_t*) "keystore"; |
| 492 | // sizeof = 9, not strlen = 8 |
| 493 | saltSize = sizeof("keystore"); |
| 494 | } |
| 495 | PKCS5_PBKDF2_HMAC_SHA1((char*) pw->value, pw->length, salt, saltSize, 8192, keySize, key); |
| 496 | } |
| 497 | |
| 498 | static bool isKeyFile(const char* filename) { |
| 499 | return ((strcmp(filename, MASTER_KEY_FILE) != 0) |
| 500 | && (strcmp(filename, ".") != 0) |
| 501 | && (strcmp(filename, "..") != 0)); |
| 502 | } |
| 503 | }; |
| 504 | |
| 505 | const char* KeyStore::MASTER_KEY_FILE = ".masterkey"; |
| 506 | |
| 507 | /* Here is the protocol used in both requests and responses: |
| 508 | * code [length_1 message_1 ... length_n message_n] end-of-file |
| 509 | * where code is one byte long and lengths are unsigned 16-bit integers in |
| 510 | * network order. Thus the maximum length of a message is 65535 bytes. */ |
| 511 | |
| 512 | static int recv_code(int sock, int8_t* code) { |
| 513 | return recv(sock, code, 1, 0) == 1; |
| 514 | } |
| 515 | |
| 516 | static int recv_message(int sock, uint8_t* message, int length) { |
| 517 | uint8_t bytes[2]; |
| 518 | if (recv(sock, &bytes[0], 1, 0) != 1 || |
| 519 | recv(sock, &bytes[1], 1, 0) != 1) { |
| 520 | return -1; |
| 521 | } else { |
| 522 | int offset = bytes[0] << 8 | bytes[1]; |
| 523 | if (length < offset) { |
| 524 | return -1; |
| 525 | } |
| 526 | length = offset; |
| 527 | offset = 0; |
| 528 | while (offset < length) { |
| 529 | int n = recv(sock, &message[offset], length - offset, 0); |
| 530 | if (n <= 0) { |
| 531 | return -1; |
| 532 | } |
| 533 | offset += n; |
| 534 | } |
| 535 | } |
| 536 | return length; |
| 537 | } |
| 538 | |
| 539 | static int recv_end_of_file(int sock) { |
| 540 | uint8_t byte; |
| 541 | return recv(sock, &byte, 1, 0) == 0; |
| 542 | } |
| 543 | |
| 544 | static void send_code(int sock, int8_t code) { |
| 545 | send(sock, &code, 1, 0); |
| 546 | } |
| 547 | |
| 548 | static void send_message(int sock, uint8_t* message, int length) { |
| 549 | uint16_t bytes = htons(length); |
| 550 | send(sock, &bytes, 2, 0); |
| 551 | send(sock, message, length, 0); |
| 552 | } |
| 553 | |
| 554 | /* Here are the actions. Each of them is a function without arguments. All |
| 555 | * information is defined in global variables, which are set properly before |
| 556 | * performing an action. The number of parameters required by each action is |
| 557 | * fixed and defined in a table. If the return value of an action is positive, |
| 558 | * it will be treated as a response code and transmitted to the client. Note |
| 559 | * that the lengths of parameters are checked when they are received, so |
| 560 | * boundary checks on parameters are omitted. */ |
| 561 | |
| 562 | static const ResponseCode NO_ERROR_RESPONSE_CODE_SENT = (ResponseCode) 0; |
| 563 | |
| 564 | static ResponseCode test(KeyStore* keyStore, int sock, uid_t uid, Value*, Value*) { |
| 565 | return (ResponseCode) keyStore->getState(); |
| 566 | } |
| 567 | |
| 568 | static ResponseCode get(KeyStore* keyStore, int sock, uid_t uid, Value* keyName, Value*) { |
| 569 | char filename[NAME_MAX]; |
| 570 | encode_key(filename, uid, keyName); |
| 571 | Blob keyBlob; |
| 572 | ResponseCode responseCode = keyStore->get(filename, &keyBlob); |
| 573 | if (responseCode != NO_ERROR) { |
| 574 | return responseCode; |
| 575 | } |
| 576 | send_code(sock, NO_ERROR); |
| 577 | send_message(sock, keyBlob.getValue(), keyBlob.getLength()); |
| 578 | return NO_ERROR_RESPONSE_CODE_SENT; |
| 579 | } |
| 580 | |
| 581 | static ResponseCode insert(KeyStore* keyStore, int sock, uid_t uid, Value* keyName, Value* val) { |
| 582 | char filename[NAME_MAX]; |
| 583 | encode_key(filename, uid, keyName); |
| 584 | Blob keyBlob(val->value, val->length, 0, NULL); |
| 585 | return keyStore->put(filename, &keyBlob); |
| 586 | } |
| 587 | |
| 588 | static ResponseCode del(KeyStore* keyStore, int sock, uid_t uid, Value* keyName, Value*) { |
| 589 | char filename[NAME_MAX]; |
| 590 | encode_key(filename, uid, keyName); |
| 591 | return (unlink(filename) && errno != ENOENT) ? SYSTEM_ERROR : NO_ERROR; |
| 592 | } |
| 593 | |
| 594 | static ResponseCode exist(KeyStore* keyStore, int sock, uid_t uid, Value* keyName, Value*) { |
| 595 | char filename[NAME_MAX]; |
| 596 | encode_key(filename, uid, keyName); |
| 597 | if (access(filename, R_OK) == -1) { |
| 598 | return (errno != ENOENT) ? SYSTEM_ERROR : KEY_NOT_FOUND; |
| 599 | } |
| 600 | return NO_ERROR; |
| 601 | } |
| 602 | |
| 603 | static ResponseCode saw(KeyStore* keyStore, int sock, uid_t uid, Value* keyPrefix, Value*) { |
| 604 | DIR* dir = opendir("."); |
| 605 | if (!dir) { |
| 606 | return SYSTEM_ERROR; |
| 607 | } |
| 608 | char filename[NAME_MAX]; |
| 609 | int n = encode_key(filename, uid, keyPrefix); |
| 610 | send_code(sock, NO_ERROR); |
| 611 | |
| 612 | struct dirent* file; |
| 613 | while ((file = readdir(dir)) != NULL) { |
| 614 | if (!strncmp(filename, file->d_name, n)) { |
| 615 | char* p = &file->d_name[n]; |
| 616 | keyPrefix->length = decode_key(keyPrefix->value, p, strlen(p)); |
| 617 | send_message(sock, keyPrefix->value, keyPrefix->length); |
| 618 | } |
| 619 | } |
| 620 | closedir(dir); |
| 621 | return NO_ERROR_RESPONSE_CODE_SENT; |
| 622 | } |
| 623 | |
| 624 | static ResponseCode reset(KeyStore* keyStore, int sock, uid_t uid, Value*, Value*) { |
| 625 | return keyStore->reset() ? NO_ERROR : SYSTEM_ERROR; |
| 626 | } |
| 627 | |
| 628 | /* Here is the history. To improve the security, the parameters to generate the |
| 629 | * master key has been changed. To make a seamless transition, we update the |
| 630 | * file using the same password when the user unlock it for the first time. If |
| 631 | * any thing goes wrong during the transition, the new file will not overwrite |
| 632 | * the old one. This avoids permanent damages of the existing data. */ |
| 633 | |
| 634 | static ResponseCode password(KeyStore* keyStore, int sock, uid_t uid, Value* pw, Value*) { |
| 635 | switch (keyStore->getState()) { |
| 636 | case STATE_UNINITIALIZED: { |
| 637 | // generate master key, encrypt with password, write to file, initialize mMasterKey*. |
| 638 | return keyStore->initialize(pw); |
| 639 | } |
| 640 | case STATE_NO_ERROR: { |
| 641 | // rewrite master key with new password. |
| 642 | return keyStore->writeMasterKey(pw); |
| 643 | } |
| 644 | case STATE_LOCKED: { |
| 645 | // read master key, decrypt with password, initialize mMasterKey*. |
| 646 | return keyStore->readMasterKey(pw); |
| 647 | } |
| 648 | } |
| 649 | return SYSTEM_ERROR; |
| 650 | } |
| 651 | |
| 652 | static ResponseCode lock(KeyStore* keyStore, int sock, uid_t uid, Value*, Value*) { |
| 653 | keyStore->lock(); |
| 654 | return NO_ERROR; |
| 655 | } |
| 656 | |
| 657 | static ResponseCode unlock(KeyStore* keyStore, int sock, uid_t uid, Value* pw, Value* unused) { |
| 658 | return password(keyStore, sock, uid, pw, unused); |
| 659 | } |
| 660 | |
| 661 | static ResponseCode zero(KeyStore* keyStore, int sock, uid_t uid, Value*, Value*) { |
| 662 | return keyStore->isEmpty() ? KEY_NOT_FOUND : NO_ERROR; |
| 663 | } |
| 664 | |
| 665 | /* Here are the permissions, actions, users, and the main function. */ |
| 666 | |
| 667 | enum perm { |
| 668 | TEST = 1, |
| 669 | GET = 2, |
| 670 | INSERT = 4, |
| 671 | DELETE = 8, |
| 672 | EXIST = 16, |
| 673 | SAW = 32, |
| 674 | RESET = 64, |
| 675 | PASSWORD = 128, |
| 676 | LOCK = 256, |
| 677 | UNLOCK = 512, |
| 678 | ZERO = 1024, |
| 679 | }; |
| 680 | |
| 681 | static const int MAX_PARAM = 2; |
| 682 | |
| 683 | static const State STATE_ANY = (State) 0; |
| 684 | |
| 685 | static struct action { |
| 686 | ResponseCode (*run)(KeyStore* keyStore, int sock, uid_t uid, Value* param1, Value* param2); |
| 687 | int8_t code; |
| 688 | State state; |
| 689 | uint32_t perm; |
| 690 | int lengths[MAX_PARAM]; |
| 691 | } actions[] = { |
| 692 | {test, 't', STATE_ANY, TEST, {0, 0}}, |
| 693 | {get, 'g', STATE_NO_ERROR, GET, {KEY_SIZE, 0}}, |
| 694 | {insert, 'i', STATE_NO_ERROR, INSERT, {KEY_SIZE, VALUE_SIZE}}, |
| 695 | {del, 'd', STATE_ANY, DELETE, {KEY_SIZE, 0}}, |
| 696 | {exist, 'e', STATE_ANY, EXIST, {KEY_SIZE, 0}}, |
| 697 | {saw, 's', STATE_ANY, SAW, {KEY_SIZE, 0}}, |
| 698 | {reset, 'r', STATE_ANY, RESET, {0, 0}}, |
| 699 | {password, 'p', STATE_ANY, PASSWORD, {PASSWORD_SIZE, 0}}, |
| 700 | {lock, 'l', STATE_NO_ERROR, LOCK, {0, 0}}, |
| 701 | {unlock, 'u', STATE_LOCKED, UNLOCK, {PASSWORD_SIZE, 0}}, |
| 702 | {zero, 'z', STATE_ANY, ZERO, {0, 0}}, |
| 703 | {NULL, 0 , STATE_ANY, 0, {0, 0}}, |
| 704 | }; |
| 705 | |
| 706 | static struct user { |
| 707 | uid_t uid; |
| 708 | uid_t euid; |
| 709 | uint32_t perms; |
| 710 | } users[] = { |
Chia-chi Yeh | 14f1486 | 2011-06-26 18:24:19 -0700 | [diff] [blame] | 711 | {AID_SYSTEM, ~0, ~0}, |
Brian Carlstrom | 5cfee3f | 2011-05-31 01:00:15 -0700 | [diff] [blame] | 712 | {AID_VPN, AID_SYSTEM, GET}, |
| 713 | {AID_WIFI, AID_SYSTEM, GET}, |
| 714 | {AID_ROOT, AID_SYSTEM, GET}, |
Brian Carlstrom | 5cfee3f | 2011-05-31 01:00:15 -0700 | [diff] [blame] | 715 | {~0, ~0, TEST | GET | INSERT | DELETE | EXIST | SAW}, |
| 716 | }; |
| 717 | |
| 718 | static ResponseCode process(KeyStore* keyStore, int sock, uid_t uid, int8_t code) { |
| 719 | struct user* user = users; |
| 720 | struct action* action = actions; |
| 721 | int i; |
| 722 | |
| 723 | while (~user->uid && user->uid != uid) { |
| 724 | ++user; |
| 725 | } |
| 726 | while (action->code && action->code != code) { |
| 727 | ++action; |
| 728 | } |
| 729 | if (!action->code) { |
| 730 | return UNDEFINED_ACTION; |
| 731 | } |
| 732 | if (!(action->perm & user->perms)) { |
| 733 | return PERMISSION_DENIED; |
| 734 | } |
| 735 | if (action->state != STATE_ANY && action->state != keyStore->getState()) { |
| 736 | return (ResponseCode) keyStore->getState(); |
| 737 | } |
| 738 | if (~user->euid) { |
| 739 | uid = user->euid; |
| 740 | } |
| 741 | Value params[MAX_PARAM]; |
| 742 | for (i = 0; i < MAX_PARAM && action->lengths[i] != 0; ++i) { |
| 743 | params[i].length = recv_message(sock, params[i].value, action->lengths[i]); |
| 744 | if (params[i].length < 0) { |
| 745 | return PROTOCOL_ERROR; |
| 746 | } |
| 747 | } |
| 748 | if (!recv_end_of_file(sock)) { |
| 749 | return PROTOCOL_ERROR; |
| 750 | } |
| 751 | return action->run(keyStore, sock, uid, ¶ms[0], ¶ms[1]); |
| 752 | } |
| 753 | |
| 754 | int main(int argc, char* argv[]) { |
| 755 | int controlSocket = android_get_control_socket("keystore"); |
| 756 | if (argc < 2) { |
| 757 | LOGE("A directory must be specified!"); |
| 758 | return 1; |
| 759 | } |
| 760 | if (chdir(argv[1]) == -1) { |
| 761 | LOGE("chdir: %s: %s", argv[1], strerror(errno)); |
| 762 | return 1; |
| 763 | } |
| 764 | |
| 765 | Entropy entropy; |
| 766 | if (!entropy.open()) { |
| 767 | return 1; |
| 768 | } |
| 769 | if (listen(controlSocket, 3) == -1) { |
| 770 | LOGE("listen: %s", strerror(errno)); |
| 771 | return 1; |
| 772 | } |
| 773 | |
| 774 | signal(SIGPIPE, SIG_IGN); |
| 775 | |
| 776 | KeyStore keyStore(&entropy); |
| 777 | int sock; |
| 778 | while ((sock = accept(controlSocket, NULL, 0)) != -1) { |
| 779 | struct timeval tv; |
| 780 | tv.tv_sec = 3; |
| 781 | setsockopt(sock, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv)); |
| 782 | setsockopt(sock, SOL_SOCKET, SO_SNDTIMEO, &tv, sizeof(tv)); |
| 783 | |
| 784 | struct ucred cred; |
| 785 | socklen_t size = sizeof(cred); |
| 786 | int credResult = getsockopt(sock, SOL_SOCKET, SO_PEERCRED, &cred, &size); |
| 787 | if (credResult != 0) { |
| 788 | LOGW("getsockopt: %s", strerror(errno)); |
| 789 | } else { |
| 790 | int8_t request; |
| 791 | if (recv_code(sock, &request)) { |
| 792 | State old_state = keyStore.getState(); |
| 793 | ResponseCode response = process(&keyStore, sock, cred.uid, request); |
| 794 | if (response == NO_ERROR_RESPONSE_CODE_SENT) { |
| 795 | response = NO_ERROR; |
| 796 | } else { |
| 797 | send_code(sock, response); |
| 798 | } |
Steve Block | 6215d3f | 2012-01-04 20:05:49 +0000 | [diff] [blame] | 799 | ALOGI("uid: %d action: %c -> %d state: %d -> %d retry: %d", |
Brian Carlstrom | 5cfee3f | 2011-05-31 01:00:15 -0700 | [diff] [blame] | 800 | cred.uid, |
| 801 | request, response, |
| 802 | old_state, keyStore.getState(), |
| 803 | keyStore.getRetry()); |
| 804 | } |
| 805 | } |
| 806 | close(sock); |
| 807 | } |
| 808 | LOGE("accept: %s", strerror(errno)); |
| 809 | return 1; |
| 810 | } |