diff options
author | Android (Google) Code Review <android-gerrit@google.com> | 2009-07-02 00:16:21 -0700 |
---|---|---|
committer | Android (Google) Code Review <android-gerrit@google.com> | 2009-07-02 00:16:21 -0700 |
commit | d45dcbec856710f9478ffc5689e7cdf95d757ab8 (patch) | |
tree | 4e3a1b0ca6b579a394f1b7655a495f6e22912752 /cmds | |
parent | 40336782eaa797bca3d1487784af113ba3b143ff (diff) | |
parent | 70246ebb9b1a3c192caf7e001b45dafbdb6bbc30 (diff) | |
download | frameworks_base-d45dcbec856710f9478ffc5689e7cdf95d757ab8.zip frameworks_base-d45dcbec856710f9478ffc5689e7cdf95d757ab8.tar.gz frameworks_base-d45dcbec856710f9478ffc5689e7cdf95d757ab8.tar.bz2 |
Merge change 5615 into donut
* changes:
Implement the generic mini-keystore for security.
Diffstat (limited to 'cmds')
-rw-r--r-- | cmds/keystore/Android.mk | 7 | ||||
-rw-r--r-- | cmds/keystore/certtool.h | 83 | ||||
-rw-r--r-- | cmds/keystore/commands.c | 227 | ||||
-rw-r--r-- | cmds/keystore/common.h | 60 | ||||
-rw-r--r-- | cmds/keystore/keymgmt.c | 365 | ||||
-rw-r--r-- | cmds/keystore/keymgmt.h | 81 | ||||
-rw-r--r-- | cmds/keystore/keystore.c | 315 | ||||
-rw-r--r-- | cmds/keystore/keystore.h | 74 | ||||
-rw-r--r-- | cmds/keystore/netkeystore.c | 409 | ||||
-rw-r--r-- | cmds/keystore/netkeystore.h | 96 |
10 files changed, 1098 insertions, 619 deletions
diff --git a/cmds/keystore/Android.mk b/cmds/keystore/Android.mk index 20f4adf..3daf44e 100644 --- a/cmds/keystore/Android.mk +++ b/cmds/keystore/Android.mk @@ -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 := diff --git a/cmds/keystore/certtool.h b/cmds/keystore/certtool.h new file mode 100644 index 0000000..7cd316b --- /dev/null +++ b/cmds/keystore/certtool.h @@ -0,0 +1,83 @@ +/* +** +** 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 diff --git a/cmds/keystore/commands.c b/cmds/keystore/commands.c deleted file mode 100644 index 17dd060..0000000 --- a/cmds/keystore/commands.c +++ /dev/null @@ -1,227 +0,0 @@ -/* -** 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); -} diff --git a/cmds/keystore/common.h b/cmds/keystore/common.h new file mode 100644 index 0000000..a18114e --- /dev/null +++ b/cmds/keystore/common.h @@ -0,0 +1,60 @@ +/* +** +** 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 diff --git a/cmds/keystore/keymgmt.c b/cmds/keystore/keymgmt.c new file mode 100644 index 0000000..e4102a9 --- /dev/null +++ b/cmds/keystore/keymgmt.c @@ -0,0 +1,365 @@ +/* +** 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; +} diff --git a/cmds/keystore/keymgmt.h b/cmds/keystore/keymgmt.h new file mode 100644 index 0000000..0f10570 --- /dev/null +++ b/cmds/keystore/keymgmt.h @@ -0,0 +1,81 @@ +/* +** 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 __KEYMGMT_H__ +#define __KEYMGMT_H__ + +#define MASTER_KEY_TAG "master_key" +#define MASTER_KEY ".keymaster" +#define MAX_PATH_LEN 128 +#define SALT "Android Keystore 0.1" +#define NAME_DELIMITER "_" +#define KEYFILE_NAME "%s"NAME_DELIMITER"%s" +#define KEYGEN_ITER 1024 +#define AES_KEY_LEN 128 +#define USER_KEY_LEN (AES_KEY_LEN/8) +#define IV_LEN USER_KEY_LEN +#define MAX_RETRY_COUNT 6 + +#define gen_key(passwd, key, len) \ + PKCS5_PBKDF2_HMAC_SHA1(passwd, strlen(passwd), \ + (unsigned char*)SALT, \ + strlen(SALT), KEYGEN_ITER, \ + len, key) + +#define KEYFILE_LEN MAX_NAMESPACE_LENGTH + MAX_KEY_NAME_LENGTH + 6 + +#define get_blob_size(blob) \ + (((blob->value_size + sizeof(uint32_t) + MAX_KEY_NAME_LENGTH \ + + USER_KEY_LEN - 1) / USER_KEY_LEN) * USER_KEY_LEN) + +#define MAX_BLOB_LEN ((MAX_KEY_VALUE_LENGTH + MAX_KEY_NAME_LENGTH + \ + sizeof(uint32_t) + USER_KEY_LEN - 1) / USER_KEY_LEN)\ + * USER_KEY_LEN + +#define data_blob_size(blob) USER_KEY_LEN + sizeof(uint32_t) + blob->blob_size + +typedef struct { + unsigned char iv[USER_KEY_LEN]; + uint32_t blob_size; + union { + unsigned char blob[1]; + struct { + uint32_t value_size; + char keyname[MAX_KEY_NAME_LENGTH]; + unsigned char value[MAX_KEY_VALUE_LENGTH]; + } __attribute__((packed)); + }; +} DATA_BLOB; + +typedef struct { + char tag[USER_KEY_LEN]; + unsigned char master_key[USER_KEY_LEN]; +} MASTER_BLOB; + +int put_key(const char *namespace, const char *keyname, + unsigned char *data, int size); +int get_key(const char *namespace, const char *keyname, + unsigned char *data, int *size); +int remove_key(const char *namespace, const char *keyname); +int list_keys(const char *namespace, char reply[BUFFER_MAX]); +int passwd(char *data); +int lock(); +int unlock(char *passwd); +KEYSTORE_STATE get_state(); +int reset_keystore(); +int init_keystore(const char *dir); + +#endif diff --git a/cmds/keystore/keystore.c b/cmds/keystore/keystore.c deleted file mode 100644 index df8d832..0000000 --- a/cmds/keystore/keystore.c +++ /dev/null @@ -1,315 +0,0 @@ -/* -** 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 inline int has_whitespace(char *name) -{ - if((strrchr(name, ' ') != NULL)) { - LOGE("'%s' contains whitespace character\n", name); - return 1; - } - return 0; -} - -static int do_list_user_certs(char **arg, char reply[REPLY_MAX]) -{ - return list_user_certs(reply); -} - -static int do_list_ca_certs(char **arg, char reply[REPLY_MAX]) -{ - return list_ca_certs(reply); -} - -static int do_install_user_cert(char **arg, char reply[REPLY_MAX]) -{ - if (has_whitespace(arg[0])) return -1; - /* copy the certificate and key to keystore */ - return install_user_cert(arg[0], arg[1], arg[2]); -} - -static int do_install_p12_cert(char **arg, char reply[REPLY_MAX]) -{ - if (has_whitespace(arg[0])) return -1; - return install_p12_cert(arg[0], arg[1]); -} - -static int do_install_ca_cert(char **arg, char reply[REPLY_MAX]) -{ - if (has_whitespace(arg[0])) return -1; - /* copy the certificate and key to keystore */ - return install_ca_cert(arg[0], arg[1]); -} - -static int do_add_ca_cert(char **arg, char reply[REPLY_MAX]) -{ - if (has_whitespace(arg[0])) return -1; - return add_ca_cert(arg[0], arg[1]); -} - -static int do_add_user_cert(char **arg, char reply[REPLY_MAX]) -{ - if (has_whitespace(arg[0])) return -1; - return add_user_cert(arg[0], arg[1]); -} - -static int do_add_user_key(char **arg, char reply[REPLY_MAX]) -{ - if (has_whitespace(arg[0])) return -1; - return add_user_key(arg[0], arg[1]); -} - -static int do_get_ca_cert(char **arg, char reply[REPLY_MAX]) -{ - return get_ca_cert(arg[0], reply); -} - -static int do_get_user_cert(char **arg, char reply[REPLY_MAX]) -{ - return get_user_cert(arg[0], reply); -} - -static int do_get_user_key(char **arg, char reply[REPLY_MAX]) -{ - return get_user_key(arg[0], reply); -} - -static int do_remove_user_cert(char **arg, char reply[REPLY_MAX]) -{ - return remove_user_cert(arg[0]); -} - -static int do_remove_ca_cert(char **arg, char reply[REPLY_MAX]) -{ - return remove_ca_cert(arg[0]); -} - - -struct cmdinfo { - const char *name; - unsigned numargs; - int (*func)(char **arg, char reply[REPLY_MAX]); -}; - - -struct cmdinfo cmds[] = { - { "listcacerts", 0, do_list_ca_certs }, - { "listusercerts", 0, do_list_user_certs }, - { "installusercert", 3, do_install_user_cert }, - { "installcacert", 2, do_install_ca_cert }, - { "installp12cert", 2, do_install_p12_cert }, - { "addusercert", 2, do_add_user_cert }, - { "adduserkey", 2, do_add_user_key }, - { "addcacert", 2, do_add_ca_cert }, - { "getusercert", 1, do_get_user_cert }, - { "getuserkey", 1, do_get_user_key }, - { "getcacert", 1, do_get_ca_cert }, - { "removecacert", 1, do_remove_ca_cert }, - { "removeusercert", 1, do_remove_user_cert }, -}; - -static int readx(int s, void *_buf, int count) -{ - char *buf = _buf; - int n = 0, r; - if (count < 0) return -1; - while (n < count) { - r = read(s, buf + n, count - n); - if (r < 0) { - if (errno == EINTR) continue; - LOGE("read error: %s\n", strerror(errno)); - return -1; - } - if (r == 0) { - LOGE("eof\n"); - return -1; /* EOF */ - } - n += r; - } - return 0; -} - -static int writex(int s, const void *_buf, int count) -{ - const char *buf = _buf; - int n = 0, r; - if (count < 0) return -1; - while (n < count) { - r = write(s, buf + n, count - n); - if (r < 0) { - if (errno == EINTR) continue; - LOGE("write error: %s\n", strerror(errno)); - return -1; - } - n += r; - } - return 0; -} - - -/* Tokenize the command buffer, locate a matching command, - * ensure that the required number of arguments are provided, - * call the function(), return the result. - */ -static int execute(int s, char cmd[BUFFER_MAX]) -{ - char reply[REPLY_MAX]; - char *arg[TOKEN_MAX+1]; - unsigned i; - unsigned n = 0; - unsigned short count; - short ret = -1; - - /* default reply is "" */ - reply[0] = 0; - - /* n is number of args (not counting arg[0]) */ - arg[0] = cmd; - while (*cmd) { - if (*cmd == CMD_DELIMITER) { - *cmd++ = 0; - n++; - arg[n] = cmd; - if (n == TOKEN_MAX) { - LOGE("too many arguments\n"); - goto done; - } - } - cmd++; - } - - for (i = 0; i < sizeof(cmds) / sizeof(cmds[0]); i++) { - if (!strcmp(cmds[i].name,arg[0])) { - if (n != cmds[i].numargs) { - LOGE("%s requires %d arguments (%d given)\n", - cmds[i].name, cmds[i].numargs, n); - } else { - ret = (short) cmds[i].func(arg + 1, reply); - } - goto done; - } - } - LOGE("unsupported command '%s'\n", arg[0]); - -done: - if (reply[0]) { - strlcpy(cmd, reply, BUFFER_MAX); - count = strlen(cmd); - } else { - count = 0; - } - if (writex(s, &ret, sizeof(ret))) return -1; - if (ret == 0) { - if (writex(s, &count, sizeof(count))) return -1; - if (writex(s, cmd, count)) return -1; - } - - return 0; -} - -int shell_command(const int argc, const char **argv) -{ - int fd, i; - short ret; - unsigned short count; - char delimiter[2] = { CMD_DELIMITER, 0 }; - char buf[BUFFER_MAX]=""; - - fd = socket_local_client(SOCKET_PATH, - ANDROID_SOCKET_NAMESPACE_RESERVED, - SOCK_STREAM); - if (fd == -1) { - fprintf(stderr, "Keystore service is not up and running\n"); - exit(1); - } - for(i = 0; i < argc; i++) { - if (i > 0) strlcat(buf, delimiter, BUFFER_MAX); - if(strlcat(buf, argv[i], BUFFER_MAX) >= BUFFER_MAX) { - fprintf(stderr, "Arguments are too long\n"); - exit(1); - } - } - count = strlen(buf); - if (writex(fd, &count, sizeof(count))) return -1; - if (writex(fd, buf, strlen(buf))) return -1; - if (readx(fd, &ret, sizeof(ret))) return -1; - if (ret == 0) { - if (readx(fd, &count, sizeof(count))) return -1; - if (readx(fd, buf, count)) return -1; - buf[count]=0; - fprintf(stdout, "%s\n", buf); - } else { - fprintf(stderr, "Failed, please check log!\n"); - } - return 0; -} - -int main(const int argc, const char *argv[]) -{ - char buf[BUFFER_MAX]; - struct sockaddr addr; - socklen_t alen; - int lsocket, s, count; - - if (argc > 1) { - return shell_command(argc - 1, argv + 1); - } - - lsocket = android_get_control_socket(SOCKET_PATH); - if (lsocket < 0) { - LOGE("Failed to get socket from environment: %s\n", strerror(errno)); - exit(1); - } - if (listen(lsocket, 5)) { - LOGE("Listen on socket failed: %s\n", strerror(errno)); - exit(1); - } - fcntl(lsocket, F_SETFD, FD_CLOEXEC); - - for (;;) { - alen = sizeof(addr); - s = accept(lsocket, &addr, &alen); - if (s < 0) { - LOGE("Accept failed: %s\n", strerror(errno)); - continue; - } - fcntl(s, F_SETFD, FD_CLOEXEC); - - LOGI("new connection\n"); - for (;;) { - unsigned short count; - if (readx(s, &count, sizeof(count))) { - LOGE("failed to read size\n"); - break; - } - if ((count < 1) || (count >= BUFFER_MAX)) { - LOGE("invalid size %d\n", count); - break; - } - if (readx(s, buf, count)) { - LOGE("failed to read command\n"); - break; - } - buf[count] = 0; - if (execute(s, buf)) break; - } - LOGI("closing connection\n"); - close(s); - } - - return 0; -} diff --git a/cmds/keystore/keystore.h b/cmds/keystore/keystore.h deleted file mode 100644 index b9cb185..0000000 --- a/cmds/keystore/keystore.h +++ /dev/null @@ -1,74 +0,0 @@ -/* -** -** 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. -*/ - -#define LOG_TAG "keystore" - -#include <stdio.h> -#include <stdlib.h> -#include <sys/stat.h> -#include <dirent.h> -#include <unistd.h> -#include <ctype.h> -#include <fcntl.h> -#include <errno.h> -#include <utime.h> -#include <sys/socket.h> -#include <sys/types.h> -#include <sys/wait.h> - -#include <cutils/sockets.h> -#include <cutils/log.h> -#include <cutils/properties.h> - -#define SOCKET_PATH "keystore" - - -/* path of the keystore */ - -#define KEYSTORE_DIR_PREFIX "/data/misc/keystore" -#define CERTS_DIR KEYSTORE_DIR_PREFIX "/keys" -#define CACERTS_DIR KEYSTORE_DIR_PREFIX "/cacerts" -#define CA_CERTIFICATE "ca.crt" -#define USER_CERTIFICATE "user.crt" -#define USER_P12_CERT "user.p12" -#define USER_KEY "user.key" -#define DOT "." -#define DOTDOT ".." - -#define BUFFER_MAX 4096 /* input buffer for commands */ -#define TOKEN_MAX 8 /* max number of arguments in buffer */ -#define REPLY_MAX 4096 /* largest reply allowed */ -#define CMD_DELIMITER '\t' -#define KEYNAME_LENGTH 128 -#define IS_CONTENT 0 -#define IS_FILE 1 - - -/* commands.c */ -int list_ca_certs(char reply[REPLY_MAX]); -int list_user_certs(char reply[REPLY_MAX]); -int install_user_cert(const char *certname, const char *cert, const char *key); -int install_ca_cert(const char *certname, const char *cert); -int install_p12_cert(const char *certname, const char *cert); -int add_ca_cert(const char *certname, const char *content); -int add_user_cert(const char *certname, const char *content); -int add_user_key(const char *keyname, const char *content); -int get_ca_cert(const char *keyname, char reply[REPLY_MAX]); -int get_user_cert(const char *keyname, char reply[REPLY_MAX]); -int get_user_key(const char *keyname, char reply[REPLY_MAX]); -int remove_user_cert(const char *certname); -int remove_ca_cert(const char *certname); diff --git a/cmds/keystore/netkeystore.c b/cmds/keystore/netkeystore.c new file mode 100644 index 0000000..b0d683c --- /dev/null +++ b/cmds/keystore/netkeystore.c @@ -0,0 +1,409 @@ +/* +** 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. +*/ + +#define LOG_TAG "keystore" + +#include <stdio.h> +#include <stdlib.h> +#include <sys/stat.h> +#include <dirent.h> +#include <unistd.h> +#include <ctype.h> +#include <fcntl.h> +#include <errno.h> +#include <utime.h> +#include <sys/socket.h> +#include <sys/types.h> +#include <sys/wait.h> +#include <private/android_filesystem_config.h> + +#include <cutils/sockets.h> +#include <cutils/log.h> +#include <cutils/properties.h> + +#include "netkeystore.h" +#include "keymgmt.h" + +#define CMD_PUT_WITH_FILE "putfile" + +typedef void CMD_FUNC(LPC_MARSHAL *cmd, LPC_MARSHAL *reply); + +struct cmdinfo { + const char *name; + CMD_FUNC *func; +}; + +static CMD_FUNC do_lock; +static CMD_FUNC do_unlock; +static CMD_FUNC do_passwd; +static CMD_FUNC do_get_state;; +static CMD_FUNC do_listkeys; +static CMD_FUNC do_get_key; +static CMD_FUNC do_put_key; +static CMD_FUNC do_remove_key; +static CMD_FUNC do_reset_keystore; + +#define str(x) #x + +struct cmdinfo cmds[] = { + { str(LOCK), do_lock }, + { str(UNLOCK), do_unlock }, + { str(PASSWD), do_passwd }, + { str(GETSTATE), do_get_state }, + { str(LISTKEYS), do_listkeys }, + { str(GET), do_get_key }, + { str(PUT), do_put_key }, + { str(REMOVE), do_remove_key }, + { str(RESET), do_reset_keystore }, +}; + +static struct ucred cr; + +static int check_get_perm(int uid) +{ + if (uid == AID_WIFI || uid == AID_VPN) return 0; + return -1; +} + +static int check_reset_perm(int uid) +{ + if (uid == AID_SYSTEM) return 0; + return -1; +} + +static int parse_keyname(char *name, uint32_t len, + char *namespace, char *keyname) +{ + int count = 0; + char *c = namespace, *p = namespace, *t = name; + + if (!name || !namespace || !keyname) return -1; + while (t < name + len && (*t != 0)) { + if (*t == ' ') { + if (c == keyname) return -1; + *p = count = 0; + c = p = keyname; + t++; + } else { + if (!isalnum(*t)) return -1; + *p++ = *t++; + // also check if the keyname/namespace is too long. + if (count++ == MAX_KEY_NAME_LENGTH) return -1; + } + } + *p = 0; + return 0; +} + +// args of passwd(): +// firstPassword - for the first time +// oldPassword newPassword - for changing the password +static void do_passwd(LPC_MARSHAL *cmd, LPC_MARSHAL *reply) +{ + reply->retcode = passwd((char*)cmd->data); +} + +// args of lock(): +// no argument +static void do_lock(LPC_MARSHAL *cmd, LPC_MARSHAL *reply) +{ + reply->retcode = lock(); +} + +// args of unlock(): +// password +static void do_unlock(LPC_MARSHAL *cmd, LPC_MARSHAL *reply) +{ + reply->retcode = unlock((char*)cmd->data); +} + +// args of get_state(): +// no argument +static void do_get_state(LPC_MARSHAL *cmd, LPC_MARSHAL *reply) +{ + reply->retcode = get_state(); +} + +// args of listkeys(): +// namespace +static void do_listkeys(LPC_MARSHAL *cmd, LPC_MARSHAL *reply) +{ + reply->retcode = list_keys((const char*)cmd->data, (char*)reply->data); + if (!reply->retcode) reply->len = strlen((char*)reply->data) + 1; +} + +// args of get(): +// namespace keyname +static void do_get_key(LPC_MARSHAL *cmd, LPC_MARSHAL *reply) +{ + char namespace[MAX_KEY_NAME_LENGTH]; + char keyname[MAX_KEY_NAME_LENGTH]; + + if (check_get_perm(cr.uid)) { + LOGE("uid %d doesn't have the permission to get key value\n", cr.uid); + reply->retcode = -1; + return; + } + + if (parse_keyname((char*)cmd->data, cmd->len, namespace, keyname)) { + reply->retcode = -1; + } else { + reply->retcode = get_key(namespace, keyname, reply->data, + (int*)&reply->len); + } +} + +static int get_value_index(LPC_MARSHAL *cmd) +{ + uint32_t count = 0, i; + for (i = 0 ; i < cmd->len ; ++i) { + if (cmd->data[i] == ' ') { + if (++count == 2) return ++i; + } + } + return -1; +} + +// args of put(): +// namespace keyname keyvalue +static void do_put_key(LPC_MARSHAL *cmd, LPC_MARSHAL *reply) +{ + char namespace[MAX_KEY_NAME_LENGTH]; + char keyname[MAX_KEY_NAME_LENGTH]; + + int p = get_value_index(cmd); + if (p == -1) { + reply->retcode = -1; + } else { + unsigned char *value; + if (parse_keyname((char*)cmd->data, p - 1, namespace, keyname)) { + reply->retcode = -1; + return; + } + value = &cmd->data[p]; + int len = cmd->len - p; + reply->retcode = put_key(namespace, keyname, value, len); + } +} + +// args of remove_key(): +// namespace keyname +static void do_remove_key(LPC_MARSHAL *cmd, LPC_MARSHAL *reply) +{ + char namespace[MAX_KEY_NAME_LENGTH]; + char keyname[MAX_KEY_NAME_LENGTH]; + if (parse_keyname((char*)cmd->data, cmd->len, namespace, keyname)) { + reply->retcode = -1; + return; + } + reply->retcode = remove_key(namespace, keyname); +} + +// args of reset_keystore(): +// no argument +static void do_reset_keystore(LPC_MARSHAL *cmd, LPC_MARSHAL *reply) +{ + if (check_reset_perm(cr.uid)) { + LOGE("uid %d doesn't have the permission to reset the keystore\n", + cr.uid); + reply->retcode = -1; + return; + } + reply->retcode = reset_keystore(); +} +static void execute(LPC_MARSHAL *cmd, LPC_MARSHAL *reply) +{ + uint32_t cmd_max = sizeof(cmds)/sizeof(struct cmdinfo); + + if (cmd->opcode >= cmd_max) { + LOGE("the opcode (%d) is not valid", cmd->opcode); + reply->retcode = -1; + return; + } + cmds[cmd->opcode].func(cmd, reply); +} + +static int set_read_timeout(int socket) +{ + struct timeval tv; + tv.tv_sec = READ_TIMEOUT; + if (setsockopt(socket, SOL_SOCKET, SO_RCVTIMEO, (char *)&tv, sizeof tv)) + { + LOGE("setsockopt failed"); + return -1; + } + return 0; +} + +static int append_input_from_file(const char *filename, LPC_MARSHAL *cmd) +{ + int fd, len, ret = 0; + + // get opcode of the function put() + if ((fd = open(filename, O_RDONLY)) == -1) { + fprintf(stderr, "Can not open file %s\n", filename); + return -1; + } + cmd->data[cmd->len] = ' '; + cmd->len++; + len = read(fd, cmd->data + cmd->len, BUFFER_MAX - cmd->len); + if (len < 0 || (len == (int)(BUFFER_MAX - cmd->len))) { + ret = -1; + } else { + cmd->len += len; + } + close(fd); + return ret; +} + +static int flatten_str_args(int argc, const char **argv, LPC_MARSHAL *cmd) +{ + int i, len = 0; + char *buf = (char*)cmd->data; + buf[0] = 0; + for (i = 0 ; i < argc ; ++i) { + if (i == 0) { + len = strlcpy(buf, argv[i], BUFFER_MAX); + } else { + len += snprintf(buf + len, BUFFER_MAX - len, " %s", argv[i]); + } + if (len >= BUFFER_MAX) return -1; + } + if (len) cmd->len = len; + return 0; +} + +static int parse_cmd(int argc, const char **argv, LPC_MARSHAL *cmd) +{ + uint32_t i, len = 0; + uint32_t cmd_max = sizeof(cmds)/sizeof(cmds[0]); + + for (i = 0 ; i < cmd_max ; ++i) { + if (!strcasecmp(argv[0], cmds[i].name)) break; + } + + if (i == cmd_max) { + // check if this is a command to put the key value with a file. + if (strcmp(argv[0], CMD_PUT_WITH_FILE) != 0) return -1; + cmd->opcode = PUT; + if (argc != 4) { + fprintf(stderr, "%s args\n\tnamespace keyname filename\n", + argv[0]); + return -1; + } + if (flatten_str_args(argc - 2, argv + 1, cmd)) return -1; + return append_input_from_file(argv[3], cmd); + } else { + cmd->opcode = i; + return flatten_str_args(argc - 1, argv + 1, cmd); + } +} + +static int shell_command(const int argc, const char **argv) +{ + int fd, i; + LPC_MARSHAL cmd; + + if (parse_cmd(argc, argv , &cmd)) { + fprintf(stderr, "Incorrect command or command line is too long.\n"); + exit(1); + } + fd = socket_local_client(SOCKET_PATH, + ANDROID_SOCKET_NAMESPACE_RESERVED, + SOCK_STREAM); + if (fd == -1) { + fprintf(stderr, "Keystore service is not up and running.\n"); + exit(1); + } + + if (write_marshal(fd, &cmd)) { + fprintf(stderr, "Incorrect command or command line is too long.\n"); + exit(1); + } + if (read_marshal(fd, &cmd)) { + fprintf(stderr, "Failed to read the result.\n"); + exit(1); + } + cmd.data[cmd.len] = 0; + fprintf(stdout, "%s\n", (cmd.retcode == 0) ? "Succeeded!" : "Failed!"); + if (cmd.len) fprintf(stdout, "\t%s\n", (char*)cmd.data); + close(fd); + return 0; +} + +int main(const int argc, const char *argv[]) +{ + struct sockaddr addr; + socklen_t alen; + int lsocket, s; + LPC_MARSHAL cmd, reply; + + if (argc > 1) { + return shell_command(argc - 1, argv + 1); + } + + if (init_keystore(KEYSTORE_DIR)) { + LOGE("Can not initialize the keystore, the directory exist?\n"); + exit(1); + } + + lsocket = android_get_control_socket(SOCKET_PATH); + if (lsocket < 0) { + LOGE("Failed to get socket from environment: %s\n", strerror(errno)); + exit(1); + } + if (listen(lsocket, 5)) { + LOGE("Listen on socket failed: %s\n", strerror(errno)); + exit(1); + } + fcntl(lsocket, F_SETFD, FD_CLOEXEC); + memset(&reply, 0, sizeof(LPC_MARSHAL)); + + for (;;) { + socklen_t cr_size = sizeof(cr); + alen = sizeof(addr); + s = accept(lsocket, &addr, &alen); + + /* retrieve the caller info here */ + if (getsockopt(s, SOL_SOCKET, SO_PEERCRED, &cr, &cr_size) < 0) { + close(s); + LOGE("Unable to recieve socket options\n"); + continue; + } + + if (s < 0) { + LOGE("Accept failed: %s\n", strerror(errno)); + continue; + } + fcntl(s, F_SETFD, FD_CLOEXEC); + if (set_read_timeout(s)) { + close(s); + continue; + } + + // read the command, execute and send the result back. + if(read_marshal(s, &cmd)) goto err; + LOGI("new connection\n"); + execute(&cmd, &reply); + write_marshal(s, &reply); +err: + memset(&reply, 0, sizeof(LPC_MARSHAL)); + LOGI("closing connection\n"); + close(s); + } + + return 0; +} diff --git a/cmds/keystore/netkeystore.h b/cmds/keystore/netkeystore.h new file mode 100644 index 0000000..a87a667 --- /dev/null +++ b/cmds/keystore/netkeystore.h @@ -0,0 +1,96 @@ +/* +** +** 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 __NETKEYSTORE_H__ +#define __NETKEYSTORE_H__ + +#include <stdio.h> +#include <cutils/sockets.h> +#include <cutils/log.h> + +#include "common.h" + +static inline int readx(int s, void *_buf, int count) +{ + char *buf = _buf; + int n = 0, r; + if (count < 0) return -1; + while (n < count) { + r = read(s, buf + n, count - n); + if (r < 0) { + if (errno == EINTR) continue; + LOGE("read error: %s\n", strerror(errno)); + return -1; + } + if (r == 0) { + LOGE("eof\n"); + return -1; /* EOF */ + } + n += r; + } + return 0; +} + +static inline int writex(int s, const void *_buf, int count) +{ + const char *buf = _buf; + int n = 0, r; + if (count < 0) return -1; + while (n < count) { + r = write(s, buf + n, count - n); + if (r < 0) { + if (errno == EINTR) continue; + LOGE("write error: %s\n", strerror(errno)); + return -1; + } + n += r; + } + return 0; +} + +static inline int read_marshal(int s, LPC_MARSHAL *cmd) +{ + if (readx(s, cmd, 2 * sizeof(uint32_t))) { + LOGE("failed to read header\n"); + return -1; + } + if (cmd->len > BUFFER_MAX) { + LOGE("invalid size %d\n", cmd->len); + return -1; + } + if (readx(s, cmd->data, cmd->len)) { + LOGE("failed to read data\n"); + return -1; + } + cmd->data[cmd->len] = 0; + return 0; +} + +static inline int write_marshal(int s, LPC_MARSHAL *cmd) +{ + if (writex(s, cmd, 2 * sizeof(uint32_t))) { + LOGE("failed to write marshal header\n"); + return -1; + } + if (writex(s, cmd->data, cmd->len)) { + LOGE("failed to write marshal data\n"); + return -1; + } + return 0; +} + +#endif |