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

Commit 01373772 authored by Brian Carlstrom's avatar Brian Carlstrom
Browse files

Integrating keystore with keyguard (Part 1 of 4)

Summary:

frameworks/base
  keystore rewrite
  keyguard integration with keystore on keyguard entry or keyguard change
  KeyStore API simplification

packages/apps/Settings
  Removed com.android.credentials.SET_PASSWORD intent support
  Added keyguard requirement for keystore use

packages/apps/CertInstaller
  Tracking KeyStore API changes
  Fix for NPE in CertInstaller when certificate lacks basic constraints

packages/apps/KeyChain
  Tracking KeyStore API changes

Details:

frameworks/base

   Move keystore from C to C++ while rewriting password
   implementation. Removed global variables. Added many comments.

	cmds/keystore/Android.mk
	cmds/keystore/keystore.h
	cmds/keystore/keystore.c => cmds/keystore/keystore.cpp
	cmds/keystore/keystore_cli.c => cmds/keystore/keystore_cli.cpp

   Changed saveLockPattern and saveLockPassword to notify the keystore
   on changes so that the keystore master key can be reencrypted when
   the keyguard changes.

	core/java/com/android/internal/widget/LockPatternUtils.java

   Changed unlock screens to pass values for keystore unlock or initialization

	policy/src/com/android/internal/policy/impl/PasswordUnlockScreen.java
	policy/src/com/android/internal/policy/impl/PatternUnlockScreen.java

   KeyStore API changes
   - renamed test() to state(), which now return a State enum
   - made APIs with byte[] key arguments private
   - added new KeyStore.isEmpty used to determine if a keyguard is required

	keystore/java/android/security/KeyStore.java

   In addition to tracking KeyStore API changes, added new testIsEmpty
   and improved some existing tests to validate expect values.

	keystore/tests/src/android/security/KeyStoreTest.java

packages/apps/Settings

    Removing com.android.credentials.SET_PASSWORD intent with the
    removal of the ability to set an explicit keystore password now
    that the keyguard value is used. Changed to ensure keyguard is
    enabled for keystore install or unlock. Cleaned up interwoven
    dialog handing into discrete dialog helper classes.

	AndroidManifest.xml
	src/com/android/settings/CredentialStorage.java

    Remove layout for entering new password

	res/layout/credentials_dialog.xml

    Remove enable credentials checkbox

	res/xml/security_settings_misc.xml
	src/com/android/settings/SecuritySettings.java

    Added ability to specify minimum quality key to ChooseLockGeneric
    Activity. Used by CredentialStorage, but could also be used by
    CryptKeeperSettings. Changed ChooseLockGeneric to understand
    minimum quality for keystore in addition to DPM and device
    encryption.

	src/com/android/settings/ChooseLockGeneric.java

    Changed to use getActivePasswordQuality from
    getKeyguardStoredPasswordQuality based on experience in
    CredentialStorage. Removed bogus class javadoc.

	src/com/android/settings/CryptKeeperSettings.java

    Tracking KeyStore API changes

	src/com/android/settings/vpn/VpnSettings.java
	src/com/android/settings/wifi/WifiSettings.java

   Removing now unused string resources

	res/values-af/strings.xml
	res/values-am/strings.xml
	res/values-ar/strings.xml
	res/values-bg/strings.xml
	res/values-ca/strings.xml
	res/values-cs/strings.xml
	res/values-da/strings.xml
	res/values-de/strings.xml
	res/values-el/strings.xml
	res/values-en-rGB/strings.xml
	res/values-es-rUS/strings.xml
	res/values-es/strings.xml
	res/values-fa/strings.xml
	res/values-fi/strings.xml
	res/values-fr/strings.xml
	res/values-hr/strings.xml
	res/values-hu/strings.xml
	res/values-in/strings.xml
	res/values-it/strings.xml
	res/values-iw/strings.xml
	res/values-ja/strings.xml
	res/values-ko/strings.xml
	res/values-lt/strings.xml
	res/values-lv/strings.xml
	res/values-ms/strings.xml
	res/values-nb/strings.xml
	res/values-nl/strings.xml
	res/values-pl/strings.xml
	res/values-pt-rPT/strings.xml
	res/values-pt/strings.xml
	res/values-rm/strings.xml
	res/values-ro/strings.xml
	res/values-ru/strings.xml
	res/values-sk/strings.xml
	res/values-sl/strings.xml
	res/values-sr/strings.xml
	res/values-sv/strings.xml
	res/values-sw/strings.xml
	res/values-th/strings.xml
	res/values-tl/strings.xml
	res/values-tr/strings.xml
	res/values-uk/strings.xml
	res/values-vi/strings.xml
	res/values-zh-rCN/strings.xml
	res/values-zh-rTW/strings.xml
	res/values-zu/strings.xml
	res/values/strings.xml

