diff options
Diffstat (limited to 'adb')
| -rw-r--r-- | adb/Android.mk | 22 | ||||
| -rw-r--r-- | adb/adb.c | 128 | ||||
| -rw-r--r-- | adb/adb.h | 14 | ||||
| -rw-r--r-- | adb/adb_auth.h | 54 | ||||
| -rw-r--r-- | adb/adb_auth_client.c | 245 | ||||
| -rw-r--r-- | adb/adb_auth_host.c | 420 | ||||
| -rw-r--r-- | adb/protocol.txt | 19 | ||||
| -rw-r--r-- | adb/sysdeps.h | 2 |
8 files changed, 882 insertions, 22 deletions
diff --git a/adb/Android.mk b/adb/Android.mk index 1a251061..681c7c73 100644 --- a/adb/Android.mk +++ b/adb/Android.mk @@ -17,32 +17,34 @@ ifeq ($(HOST_OS),linux) USB_SRCS := usb_linux.c EXTRA_SRCS := get_my_path_linux.c LOCAL_LDLIBS += -lrt -lncurses -lpthread + LOCAL_SHARED_LIBRARIES := libcrypto endif ifeq ($(HOST_OS),darwin) USB_SRCS := usb_osx.c EXTRA_SRCS := get_my_path_darwin.c - LOCAL_LDLIBS += -lpthread -framework CoreFoundation -framework IOKit -framework Carbon + LOCAL_LDLIBS += -lpthread -lcrypto -framework CoreFoundation -framework IOKit -framework Carbon endif ifeq ($(HOST_OS),freebsd) USB_SRCS := usb_libusb.c EXTRA_SRCS := get_my_path_freebsd.c LOCAL_LDLIBS += -lpthread -lusb + LOCAL_SHARED_LIBRARIES := libcrypto endif ifeq ($(HOST_OS),windows) USB_SRCS := usb_windows.c - EXTRA_SRCS := get_my_path_windows.c - EXTRA_STATIC_LIBS := AdbWinApi + EXTRA_SRCS := get_my_path_windows.c ../libcutils/list.c + EXTRA_STATIC_LIBS := AdbWinApi libcrypto_static ifneq ($(strip $(USE_CYGWIN)),) # Pure cygwin case - LOCAL_LDLIBS += -lpthread + LOCAL_LDLIBS += -lpthread -lgdi32 LOCAL_C_INCLUDES += /usr/include/w32api/ddk endif ifneq ($(strip $(USE_MINGW)),) # MinGW under Linux case - LOCAL_LDLIBS += -lws2_32 + LOCAL_LDLIBS += -lws2_32 -lgdi32 USE_SYSDEPS_WIN32 := 1 LOCAL_C_INCLUDES += /usr/i586-mingw32msvc/include/ddk endif @@ -57,6 +59,7 @@ LOCAL_SRC_FILES := \ transport_usb.c \ commandline.c \ adb_client.c \ + adb_auth_host.c \ sockets.c \ services.c \ file_sync_client.c \ @@ -65,6 +68,7 @@ LOCAL_SRC_FILES := \ utils.c \ usb_vendors.c +LOCAL_C_INCLUDES += external/openssl/include ifneq ($(USE_SYSDEPS_WIN32),) LOCAL_SRC_FILES += sysdeps_win32.c @@ -104,6 +108,7 @@ LOCAL_SRC_FILES := \ transport.c \ transport_local.c \ transport_usb.c \ + adb_auth_client.c \ sockets.c \ services.c \ file_sync_service.c \ @@ -127,7 +132,7 @@ LOCAL_FORCE_STATIC_EXECUTABLE := true LOCAL_MODULE_PATH := $(TARGET_ROOT_OUT_SBIN) LOCAL_UNSTRIPPED_PATH := $(TARGET_ROOT_OUT_SBIN_UNSTRIPPED) -LOCAL_STATIC_LIBRARIES := libcutils libc +LOCAL_STATIC_LIBRARIES := libcutils libc libmincrypt include $(BUILD_EXECUTABLE) @@ -146,6 +151,7 @@ LOCAL_SRC_FILES := \ transport_usb.c \ commandline.c \ adb_client.c \ + adb_auth_host.c \ sockets.c \ services.c \ file_sync_client.c \ @@ -165,9 +171,13 @@ LOCAL_CFLAGS := \ -D_XOPEN_SOURCE \ -D_GNU_SOURCE +LOCAL_C_INCLUDES += external/openssl/include + LOCAL_MODULE := adb LOCAL_STATIC_LIBRARIES := libzipfile libunz libcutils +LOCAL_SHARED_LIBRARIES := libcrypto + include $(BUILD_EXECUTABLE) endif @@ -28,6 +28,7 @@ #include "sysdeps.h" #include "adb.h" +#include "adb_auth.h" #define ARRAY_SIZE(a) (sizeof(a) / sizeof((a)[0])) @@ -46,6 +47,8 @@ ADB_MUTEX_DEFINE( D_lock ); int HOST = 0; +static int auth_enabled = 0; + #if !ADB_HOST static const char *adb_device_banner = "device"; #endif @@ -100,6 +103,7 @@ void adb_trace_init(void) { "transport", TRACE_TRANSPORT }, { "jdwp", TRACE_JDWP }, { "services", TRACE_SERVICES }, + { "auth", TRACE_AUTH }, { NULL, 0 } }; @@ -203,19 +207,21 @@ void put_apacket(apacket *p) free(p); } -void handle_online(void) +void handle_online(atransport *t) { D("adb: online\n"); + t->online = 1; } void handle_offline(atransport *t) { D("adb: offline\n"); //Close the associated usb + t->online = 0; run_transport_disconnects(t); } -#if TRACE_PACKETS +#if DEBUG_PACKETS #define DUMPMAX 32 void print_packet(const char *label, apacket *p) { @@ -230,6 +236,7 @@ void print_packet(const char *label, apacket *p) case A_OKAY: tag = "OKAY"; break; case A_CLSE: tag = "CLSE"; break; case A_WRTE: tag = "WRTE"; break; + case A_AUTH: tag = "AUTH"; break; default: tag = "????"; break; } @@ -251,7 +258,7 @@ void print_packet(const char *label, apacket *p) } x++; } - fprintf(stderr, tag); + fputs(tag, stderr); } #endif @@ -315,11 +322,70 @@ static void send_connect(atransport *t) cp->msg.data_length = fill_connect_data((char *)cp->data, sizeof(cp->data)); send_packet(cp, t); -#if ADB_HOST - /* XXX why sleep here? */ - // allow the device some time to respond to the connect message - adb_sleep_ms(1000); -#endif +} + +static void send_auth_request(atransport *t) +{ + D("Calling send_auth_request\n"); + apacket *p; + int ret; + + ret = adb_auth_generate_token(t->token, sizeof(t->token)); + if (ret != sizeof(t->token)) { + D("Error generating token ret=%d\n", ret); + return; + } + + p = get_apacket(); + memcpy(p->data, t->token, ret); + p->msg.command = A_AUTH; + p->msg.arg0 = ADB_AUTH_TOKEN; + p->msg.data_length = ret; + send_packet(p, t); +} + +static void send_auth_response(uint8_t *token, size_t token_size, atransport *t) +{ + D("Calling send_auth_response\n"); + apacket *p = get_apacket(); + int ret; + + ret = adb_auth_sign(t->key, token, token_size, p->data); + if (!ret) { + D("Error signing the token\n"); + put_apacket(p); + return; + } + + p->msg.command = A_AUTH; + p->msg.arg0 = ADB_AUTH_SIGNATURE; + p->msg.data_length = ret; + send_packet(p, t); +} + +static void send_auth_publickey(atransport *t) +{ + D("Calling send_auth_publickey\n"); + apacket *p = get_apacket(); + int ret; + + ret = adb_auth_get_userkey(p->data, sizeof(p->data)); + if (!ret) { + D("Failed to get user public key\n"); + put_apacket(p); + return; + } + + p->msg.command = A_AUTH; + p->msg.arg0 = ADB_AUTH_RSAPUBLICKEY; + p->msg.data_length = ret; + send_packet(p, t); +} + +void adb_auth_verified(atransport *t) +{ + handle_online(t); + send_connect(t); } static char *connection_state_name(atransport *t) @@ -451,13 +517,42 @@ void handle_packet(apacket *p, atransport *t) t->connection_state = CS_OFFLINE; handle_offline(t); } + parse_banner((char*) p->data, t); - handle_online(); - if(!HOST) send_connect(t); + + if (HOST || !auth_enabled) { + handle_online(t); + if(!HOST) send_connect(t); + } else { + send_auth_request(t); + } + break; + + case A_AUTH: + if (p->msg.arg0 == ADB_AUTH_TOKEN) { + t->key = adb_auth_nextkey(t->key); + if (t->key) { + send_auth_response(p->data, p->msg.data_length, t); + } else { + /* No more private keys to try, send the public key */ + send_auth_publickey(t); + } + } else if (p->msg.arg0 == ADB_AUTH_SIGNATURE) { + if (adb_auth_verify(t->token, p->data, p->msg.data_length)) { + adb_auth_verified(t); + t->failed_auth_attempts = 0; + } else { + if (t->failed_auth_attempts++ > 10) + adb_sleep_ms(1000); + send_auth_request(t); + } + } else if (p->msg.arg0 == ADB_AUTH_RSAPUBLICKEY) { + adb_auth_confirm_key(p->data, p->msg.data_length, t); + } break; case A_OPEN: /* OPEN(local-id, 0, "destination") */ - if(t->connection_state != CS_OFFLINE) { + if (t->online) { char *name = (char*) p->data; name[p->msg.data_length > 0 ? p->msg.data_length - 1 : 0] = 0; s = create_local_service_socket(name); @@ -473,7 +568,7 @@ void handle_packet(apacket *p, atransport *t) break; case A_OKAY: /* READY(local-id, remote-id, "") */ - if(t->connection_state != CS_OFFLINE) { + if (t->online) { if((s = find_local_socket(p->msg.arg1))) { if(s->peer == 0) { s->peer = create_remote_socket(p->msg.arg0, t); @@ -485,7 +580,7 @@ void handle_packet(apacket *p, atransport *t) break; case A_CLSE: /* CLOSE(local-id, remote-id, "") */ - if(t->connection_state != CS_OFFLINE) { + if (t->online) { if((s = find_local_socket(p->msg.arg1))) { s->close(s); } @@ -493,7 +588,7 @@ void handle_packet(apacket *p, atransport *t) break; case A_WRTE: - if(t->connection_state != CS_OFFLINE) { + if (t->online) { if((s = find_local_socket(p->msg.arg1))) { unsigned rid = p->msg.arg0; p->len = p->msg.data_length; @@ -1014,6 +1109,7 @@ int adb_main(int is_daemon, int server_port) usb_vendors_init(); usb_init(); local_init(DEFAULT_ADB_LOCAL_TRANSPORT_PORT); + adb_auth_init(); char local_name[30]; build_local_name(local_name, sizeof(local_name), server_port); @@ -1021,6 +1117,10 @@ int adb_main(int is_daemon, int server_port) exit(1); } #else + property_get("ro.adb.secure", value, "0"); + auth_enabled = !strcmp(value, "1"); + if (auth_enabled) + adb_auth_init(); /* don't listen on a port (default 5037) if running in secure mode */ /* don't run as root if we are running in secure mode */ @@ -29,13 +29,14 @@ #define A_OKAY 0x59414b4f #define A_CLSE 0x45534c43 #define A_WRTE 0x45545257 +#define A_AUTH 0x48545541 #define A_VERSION 0x01000000 // ADB protocol version #define ADB_VERSION_MAJOR 1 // Used for help/version information #define ADB_VERSION_MINOR 0 // Used for help/version information -#define ADB_SERVER_VERSION 29 // Increment this when we want to force users to start a new adb server +#define ADB_SERVER_VERSION 30 // Increment this when we want to force users to start a new adb server typedef struct amessage amessage; typedef struct apacket apacket; @@ -165,6 +166,8 @@ typedef enum transport_type { kTransportHost, } transport_type; +#define TOKEN_SIZE 20 + struct atransport { atransport *next; @@ -181,6 +184,7 @@ struct atransport int ref_count; unsigned sync_token; int connection_state; + int online; transport_type type; /* usb handle or socket fd as needed */ @@ -198,6 +202,11 @@ struct atransport /* a list of adisconnect callbacks called when the transport is kicked */ int kicked; adisconnect disconnects; + + void *key; + unsigned char token[TOKEN_SIZE]; + fdevent auth_fde; + unsigned failed_auth_attempts; }; @@ -349,6 +358,7 @@ typedef enum { TRACE_SYSDEPS, TRACE_JDWP, /* 0x100 */ TRACE_SERVICES, + TRACE_AUTH, } AdbTrace; #if ADB_TRACE @@ -408,7 +418,7 @@ void adb_qemu_trace(const char* fmt, ...); #endif -#if !TRACE_PACKETS +#if !DEBUG_PACKETS #define print_packet(tag,p) do {} while (0) #endif diff --git a/adb/adb_auth.h b/adb/adb_auth.h new file mode 100644 index 00000000..1fffa497 --- /dev/null +++ b/adb/adb_auth.h @@ -0,0 +1,54 @@ +/* + * 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 __ADB_AUTH_H +#define __ADB_AUTH_H + +void adb_auth_init(void); +void adb_auth_verified(atransport *t); + +/* AUTH packets first argument */ +/* Request */ +#define ADB_AUTH_TOKEN 1 +/* Response */ +#define ADB_AUTH_SIGNATURE 2 +#define ADB_AUTH_RSAPUBLICKEY 3 + +#if ADB_HOST + +int adb_auth_sign(void *key, void *token, size_t token_size, void *sig); +void *adb_auth_nextkey(void *current); +int adb_auth_get_userkey(unsigned char *data, size_t len); + +static inline int adb_auth_generate_token(void *token, size_t token_size) { return 0; } +static inline int adb_auth_verify(void *token, void *sig, int siglen) { return 0; } +static inline void adb_auth_confirm_key(unsigned char *data, size_t len, atransport *t) { } +static inline void adb_auth_reload_keys(void) { } + +#else // !ADB_HOST + +static inline int adb_auth_sign(void* key, void *token, size_t token_size, void *sig) { return 0; } +static inline void *adb_auth_nextkey(void *current) { return NULL; } +static inline int adb_auth_get_userkey(unsigned char *data, size_t len) { return 0; } + +int adb_auth_generate_token(void *token, size_t token_size); +int adb_auth_verify(void *token, void *sig, int siglen); +void adb_auth_confirm_key(unsigned char *data, size_t len, atransport *t); +void adb_auth_reload_keys(void); + +#endif // ADB_HOST + +#endif // __ADB_AUTH_H diff --git a/adb/adb_auth_client.c b/adb/adb_auth_client.c new file mode 100644 index 00000000..0b4913ef --- /dev/null +++ b/adb/adb_auth_client.c @@ -0,0 +1,245 @@ +/* + * 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 <stdio.h> +#include <string.h> +#include <resolv.h> +#include <cutils/list.h> +#include <cutils/sockets.h> + +#include "sysdeps.h" +#include "adb.h" +#include "adb_auth.h" +#include "fdevent.h" +#include "mincrypt/rsa.h" + +#define TRACE_TAG TRACE_AUTH + + +struct adb_public_key { + struct listnode node; + RSAPublicKey key; +}; + +static struct listnode key_list; + +static char *key_paths[] = { + "/adb_keys", + "/data/misc/adb/adb_keys", + NULL +}; + +static fdevent listener_fde; +static int framework_fd = -1; + + +static void read_keys(const char *file, struct listnode *list) +{ + struct adb_public_key *key; + FILE *f; + char buf[MAX_PAYLOAD]; + char *sep; + int ret; + + f = fopen(file, "r"); + if (!f) { + D("Can't open '%s'\n", file); + return; + } + + while (fgets(buf, sizeof(buf), f)) { + /* Allocate 4 extra bytes to decode the base64 data in-place */ + key = calloc(1, sizeof(*key) + 4); + if (!key) { + D("Can't malloc key\n"); + break; + } + + sep = strpbrk(buf, " \t"); + if (sep) + *sep = '\0'; + + ret = __b64_pton(buf, (u_char *)&key->key, sizeof(key->key) + 4); + if (ret != sizeof(key->key)) { + D("%s: Invalid base64 data ret=%d\n", file, ret); + free(key); + continue; + } + + if (key->key.len != RSANUMWORDS) { + D("%s: Invalid key len %d\n", file, key->key.len); + free(key); + continue; + } + + list_add_tail(list, &key->node); + } + + fclose(f); +} + +static void free_keys(struct listnode *list) +{ + struct listnode *item; + + while (!list_empty(list)) { + item = list_head(list); + list_remove(item); + free(node_to_item(item, struct adb_public_key, node)); + } +} + +void adb_auth_reload_keys(void) +{ + char *path; + char **paths = key_paths; + struct stat buf; + + free_keys(&key_list); + + while ((path = *paths++)) { + if (!stat(path, &buf)) { + D("Loading keys from '%s'\n", path); + read_keys(path, &key_list); + } + } +} + +int adb_auth_generate_token(void *token, size_t token_size) +{ + FILE *f; + int ret; + + f = fopen("/dev/urandom", "r"); + if (!f) + return 0; + + ret = fread(token, token_size, 1, f); + + fclose(f); + return ret * token_size; +} + +int adb_auth_verify(void *token, void *sig, int siglen) +{ + struct listnode *item; + struct adb_public_key *key; + int ret; + + if (siglen != RSANUMBYTES) + return 0; + + list_for_each(item, &key_list) { + key = node_to_item(item, struct adb_public_key, node); + ret = RSA_verify(&key->key, sig, siglen, token); + if (ret) + return 1; + } + + return 0; +} + +static void adb_auth_event(int fd, unsigned events, void *data) +{ + atransport *t = data; + char response[2]; + int ret; + + if (events & FDE_READ) { + ret = unix_read(fd, response, sizeof(response)); + if (ret < 0) { + D("Disconnect"); + fdevent_remove(&t->auth_fde); + framework_fd = -1; + } + else if (ret == 2 && response[0] == 'O' && response[1] == 'K') { + adb_auth_reload_keys(); + adb_auth_verified(t); + } + } +} + +void adb_auth_confirm_key(unsigned char *key, size_t len, atransport *t) +{ + char msg[MAX_PAYLOAD]; + int ret; + + if (framework_fd < 0) { + D("Client not connected\n"); + return; + } + + if (key[len - 1] != '\0') { + D("Key must be a null-terminated string\n"); + return; + } + + ret = snprintf(msg, sizeof(msg), "PK%s", key); + if (ret >= (signed)sizeof(msg)) { + D("Key too long. ret=%d", ret); + return; + } + D("Sending '%s'\n", msg); + + ret = unix_write(framework_fd, msg, ret); + if (ret < 0) { + D("Failed to write PK, errno=%d\n", errno); + return; + } + + fdevent_install(&t->auth_fde, framework_fd, adb_auth_event, t); + fdevent_add(&t->auth_fde, FDE_READ); +} + +static void adb_auth_listener(int fd, unsigned events, void *data) +{ + struct sockaddr addr; + socklen_t alen; + int s; + + alen = sizeof(addr); + + s = adb_socket_accept(fd, &addr, &alen); + if (s < 0) { + D("Failed to accept: errno=%d\n", errno); + return; + } + + framework_fd = s; +} + +void adb_auth_init(void) +{ + int fd, ret; + + list_init(&key_list); + adb_auth_reload_keys(); + + fd = android_get_control_socket("adbd"); + if (fd < 0) { + D("Failed to get adbd socket\n"); + return; + } + + ret = listen(fd, 4); + if (ret < 0) { + D("Failed to listen on '%d'\n", fd); + return; + } + + fdevent_install(&listener_fde, fd, adb_auth_listener, NULL); + fdevent_add(&listener_fde, FDE_READ); +} diff --git a/adb/adb_auth_host.c b/adb/adb_auth_host.c new file mode 100644 index 00000000..99dcfcba --- /dev/null +++ b/adb/adb_auth_host.c @@ -0,0 +1,420 @@ +/* + * 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 <stdio.h> + +#ifdef _WIN32 +# define WIN32_LEAN_AND_MEAN +# include "windows.h" +# include "shlobj.h" +#else +# include <sys/types.h> +# include <sys/stat.h> +# include <unistd.h> +#endif +#include <string.h> + +#include "sysdeps.h" +#include "adb.h" +#include "adb_auth.h" + +/* HACK: we need the RSAPublicKey struct + * but RSA_verify conflits with openssl */ +#define RSA_verify RSA_verify_mincrypt +#include "mincrypt/rsa.h" +#undef RSA_verify + +#include <cutils/list.h> + +#include <openssl/evp.h> +#include <openssl/objects.h> +#include <openssl/pem.h> +#include <openssl/rsa.h> +#include <openssl/sha.h> + +#define TRACE_TAG TRACE_AUTH + +#define ANDROID_PATH ".android" +#define ADB_KEY_FILE "adb_key" + + +struct adb_private_key { + struct listnode node; + RSA *rsa; +}; + +static struct listnode key_list; + + +/* Convert OpenSSL RSA private key to android pre-computed RSAPublicKey format */ +static int RSA_to_RSAPublicKey(RSA *rsa, RSAPublicKey *pkey) +{ + int ret = 1; + unsigned int i; + + BN_CTX* ctx = BN_CTX_new(); + BIGNUM* r32 = BN_new(); + BIGNUM* rr = BN_new(); + BIGNUM* r = BN_new(); + BIGNUM* rem = BN_new(); + BIGNUM* n = BN_new(); + BIGNUM* n0inv = BN_new(); + + if (RSA_size(rsa) != RSANUMBYTES) { + ret = 0; + goto out; + } + + BN_set_bit(r32, 32); + BN_copy(n, rsa->n); + BN_set_bit(r, RSANUMWORDS * 32); + BN_mod_sqr(rr, r, n, ctx); + BN_div(NULL, rem, n, r32, ctx); + BN_mod_inverse(n0inv, rem, r32, ctx); + + pkey->len = RSANUMWORDS; + pkey->n0inv = 0 - BN_get_word(n0inv); + for (i = 0; i < RSANUMWORDS; i++) { + BN_div(rr, rem, rr, r32, ctx); + pkey->rr[i] = BN_get_word(rem); + BN_div(n, rem, n, r32, ctx); + pkey->n[i] = BN_get_word(rem); + } + pkey->exponent = BN_get_word(rsa->e); + +out: + BN_free(n0inv); + BN_free(n); + BN_free(rem); + BN_free(r); + BN_free(rr); + BN_free(r32); + BN_CTX_free(ctx); + + return ret; +} + +static void get_user_info(char *buf, size_t len) +{ + char hostname[1024], username[1024]; + int ret; + +#ifndef _WIN32 + ret = gethostname(hostname, sizeof(hostname)); + if (ret < 0) +#endif + strcpy(hostname, "unknown"); + +#if !defined _WIN32 && !defined ADB_HOST_ON_TARGET + ret = getlogin_r(username, sizeof(username)); + if (ret < 0) +#endif + strcpy(username, "unknown"); + + ret = snprintf(buf, len, " %s@%s", username, hostname); + if (ret >= (signed)len) + buf[len - 1] = '\0'; +} + +static int write_public_keyfile(RSA *private_key, const char *private_key_path) +{ + RSAPublicKey pkey; + BIO *bio, *b64, *bfile; + char path[PATH_MAX], info[MAX_PAYLOAD]; + int ret; + + ret = snprintf(path, sizeof(path), "%s.pub", private_key_path); + if (ret >= (signed)sizeof(path)) + return 0; + + ret = RSA_to_RSAPublicKey(private_key, &pkey); + if (!ret) { + D("Failed to convert to publickey\n"); + return 0; + } + + bfile = BIO_new_file(path, "w"); + if (!bfile) { + D("Failed to open '%s'\n", path); + return 0; + } + + D("Writing public key to '%s'\n", path); + + b64 = BIO_new(BIO_f_base64()); + BIO_set_flags(b64, BIO_FLAGS_BASE64_NO_NL); + + bio = BIO_push(b64, bfile); + BIO_write(bio, &pkey, sizeof(pkey)); + BIO_flush(bio); + BIO_pop(b64); + BIO_free(b64); + + get_user_info(info, sizeof(info)); + BIO_write(bfile, info, strlen(info)); + BIO_flush(bfile); + BIO_free_all(bfile); + + return 1; +} + +static int generate_key(const char *file) +{ + EVP_PKEY* pkey = EVP_PKEY_new(); + BIGNUM* exponent = BN_new(); + RSA* rsa = RSA_new(); + FILE *f = NULL; + int ret = 0; + + D("generate_key '%s'\n", file); + + if (!pkey || !exponent || !rsa) { + D("Failed to allocate key\n"); + goto out; + } + + BN_set_word(exponent, RSA_F4); + RSA_generate_key_ex(rsa, 2048, exponent, NULL); + EVP_PKEY_set1_RSA(pkey, rsa); + + f = fopen(file, "w"); + if (!f) { + D("Failed to open '%s'\n", file); + goto out; + } + + if (!PEM_write_PrivateKey(f, pkey, NULL, NULL, 0, NULL, NULL)) { + D("Failed to write key\n"); + goto out; + } + + if (!write_public_keyfile(rsa, file)) { + D("Failed to write public key\n"); + goto out; + } + + ret = 1; + +out: + if (f) + fclose(f); + EVP_PKEY_free(pkey); + RSA_free(rsa); + BN_free(exponent); + return ret; +} + +static int read_key(const char *file, struct listnode *list) +{ + struct adb_private_key *key; + FILE *f; + + D("read_key '%s'\n", file); + + f = fopen(file, "r"); + if (!f) { + D("Failed to open '%s'\n", file); + return 0; + } + + key = malloc(sizeof(*key)); + if (!key) { + D("Failed to alloc key\n"); + fclose(f); + return 0; + } + key->rsa = RSA_new(); + + if (!PEM_read_RSAPrivateKey(f, &key->rsa, NULL, NULL)) { + D("Failed to read key\n"); + fclose(f); + RSA_free(key->rsa); + free(key); + return 0; + } + + fclose(f); + list_add_tail(list, &key->node); + return 1; +} + +static int get_user_keyfilepath(char *filename, size_t len) +{ + const char *format, *home; + char android_dir[PATH_MAX]; + struct stat buf; +#ifdef _WIN32 + char path[PATH_MAX]; + home = getenv("ANDROID_SDK_HOME"); + if (!home) { + SHGetFolderPath(NULL, CSIDL_PROFILE, NULL, 0, path); + home = path; + } + format = "%s\\%s"; +#else + home = getenv("HOME"); + if (!home) + return -1; + format = "%s/%s"; +#endif + + D("home '%s'\n", home); + + if (snprintf(android_dir, sizeof(android_dir), format, home, + ANDROID_PATH) >= (int)sizeof(android_dir)) + return -1; + + if (stat(android_dir, &buf)) { + if (adb_mkdir(android_dir, 0750) < 0) { + D("Cannot mkdir '%s'", android_dir); + return -1; + } + } + + return snprintf(filename, len, format, android_dir, ADB_KEY_FILE); +} + +static int get_user_key(struct listnode *list) +{ + struct stat buf; + char path[PATH_MAX]; + int ret; + + ret = get_user_keyfilepath(path, sizeof(path)); + if (ret < 0 || ret >= (signed)sizeof(path)) { + D("Error getting user key filename"); + return 0; + } + + D("user key '%s'\n", path); + + if (stat(path, &buf) == -1) { + if (!generate_key(path)) { + D("Failed to generate new key\n"); + return 0; + } + } + + return read_key(path, list); +} + +static void get_vendor_keys(struct listnode *list) +{ + const char *adb_keys_path; + char keys_path[MAX_PAYLOAD]; + char *path; + char *save; + struct stat buf; + + adb_keys_path = getenv("ADB_VENDOR_KEYS"); + if (!adb_keys_path) + return; + strncpy(keys_path, adb_keys_path, sizeof(keys_path)); + + path = adb_strtok_r(keys_path, ENV_PATH_SEPARATOR_STR, &save); + while (path) { + D("Reading: '%s'\n", path); + + if (stat(path, &buf)) + D("Can't read '%s'\n", path); + else if (!read_key(path, list)) + D("Failed to read '%s'\n", path); + + path = adb_strtok_r(NULL, ENV_PATH_SEPARATOR_STR, &save); + } +} + +int adb_auth_sign(void *node, void *token, size_t token_size, void *sig) +{ + unsigned int len; + struct adb_private_key *key = node_to_item(node, struct adb_private_key, node); + + if (!RSA_sign(NID_sha1, token, token_size, sig, &len, key->rsa)) { + return 0; + } + + D("adb_auth_sign len=%d\n", len); + return (int)len; +} + +void *adb_auth_nextkey(void *current) +{ + struct listnode *item; + + if (list_empty(&key_list)) + return NULL; + + if (!current) + return list_head(&key_list); + + list_for_each(item, &key_list) { + if (item == current) { + /* current is the last item, we tried all the keys */ + if (item->next == &key_list) + return NULL; + return item->next; + } + } + + return NULL; +} + +int adb_auth_get_userkey(unsigned char *data, size_t len) +{ + char path[PATH_MAX]; + char *file; + int ret; + + ret = get_user_keyfilepath(path, sizeof(path) - 4); + if (ret < 0 || ret >= (signed)(sizeof(path) - 4)) { + D("Error getting user key filename"); + return 0; + } + strcat(path, ".pub"); + + file = load_file(path, (unsigned*)&ret); + if (!file) { + D("Can't load '%s'\n", path); + return 0; + } + + if (len < (size_t)(ret + 1)) { + D("%s: Content too large ret=%d\n", path, ret); + return 0; + } + + memcpy(data, file, ret); + data[ret] = '\0'; + + return ret + 1; +} + +void adb_auth_init(void) +{ + int ret; + + D("adb_auth_init\n"); + + list_init(&key_list); + + ret = get_user_key(&key_list); + if (!ret) { + D("Failed to get user key\n"); + return; + } + + get_vendor_keys(&key_list); +} diff --git a/adb/protocol.txt b/adb/protocol.txt index abd63f90..c9d3c240 100644 --- a/adb/protocol.txt +++ b/adb/protocol.txt @@ -75,6 +75,24 @@ kind of unique ID (or empty), and banner is a human-readable version or identifier string. The banner is used to transmit useful properties. +--- AUTH(type, 0, "data") ---------------------------------------------- + +The AUTH message informs the recipient that authentication is required to +connect to the sender. If type is TOKEN(1), data is a random token that +the recipient can sign with a private key. The recipient replies with an +AUTH packet where type is SIGNATURE(2) and data is the signature. If the +signature verification succeeds, the sender replies with a CONNECT packet. + +If the signature verification fails, the sender replies with a new AUTH +packet and a new random token, so that the recipient can retry signing +with a different private key. + +Once the recipient has tried all its private keys, it can reply with an +AUTH packet where type is RSAPUBLICKEY(3) and data is the public key. If +possible, an on-screen confirmation may be displayed for the user to +confirm they want to install the public key on the device. + + --- OPEN(local-id, 0, "destination") ----------------------------------- The OPEN message informs the recipient that the sender has a stream @@ -166,6 +184,7 @@ to send across the wire. #define A_SYNC 0x434e5953 #define A_CNXN 0x4e584e43 +#define A_AUTH 0x48545541 #define A_OPEN 0x4e45504f #define A_OKAY 0x59414b4f #define A_CLSE 0x45534c43 diff --git a/adb/sysdeps.h b/adb/sysdeps.h index 605fa178..66b60cc0 100644 --- a/adb/sysdeps.h +++ b/adb/sysdeps.h @@ -38,6 +38,7 @@ #define OS_PATH_SEPARATOR '\\' #define OS_PATH_SEPARATOR_STR "\\" +#define ENV_PATH_SEPARATOR_STR ";" typedef CRITICAL_SECTION adb_mutex_t; @@ -277,6 +278,7 @@ extern char* adb_strtok_r(char *str, const char *delim, char **saveptr); #define OS_PATH_SEPARATOR '/' #define OS_PATH_SEPARATOR_STR "/" +#define ENV_PATH_SEPARATOR_STR ":" typedef pthread_mutex_t adb_mutex_t; |
