Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 7d9e45b2 authored by Android (Google) Code Review's avatar Android (Google) Code Review Committed by The Android Open Source Project
Browse files

am d45dcbec: Merge change 5615 into donut

Merge commit 'd45dcbec'

* commit 'd45dcbec':
  Implement the generic mini-keystore for security.
parents 92aaac8c d45dcbec
Loading
Loading
Loading
Loading
+4 −3
Original line number Diff line number Diff line
@@ -4,13 +4,14 @@ LOCAL_PATH:= $(call my-dir)
include $(CLEAR_VARS)

LOCAL_SRC_FILES:= \
    keystore.c commands.c
    netkeystore.c keymgmt.c

LOCAL_C_INCLUDES := \
    $(call include-path-for, system-core)/cutils
    $(call include-path-for, system-core)/cutils \
    external/openssl/include

LOCAL_SHARED_LIBRARIES := \
    libcutils
    libcutils libssl

LOCAL_STATIC_LIBRARIES :=

+83 −0
Original line number Diff line number Diff line
/*
**
** Copyright 2009, The Android Open Source Project
**
** Licensed under the Apache License, Version 2.0 (the "License");
** you may not use this file except in compliance with the License.
** You may obtain a copy of the License at
**
**     http://www.apache.org/licenses/LICENSE-2.0
**
** Unless required by applicable law or agreed to in writing, software
** distributed under the License is distributed on an "AS IS" BASIS,
** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
** See the License for the specific language governing permissions and
** limitations under the License.
*/

#ifndef __CERTTOOL_H__
#define __CERTTOOL_H__

#include <stdio.h>
#include <string.h>
#include <cutils/sockets.h>
#include <cutils/log.h>

#include "common.h"
#include "netkeystore.h"

/*
 * The specific function 'get_cert' is used in daemons to get the key value
 * from keystore. Caller should allocate the buffer and the length of the buffer
 * should be MAX_KEY_VALUE_LENGTH.
 */
static inline int get_cert(char *certname, unsigned char *value, int *size)
{
    int count, fd, ret = -1;
    LPC_MARSHAL cmd;
    char delimiter[] = "_";
    char *namespace, *keyname;
    char *context = NULL;

    if (value == NULL) {
        LOGE("get_cert: value is null\n");
        return -1;
    }

    fd = socket_local_client(SOCKET_PATH,
                             ANDROID_SOCKET_NAMESPACE_RESERVED,
                             SOCK_STREAM);
    if (fd == -1) {
        LOGE("Keystore service is not up and running.\n");
        return -1;
    }

    cmd.opcode = GET;
    if (((namespace = strtok_r(certname, delimiter, &context)) == NULL) ||
        ((keyname = strtok_r(NULL, delimiter, &context)) == NULL)) {
        goto err;
    }
    if ((cmd.len = snprintf((char*)cmd.data, BUFFER_MAX, "%s %s", namespace, keyname))
        > (2 * MAX_KEY_NAME_LENGTH + 1)) goto err;

    if (write_marshal(fd, &cmd)) {
        LOGE("Incorrect command or command line is too long.\n");
        goto err;
    }
    if (read_marshal(fd, &cmd)) {
        LOGE("Failed to read the result.\n");
        goto err;
    }

    // copy the result if succeeded.
    if (!cmd.retcode && cmd.len <= BUFFER_MAX) {
        memcpy(value, cmd.data, cmd.len);
        ret = 0;
        *size = cmd.len;
    }
err:
    close(fd);
    return ret;
}

#endif

cmds/keystore/commands.c

deleted100644 → 0
+0 −227
Original line number Diff line number Diff line
/*
** Copyright 2009, The Android Open Source Project
**
** Licensed under the Apache License, Version 2.0 (the "License");
** you may not use this file except in compliance with the License.
** You may obtain a copy of the License at
**
**     http://www.apache.org/licenses/LICENSE-2.0
**
** Unless required by applicable law or agreed to in writing, software
** distributed under the License is distributed on an "AS IS" BASIS,
** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
** See the License for the specific language governing permissions and
** limitations under the License.
*/