packages/apps/CertInstaller

  Tracking KeyStore API changes
	src/com/android/certinstaller/CertInstaller.java

  Fix for NPE in CertInstaller when certificate lacks basic constraints
	src/com/android/certinstaller/CredentialHelper.java

packages/apps/KeyChain

  Tracking KeyStore API changes
	src/com/android/keychain/KeyChainActivity.java
	src/com/android/keychain/KeyChainService.java
	support/src/com/android/keychain/tests/support/IKeyChainServiceTestSupport.aidl
	support/src/com/android/keychain/tests/support/KeyChainServiceTestSupport.java
	tests/src/com/android/keychain/tests/KeyChainServiceTest.java

Change-Id: Ic141fb5d4b43d12fe62cb1e29c7cbd891b4be35d
parent 11d8ad4c
Loading
Loading
Loading
Loading
+2 −2
Original line number Diff line number Diff line
@@ -19,14 +19,14 @@ ifneq ($(TARGET_SIMULATOR),true)
LOCAL_PATH:= $(call my-dir)

include $(CLEAR_VARS)
LOCAL_SRC_FILES := keystore.c
LOCAL_SRC_FILES := keystore.cpp
LOCAL_C_INCLUDES := external/openssl/include
LOCAL_SHARED_LIBRARIES := libcutils libcrypto
LOCAL_MODULE:= keystore
include $(BUILD_EXECUTABLE)

include $(CLEAR_VARS)
LOCAL_SRC_FILES := keystore_cli.c
LOCAL_SRC_FILES := keystore_cli.cpp
LOCAL_C_INCLUDES := external/openssl/include
LOCAL_SHARED_LIBRARIES := libcutils libcrypto
LOCAL_MODULE:= keystore_cli

cmds/keystore/keystore.c

deleted100644 → 0
+0 −590
Original line number Diff line number Diff line
/*
 * Copyright (C) 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 <stdint.h>
#include <string.h>
#include <unistd.h>
#include <signal.h>
#include <errno.h>
#include <dirent.h>
#include <fcntl.h>
#include <limits.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/stat.h>
#include <sys/time.h>
#include <arpa/inet.h>

#include <openssl/aes.h>
#include <openssl/evp.h>
#include <openssl/md5.h>

#define LOG_TAG "keystore"
#include <cutils/log.h>
#include <cutils/sockets.h>
#include <private/android_filesystem_config.h>

#include "keystore.h"

/* KeyStore is a secured storage for key-value pairs. In this implementation,
 * each file stores one key-value pair. Keys are encoded in file names, and
 * values are encrypted with checksums. The encryption key is protected by a
 * user-defined password. To keep things simple, buffers are always larger than
 * the maximum space we needed, so boundary checks on buffers are omitted. */

#define KEY_SIZE        ((NAME_MAX - 15) / 2)
#define VALUE_SIZE      32768
#define PASSWORD_SIZE   VALUE_SIZE

/* Here is the encoding of keys. This is necessary in order to allow arbitrary
 * characters in keys. Characters in [0-~] are not encoded. Others are encoded
 * into two bytes. The first byte is one of [+-.] which represents the first
 * two bits of the character. The second byte encodes the rest of the bits into
 * [0-o]. Therefore in the worst case the length of a key gets doubled. Note
 * that Base64 cannot be used here due to the need of prefix match on keys. */

static int encode_key(char *out, uint8_t *in, int length)
{
    int i;
    for (i = length; i > 0; --i, ++in, ++out) {
        if (*in >= '0' && *in <= '~') {
            *out = *in;
        } else {
            *out = '+' + (*in >> 6);
            *++out = '0' + (*in & 0x3F);
            ++length;
        }
    }
    *out = 0;
    return length;
}

