1/*
2 * Copyright (C) 2016 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#define LOG_TAG "keystore"
18
19#include "user_state.h"
20
21#include <dirent.h>
22#include <fcntl.h>
23#include <stdio.h>
24#include <stdlib.h>
25#include <sys/stat.h>
26
27#include <openssl/evp.h>
28
29#include <cutils/log.h>
30
31#include "blob.h"
32#include "keystore_utils.h"
33
34
35UserState::UserState(uid_t userId) :
36        mUserId(userId), mState(STATE_UNINITIALIZED), mRetry(MAX_RETRY) {
37    asprintf(&mUserDir, "user_%u", mUserId);
38    asprintf(&mMasterKeyFile, "%s/.masterkey", mUserDir);
39}
40
41UserState::~UserState() {
42    free(mUserDir);
43    free(mMasterKeyFile);
44}
45
46bool UserState::initialize() {
47    if ((mkdir(mUserDir, S_IRUSR | S_IWUSR | S_IXUSR) < 0) && (errno != EEXIST)) {
48        ALOGE("Could not create directory '%s'", mUserDir);
49        return false;
50    }
51
52    if (access(mMasterKeyFile, R_OK) == 0) {
53        setState(STATE_LOCKED);
54    } else {
55        setState(STATE_UNINITIALIZED);
56    }
57
58    return true;
59}
60
61void UserState::setState(State state) {
62    mState = state;
63    if (mState == STATE_NO_ERROR || mState == STATE_UNINITIALIZED) {
64        mRetry = MAX_RETRY;
65    }
66}
67
68void UserState::zeroizeMasterKeysInMemory() {
69    memset(mMasterKey, 0, sizeof(mMasterKey));
70    memset(mSalt, 0, sizeof(mSalt));
71    memset(&mMasterKeyEncryption, 0, sizeof(mMasterKeyEncryption));
72    memset(&mMasterKeyDecryption, 0, sizeof(mMasterKeyDecryption));
73}
74
75bool UserState::deleteMasterKey() {
76    setState(STATE_UNINITIALIZED);
77    zeroizeMasterKeysInMemory();
78    return unlink(mMasterKeyFile) == 0 || errno == ENOENT;
79}
80
81ResponseCode UserState::initialize(const android::String8& pw, Entropy* entropy) {
82    if (!generateMasterKey(entropy)) {
83        return ResponseCode::SYSTEM_ERROR;
84    }
85    ResponseCode response = writeMasterKey(pw, entropy);
86    if (response != ResponseCode::NO_ERROR) {
87        return response;
88    }
89    setupMasterKeys();
90    return ResponseCode::NO_ERROR;
91}
92
93ResponseCode UserState::copyMasterKey(UserState* src) {
94    if (mState != STATE_UNINITIALIZED) {
95        return ResponseCode::SYSTEM_ERROR;
96    }
97    if (src->getState() != STATE_NO_ERROR) {
98        return ResponseCode::SYSTEM_ERROR;
99    }
100    memcpy(mMasterKey, src->mMasterKey, MASTER_KEY_SIZE_BYTES);
101    setupMasterKeys();
102    return copyMasterKeyFile(src);
103}
104
105ResponseCode UserState::copyMasterKeyFile(UserState* src) {
106    /* Copy the master key file to the new user.  Unfortunately we don't have the src user's
107     * password so we cannot generate a new file with a new salt.
108     */
109    int in = TEMP_FAILURE_RETRY(open(src->getMasterKeyFileName(), O_RDONLY));
110    if (in < 0) {
111        return ResponseCode::SYSTEM_ERROR;
112    }
113    blob rawBlob;
114    size_t length = readFully(in, (uint8_t*)&rawBlob, sizeof(rawBlob));
115    if (close(in) != 0) {
116        return ResponseCode::SYSTEM_ERROR;
117    }
118    int out =
119        TEMP_FAILURE_RETRY(open(mMasterKeyFile, O_WRONLY | O_TRUNC | O_CREAT, S_IRUSR | S_IWUSR));
120    if (out < 0) {
121        return ResponseCode::SYSTEM_ERROR;
122    }
123    size_t outLength = writeFully(out, (uint8_t*)&rawBlob, length);
124    if (close(out) != 0) {
125        return ResponseCode::SYSTEM_ERROR;
126    }
127    if (outLength != length) {
128        ALOGW("blob not fully written %zu != %zu", outLength, length);
129        unlink(mMasterKeyFile);
130        return ResponseCode::SYSTEM_ERROR;
131    }
132    return ResponseCode::NO_ERROR;
133}
134
135ResponseCode UserState::writeMasterKey(const android::String8& pw, Entropy* entropy) {
136    uint8_t passwordKey[MASTER_KEY_SIZE_BYTES];
137    generateKeyFromPassword(passwordKey, MASTER_KEY_SIZE_BYTES, pw, mSalt);
138    AES_KEY passwordAesKey;
139    AES_set_encrypt_key(passwordKey, MASTER_KEY_SIZE_BITS, &passwordAesKey);
140    Blob masterKeyBlob(mMasterKey, sizeof(mMasterKey), mSalt, sizeof(mSalt), TYPE_MASTER_KEY);
141    return masterKeyBlob.writeBlob(mMasterKeyFile, &passwordAesKey, STATE_NO_ERROR, entropy);
142}
143
144ResponseCode UserState::readMasterKey(const android::String8& pw, Entropy* entropy) {
145    int in = TEMP_FAILURE_RETRY(open(mMasterKeyFile, O_RDONLY));
146    if (in < 0) {
147        return ResponseCode::SYSTEM_ERROR;
148    }
149
150    // We read the raw blob to just to get the salt to generate the AES key, then we create the Blob
151    // to use with decryptBlob
152    blob rawBlob;
153    size_t length = readFully(in, (uint8_t*)&rawBlob, sizeof(rawBlob));
154    if (close(in) != 0) {
155        return ResponseCode::SYSTEM_ERROR;
156    }
157    // find salt at EOF if present, otherwise we have an old file
158    uint8_t* salt;
159    if (length > SALT_SIZE && rawBlob.info == SALT_SIZE) {
160        salt = (uint8_t*)&rawBlob + length - SALT_SIZE;
161    } else {
162        salt = NULL;
163    }
164    uint8_t passwordKey[MASTER_KEY_SIZE_BYTES];
165    generateKeyFromPassword(passwordKey, MASTER_KEY_SIZE_BYTES, pw, salt);
166    AES_KEY passwordAesKey;
167    AES_set_decrypt_key(passwordKey, MASTER_KEY_SIZE_BITS, &passwordAesKey);
168    Blob masterKeyBlob(rawBlob);
169    ResponseCode response = masterKeyBlob.readBlob(mMasterKeyFile, &passwordAesKey, STATE_NO_ERROR);
170    if (response == ResponseCode::SYSTEM_ERROR) {
171        return response;
172    }
173    if (response == ResponseCode::NO_ERROR && masterKeyBlob.getLength() == MASTER_KEY_SIZE_BYTES) {
174        // If salt was missing, generate one and write a new master key file with the salt.
175        if (salt == NULL) {
176            if (!generateSalt(entropy)) {
177                return ResponseCode::SYSTEM_ERROR;
178            }
179            response = writeMasterKey(pw, entropy);
180        }
181        if (response == ResponseCode::NO_ERROR) {
182            memcpy(mMasterKey, masterKeyBlob.getValue(), MASTER_KEY_SIZE_BYTES);
183            setupMasterKeys();
184        }
185        return response;
186    }
187    if (mRetry <= 0) {
188        reset();
189        return ResponseCode::UNINITIALIZED;
190    }
191    --mRetry;
192    switch (mRetry) {
193    case 0:
194        return ResponseCode::WRONG_PASSWORD_0;
195    case 1:
196        return ResponseCode::WRONG_PASSWORD_1;
197    case 2:
198        return ResponseCode::WRONG_PASSWORD_2;
199    case 3:
200        return ResponseCode::WRONG_PASSWORD_3;
201    default:
202        return ResponseCode::WRONG_PASSWORD_3;
203    }
204}
205
206bool UserState::reset() {
207    DIR* dir = opendir(getUserDirName());
208    if (!dir) {
209        // If the directory doesn't exist then nothing to do.
210        if (errno == ENOENT) {
211            return true;
212        }
213        ALOGW("couldn't open user directory: %s", strerror(errno));
214        return false;
215    }
216
217    struct dirent* file;
218    while ((file = readdir(dir)) != NULL) {
219        // skip . and ..
220        if (!strcmp(".", file->d_name) || !strcmp("..", file->d_name)) {
221            continue;
222        }
223
224        unlinkat(dirfd(dir), file->d_name, 0);
225    }
226    closedir(dir);
227    return true;
228}
229
230void UserState::generateKeyFromPassword(uint8_t* key, ssize_t keySize, const android::String8& pw,
231                                        uint8_t* salt) {
232    size_t saltSize;
233    if (salt != NULL) {
234        saltSize = SALT_SIZE;
235    } else {
236        // Pre-gingerbread used this hardwired salt, readMasterKey will rewrite these when found
237        salt = (uint8_t*)"keystore";
238        // sizeof = 9, not strlen = 8
239        saltSize = sizeof("keystore");
240    }
241
242    PKCS5_PBKDF2_HMAC_SHA1(reinterpret_cast<const char*>(pw.string()), pw.length(), salt, saltSize,
243                           8192, keySize, key);
244}
245
246bool UserState::generateSalt(Entropy* entropy) {
247    return entropy->generate_random_data(mSalt, sizeof(mSalt));
248}
249
250bool UserState::generateMasterKey(Entropy* entropy) {
251    if (!entropy->generate_random_data(mMasterKey, sizeof(mMasterKey))) {
252        return false;
253    }
254    if (!generateSalt(entropy)) {
255        return false;
256    }
257    return true;
258}
259
260void UserState::setupMasterKeys() {
261    AES_set_encrypt_key(mMasterKey, MASTER_KEY_SIZE_BITS, &mMasterKeyEncryption);
262    AES_set_decrypt_key(mMasterKey, MASTER_KEY_SIZE_BITS, &mMasterKeyDecryption);
263    setState(STATE_NO_ERROR);
264}
265