#include "keystore.h"

static DIR *open_keystore(const char *dir)
{
    DIR *d;
    if ((d = opendir(dir)) == NULL) {
        if (mkdir(dir, 0770) < 0) {
            LOGE("cannot create dir '%s': %s\n", dir, strerror(errno));
            unlink(dir);
            return NULL;
        }
        d = open_keystore(dir);
    }
    return d;
}

static int list_files(const char *dir, char reply[REPLY_MAX])
{
    struct dirent *de;
    DIR *d;

    if ((d = open_keystore(dir)) == NULL) {
        return -1;
    }
    reply[0]=0;
    while ((de = readdir(d))) {
        if (de->d_type != DT_DIR) continue;
        if ((strcmp(DOT, de->d_name) == 0) ||
                (strcmp(DOTDOT, de->d_name) == 0)) continue;
        if (reply[0] != 0) strlcat(reply, " ", REPLY_MAX);
        if (strlcat(reply, de->d_name, REPLY_MAX) >= REPLY_MAX) {
            LOGE("reply is too long(too many files under '%s'\n", dir);
            return -1;
        }
    }
    closedir(d);
    return 0;
}

static int copy_keyfile(const char *src, int src_type, const char *dstfile) {
    int srcfd = -1, dstfd;
    char buf[REPLY_MAX];

    if ((src_type == IS_FILE) && (srcfd = open(src, O_RDONLY)) == -1) {
        LOGE("Cannot open the original file '%s'\n", src);
        return -1;
    }
    if ((dstfd = open(dstfile, O_CREAT|O_RDWR)) == -1) {
        LOGE("Cannot open the destination file '%s'\n", dstfile);
        return -1;
    }
    if (src_type == IS_FILE) {
        int length;
        while((length = read(srcfd, buf, REPLY_MAX)) > 0) {
            write(dstfd, buf, length);
        }
    } else {
        write(dstfd, src, strlen(src));
    }
    close(srcfd);
    close(dstfd);
    chmod(dstfile, 0440);
    return 0;
}

static int install_key(const char *path, const char *certname, const char *src,
        int src_is_file, char *dstfile)
{
    struct dirent *de;
    char fullpath[KEYNAME_LENGTH];
    DIR *d;

    if (snprintf(fullpath, sizeof(fullpath), "%s/%s/", path, certname)
            >= KEYNAME_LENGTH) {
        LOGE("cert name '%s' is too long.\n", certname);
        return -1;
    }

    if ((d = open_keystore(fullpath)) == NULL) {
        LOGE("Can not open the keystore '%s'\n", fullpath);
        return -1;
    }
    closedir(d);
    if (strlcat(fullpath, dstfile, KEYNAME_LENGTH) >= KEYNAME_LENGTH) {
        LOGE("cert name '%s' is too long.\n", certname);
        return -1;
    }
    return copy_keyfile(src, src_is_file, fullpath);
}

static int get_key(const char *path, const char *keyname, const char *file,
        char reply[REPLY_MAX])
{
    struct dirent *de;
    char filename[KEYNAME_LENGTH];
    int fd;

    if (snprintf(filename, sizeof(filename), "%s/%s/%s", path, keyname, file)
            >= KEYNAME_LENGTH) {
        LOGE("cert name '%s' is too long.\n", keyname);
        return -1;
    }

    if ((fd = open(filename, O_RDONLY)) == -1) {
        return -1;
    }
    close(fd);
    strlcpy(reply, filename, REPLY_MAX);
    return 0;
}