static int decode_key(uint8_t *out, char *in, int length)
{
    int i;
    for (i = 0; i < length; ++i, ++in, ++out) {
        if (*in >= '0' && *in <= '~') {
            *out = *in;
        } else {
            *out = (*in - '+') << 6;
            *out |= (*++in - '0') & 0x3F;
            --length;
        }
    }
    *out = 0;
    return length;
}

/* Here is the protocol used in both requests and responses:
 *     code [length_1 message_1 ... length_n message_n] end-of-file
 * where code is one byte long and lengths are unsigned 16-bit integers in
 * network order. Thus the maximum length of a message is 65535 bytes. */

static int the_socket = -1;

static int recv_code(int8_t *code)
{
    return recv(the_socket, code, 1, 0) == 1;
}

static int recv_message(uint8_t *message, int length)
{
    uint8_t bytes[2];
    if (recv(the_socket, &bytes[0], 1, 0) != 1 ||
        recv(the_socket, &bytes[1], 1, 0) != 1) {
        return -1;
    } else {
        int offset = bytes[0] << 8 | bytes[1];
        if (length < offset) {
            return -1;
        }
        length = offset;
        offset = 0;
        while (offset < length) {
            int n = recv(the_socket, &message[offset], length - offset, 0);
            if (n <= 0) {
                return -1;
            }
            offset += n;
        }
    }
    return length;
}

static int recv_end_of_file()
{
    uint8_t byte;
    return recv(the_socket, &byte, 1, 0) == 0;
}

static void send_code(int8_t code)
{
    send(the_socket, &code, 1, 0);
}

static void send_message(uint8_t *message, int length)
{
    uint16_t bytes = htons(length);
    send(the_socket, &bytes, 2, 0);
    send(the_socket, message, length, 0);
}

/* Here is the file format. There are two parts in blob.value, the secret and
 * the description. The secret is stored in ciphertext, and its original size
 * can be found in blob.length. The description is stored after the secret in
 * plaintext, and its size is specified in blob.info. The total size of the two
 * parts must be no more than VALUE_SIZE bytes. The first three bytes of the
 * file are reserved for future use and are always set to zero. Fields other
 * than blob.info, blob.length, and blob.value are modified by encrypt_blob()
 * and decrypt_blob(). Thus they should not be accessed from outside. */

static int the_entropy = -1;

static struct __attribute__((packed)) {
    uint8_t reserved[3];
    uint8_t info;
    uint8_t vector[AES_BLOCK_SIZE];
    uint8_t encrypted[0];
    uint8_t digest[MD5_DIGEST_LENGTH];
    uint8_t digested[0];
    int32_t length;
    uint8_t value[VALUE_SIZE + AES_BLOCK_SIZE];
} blob;

static int8_t encrypt_blob(char *name, AES_KEY *aes_key)
{
    uint8_t vector[AES_BLOCK_SIZE];
    int length;
    int fd;

    if (read(the_entropy, blob.vector, AES_BLOCK_SIZE) != AES_BLOCK_SIZE) {
        return SYSTEM_ERROR;
    }

    length = blob.length + (blob.value - blob.encrypted);
    length = (length + AES_BLOCK_SIZE - 1) / AES_BLOCK_SIZE * AES_BLOCK_SIZE;

    if (blob.info != 0) {
        memmove(&blob.encrypted[length], &blob.value[blob.length], blob.info);
    }

    blob.length = htonl(blob.length);
    MD5(blob.digested, length - (blob.digested - blob.encrypted), blob.digest);

    memcpy(vector, blob.vector, AES_BLOCK_SIZE);
    AES_cbc_encrypt(blob.encrypted, blob.encrypted, length, aes_key, vector,
                    AES_ENCRYPT);

    memset(blob.reserved, 0, sizeof(blob.reserved));
    length += (blob.encrypted - (uint8_t *)&blob) + blob.info;

    fd = open(".tmp", O_WRONLY | O_TRUNC | O_CREAT, S_IRUSR | S_IWUSR);
    length -= write(fd, &blob, length);
    close(fd);
    return (length || rename(".tmp", name)) ? SYSTEM_ERROR : NO_ERROR;
}

