1 /*
2  * Copyright (C) 2021 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 "VehicleBindingUtil.h"
18 
19 #include <android-base/logging.h>
20 #include <android/hardware/automotive/vehicle/2.0/types.h>
21 #include <cutils/properties.h>  // for property_get
22 #include <logwrap/logwrap.h>
23 #include <utils/SystemClock.h>
24 
25 #include <fcntl.h>
26 #include <stdlib.h>
27 #include <sys/random.h>
28 #include <sys/stat.h>
29 #include <sys/types.h>
30 #include <unistd.h>
31 
32 #include <vector>
33 
34 namespace android {
35 namespace automotive {
36 namespace security {
37 namespace {
38 
39 using android::hardware::automotive::vehicle::V2_0::IVehicle;
40 using android::hardware::automotive::vehicle::V2_0::StatusCode;
41 using android::hardware::automotive::vehicle::V2_0::VehicleArea;
42 using android::hardware::automotive::vehicle::V2_0::VehiclePropConfig;
43 using android::hardware::automotive::vehicle::V2_0::VehicleProperty;
44 using android::hardware::automotive::vehicle::V2_0::VehiclePropertyStatus;
45 using android::hardware::automotive::vehicle::V2_0::VehiclePropValue;
46 
47 template <typename T>
48 using hidl_vec = android::hardware::hidl_vec<T>;
49 
isSeedVhalPropertySupported(sp<IVehicle> vehicle)50 bool isSeedVhalPropertySupported(sp<IVehicle> vehicle) {
51     bool is_supported = false;
52 
53     hidl_vec<int32_t> props = {
54             static_cast<int32_t>(VehicleProperty::STORAGE_ENCRYPTION_BINDING_SEED)};
55     vehicle->getPropConfigs(props,
56                             [&is_supported](StatusCode status,
57                                             hidl_vec<VehiclePropConfig> /*propConfigs*/) {
58                                 is_supported = (status == StatusCode::OK);
59                             });
60     return is_supported;
61 }
62 
toHexString(const std::vector<uint8_t> & bytes)63 std::string toHexString(const std::vector<uint8_t>& bytes) {
64     const char lookup[] = "0123456789abcdef";
65     std::string out;
66     out.reserve(bytes.size() * 2);
67     for (auto b : bytes) {
68         out += lookup[b >> 4];
69         out += lookup[b & 0xf];
70     }
71     return out;
72 }
73 
setSeedVhalProperty(sp<IVehicle> vehicle,const std::vector<uint8_t> & seed)74 BindingStatus setSeedVhalProperty(sp<IVehicle> vehicle, const std::vector<uint8_t>& seed) {
75     VehiclePropValue propValue;
76     propValue.timestamp = elapsedRealtimeNano();
77     propValue.areaId = toInt(VehicleArea::GLOBAL);
78     propValue.prop = toInt(VehicleProperty::STORAGE_ENCRYPTION_BINDING_SEED);
79     propValue.status = VehiclePropertyStatus::AVAILABLE;
80     propValue.value.bytes = seed;
81     StatusCode vhal_status = vehicle->set(propValue);
82     if (vhal_status == StatusCode::OK) {
83         return BindingStatus::OK;
84     }
85 
86     LOG(ERROR) << "Unable to set the VHAL property: " << toString(vhal_status);
87     return BindingStatus::ERROR;
88 }
89 
getSeedVhalProperty(sp<IVehicle> vehicle,std::vector<uint8_t> * seed)90 BindingStatus getSeedVhalProperty(sp<IVehicle> vehicle, std::vector<uint8_t>* seed) {
91     VehiclePropValue desired_prop;
92     desired_prop.prop = static_cast<int32_t>(VehicleProperty::STORAGE_ENCRYPTION_BINDING_SEED);
93     BindingStatus status = BindingStatus::ERROR;
94     vehicle->get(desired_prop,
95                  [&status, &seed](StatusCode prop_status, const VehiclePropValue& propValue) {
96                      if (prop_status != StatusCode::OK) {
97                          LOG(ERROR) << "Error reading vehicle property: " << toString(prop_status);
98                      } else {
99                          status = BindingStatus::OK;
100                          *seed = std::vector<uint8_t>{propValue.value.bytes.begin(),
101                                                       propValue.value.bytes.end()};
102                      }
103                  });
104 
105     return status;
106 }
107 
sendSeedToVold(const Executor & executor,const std::vector<uint8_t> & seed)108 BindingStatus sendSeedToVold(const Executor& executor, const std::vector<uint8_t>& seed) {
109     int status = 0;
110 
111     // we pass the seed value via environment variable in the forked process
112     setenv("SEED_VALUE", toHexString(seed).c_str(), 1);
113     int rc = executor.run({"/system/bin/vdc", "cryptfs", "bindkeys"}, &status);
114     unsetenv("SEED_VALUE");
115     LOG(INFO) << "rc: " << rc;
116     LOG(INFO) << "status: " << status;
117     if (rc != 0 || status != 0) {
118         LOG(ERROR) << "Error running vdc: " << rc << ", " << status;
119         return BindingStatus::ERROR;
120     }
121     return BindingStatus::OK;
122 }
123 
124 }  // namespace
125 
fill(void * buffer,size_t size) const126 bool DefaultCsrng::fill(void* buffer, size_t size) const {
127     int fd = TEMP_FAILURE_RETRY(open("/dev/urandom", O_RDONLY | O_CLOEXEC | O_NOFOLLOW));
128     if (fd == -1) {
129         LOG(ERROR) << "Error opening urandom: " << errno;
130         return false;
131     }
132 
133     ssize_t bytes_read;
134     uint8_t* bufptr = static_cast<uint8_t*>(buffer);
135     while ((bytes_read = TEMP_FAILURE_RETRY(read(fd, bufptr, size))) > 0) {
136         size -= bytes_read;
137         bufptr += bytes_read;
138     }
139 
140     close(fd);
141 
142     if (size != 0) {
143         LOG(ERROR) << "Unable to read " << size << " bytes from urandom";
144         return false;
145     }
146     return true;
147 }
148 
run(const std::vector<std::string> & cmd_args,int * exit_code) const149 int DefaultExecutor::run(const std::vector<std::string>& cmd_args, int* exit_code) const {
150     std::vector<const char*> argv;
151     argv.reserve(cmd_args.size());
152     for (auto& arg : cmd_args) {
153         argv.push_back(arg.c_str());
154     }
155     int status = 0;
156     return logwrap_fork_execvp(argv.size(), argv.data(), exit_code, false /*forward_signals*/,
157                                LOG_KLOG, true /*abbreviated*/, nullptr /*file_path*/);
158 }
159 
setVehicleBindingSeed(sp<IVehicle> vehicle,const Executor & executor,const Csrng & csrng)160 BindingStatus setVehicleBindingSeed(sp<IVehicle> vehicle, const Executor& executor,
161                                     const Csrng& csrng) {
162     if (!isSeedVhalPropertySupported(vehicle)) {
163         LOG(WARNING) << "Vehicle binding seed is not supported by the VHAL.";
164         return BindingStatus::NOT_SUPPORTED;
165     }
166 
167     std::vector<uint8_t> seed;
168     BindingStatus status = getSeedVhalProperty(vehicle, &seed);
169     if (status != BindingStatus::OK) {
170         LOG(ERROR) << "Unable to read the seed from the VHAL: " << static_cast<int>(status);
171         return status;
172     }
173 
174     if (seed.empty()) {
175         seed = std::vector<uint8_t>(SEED_BYTE_SIZE);
176         if (!csrng.fill(seed.data(), seed.size())) {
177             LOG(ERROR) << "Error getting random seed: " << static_cast<int>(status);
178             return BindingStatus::ERROR;
179         }
180 
181         status = setSeedVhalProperty(vehicle, seed);
182         if (status != BindingStatus::OK) {
183             LOG(ERROR) << "Error storing the seed in the VHAL: " << static_cast<int>(status);
184             return status;
185         }
186     }
187 
188     status = sendSeedToVold(executor, seed);
189     if (status == BindingStatus::OK) {
190         LOG(INFO) << "Successfully bound vehicle storage to seed.";
191     }
192     return status;
193 }
194 
195 }  // namespace security
196 }  // namespace automotive
197 }  // namespace android
198