Switch from Android's keystore_get() to our own keystore_fetch()
authorDavid Woodhouse <David.Woodhouse@intel.com>
Sun, 17 Jun 2012 21:02:16 +0000 (22:02 +0100)
committerDavid Woodhouse <David.Woodhouse@intel.com>
Sun, 17 Jun 2012 22:43:09 +0000 (23:43 +0100)
This gives proper error handling which Android's lacks.

Signed-off-by: David Woodhouse <David.Woodhouse@intel.com>
gnutls.c
openconnect-internal.h
openssl.c
ssl.c

index 2f3fe79..e6bf879 100644 (file)
--- 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;
        }
index d3af5ee..8fc5859 100644 (file)
@@ -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 <keystore_get.h>
-#elif defined (FAKE_ANDROID_KEYSTORE) /* For testing */
-#define ANDROID_KEYSTORE
-#define KEYSTORE_MESSAGE_SIZE 16384
-#include <sys/types.h>
-#include <sys/stat.h>
-#include <fcntl.h>
-
-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__ */
index 552efa9..d865de1 100644 (file)
--- 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 32eeceb..d6fe693 100644 (file)
--- a/ssl.c
+++ b/ssl.c
@@ -24,6 +24,7 @@
 
 #include <sys/types.h>
 #include <sys/socket.h>
+#include <sys/stat.h>
 #include <netinet/in.h>
 #include <arpa/inet.h>
 #include <netdb.h>
@@ -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 <cutils/sockets.h>
+#include <keystore.h>
+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