Implement the generic mini-keystore for security.

1. We will progressively migrate to this implementation.
2. For richc to have a quick review on the keymgmt part.
3. Add remove_key and make sure all functions are working.
4. Add permission check for get operation.
5. Return the retry count if unlock failed.
6. Add the reset operation for keystore reset.
7. Add the putfile shell command for putting the key value from file.
8. Fix the boundary issue during parsing command.
9. Use the ' ' as delimiter and reset the reply structure for each request.
10. Add change password retry count check.
11. Extract the read_marshal/write_marshal for certtool.h.
12. Remove the old implementation.
This commit is contained in:
Chung-yih Wang 2009-06-29 03:12:56 +08:00
parent ce6d172107
commit c186c66a29
10 changed files with 1098 additions and 619 deletions

View File

@ -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
cmds/keystore/certtool.h Normal file
View File

@ -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

View File

@ -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);
}

60
cmds/keystore/common.h Normal file
View File

@ -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

365
cmds/keystore/keymgmt.c Normal file
View File

@ -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;
}

81
cmds/keystore/keymgmt.h Normal file
View File

@ -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

View File

@ -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;
}

View File

@ -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);

409
cmds/keystore/netkeystore.c Normal file
View File

@ -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;
}

View File

@ -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