From dd9b0ab34361a208ab0e6143679bc001b170c483 Mon Sep 17 00:00:00 2001 From: David Woodhouse Date: Sun, 17 Jun 2012 22:02:16 +0100 Subject: [PATCH] Switch from Android's keystore_get() to our own keystore_fetch() This gives proper error handling which Android's lacks. Signed-off-by: David Woodhouse --- gnutls.c | 17 ++---- openconnect-internal.h | 30 +++-------- openssl.c | 17 +++--- ssl.c | 119 +++++++++++++++++++++++++++++++++++++++++ 4 files changed, 138 insertions(+), 45 deletions(-) diff --git a/gnutls.c b/gnutls.c index 2f3fe799..e6bf879d 100644 --- a/gnutls.c +++ b/gnutls.c @@ -249,7 +249,6 @@ static int load_datum(struct openconnect_info *vpninfo, int fd, err; #ifdef ANDROID_KEYSTORE if (!strncmp(fname, "keystore:", 9)) { - char content[KEYSTORE_MESSAGE_SIZE]; int len; const char *p = fname + 9; @@ -259,21 +258,13 @@ static int load_datum(struct openconnect_info *vpninfo, p++; if (*p == '/') p++; - len = keystore_get(p, strlen(p), content); - if (len < 0) { + len = keystore_fetch(p, &datum->data); + if (len <= 0) { vpn_progress(vpninfo, PRG_ERR, - _("Failed to lead item '%s' from keystore\n"), - p); + _("Failed to load item '%s' from keystore: %s\n"), + p, keystore_strerror(len)); return -EINVAL; } - datum->data = gnutls_malloc(len + 1); - if (!datum->data) { - vpn_progress(vpninfo, PRG_ERR, - _("Failed to allocate memory for keystore item\n")); - return -ENOMEM; - } - datum->data[len] = 0; - memcpy(datum->data, content, len); datum->size = len; return 0; } diff --git a/openconnect-internal.h b/openconnect-internal.h index d3af5eed..8fc5859e 100644 --- a/openconnect-internal.h +++ b/openconnect-internal.h @@ -346,6 +346,13 @@ int __attribute__ ((format (printf, 2, 3))) openconnect_SSL_printf(struct openconnect_info *vpninfo, const char *fmt, ...); int openconnect_print_err_cb(const char *str, size_t len, void *ptr); #define openconnect_report_ssl_errors(v) ERR_print_errors_cb(openconnect_print_err_cb, (v)) +#ifdef FAKE_ANDROID_KEYSTORE +#define ANDROID_KEYSTORE +#endif +#ifdef ANDROID_KEYSTORE +char *keystore_strerror(int err); +int keystore_fetch(const char *key, unsigned char **result); +#endif /* ${SSL_LIBRARY}.c */ int openconnect_SSL_gets(struct openconnect_info *vpninfo, char *buf, size_t len); @@ -394,27 +401,4 @@ int add_securid_pin(char *token, char *pin); /* version.c */ extern const char *openconnect_version_str; -#ifdef ANDROID_KEYSTORE -#include -#elif defined (FAKE_ANDROID_KEYSTORE) /* For testing */ -#define ANDROID_KEYSTORE -#define KEYSTORE_MESSAGE_SIZE 16384 -#include -#include -#include - -static inline int keystore_get(const char *p, int plen, char *content) -{ - int fd = open(p, O_RDONLY); - int len; - if (fd == -1) - return fd; - len = read(fd, content, KEYSTORE_MESSAGE_SIZE); - close(fd); - if (len <= 0) - return -1; - return len; -} -#endif /* FAKE_ANDROID_KEYSTORE */ - #endif /* __OPENCONNECT_INTERNAL_H__ */ diff --git a/openssl.c b/openssl.c index 552efa99..d865de11 100644 --- a/openssl.c +++ b/openssl.c @@ -603,7 +603,7 @@ static int reload_pem_cert(struct openconnect_info *vpninfo) #ifdef ANDROID_KEYSTORE static BIO *BIO_from_keystore(struct openconnect_info *vpninfo, const char *item) { - char content[KEYSTORE_MESSAGE_SIZE]; + unsigned char *content; BIO *b; int len; const char *p = item + 9; @@ -614,24 +614,23 @@ static BIO *BIO_from_keystore(struct openconnect_info *vpninfo, const char *item p++; if (*p == '/') p++; - /* Old versions of keystore_get.h will return the input length - instead of an error, in some circumstances. So check the - content actually changes, too. */ - content[0] = 0; - len = keystore_get(p, strlen(p), content); - if (len < 0 || content[0] == 0) { + + len = keystore_fetch(p, &content); + if (len < 0) { vpn_progress(vpninfo, PRG_ERR, - _("Failed to lead item '%s' from keystore\n"), - p); + _("Failed to load item '%s' from keystore: %s\n"), + p, keystore_strerror(len)); return NULL; } if (!(b = BIO_new(BIO_s_mem())) || BIO_write(b, content, len) != len) { vpn_progress(vpninfo, PRG_ERR, _("Failed to create BIO for keystore item '%s'\n"), p); + free(content); BIO_free(b); return NULL; } + free(content); return b; } #endif diff --git a/ssl.c b/ssl.c index 32eecebb..d6fe6930 100644 --- a/ssl.c +++ b/ssl.c @@ -24,6 +24,7 @@ #include #include +#include #include #include #include @@ -365,3 +366,121 @@ int openconnect_print_err_cb(const char *str, size_t len, void *ptr) return 0; } #endif + +#ifdef FAKE_ANDROID_KEYSTORE +char *keystore_strerror(int err) +{ + return (char *)strerror(-err); +} + +int keystore_fetch(const char *key, unsigned char **result) +{ + unsigned char *data; + struct stat st; + int fd; + int ret; + + fd = open(key, O_RDONLY); + if (fd < 0) + return -errno; + + if (fstat(fd, &st)) { + ret = -errno; + goto out_fd; + } + + data = malloc(st.st_size); + if (!data) { + ret = -ENOMEM; + goto out_fd; + } + + if (read(fd, data, st.st_size) != st.st_size) { + ret = -EIO; + free(data); + goto out_fd; + } + *result = data; + ret = st.st_size; + out_fd: + close(fd); + return ret; +} +#elif defined (ANDROID_KEYSTORE) +#include +#include +char *keystore_strerror(int err) +{ + switch (-err) { + case NO_ERROR: return _("No error"); + case LOCKED: return _("Keystore ocked"); + case UNINITIALIZED: return _("Keystore uninitialized"); + case SYSTEM_ERROR: return _("System error"); + case PROTOCOL_ERROR: return _("Protocol error"); + case PERMISSION_DENIED: return _("Permission denied"); + case KEY_NOT_FOUND: return _("Key not found"); + case VALUE_CORRUPTED: return _("Value corrupted"); + case UNDEFINED_ACTION: return _("Undefined action"); + case WRONG_PASSWORD_0: + case WRONG_PASSWORD_1: + case WRONG_PASSWORD_2: + case WRONG_PASSWORD_3: return _("Wrong password"); + default: return _("Unknown error"); + } +} + +/* Returns length, or a negative errno in its own namespace (handled by its + own strerror function above). The numbers are from Android's keystore.h */ +int keystore_fetch(const char *key, unsigned char **result) +{ + unsigned char *data, *p; + unsigned char buf[3]; + int len, fd, ofs; + int ret = -SYSTEM_ERROR; + + fd = socket_local_client("keystore", + ANDROID_SOCKET_NAMESPACE_RESERVED, + SOCK_STREAM); + if (fd < 0) + return -SYSTEM_ERROR; + + len = strlen(key); + buf[0] = 'g'; + buf[1] = len >> 8; + buf[2] = len & 0xff; + + if (send(fd, buf, 3, 0) != 3 || send(fd, key, len, 0) != len || + shutdown(fd, SHUT_WR) || recv(fd, buf, 1, 0) != 1) + goto out; + + if (buf[0] != NO_ERROR) { + /* Should never be zero */ + ret = buf[0] ? -buf[0] : -PROTOCOL_ERROR; + goto out; + } + if (recv(fd, buf, 2, 0) != 2) + goto out; + len = (buf[0] << 8) + buf[1]; + data = malloc(len); + if (!data) + goto out; + p = data; + ret = len; + while (len) { + int got = recv(fd, p, len, 0); + if (got <= 0) { + free(data); + ret = -PROTOCOL_ERROR; + goto out; + } + len -= got; + p += got; + } + + *result = data; + + out: + close(fd); + return ret; +} +#endif