static int remove_key(const char *dir, const char *key)
{
    char dstfile[KEYNAME_LENGTH];
    char *keyfile[4] = { USER_KEY, USER_P12_CERT, USER_CERTIFICATE,
            CA_CERTIFICATE };
    int i, count = 0;

    for ( i = 0 ; i < 4 ; i++) {
        if (snprintf(dstfile, KEYNAME_LENGTH, "%s/%s/%s", dir, key, keyfile[i])
                >= KEYNAME_LENGTH) {
            LOGE("keyname is too long '%s'\n", key);
            return -1;
        }
        if (unlink(dstfile) == 0) count++;
    }

    if (count == 0) {
        LOGE("can not clean up '%s' keys or not exist\n", key);
        return -1;
    }

    snprintf(dstfile, KEYNAME_LENGTH, "%s/%s", dir, key);
    if (rmdir(dstfile)) {
        LOGE("can not clean up '%s' directory\n", key);
        return -1;
    }
    return 0;
}

int list_user_certs(char reply[REPLY_MAX])
{
    return list_files(CERTS_DIR, reply);
}

int list_ca_certs(char reply[REPLY_MAX])
{
    return list_files(CACERTS_DIR, reply);
}

int install_user_cert(const char *keyname, const char *cert, const char *key)
{
    if (install_key(CERTS_DIR, keyname, cert, IS_FILE, USER_CERTIFICATE) == 0) {
        return install_key(CERTS_DIR, keyname, key, IS_FILE, USER_KEY);
    }
    return -1;
}

int install_ca_cert(const char *keyname, const char *certfile)
{
    return install_key(CACERTS_DIR, keyname, certfile, IS_FILE, CA_CERTIFICATE);
}

int install_p12_cert(const char *keyname, const char *certfile)
{
    return install_key(CERTS_DIR, keyname, certfile, IS_FILE, USER_P12_CERT);
}

int add_ca_cert(const char *keyname, const char *certificate)
{
    return install_key(CACERTS_DIR, keyname, certificate, IS_CONTENT,
            CA_CERTIFICATE);
}

int add_user_cert(const char *keyname, const char *certificate)
{
    return install_key(CERTS_DIR, keyname, certificate, IS_CONTENT,
            USER_CERTIFICATE);
}

int add_user_key(const char *keyname, const char *key)
{
    return install_key(CERTS_DIR, keyname, key, IS_CONTENT, USER_KEY);
}

int get_ca_cert(const char *keyname, char reply[REPLY_MAX])
{
    return get_key(CACERTS_DIR, keyname, CA_CERTIFICATE, reply);
}

int get_user_cert(const char *keyname, char reply[REPLY_MAX])
{
    return get_key(CERTS_DIR, keyname, USER_CERTIFICATE, reply);
}

int get_user_key(const char *keyname, char reply[REPLY_MAX])
{
    if(get_key(CERTS_DIR, keyname, USER_KEY, reply))
        return get_key(CERTS_DIR, keyname, USER_P12_CERT, reply);
    return 0;
}

int remove_user_cert(const char *key)
{
    return remove_key(CERTS_DIR, key);
}

int remove_ca_cert(const char *key)
{
    return remove_key(CACERTS_DIR, key);
}

cmds/keystore/common.h

0 → 100644
+60 −0
Original line number Diff line number Diff line
/*
**
** Copyright 2009, The Android Open Source Project
**
** Licensed under the Apache License, Version 2.0 (the "License");
** you may not use this file except in compliance with the License.
** You may obtain a copy of the License at
**
**     http://www.apache.org/licenses/LICENSE-2.0
**
** Unless required by applicable law or agreed to in writing, software
** distributed under the License is distributed on an "AS IS" BASIS,
** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
** See the License for the specific language governing permissions and
** limitations under the License.
*/

#ifndef __COMMON_H__
#define __COMMON_H__

#define SOCKET_PATH             "keystore"
#define KEYSTORE_DIR            "/data/misc/keystore/"

#define READ_TIMEOUT            3
#define MAX_KEY_NAME_LENGTH     64
#define MAX_NAMESPACE_LENGTH    MAX_KEY_NAME_LENGTH
#define MAX_KEY_VALUE_LENGTH    4096

#define BUFFER_MAX              MAX_KEY_VALUE_LENGTH

