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