static int8_t decrypt_blob(char *name, AES_KEY *aes_key)
{
    int fd = open(name, O_RDONLY);
    int length;

    if (fd == -1) {
        return (errno == ENOENT) ? KEY_NOT_FOUND : SYSTEM_ERROR;
    }
    length = read(fd, &blob, sizeof(blob));
    close(fd);

    length -= (blob.encrypted - (uint8_t *)&blob) + blob.info;
    if (length < blob.value - blob.encrypted || length % AES_BLOCK_SIZE != 0) {
        return VALUE_CORRUPTED;
    }

    AES_cbc_encrypt(blob.encrypted, blob.encrypted, length, aes_key,
                    blob.vector, AES_DECRYPT);
    length -= blob.digested - blob.encrypted;
    if (memcmp(blob.digest, MD5(blob.digested, length, NULL),
               MD5_DIGEST_LENGTH)) {
        return VALUE_CORRUPTED;
    }

    length -= blob.value - blob.digested;
    blob.length = ntohl(blob.length);
    if (blob.length < 0 || blob.length > length) {
        return VALUE_CORRUPTED;
    }
    if (blob.info != 0) {
        memmove(&blob.value[blob.length], &blob.value[length], blob.info);
    }
    return NO_ERROR;
}

/* Here are the actions. Each of them is a function without arguments. All
 * information is defined in global variables, which are set properly before
 * performing an action. The number of parameters required by each action is
 * fixed and defined in a table. If the return value of an action is positive,
 * it will be treated as a response code and transmitted to the client. Note
 * that the lengths of parameters are checked when they are received, so
 * boundary checks on parameters are omitted. */

#define MAX_PARAM   2
#define MAX_RETRY   4

static uid_t uid = -1;
static int8_t state = UNINITIALIZED;
static int8_t retry = MAX_RETRY;

static struct {
    int length;
    uint8_t value[VALUE_SIZE];
} params[MAX_PARAM];

static AES_KEY encryption_key;
static AES_KEY decryption_key;

static int8_t test()
{
    return state;
}

static int8_t get()
{
    char name[NAME_MAX];
    int n = sprintf(name, "%u_", uid);
    encode_key(&name[n], params[0].value, params[0].length);
    n = decrypt_blob(name, &decryption_key);
    if (n != NO_ERROR) {
        return n;
    }
    send_code(NO_ERROR);
    send_message(blob.value, blob.length);
    return -NO_ERROR;
}

static int8_t insert()
{
    char name[NAME_MAX];
    int n = sprintf(name, "%u_", uid);
    encode_key(&name[n], params[0].value, params[0].length);
    blob.info = 0;
    blob.length = params[1].length;
    memcpy(blob.value, params[1].value, params[1].length);
    return encrypt_blob(name, &encryption_key);
}

static int8_t delete()
{
    char name[NAME_MAX];
    int n = sprintf(name, "%u_", uid);
    encode_key(&name[n], params[0].value, params[0].length);
    return (unlink(name) && errno != ENOENT) ? SYSTEM_ERROR : NO_ERROR;
}

static int8_t exist()
{
    char name[NAME_MAX];
    int n = sprintf(name, "%u_", uid);
    encode_key(&name[n], params[0].value, params[0].length);
    if (access(name, R_OK) == -1) {
        return (errno != ENOENT) ? SYSTEM_ERROR : KEY_NOT_FOUND;
    }
    return NO_ERROR;
}

static int8_t saw()
{
    DIR *dir = opendir(".");
    struct dirent *file;
    char name[NAME_MAX];
    int n;

    if (!dir) {
        return SYSTEM_ERROR;
    }
    n = sprintf(name, "%u_", uid);
    n += encode_key(&name[n], params[0].value, params[0].length);
    send_code(NO_ERROR);
    while ((file = readdir(dir)) != NULL) {
        if (!strncmp(name, file->d_name, n)) {
            char *p = &file->d_name[n];
            params[0].length = decode_key(params[0].value, p, strlen(p));
            send_message(params[0].value, params[0].length);
        }
    }
    closedir(dir);
    return -NO_ERROR;
}

static int8_t reset()
{
    DIR *dir = opendir(".");
    struct dirent *file;

    memset(&encryption_key, 0, sizeof(encryption_key));
    memset(&decryption_key, 0, sizeof(decryption_key));
    state = UNINITIALIZED;
    retry = MAX_RETRY;

    if (!dir) {
        return SYSTEM_ERROR;
    }
    while ((file = readdir(dir)) != NULL) {
        unlink(file->d_name);
    }
    closedir(dir);
    return NO_ERROR;
}