typedef enum {
    BOOTUP,
    UNINITIALIZED,
    LOCKED,
    UNLOCKED,
} KEYSTORE_STATE;

typedef enum {
    LOCK,
    UNLOCK,
    PASSWD,
    GETSTATE,
    LISTKEYS,
    GET,
    PUT,
    REMOVE,
    RESET,
    MAX_OPCODE
} KEYSTORE_OPCODE;

typedef struct {
    uint32_t  len;
    union {
        uint32_t  opcode;
        uint32_t  retcode;
    };
    unsigned char data[BUFFER_MAX + 1];
} LPC_MARSHAL;

#endif
+365 −0
Original line number Diff line number Diff line
/*
** Copyright 2009, The Android Open Source Project
**
** Licensed under the Apache License, Version 2.0 (the "License");
** you may not use this file except in compliance with the License.
** You may obtain a copy of the License at
**
**     http://www.apache.org/licenses/LICENSE-2.0
**
** Unless required by applicable law or agreed to in writing, software
** distributed under the License is distributed on an "AS IS" BASIS,
** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
** See the License for the specific language governing permissions and
** limitations under the License.
*/

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <ctype.h>
#include <fcntl.h>
#include <dirent.h>
#include <errno.h>
#include <openssl/aes.h>
#include <openssl/evp.h>
#include <cutils/log.h>

#include "common.h"
#include "keymgmt.h"

static int  retry_count = 0;
static unsigned char iv[IV_LEN];
static KEYSTORE_STATE state = BOOTUP;
static AES_KEY encryptKey, decryptKey;

inline void unlock_keystore(unsigned char *master_key)
{
    AES_set_encrypt_key(master_key, AES_KEY_LEN, &encryptKey);
    AES_set_decrypt_key(master_key, AES_KEY_LEN, &decryptKey);
    memset(master_key, 0, sizeof(master_key));
    state = UNLOCKED;
}

inline void lock_keystore()
{
    memset(&encryptKey, 0 , sizeof(AES_KEY));
    memset(&decryptKey, 0 , sizeof(AES_KEY));
    state = LOCKED;
}

inline void get_encrypt_key(char *passwd, AES_KEY *key)
{
    unsigned char user_key[USER_KEY_LEN];
    gen_key(passwd, user_key, USER_KEY_LEN);
    AES_set_encrypt_key(user_key, AES_KEY_LEN, key);
}

inline void get_decrypt_key(char *passwd, AES_KEY *key)
{
    unsigned char user_key[USER_KEY_LEN];
    gen_key(passwd, user_key, USER_KEY_LEN);
    AES_set_decrypt_key(user_key, AES_KEY_LEN, key);
}

static int gen_random_blob(unsigned char *key, int size)
{
    int ret = 0;
    int fd = open("/dev/urandom", O_RDONLY);
    if (fd == -1) return -1;
    if (read(fd, key, size) != size) ret = -1;
    close(fd);
    return ret;
}

static int encrypt_n_save(AES_KEY *enc_key, DATA_BLOB *blob,
                          const char *keyfile)
{
    int size, fd, ret = -1;
    unsigned char enc_blob[MAX_BLOB_LEN];

    char tmpfile[KEYFILE_LEN];
    strcpy(tmpfile, keyfile);
    strcat(tmpfile, ".tmp");

    // prepare the blob
    memcpy(blob->iv, iv, IV_LEN);
    blob->blob_size = get_blob_size(blob);
    memcpy(enc_blob, blob->blob, blob->blob_size);
    AES_cbc_encrypt((unsigned char *)enc_blob, (unsigned char *)blob->blob,
                    blob->blob_size, enc_key, iv, AES_ENCRYPT);
    // write to keyfile
    size = data_blob_size(blob);
    if ((fd = open(tmpfile, O_CREAT|O_RDWR)) == -1) return -1;
    if (write(fd, blob, size) == size) ret = 0;
    close(fd);
    if (!ret) {
        unlink(keyfile);
        rename(tmpfile, keyfile);
        chmod(keyfile, 0440);
    }
    return ret;
}

