keystore_client shared library
Add a libkeystore_client.so library for clients to use.
Add const-correctness to the keystore.cpp classes.
Increase maximum arguments for future work.
Change-Id: Ia22f8b893aea3115a7b4a0543ad392c17c8528f2
diff --git a/keystore/Android.mk b/keystore/Android.mk
index 4fcd7bb..d0ab19d 100644
--- a/keystore/Android.mk
+++ b/keystore/Android.mk
@@ -30,3 +30,11 @@
LOCAL_MODULE := keystore_cli
LOCAL_MODULE_TAGS := debug
include $(BUILD_EXECUTABLE)
+
+# Library for keystore clients
+include $(CLEAR_VARS)
+LOCAL_SRC_FILES := keystore_client.cpp
+LOCAL_SHARED_LIBRARIES := libcutils
+LOCAL_MODULE := libkeystore_client
+LOCAL_MODULE_TAGS := optional
+include $(BUILD_SHARED_LIBRARY)
\ No newline at end of file
diff --git a/keystore/keystore.cpp b/keystore/keystore.cpp
index 2c9cb35..89d578e 100644
--- a/keystore/keystore.cpp
+++ b/keystore/keystore.cpp
@@ -80,7 +80,7 @@
return n + length;
}
-static int decode_key(uint8_t* out, char* in, int length) {
+static int decode_key(uint8_t* out, const char* in, int length) {
for (int i = 0; i < length; ++i, ++in, ++out) {
if (*in >= '0' && *in <= '~') {
*out = *in;
@@ -139,7 +139,7 @@
return true;
}
- bool generate_random_data(uint8_t* data, size_t size) {
+ bool generate_random_data(uint8_t* data, size_t size) const {
return (readFully(mRandom, data, size) == size);
}
@@ -183,15 +183,19 @@
Blob() {}
- uint8_t* getValue() {
+ const uint8_t* getValue() const {
return mBlob.value;
}
- int32_t getLength() {
+ int32_t getLength() const {
return mBlob.length;
}
- uint8_t getInfo() {
+ const uint8_t* getInfo() const {
+ return mBlob.value + mBlob.length;
+ }
+
+ uint8_t getInfoLength() const {
return mBlob.info;
}
@@ -288,7 +292,10 @@
class KeyStore {
public:
- KeyStore(Entropy* entropy) : mEntropy(entropy), mRetry(MAX_RETRY) {
+ KeyStore(Entropy* entropy)
+ : mEntropy(entropy)
+ , mRetry(MAX_RETRY)
+ {
if (access(MASTER_KEY_FILE, R_OK) == 0) {
setState(STATE_LOCKED);
} else {
@@ -296,11 +303,11 @@
}
}
- State getState() {
+ State getState() const {
return mState;
}
- int8_t getRetry() {
+ int8_t getRetry() const {
return mRetry;
}
@@ -399,7 +406,7 @@
return true;
}
- bool isEmpty() {
+ bool isEmpty() const {
DIR* dir = opendir(".");
struct dirent* file;
if (!dir) {
@@ -545,7 +552,7 @@
send(sock, &code, 1, 0);
}
-static void send_message(int sock, uint8_t* message, int length) {
+static void send_message(int sock, const uint8_t* message, int length) {
uint16_t bytes = htons(length);
send(sock, &bytes, 2, 0);
send(sock, message, length, 0);
@@ -561,11 +568,11 @@
static const ResponseCode NO_ERROR_RESPONSE_CODE_SENT = (ResponseCode) 0;
-static ResponseCode test(KeyStore* keyStore, int sock, uid_t uid, Value*, Value*) {
+static ResponseCode test(KeyStore* keyStore, int sock, uid_t uid, Value*, Value*, Value*) {
return (ResponseCode) keyStore->getState();
}
-static ResponseCode get(KeyStore* keyStore, int sock, uid_t uid, Value* keyName, Value*) {
+static ResponseCode get(KeyStore* keyStore, int sock, uid_t uid, Value* keyName, Value*, Value*) {
char filename[NAME_MAX];
encode_key(filename, uid, keyName);
Blob keyBlob;
@@ -578,20 +585,21 @@
return NO_ERROR_RESPONSE_CODE_SENT;
}
-static ResponseCode insert(KeyStore* keyStore, int sock, uid_t uid, Value* keyName, Value* val) {
+static ResponseCode insert(KeyStore* keyStore, int sock, uid_t uid, Value* keyName, Value* val,
+ Value*) {
char filename[NAME_MAX];
encode_key(filename, uid, keyName);
Blob keyBlob(val->value, val->length, NULL, 0);
return keyStore->put(filename, &keyBlob);
}
-static ResponseCode del(KeyStore* keyStore, int sock, uid_t uid, Value* keyName, Value*) {
+static ResponseCode del(KeyStore* keyStore, int sock, uid_t uid, Value* keyName, Value*, Value*) {
char filename[NAME_MAX];
encode_key(filename, uid, keyName);
return (unlink(filename) && errno != ENOENT) ? SYSTEM_ERROR : NO_ERROR;
}
-static ResponseCode exist(KeyStore* keyStore, int sock, uid_t uid, Value* keyName, Value*) {
+static ResponseCode exist(KeyStore* keyStore, int sock, uid_t uid, Value* keyName, Value*, Value*) {
char filename[NAME_MAX];
encode_key(filename, uid, keyName);
if (access(filename, R_OK) == -1) {
@@ -600,7 +608,7 @@
return NO_ERROR;
}
-static ResponseCode saw(KeyStore* keyStore, int sock, uid_t uid, Value* keyPrefix, Value*) {
+static ResponseCode saw(KeyStore* keyStore, int sock, uid_t uid, Value* keyPrefix, Value*, Value*) {
DIR* dir = opendir(".");
if (!dir) {
return SYSTEM_ERROR;
@@ -612,7 +620,7 @@
struct dirent* file;
while ((file = readdir(dir)) != NULL) {
if (!strncmp(filename, file->d_name, n)) {
- char* p = &file->d_name[n];
+ const char* p = &file->d_name[n];
keyPrefix->length = decode_key(keyPrefix->value, p, strlen(p));
send_message(sock, keyPrefix->value, keyPrefix->length);
}
@@ -621,7 +629,7 @@
return NO_ERROR_RESPONSE_CODE_SENT;
}
-static ResponseCode reset(KeyStore* keyStore, int sock, uid_t uid, Value*, Value*) {
+static ResponseCode reset(KeyStore* keyStore, int sock, uid_t uid, Value*, Value*, Value*) {
return keyStore->reset() ? NO_ERROR : SYSTEM_ERROR;
}
@@ -631,7 +639,7 @@
* any thing goes wrong during the transition, the new file will not overwrite
* the old one. This avoids permanent damages of the existing data. */
-static ResponseCode password(KeyStore* keyStore, int sock, uid_t uid, Value* pw, Value*) {
+static ResponseCode password(KeyStore* keyStore, int sock, uid_t uid, Value* pw, Value*, Value*) {
switch (keyStore->getState()) {
case STATE_UNINITIALIZED: {
// generate master key, encrypt with password, write to file, initialize mMasterKey*.
@@ -649,58 +657,60 @@
return SYSTEM_ERROR;
}
-static ResponseCode lock(KeyStore* keyStore, int sock, uid_t uid, Value*, Value*) {
+static ResponseCode lock(KeyStore* keyStore, int sock, uid_t uid, Value*, Value*, Value*) {
keyStore->lock();
return NO_ERROR;
}
-static ResponseCode unlock(KeyStore* keyStore, int sock, uid_t uid, Value* pw, Value* unused) {
- return password(keyStore, sock, uid, pw, unused);
+static ResponseCode unlock(KeyStore* keyStore, int sock, uid_t uid, Value* pw, Value* unused,
+ Value* unused2) {
+ return password(keyStore, sock, uid, pw, unused, unused2);
}
-static ResponseCode zero(KeyStore* keyStore, int sock, uid_t uid, Value*, Value*) {
+static ResponseCode zero(KeyStore* keyStore, int sock, uid_t uid, Value*, Value*, Value*) {
return keyStore->isEmpty() ? KEY_NOT_FOUND : NO_ERROR;
}
/* Here are the permissions, actions, users, and the main function. */
enum perm {
- TEST = 1,
- GET = 2,
- INSERT = 4,
- DELETE = 8,
- EXIST = 16,
- SAW = 32,
- RESET = 64,
- PASSWORD = 128,
- LOCK = 256,
- UNLOCK = 512,
- ZERO = 1024,
+ P_TEST = 1 << TEST,
+ P_GET = 1 << GET,
+ P_INSERT = 1 << INSERT,
+ P_DELETE = 1 << DELETE,
+ P_EXIST = 1 << EXIST,
+ P_SAW = 1 << SAW,
+ P_RESET = 1 << RESET,
+ P_PASSWORD = 1 << PASSWORD,
+ P_LOCK = 1 << LOCK,
+ P_UNLOCK = 1 << UNLOCK,
+ P_ZERO = 1 << ZERO,
};
-static const int MAX_PARAM = 2;
+static const int MAX_PARAM = 3;
static const State STATE_ANY = (State) 0;
static struct action {
- ResponseCode (*run)(KeyStore* keyStore, int sock, uid_t uid, Value* param1, Value* param2);
+ ResponseCode (*run)(KeyStore* keyStore, int sock, uid_t uid, Value* param1, Value* param2,
+ Value* param3);
int8_t code;
State state;
uint32_t perm;
int lengths[MAX_PARAM];
} actions[] = {
- {test, 't', STATE_ANY, TEST, {0, 0}},
- {get, 'g', STATE_NO_ERROR, GET, {KEY_SIZE, 0}},
- {insert, 'i', STATE_NO_ERROR, INSERT, {KEY_SIZE, VALUE_SIZE}},
- {del, 'd', STATE_ANY, DELETE, {KEY_SIZE, 0}},
- {exist, 'e', STATE_ANY, EXIST, {KEY_SIZE, 0}},
- {saw, 's', STATE_ANY, SAW, {KEY_SIZE, 0}},
- {reset, 'r', STATE_ANY, RESET, {0, 0}},
- {password, 'p', STATE_ANY, PASSWORD, {PASSWORD_SIZE, 0}},
- {lock, 'l', STATE_NO_ERROR, LOCK, {0, 0}},
- {unlock, 'u', STATE_LOCKED, UNLOCK, {PASSWORD_SIZE, 0}},
- {zero, 'z', STATE_ANY, ZERO, {0, 0}},
- {NULL, 0 , STATE_ANY, 0, {0, 0}},
+ {test, CommandCodes[TEST], STATE_ANY, P_TEST, {0, 0, 0}},
+ {get, CommandCodes[GET], STATE_NO_ERROR, P_GET, {KEY_SIZE, 0, 0}},
+ {insert, CommandCodes[INSERT], STATE_NO_ERROR, P_INSERT, {KEY_SIZE, VALUE_SIZE, 0}},
+ {del, CommandCodes[DELETE], STATE_ANY, P_DELETE, {KEY_SIZE, 0, 0}},
+ {exist, CommandCodes[EXIST], STATE_ANY, P_EXIST, {KEY_SIZE, 0, 0}},
+ {saw, CommandCodes[SAW], STATE_ANY, P_SAW, {KEY_SIZE, 0, 0}},
+ {reset, CommandCodes[RESET], STATE_ANY, P_RESET, {0, 0, 0}},
+ {password, CommandCodes[PASSWORD], STATE_ANY, P_PASSWORD, {PASSWORD_SIZE, 0, 0}},
+ {lock, CommandCodes[LOCK], STATE_NO_ERROR, P_LOCK, {0, 0, 0}},
+ {unlock, CommandCodes[UNLOCK], STATE_LOCKED, P_UNLOCK, {PASSWORD_SIZE, 0, 0}},
+ {zero, CommandCodes[ZERO], STATE_ANY, P_ZERO, {0, 0, 0}},
+ {NULL, 0, STATE_ANY, 0, {0, 0, 0}},
};
static struct user {
@@ -709,10 +719,10 @@
uint32_t perms;
} users[] = {
{AID_SYSTEM, ~0, ~0},
- {AID_VPN, AID_SYSTEM, GET},
- {AID_WIFI, AID_SYSTEM, GET},
- {AID_ROOT, AID_SYSTEM, GET},
- {~0, ~0, TEST | GET | INSERT | DELETE | EXIST | SAW},
+ {AID_VPN, AID_SYSTEM, P_GET},
+ {AID_WIFI, AID_SYSTEM, P_GET},
+ {AID_ROOT, AID_SYSTEM, P_GET},
+ {~0, ~0, P_TEST | P_GET | P_INSERT | P_DELETE | P_EXIST | P_SAW},
};
static ResponseCode process(KeyStore* keyStore, int sock, uid_t uid, int8_t code) {
@@ -748,7 +758,7 @@
if (!recv_end_of_file(sock)) {
return PROTOCOL_ERROR;
}
- return action->run(keyStore, sock, uid, ¶ms[0], ¶ms[1]);
+ return action->run(keyStore, sock, uid, ¶ms[0], ¶ms[1], ¶ms[2]);
}
int main(int argc, char* argv[]) {
diff --git a/keystore/keystore.h b/keystore/keystore.h
index 5ae3d24..93eeb87 100644
--- a/keystore/keystore.h
+++ b/keystore/keystore.h
@@ -17,6 +17,8 @@
#ifndef __KEYSTORE_H__
#define __KEYSTORE_H__
+#include <stdint.h>
+
// note state values overlap with ResponseCode for the purposes of the state() API
enum State {
STATE_NO_ERROR = 1,
@@ -40,4 +42,34 @@
WRONG_PASSWORD_3 = 13, // MAX_RETRY = 4
};
+enum CommandNames {
+ TEST = 0,
+ GET = 1,
+ INSERT = 2,
+ DELETE = 3,
+ EXIST = 4,
+ SAW = 5,
+ RESET = 6,
+ PASSWORD = 7,
+ LOCK = 8,
+ UNLOCK = 9,
+ ZERO = 10,
+};
+
+typedef uint8_t command_code_t;
+
+command_code_t CommandCodes[] = {
+ 't', // TEST
+ 'g', // GET
+ 'i', // INSERT
+ 'd', // DELETE
+ 'e', // EXIST
+ 's', // SAW
+ 'r', // RESET
+ 'p', // PASSWORD
+ 'l', // LOCK
+ 'u', // UNLOCK
+ 'z', // ZERO
+};
+
#endif
diff --git a/keystore/keystore_client.cpp b/keystore/keystore_client.cpp
new file mode 100644
index 0000000..db9eb68
--- /dev/null
+++ b/keystore/keystore_client.cpp
@@ -0,0 +1,133 @@
+/*
+ * Copyright (C) 2012 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>
+#include <keystore_client.h>
+
+#include <cutils/sockets.h>
+
+#define LOG_TAG "keystore_client"
+#include <cutils/log.h>
+
+ResponseCode keystore_cmd(command_code_t cmd, Keystore_Reply* reply, int numArgs, ...) {
+ int sock;
+
+ sock = socket_local_client("keystore", ANDROID_SOCKET_NAMESPACE_RESERVED, SOCK_STREAM);
+ if (sock == -1) {
+ return SYSTEM_ERROR;
+ }
+
+ if (TEMP_FAILURE_RETRY(send(sock, &cmd, 1, MSG_NOSIGNAL)) != 1) {
+ close(sock);
+ return SYSTEM_ERROR;
+ }
+
+ va_list vl;
+ va_start(vl, numArgs);
+ for (int i = 0; i < numArgs; i++) {
+ size_t argLen = va_arg(vl, size_t);
+ uint8_t* arg = va_arg(vl, uint8_t*);
+
+ if (argLen > KEYSTORE_MESSAGE_SIZE) {
+ ALOGE("code called us with an argLen out of bounds: %llu", (unsigned long long) argLen);
+ close(sock);
+ return SYSTEM_ERROR;
+ }
+
+ uint8_t bytes[2] = { argLen >> 8, argLen };
+ if (TEMP_FAILURE_RETRY(send(sock, bytes, 2, MSG_NOSIGNAL)) != 2
+ || TEMP_FAILURE_RETRY(send(sock, arg, argLen, MSG_NOSIGNAL))
+ != static_cast<ssize_t>(argLen)) {
+ ALOGW("truncated write to keystore");
+ close(sock);
+ return SYSTEM_ERROR;
+ }
+ }
+ va_end(vl);
+
+ uint8_t code = 0;
+ if (shutdown(sock, SHUT_WR) != 0
+ || TEMP_FAILURE_RETRY(recv(sock, &code, 1, 0)) != 1
+ || code != NO_ERROR) {
+ ALOGW("Error from keystore: %d", code);
+ close(sock);
+ return SYSTEM_ERROR;
+ }
+
+ if (reply != NULL) {
+ reply->setCode(static_cast<ResponseCode>(code));
+
+ uint8_t bytes[2];
+ uint8_t* data = reply->get();
+ if (TEMP_FAILURE_RETRY(recv(sock, &bytes[0], 1, 0)) == 1
+ && TEMP_FAILURE_RETRY(recv(sock, &bytes[1], 1, 0)) == 1) {
+ int offset = 0;
+ int length = bytes[0] << 8 | bytes[1];
+ while (offset < length) {
+ int n = TEMP_FAILURE_RETRY(recv(sock, &data[offset], length - offset, 0));
+ if (n <= 0) {
+ ALOGW("truncated read from keystore for data");
+ code = SYSTEM_ERROR;
+ break;
+ }
+ offset += n;
+ }
+ reply->setLength(length);
+ } else {
+ ALOGW("truncated read from keystore for length");
+ code = SYSTEM_ERROR;
+ }
+ }
+
+ close(sock);
+ return static_cast<ResponseCode>(code);
+}
+
+Keystore_Reply::Keystore_Reply()
+ : mCode(SYSTEM_ERROR)
+ , mLength(-1) {
+ mData = new uint8_t[KEYSTORE_MESSAGE_SIZE];
+}
+
+Keystore_Reply::~Keystore_Reply() {
+ delete[] mData;
+}
+
+uint8_t* Keystore_Reply::get() {
+ return mData;
+}
+
+void Keystore_Reply::setLength(size_t length) {
+ mLength = length;
+}
+
+size_t Keystore_Reply::length() const {
+ return mLength;
+}
+
+void Keystore_Reply::setCode(ResponseCode code) {
+ mCode = code;
+}
+
+ResponseCode Keystore_Reply::code() const {
+ return mCode;
+}
+
+uint8_t* Keystore_Reply::release() {
+ uint8_t* data = mData;
+ mData = NULL;
+ return data;
+}
diff --git a/keystore/keystore_client.h b/keystore/keystore_client.h
new file mode 100644
index 0000000..7ea6bb8
--- /dev/null
+++ b/keystore/keystore_client.h
@@ -0,0 +1,51 @@
+/*
+ * Copyright (C) 2012 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 __KEYSTORE_CLIENT_H__
+#define __KEYSTORE_CLIENT_H__
+
+#include <keystore.h>
+
+#define KEYSTORE_MESSAGE_SIZE 65535
+
+
+class Keystore_Reply {
+public:
+ Keystore_Reply();
+ ~Keystore_Reply();
+
+ uint8_t* get();
+ void setLength(size_t length);
+ size_t length() const;
+ void setCode(ResponseCode code);
+ ResponseCode code() const;
+ uint8_t* release();
+
+private:
+ ResponseCode mCode;
+ uint8_t* mData;
+ size_t mLength;
+};
+
+
+/**
+ * This sends a command to the keystore. The arguments must be of the format:
+ *
+ * size_t length, const uint8_t* data, [size_t length, const uint8_t* data, [...]]
+ */
+ResponseCode keystore_cmd(command_code_t cmd, Keystore_Reply* reply, int numArgs, ...);
+
+#endif /* __KEYSTORE_CLIENT_H__ */