#define MASTER_KEY_FILE ".masterkey"
#define MASTER_KEY_SIZE 16
#define SALT_SIZE       16

static void set_key(uint8_t *key, uint8_t *password, int length, uint8_t *salt)
{
    if (salt) {
        PKCS5_PBKDF2_HMAC_SHA1((char *)password, length, salt, SALT_SIZE,
                               8192, MASTER_KEY_SIZE, key);
    } else {
        PKCS5_PBKDF2_HMAC_SHA1((char *)password, length, (uint8_t *)"keystore",
                               sizeof("keystore"), 1024, MASTER_KEY_SIZE, key);
    }
}

/* Here is the history. To improve the security, the parameters to generate the
 * master key has been changed. To make a seamless transition, we update the
 * file using the same password when the user unlock it for the first time. If
 * 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 int8_t password()
{
    uint8_t key[MASTER_KEY_SIZE];
    AES_KEY aes_key;
    int8_t response = SYSTEM_ERROR;

    if (state == UNINITIALIZED) {
        if (read(the_entropy, blob.value, MASTER_KEY_SIZE) != MASTER_KEY_SIZE) {
           return SYSTEM_ERROR;
        }
    } else {
        int fd = open(MASTER_KEY_FILE, O_RDONLY);
        uint8_t *salt = NULL;
        if (fd != -1) {
            int length = read(fd, &blob, sizeof(blob));
            close(fd);
            if (length > SALT_SIZE && blob.info == SALT_SIZE) {
                salt = (uint8_t *)&blob + length - SALT_SIZE;
            }
        }

        set_key(key, params[0].value, params[0].length, salt);
        AES_set_decrypt_key(key, MASTER_KEY_SIZE * 8, &aes_key);
        response = decrypt_blob(MASTER_KEY_FILE, &aes_key);
        if (response == SYSTEM_ERROR) {
            return SYSTEM_ERROR;
        }
        if (response != NO_ERROR || blob.length != MASTER_KEY_SIZE) {
            if (retry <= 0) {
                reset();
                return UNINITIALIZED;
            }
            return WRONG_PASSWORD + --retry;
        }

        if (!salt && params[1].length == -1) {
            params[1] = params[0];
        }
    }

    if (params[1].length == -1) {
        memcpy(key, blob.value, MASTER_KEY_SIZE);
    } else {
        uint8_t *salt = &blob.value[MASTER_KEY_SIZE];
        if (read(the_entropy, salt, SALT_SIZE) != SALT_SIZE) {
            return SYSTEM_ERROR;
        }

        set_key(key, params[1].value, params[1].length, salt);
        AES_set_encrypt_key(key, MASTER_KEY_SIZE * 8, &aes_key);
        memcpy(key, blob.value, MASTER_KEY_SIZE);
        blob.info = SALT_SIZE;
        blob.length = MASTER_KEY_SIZE;
        response = encrypt_blob(MASTER_KEY_FILE, &aes_key);
    }

    if (response == NO_ERROR) {
        AES_set_encrypt_key(key, MASTER_KEY_SIZE * 8, &encryption_key);
        AES_set_decrypt_key(key, MASTER_KEY_SIZE * 8, &decryption_key);
        state = NO_ERROR;
        retry = MAX_RETRY;
    }
    return response;
}

static int8_t lock()
{
    memset(&encryption_key, 0, sizeof(encryption_key));
    memset(&decryption_key, 0, sizeof(decryption_key));
    state = LOCKED;
    return NO_ERROR;
}

static int8_t unlock()
{
    params[1].length = -1;
    return password();
}

/* 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,
};

static struct action {
    int8_t (*run)();
    int8_t code;
    int8_t state;
    uint32_t perm;
    int lengths[MAX_PARAM];
} actions[] = {
    {test,     't', 0,        TEST,     {0}},
    {get,      'g', NO_ERROR, GET,      {KEY_SIZE}},
    {insert,   'i', NO_ERROR, INSERT,   {KEY_SIZE, VALUE_SIZE}},
    {delete,   'd', 0,        DELETE,   {KEY_SIZE}},
    {exist,    'e', 0,        EXIST,    {KEY_SIZE}},
    {saw,      's', 0,        SAW,      {KEY_SIZE}},
    {reset,    'r', 0,        RESET,    {0}},
    {password, 'p', 0,        PASSWORD, {PASSWORD_SIZE, PASSWORD_SIZE}},
    {lock,     'l', NO_ERROR, LOCK,     {0}},
    {unlock,   'u', LOCKED,   UNLOCK,   {PASSWORD_SIZE}},
    {NULL,      0 , 0,        0,        {0}},
};

static struct user {
    uid_t uid;
    uid_t euid;
    uint32_t perms;
} users[] = {
    {AID_SYSTEM,   ~0,         ~GET},
    {AID_VPN,      AID_SYSTEM, GET},
    {AID_WIFI,     AID_SYSTEM, GET},
    {AID_ROOT,     AID_SYSTEM, GET},
    {AID_KEYCHAIN, AID_SYSTEM, TEST | GET | SAW},
    {~0,           ~0,         TEST | GET | INSERT | DELETE | EXIST | SAW},
};

static int8_t process(int8_t code) {
    struct user *user = users;
    struct action *action = actions;
    int i;

    while (~user->uid && user->uid != uid) {
        ++user;
    }
    while (action->code && action->code != code) {
        ++action;
    }
    if (!action->code) {
        return UNDEFINED_ACTION;
    }
    if (!(action->perm & user->perms)) {
        return PERMISSION_DENIED;
    }
    if (action->state && action->state != state) {
        return state;
    }
    if (~user->euid) {
        uid = user->euid;
    }
    for (i = 0; i < MAX_PARAM && action->lengths[i]; ++i) {
        params[i].length = recv_message(params[i].value, action->lengths[i]);
        if (params[i].length == -1) {
            return PROTOCOL_ERROR;
        }
    }
    if (!recv_end_of_file()) {
        return PROTOCOL_ERROR;
    }
    return action->run();
}

#define RANDOM_DEVICE   "/dev/urandom"

int main(int argc, char **argv)
{
    int control_socket = android_get_control_socket("keystore");
    if (argc < 2) {
        LOGE("A directory must be specified!");
        return 1;
    }
    if (chdir(argv[1]) == -1) {
        LOGE("chdir: %s: %s", argv[1], strerror(errno));
        return 1;
    }
    if ((the_entropy = open(RANDOM_DEVICE, O_RDONLY)) == -1) {
        LOGE("open: %s: %s", RANDOM_DEVICE, strerror(errno));
        return 1;
    }
    if (listen(control_socket, 3) == -1) {
        LOGE("listen: %s", strerror(errno));
        return 1;
    }

    signal(SIGPIPE, SIG_IGN);
    if (access(MASTER_KEY_FILE, R_OK) == 0) {
        state = LOCKED;
    }

    while ((the_socket = accept(control_socket, NULL, 0)) != -1) {
        struct timeval tv = {.tv_sec = 3};
        struct ucred cred;
        socklen_t size = sizeof(cred);
        int8_t request;

        setsockopt(the_socket, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv));
        setsockopt(the_socket, SOL_SOCKET, SO_SNDTIMEO, &tv, sizeof(tv));

        if (getsockopt(the_socket, SOL_SOCKET, SO_PEERCRED, &cred, &size)) {
            LOGW("getsockopt: %s", strerror(errno));
        } else if (recv_code(&request)) {
            int8_t old_state = state;
            int8_t response;
            uid = cred.uid;

            if ((response = process(request)) > 0) {
                send_code(response);
                response = -response;
            }

            LOGI("uid: %d action: %c -> %d state: %d -> %d retry: %d",
                 cred.uid, request, -response, old_state, state, retry);
        }
        close(the_socket);
    }
    LOGE("accept: %s", strerror(errno));
    return 1;
}
+812 −0

File added.

Preview size limit exceeded, changes collapsed.

+15 −5
Original line number Diff line number Diff line
@@ -17,17 +17,27 @@
#ifndef __KEYSTORE_H__
#define __KEYSTORE_H__

enum response_code {
    NO_ERROR          =  1,
    LOCKED            =  2,
    UNINITIALIZED     =  3,
// note state values overlap with ResponseCode for the purposes of the state() API
enum State {
    STATE_NO_ERROR      = 1,
    STATE_LOCKED        = 2,
    STATE_UNINITIALIZED = 3,
};

enum ResponseCode {
    NO_ERROR          =  STATE_NO_ERROR, // 1
    LOCKED            =  STATE_LOCKED, // 2
    UNINITIALIZED     =  STATE_UNINITIALIZED, // 3
    SYSTEM_ERROR      =  4,
    PROTOCOL_ERROR    =  5,
    PERMISSION_DENIED =  6,
    KEY_NOT_FOUND     =  7,
    VALUE_CORRUPTED   =  8,
    UNDEFINED_ACTION  =  9,
    WRONG_PASSWORD    = 10,
    WRONG_PASSWORD_0  = 10,
    WRONG_PASSWORD_1  = 11,
    WRONG_PASSWORD_2  = 12,
    WRONG_PASSWORD_3  = 13, // MAX_RETRY = 4
};

#endif
+22 −24
Original line number Diff line number Diff line
@@ -24,36 +24,31 @@

#include "keystore.h"

char *responses[256] = {
    [NO_ERROR]           = "No error",
    [LOCKED]             = "Locked",
    [UNINITIALIZED]      = "Uninitialized",
    [SYSTEM_ERROR]       = "System error",
    [PROTOCOL_ERROR]     = "Protocol error",
    [PERMISSION_DENIED]  = "Permission denied",
    [KEY_NOT_FOUND]      = "Key not found",
    [VALUE_CORRUPTED]    = "Value corrupted",
    [UNDEFINED_ACTION]   = "Undefined action",
    [WRONG_PASSWORD]     = "Wrong password (last chance)",
    [WRONG_PASSWORD + 1] = "Wrong password (2 tries left)",
    [WRONG_PASSWORD + 2] = "Wrong password (3 tries left)",
    [WRONG_PASSWORD + 3] = "Wrong password (4 tries left)",
static const char* responses[] = {
    NULL,
    /* [NO_ERROR]           = */ "No error",
    /* [LOCKED]             = */ "Locked",
    /* [UNINITIALIZED]      = */ "Uninitialized",
    /* [SYSTEM_ERROR]       = */ "System error",
    /* [PROTOCOL_ERROR]     = */ "Protocol error",
    /* [PERMISSION_DENIED]  = */ "Permission denied",
    /* [KEY_NOT_FOUND]      = */ "Key not found",
    /* [VALUE_CORRUPTED]    = */ "Value corrupted",
    /* [UNDEFINED_ACTION]   = */ "Undefined action",
    /* [WRONG_PASSWORD]     = */ "Wrong password (last chance)",
    /* [WRONG_PASSWORD + 1] = */ "Wrong password (2 tries left)",
    /* [WRONG_PASSWORD + 2] = */ "Wrong password (3 tries left)",
    /* [WRONG_PASSWORD + 3] = */ "Wrong password (4 tries left)",
};