static int load_n_decrypt(const char *keyname, const char *keyfile,
                          AES_KEY *key, DATA_BLOB *blob)
{
    int fd, ret = -1;
    if ((fd = open(keyfile, O_RDONLY)) == -1) return -1;
    // get the encrypted blob and iv
    if ((read(fd, blob->iv, sizeof(blob->iv)) != sizeof(blob->iv)) ||
        (read(fd, &blob->blob_size, sizeof(uint32_t)) != sizeof(uint32_t)) ||
        (blob->blob_size > MAX_BLOB_LEN)) {
        goto err;
    } else {
        unsigned char enc_blob[MAX_BLOB_LEN];
        if (read(fd, enc_blob, blob->blob_size) !=
            (int) blob->blob_size) goto err;
        // decrypt the blob
        AES_cbc_encrypt((unsigned char *)enc_blob, (unsigned char*)blob->blob,
                        blob->blob_size, key, blob->iv, AES_DECRYPT);
        if (strcmp(keyname, (char*)blob->keyname) == 0) ret = 0;
    }
err:
    close(fd);
    return ret;
}

static int store_master_key(char *upasswd, unsigned char *master_key)
{
    AES_KEY key;
    DATA_BLOB blob;

    // prepare the blob
    strlcpy(blob.keyname, MASTER_KEY_TAG, USER_KEY_LEN);
    blob.value_size = USER_KEY_LEN;
    memcpy((void*)blob.value, (const void*)master_key, USER_KEY_LEN);

    // generate the encryption key
    get_encrypt_key(upasswd, &key);
    return encrypt_n_save(&key, &blob, MASTER_KEY);
}

static int get_master_key(char *upasswd, unsigned char *master_key)
{
    AES_KEY key;
    int size, ret = 0;
    DATA_BLOB blob;

    get_decrypt_key(upasswd, &key);
    ret = load_n_decrypt(MASTER_KEY_TAG, MASTER_KEY, &key, &blob);
    if (!ret) memcpy(master_key, blob.value, blob.value_size);
    return ret;
}

static int create_master_key(char *upasswd)
{
    int ret;
    unsigned char mpasswd[AES_KEY_LEN];
    unsigned char master_key[USER_KEY_LEN];

    gen_random_blob(mpasswd, AES_KEY_LEN);
    gen_key((char*)mpasswd, master_key, USER_KEY_LEN);
    if ((ret = store_master_key(upasswd, master_key)) == 0) {
        unlock_keystore(master_key);
    }
    memset(master_key, 0, USER_KEY_LEN);
    memset(mpasswd, 0, AES_KEY_LEN);

    return ret;
}

static int change_passwd(char *data)
{
    unsigned char master_key[USER_KEY_LEN];
    char *old_pass, *new_pass = NULL, *p, *delimiter=" ";
    int ret, count = 0;
    char *context = NULL;

    old_pass = p = strtok_r(data, delimiter, &context);
    while (p != NULL) {
        count++;
        new_pass = p;
        p = strtok_r(NULL, delimiter, &context);
    }
    if (count != 2) return -1;
    if ((ret = get_master_key(old_pass, master_key)) == 0) {
        ret = store_master_key(new_pass, master_key);
        retry_count = 0;
    } else {
        ret = MAX_RETRY_COUNT - ++retry_count;
        if (ret == 0) {
            retry_count = 0;
            LOGE("passwd:reach max retry count, reset the keystore now.");
            reset_keystore();
            return -1;
        }

    }
    return ret;
}

int remove_key(const char *namespace, const char *keyname)
{
    char keyfile[KEYFILE_LEN];

    if (state != UNLOCKED) return -state;
    sprintf(keyfile, KEYFILE_NAME, namespace, keyname);
    return unlink(keyfile);
}

