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, &params[0], &params[1]);
+    return action->run(keyStore, sock, uid, &params[0], &params[1], &params[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__ */