1 /*
2  * Copyright (C) 2019 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 #include "adb_wifi.h"
18 
19 #include <fstream>
20 #include <random>
21 #include <thread>
22 
23 #include <adb/crypto/key.h>
24 #include <adb/crypto/x509_generator.h>
25 #include <android-base/file.h>
26 #include <android-base/parsenetaddress.h>
27 #include "client/pairing/pairing_client.h"
28 
29 #include "adb_auth.h"
30 #include "adb_known_hosts.pb.h"
31 #include "adb_utils.h"
32 #include "client/adb_client.h"
33 #include "sysdeps.h"
34 
35 using adbwifi::pairing::PairingClient;
36 using namespace adb::crypto;
37 
38 struct PairingResultWaiter {
39     std::mutex mutex_;
40     std::condition_variable cv_;
41     std::optional<bool> is_valid_;
42     PeerInfo peer_info_;
43 
OnResultPairingResultWaiter44     static void OnResult(const PeerInfo* peer_info, void* opaque) {
45         CHECK(opaque);
46         auto* p = reinterpret_cast<PairingResultWaiter*>(opaque);
47         {
48             std::lock_guard<std::mutex> lock(p->mutex_);
49             if (peer_info) {
50                 memcpy(&(p->peer_info_), peer_info, sizeof(PeerInfo));
51             }
52             p->is_valid_ = (peer_info != nullptr);
53         }
54         p->cv_.notify_one();
55     }
56 };  // PairingResultWaiter
57 
adb_wifi_init()58 void adb_wifi_init() {}
59 
stringToUint8(const std::string & str)60 static std::vector<uint8_t> stringToUint8(const std::string& str) {
61     auto* p8 = reinterpret_cast<const uint8_t*>(str.data());
62     return std::vector<uint8_t>(p8, p8 + str.length());
63 }
64 
65 // Tries to replace the |old_file| with |new_file|.
66 // On success, then |old_file| has been removed and replaced with the
67 // contents of |new_file|, |new_file| will be removed, and only |old_file| will
68 // remain.
69 // On failure, both files will be unchanged.
70 // |new_file| must exist, but |old_file| does not need to exist.
SafeReplaceFile(std::string_view old_file,std::string_view new_file)71 bool SafeReplaceFile(std::string_view old_file, std::string_view new_file) {
72     std::string to_be_deleted(old_file);
73     to_be_deleted += ".tbd";
74 
75     bool old_renamed = true;
76     if (adb_rename(old_file.data(), to_be_deleted.c_str()) != 0) {
77         // Don't exit here. This is not necessarily an error, because |old_file|
78         // may not exist.
79         PLOG(INFO) << "Failed to rename " << old_file;
80         old_renamed = false;
81     }
82 
83     if (adb_rename(new_file.data(), old_file.data()) != 0) {
84         PLOG(ERROR) << "Unable to rename file (" << new_file << " => " << old_file << ")";
85         if (old_renamed) {
86             // Rename the .tbd file back to it's original name
87             adb_rename(to_be_deleted.c_str(), old_file.data());
88         }
89         return false;
90     }
91 
92     adb_unlink(to_be_deleted.c_str());
93     return true;
94 }
95 
get_user_known_hosts_path()96 static std::string get_user_known_hosts_path() {
97     return adb_get_android_dir_path() + OS_PATH_SEPARATOR + "adb_known_hosts.pb";
98 }
99 
load_known_hosts_from_file(const std::string & path,adb::proto::AdbKnownHosts & known_hosts)100 bool load_known_hosts_from_file(const std::string& path, adb::proto::AdbKnownHosts& known_hosts) {
101     // Check for file existence.
102     struct stat buf;
103     if (stat(path.c_str(), &buf) == -1) {
104         LOG(INFO) << "Known hosts file [" << path << "] does not exist...";
105         return false;
106     }
107 
108     std::ifstream file(path, std::ios::binary);
109     if (!file) {
110         PLOG(ERROR) << "Unable to open [" << path << "].";
111         return false;
112     }
113 
114     if (!known_hosts.ParseFromIstream(&file)) {
115         PLOG(ERROR) << "Failed to parse [" << path << "]. Deleting it as it may be corrupted.";
116         adb_unlink(path.c_str());
117         return false;
118     }
119 
120     return true;
121 }
122 
write_known_host_to_file(std::string & known_host)123 static bool write_known_host_to_file(std::string& known_host) {
124     std::string path = get_user_known_hosts_path();
125     if (path.empty()) {
126         PLOG(ERROR) << "Error getting user known hosts filename";
127         return false;
128     }
129 
130     adb::proto::AdbKnownHosts known_hosts;
131     load_known_hosts_from_file(path, known_hosts);
132     auto* host_info = known_hosts.add_host_infos();
133     host_info->set_guid(known_host);
134 
135     std::unique_ptr<TemporaryFile> temp_file(new TemporaryFile(adb_get_android_dir_path()));
136     if (temp_file->fd == -1) {
137         PLOG(ERROR) << "Failed to open [" << temp_file->path << "] for writing";
138         return false;
139     }
140 
141     if (!known_hosts.SerializeToFileDescriptor(temp_file->fd)) {
142         LOG(ERROR) << "Unable to write out adb_knowns_hosts";
143         return false;
144     }
145     temp_file->DoNotRemove();
146     std::string temp_file_name(temp_file->path);
147     temp_file.reset();
148 
149     // Replace the existing adb_known_hosts with the new one
150     if (!SafeReplaceFile(path, temp_file_name.c_str())) {
151         LOG(ERROR) << "Failed to replace old adb_known_hosts";
152         adb_unlink(temp_file_name.c_str());
153         return false;
154     }
155     chmod(path.c_str(), S_IRUSR | S_IWUSR | S_IRGRP);
156 
157     return true;
158 }
159 
adb_wifi_is_known_host(const std::string & host)160 bool adb_wifi_is_known_host(const std::string& host) {
161     std::string path = get_user_known_hosts_path();
162     if (path.empty()) {
163         PLOG(ERROR) << "Error getting user known hosts filename";
164         return false;
165     }
166 
167     adb::proto::AdbKnownHosts known_hosts;
168     if (!load_known_hosts_from_file(path, known_hosts)) {
169         return false;
170     }
171 
172     for (const auto& host_info : known_hosts.host_infos()) {
173         if (host == host_info.guid()) {
174             return true;
175         }
176     }
177     return false;
178 }
179 
adb_wifi_pair_device(const std::string & host,const std::string & password,std::string & response)180 void adb_wifi_pair_device(const std::string& host, const std::string& password,
181                           std::string& response) {
182     auto mdns_info = mdns_get_pairing_service_info(host);
183 
184     if (!mdns_info.has_value()) {
185         // Check the address for a valid address and port.
186         std::string parsed_host;
187         std::string err;
188         int port = -1;
189         if (!android::base::ParseNetAddress(host, &parsed_host, &port, nullptr, &err)) {
190             response = "Failed to parse address for pairing: " + err;
191             return;
192         }
193         if (port <= 0 || port > 65535) {
194             response = "Invalid port while parsing address [" + host + "]";
195             return;
196         }
197     }
198 
199     auto priv_key = adb_auth_get_user_privkey();
200     auto x509_cert = GenerateX509Certificate(priv_key.get());
201     if (!x509_cert) {
202         LOG(ERROR) << "Unable to create X509 certificate for pairing";
203         return;
204     }
205     auto cert_str = X509ToPEMString(x509_cert.get());
206     auto priv_str = Key::ToPEMString(priv_key.get());
207 
208     // Send our public key on pairing success
209     PeerInfo system_info = {};
210     system_info.type = ADB_RSA_PUB_KEY;
211     std::string public_key = adb_auth_get_userkey();
212     CHECK_LE(public_key.size(), sizeof(system_info.data) - 1);  // -1 for null byte
213     memcpy(system_info.data, public_key.data(), public_key.size());
214 
215     auto pswd8 = stringToUint8(password);
216     auto cert8 = stringToUint8(cert_str);
217     auto priv8 = stringToUint8(priv_str);
218 
219     auto client = PairingClient::Create(pswd8, system_info, cert8, priv8);
220     if (client == nullptr) {
221         response = "Failed: unable to create pairing client.";
222         return;
223     }
224 
225     PairingResultWaiter waiter;
226     std::unique_lock<std::mutex> lock(waiter.mutex_);
227     if (!client->Start(mdns_info.has_value()
228                                ? android::base::StringPrintf("%s:%d", mdns_info->addr.c_str(),
229                                                              mdns_info->port)
230                                : host,
231                        waiter.OnResult, &waiter)) {
232         response = "Failed: Unable to start pairing client.";
233         return;
234     }
235     waiter.cv_.wait(lock, [&]() { return waiter.is_valid_.has_value(); });
236     if (!*(waiter.is_valid_)) {
237         response = "Failed: Wrong password or connection was dropped.";
238         return;
239     }
240 
241     if (waiter.peer_info_.type != ADB_DEVICE_GUID) {
242         response = "Failed: Successfully paired but server returned unknown response=";
243         response += waiter.peer_info_.type;
244         return;
245     }
246 
247     std::string device_guid = reinterpret_cast<const char*>(waiter.peer_info_.data);
248     response = "Successfully paired to " + host + " [guid=" + device_guid + "]";
249 
250     // Write to adb_known_hosts
251     write_known_host_to_file(device_guid);
252     // Try to auto-connect.
253     adb_secure_connect_by_service_name(device_guid.c_str());
254 }
255