#define MAX_RESPONSE (WRONG_PASSWORD + 3)

int main(int argc, char **argv)
int main(int argc, char* argv[])
{
    uint8_t bytes[65536];
    uint8_t code;
    int sock, i;

    if (argc < 2) {
        printf("Usage: %s action [parameter ...]\n", argv[0]);
        return 0;
    }

    sock = socket_local_client("keystore", ANDROID_SOCKET_NAMESPACE_RESERVED,
    int sock = socket_local_client("keystore", ANDROID_SOCKET_NAMESPACE_RESERVED,
                                   SOCK_STREAM);
    if (sock == -1) {
        puts("Failed to connect");
@@ -61,7 +56,8 @@ int main(int argc, char **argv)
    }

    send(sock, argv[1], 1, 0);
    for (i = 2; i < argc; ++i) {
    uint8_t bytes[65536];
    for (int i = 2; i < argc; ++i) {
        uint16_t length = strlen(argv[i]);
        bytes[0] = length >> 8;
        bytes[1] = length;
@@ -70,11 +66,13 @@ int main(int argc, char **argv)
    }
    shutdown(sock, SHUT_WR);

    uint8_t code;
    if (recv(sock, &code, 1, 0) != 1) {
        puts("Failed to receive");
        return 1;
    }
    printf("%d %s\n", code , responses[code] ? responses[code] : "Unknown");
    int i;
    while ((i = recv(sock, &bytes[0], 1, 0)) == 1) {
        int length;
        int offset;