Merge changes I9761298a,I8d3312b2
am: 0b4fcb75d4

Change-Id: I330c467e408bc10ebdab0f5708d24d2297cf39a2
diff --git a/adb_auth.cpp b/adb_auth.cpp
index 446c3df..0b07158 100644
--- a/adb_auth.cpp
+++ b/adb_auth.cpp
@@ -64,12 +64,14 @@
 
     p->msg.command = A_AUTH;
     p->msg.arg0 = ADB_AUTH_RSAPUBLICKEY;
-    p->msg.data_length = key.size();
+
+    // adbd expects a null-terminated string.
+    p->msg.data_length = key.size() + 1;
     send_packet(p, t);
 }
 
 void send_auth_response(uint8_t* token, size_t token_size, atransport* t) {
-    RSA* key = t->NextKey();
+    std::shared_ptr<RSA> key = t->NextKey();
     if (key == nullptr) {
         // No more private keys to try, send the public key.
         send_auth_publickey(t);
@@ -79,12 +81,7 @@
     LOG(INFO) << "Calling send_auth_response";
     apacket* p = get_apacket();
 
-    int ret = adb_auth_sign(key, token, token_size, p->data);
-
-    // Stop sharing this key.
-    RSA_free(key);
-    key = nullptr;
-
+    int ret = adb_auth_sign(key.get(), token, token_size, p->data);
     if (!ret) {
         D("Error signing the token");
         put_apacket(p);
diff --git a/adb_auth.h b/adb_auth.h
index 723ded5..59b80d8 100644
--- a/adb_auth.h
+++ b/adb_auth.h
@@ -20,6 +20,7 @@
 #include "adb.h"
 
 #include <deque>
+#include <memory>
 
 #include <openssl/rsa.h>
 
@@ -43,7 +44,7 @@
 void adb_auth_init();
 int adb_auth_sign(RSA* key, const unsigned char* token, size_t token_size, unsigned char* sig);
 std::string adb_auth_get_userkey();
-std::deque<RSA*> adb_auth_get_private_keys();
+std::deque<std::shared_ptr<RSA>> adb_auth_get_private_keys();
 
 static inline bool adb_auth_generate_token(void*, size_t) { abort(); }
 static inline bool adb_auth_verify(void*, size_t, void*, int) { abort(); }
@@ -53,7 +54,7 @@
 
 static inline int adb_auth_sign(void*, const unsigned char*, size_t, unsigned char*) { abort(); }
 static inline std::string adb_auth_get_userkey() { abort(); }
-static inline std::deque<RSA*> adb_auth_get_private_keys() { abort(); }
+static inline std::deque<std::shared_ptr<RSA>> adb_auth_get_private_keys() { abort(); }
 
 void adbd_auth_init(void);
 void adbd_cloexec_auth_socket();
diff --git a/adb_auth_host.cpp b/adb_auth_host.cpp
index ce269cc..4f4f382 100644
--- a/adb_auth_host.cpp
+++ b/adb_auth_host.cpp
@@ -16,16 +16,18 @@
 
 #define TRACE_TAG AUTH
 
-#include "adb_auth.h"
-#include "adb.h"
-#include "adb_utils.h"
-#include "sysdeps.h"
-
+#include <dirent.h>
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
+#if defined(__linux__)
+#include <sys/inotify.h>
+#endif
 
+#include <map>
 #include <mutex>
+#include <set>
+#include <string>
 
 #include <android-base/errors.h>
 #include <android-base/file.h>
@@ -39,10 +41,16 @@
 #include <openssl/rsa.h>
 #include <openssl/sha.h>
 
+#include "adb.h"
+#include "adb_auth.h"
+#include "adb_utils.h"
+#include "sysdeps.h"
 #include "sysdeps/mutex.h"
 
-static std::mutex& g_key_list_mutex = *new std::mutex;
-static std::deque<RSA*>& g_key_list = *new std::deque<RSA*>;
+static std::mutex& g_keys_mutex = *new std::mutex;
+static std::map<std::string, std::shared_ptr<RSA>>& g_keys =
+    *new std::map<std::string, std::shared_ptr<RSA>>;
+static std::map<int, std::string>& g_monitored_paths = *new std::map<int, std::string>;
 
 static std::string get_user_info() {
     LOG(INFO) << "get_user_info...";
@@ -146,8 +154,23 @@
     return ret;
 }
 
-static bool read_key(const std::string& file) {
-    LOG(INFO) << "read_key '" << file << "'...";
+static std::string hash_key(RSA* key) {
+    unsigned char* pubkey = nullptr;
+    int len = i2d_RSA_PUBKEY(key, &pubkey);
+    if (len < 0) {
+        LOG(ERROR) << "failed to encode RSA public key";
+        return std::string();
+    }
+
+    std::string result;
+    result.resize(SHA256_DIGEST_LENGTH);
+    SHA256(pubkey, len, reinterpret_cast<unsigned char*>(&result[0]));
+    OPENSSL_free(pubkey);
+    return result;
+}
+
+static bool read_key_file(const std::string& file) {
+    LOG(INFO) << "read_key_file '" << file << "'...";
 
     std::unique_ptr<FILE, decltype(&fclose)> fp(fopen(file.c_str(), "r"), fclose);
     if (!fp) {
@@ -162,10 +185,66 @@
         return false;
     }
 
-    g_key_list.push_back(key);
+    std::lock_guard<std::mutex> lock(g_keys_mutex);
+    std::string fingerprint = hash_key(key);
+    if (g_keys.find(fingerprint) != g_keys.end()) {
+        LOG(INFO) << "ignoring already-loaded key: " << file;
+        RSA_free(key);
+    } else {
+        g_keys[fingerprint] = std::shared_ptr<RSA>(key, RSA_free);
+    }
+
     return true;
 }
 
+static bool read_keys(const std::string& path, bool allow_dir = true) {
+    LOG(INFO) << "read_keys '" << path << "'...";
+
+    struct stat st;
+    if (stat(path.c_str(), &st) != 0) {
+        PLOG(ERROR) << "failed to stat '" << path << "'";
+        return false;
+    }
+
+    if (S_ISREG(st.st_mode)) {
+        if (!android::base::EndsWith(path, ".adb_key")) {
+            LOG(INFO) << "skipping non-adb_key '" << path << "'";
+            return false;
+        }
+
+        return read_key_file(path);
+    } else if (S_ISDIR(st.st_mode)) {
+        if (!allow_dir) {
+            // inotify isn't recursive. It would break expectations to load keys in nested
+            // directories but not monitor them for new keys.
+            LOG(WARNING) << "refusing to recurse into directory '" << path << "'";
+            return false;
+        }
+
+        std::unique_ptr<DIR, decltype(&closedir)> dir(opendir(path.c_str()), closedir);
+        if (!dir) {
+            PLOG(ERROR) << "failed to open directory '" << path << "'";
+            return false;
+        }
+
+        bool result = false;
+        while (struct dirent* dent = readdir(dir.get())) {
+            std::string name = dent->d_name;
+
+            // We can't use dent->d_type here because it's not available on Windows.
+            if (name == "." || name == "..") {
+                continue;
+            }
+
+            result |= read_keys((path + OS_PATH_SEPARATOR + name).c_str(), false);
+        }
+        return result;
+    }
+
+    LOG(ERROR) << "unexpected type for '" << path << "': 0x" << std::hex << st.st_mode;
+    return false;
+}
+
 static std::string get_user_key_path() {
     const std::string home = adb_get_homedir_path(true);
     LOG(DEBUG) << "adb_get_homedir_path returned '" << home << "'";
@@ -200,31 +279,29 @@
         }
     }
 
-    return read_key(path);
+    return read_key_file(path);
 }
 
-static void get_vendor_keys() {
+static std::set<std::string> get_vendor_keys() {
     const char* adb_keys_path = getenv("ADB_VENDOR_KEYS");
     if (adb_keys_path == nullptr) {
-        return;
+        return std::set<std::string>();
     }
 
+    std::set<std::string> result;
     for (const auto& path : android::base::Split(adb_keys_path, ENV_PATH_SEPARATOR_STR)) {
-        if (!read_key(path.c_str())) {
-            PLOG(ERROR) << "Failed to read '" << path << "'";
-        }
+        result.emplace(path);
     }
+    return result;
 }
 
-std::deque<RSA*> adb_auth_get_private_keys() {
-    std::deque<RSA*> result;
+std::deque<std::shared_ptr<RSA>> adb_auth_get_private_keys() {
+    std::deque<std::shared_ptr<RSA>> result;
 
-    // Copy all the currently known keys, increasing their reference count so they're
-    // usable until both we and the caller have freed our pointers.
-    std::lock_guard<std::mutex> lock(g_key_list_mutex);
-    for (const auto& key : g_key_list) {
-        RSA_up_ref(key); // Since we're handing out another pointer to this key...
-        result.push_back(key);
+    // Copy all the currently known keys.
+    std::lock_guard<std::mutex> lock(g_keys_mutex);
+    for (const auto& it : g_keys) {
+        result.push_back(it.second);
     }
 
     // Add a sentinel to the list. Our caller uses this to mean "out of private keys,
@@ -270,6 +347,77 @@
     return (generate_key(filename) == 0);
 }
 
+#if defined(__linux__)
+static void adb_auth_inotify_update(int fd, unsigned fd_event, void*) {
+    LOG(INFO) << "adb_auth_inotify_update called";
+    if (!(fd_event & FDE_READ)) {
+        return;
+    }
+
+    char buf[sizeof(struct inotify_event) + NAME_MAX + 1];
+    while (true) {
+        ssize_t rc = TEMP_FAILURE_RETRY(unix_read(fd, buf, sizeof(buf)));
+        if (rc == -1) {
+            if (errno == EAGAIN) {
+                LOG(INFO) << "done reading inotify fd";
+                break;
+            }
+            PLOG(FATAL) << "read of inotify event failed";
+        }
+
+        // The read potentially returned multiple events.
+        char* start = buf;
+        char* end = buf + rc;
+
+        while (start < end) {
+            inotify_event* event = reinterpret_cast<inotify_event*>(start);
+            auto root_it = g_monitored_paths.find(event->wd);
+            if (root_it == g_monitored_paths.end()) {
+                LOG(FATAL) << "observed inotify event for unmonitored path, wd = " << event->wd;
+            }
+
+            std::string path = root_it->second;
+            if (event->len > 0) {
+                path += '/';
+                path += event->name;
+            }
+
+            if (event->mask & (IN_CREATE | IN_MOVED_TO)) {
+                if (event->mask & IN_ISDIR) {
+                    LOG(INFO) << "ignoring new directory at '" << path << "'";
+                } else {
+                    LOG(INFO) << "observed new file at '" << path << "'";
+                    read_keys(path, false);
+                }
+            } else {
+                LOG(WARNING) << "unmonitored event for " << path << ": 0x" << std::hex
+                             << event->mask;
+            }
+
+            start += sizeof(struct inotify_event) + event->len;
+        }
+    }
+}
+
+static void adb_auth_inotify_init(const std::set<std::string>& paths) {
+    LOG(INFO) << "adb_auth_inotify_init...";
+    int infd = inotify_init1(IN_CLOEXEC | IN_NONBLOCK);
+    for (const std::string& path : paths) {
+        int wd = inotify_add_watch(infd, path.c_str(), IN_CREATE | IN_MOVED_TO);
+        if (wd < 0) {
+            PLOG(ERROR) << "failed to inotify_add_watch on path '" << path;
+            continue;
+        }
+
+        g_monitored_paths[wd] = path;
+        LOG(INFO) << "watch descriptor " << wd << " registered for " << path;
+    }
+
+    fdevent* event = fdevent_create(infd, adb_auth_inotify_update, nullptr);
+    fdevent_add(event, FDE_READ);
+}
+#endif
+
 void adb_auth_init() {
     LOG(INFO) << "adb_auth_init...";
 
@@ -278,6 +426,13 @@
         return;
     }
 
-    std::lock_guard<std::mutex> lock(g_key_list_mutex);
-    get_vendor_keys();
+    const auto& key_paths = get_vendor_keys();
+
+#if defined(__linux__)
+    adb_auth_inotify_init(key_paths);
+#endif
+
+    for (const std::string& path : key_paths) {
+        read_keys(path.c_str());
+    }
 }
diff --git a/transport.cpp b/transport.cpp
index 0d376b1..3eaeb06 100644
--- a/transport.cpp
+++ b/transport.cpp
@@ -1074,10 +1074,10 @@
     }
 }
 
-RSA* atransport::NextKey() {
+std::shared_ptr<RSA> atransport::NextKey() {
     if (keys_.empty()) keys_ = adb_auth_get_private_keys();
 
-    RSA* result = keys_[0];
+    std::shared_ptr<RSA> result = keys_[0];
     keys_.pop_front();
     return result;
 }
diff --git a/transport.h b/transport.h
index d41c8bd..959681f 100644
--- a/transport.h
+++ b/transport.h
@@ -21,6 +21,7 @@
 
 #include <deque>
 #include <list>
+#include <memory>
 #include <string>
 #include <unordered_set>
 
@@ -107,7 +108,7 @@
         return type == kTransportLocal && local_port_for_emulator_ == -1;
     }
 
-    RSA* NextKey();
+    std::shared_ptr<RSA> NextKey();
 
     unsigned char token[TOKEN_SIZE] = {};
     size_t failed_auth_attempts = 0;
@@ -160,7 +161,7 @@
     // A list of adisconnect callbacks called when the transport is kicked.
     std::list<adisconnect*> disconnects_;
 
-    std::deque<RSA*> keys_;
+    std::deque<std::shared_ptr<RSA>> keys_;
 
     DISALLOW_COPY_AND_ASSIGN(atransport);
 };