int put_key(const char *namespace, const char *keyname,
            unsigned char *data, int size)
{
    DATA_BLOB blob;
    uint32_t  real_size;
    char keyfile[KEYFILE_LEN];

    if (state != UNLOCKED) {
        LOGE("Can not store key with current state %d\n", state);
        return -state;
    }
    sprintf(keyfile, KEYFILE_NAME, namespace, keyname);
    // flatten the args
    strcpy(blob.keyname, keyname);
    blob.value_size = size;
    memcpy(blob.value, data, size);
    return encrypt_n_save(&encryptKey, &blob, keyfile);
}

int get_key(const char *namespace, const char *keyname,
            unsigned char *data, int *size)
{
    int ret;
    DATA_BLOB blob;
    uint32_t  blob_size;
    char keyfile[KEYFILE_LEN];

    if (state != UNLOCKED) {
        LOGE("Can not retrieve key value with current state %d\n", state);
        return -state;
    }
    sprintf(keyfile, KEYFILE_NAME, namespace, keyname);
    ret = load_n_decrypt(keyname, keyfile, &decryptKey, &blob);
    if (!ret) {
        if ((blob.value_size > MAX_KEY_VALUE_LENGTH)) {
            ret = -1;
        } else {
            *size = blob.value_size;
            memcpy(data, blob.value, *size);
        }
    }
    return ret;
}

int list_keys(const char *namespace, char reply[BUFFER_MAX])
{
    DIR *d;
    struct dirent *de;

    if (!namespace || ((d = opendir("."))) == NULL) {
        LOGE("cannot open keystore dir or namespace is null\n");
        return -1;
    }
    while ((de = readdir(d))) {
        char *prefix, *name, *keyfile = de->d_name;
        char *context = NULL;

        if (de->d_type != DT_REG) continue;
        if ((prefix = strtok_r(keyfile, NAME_DELIMITER, &context))
            == NULL) continue;
        if (strcmp(prefix, namespace)) continue;
        if ((name = strtok_r(NULL, NAME_DELIMITER, &context)) == NULL) continue;
        // append the key name into reply
        if (reply[0] != 0) strlcat(reply, " ", BUFFER_MAX);
        if (strlcat(reply, name, BUFFER_MAX) >= BUFFER_MAX) {
            LOGE("too many files under keystore directory\n");
            return -1;
        }
    }
    closedir(d);
    return 0;
}

int passwd(char *data)
{
    if (state == UNINITIALIZED) {
        if (strchr(data, ' ')) return -1;
        return create_master_key(data);
    }
    return change_passwd(data);
}

int lock()
{
    switch(state) {
        case UNLOCKED:
            lock_keystore();
        case LOCKED:
            return 0;
        default:
            return -1;
    }
}

int unlock(char *passwd)
{
    unsigned char master_key[USER_KEY_LEN];
    int ret = get_master_key(passwd, master_key);
    if (!ret) {
        unlock_keystore(master_key);
        retry_count = 0;
    } else {
        ret = MAX_RETRY_COUNT - ++retry_count;
        if (ret == 0) {
            retry_count = 0;
            LOGE("unlock:reach max retry count, reset the keystore now.");
            reset_keystore();
            return -1;
        }
    }
    return ret;
}

KEYSTORE_STATE get_state()
{
    return state;
}

int reset_keystore()
{
    DIR *d;
    struct dirent *de;

    if ((d = opendir(".")) == NULL) {
        LOGE("cannot open keystore dir\n");
        return -1;
    }
    while ((de = readdir(d))) unlink(de->d_name);
    closedir(d);
    state = UNINITIALIZED;
    LOGI("keystore is reset.");
    return 0;
}

int init_keystore(const char *dir)
{
    int fd;

    if (!dir) mkdir(dir, 0770);
    if (!dir || chdir(dir)) {
        LOGE("Can not open/create the keystore directory %s\n",
             dir ? dir : "(null)");
        return -1;
    }
    gen_random_blob(iv, IV_LEN);
    if ((fd = open(MASTER_KEY, O_RDONLY)) == -1) {
        state = UNINITIALIZED;
        return 0;
    }
    close(fd);
    state = LOCKED;
    return 0;
}
Loading