1 /*
2  * Copyright (C) 2020 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 <filesystem>
18 #include <map>
19 #include <span>
20 #include <string>
21 
22 #include <fcntl.h>
23 #include <linux/fs.h>
24 #include <sys/stat.h>
25 #include <sys/types.h>
26 #include <sys/wait.h>
27 
28 #include <android-base/logging.h>
29 #include <android-base/unique_fd.h>
30 #include <libfsverity.h>
31 #include <linux/fsverity.h>
32 
33 #include "CertUtils.h"
34 #include "SigningKey.h"
35 
36 #define FS_VERITY_MAX_DIGEST_SIZE 64
37 
38 using android::base::ErrnoError;
39 using android::base::Error;
40 using android::base::Result;
41 using android::base::unique_fd;
42 
43 static const char* kFsVerityInitPath = "/system/bin/fsverity_init";
44 
45 #if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
46 #define cpu_to_le16(v) ((__force __le16)(uint16_t)(v))
47 #define le16_to_cpu(v) ((__force uint16_t)(__le16)(v))
48 #else
49 #define cpu_to_le16(v) ((__force __le16)__builtin_bswap16(v))
50 #define le16_to_cpu(v) (__builtin_bswap16((__force uint16_t)(v)))
51 #endif
52 
53 struct fsverity_signed_digest {
54     char magic[8]; /* must be "FSVerity" */
55     __le16 digest_algorithm;
56     __le16 digest_size;
57     __u8 digest[];
58 };
59 
toHex(std::span<uint8_t> data)60 static std::string toHex(std::span<uint8_t> data) {
61     std::stringstream ss;
62     for (auto it = data.begin(); it != data.end(); ++it) {
63         ss << std::setfill('0') << std::setw(2) << std::hex << static_cast<unsigned>(*it);
64     }
65     return ss.str();
66 }
67 
read_callback(void * file,void * buf,size_t count)68 static int read_callback(void* file, void* buf, size_t count) {
69     int* fd = (int*)file;
70     if (TEMP_FAILURE_RETRY(read(*fd, buf, count)) < 0) return errno ? -errno : -EIO;
71     return 0;
72 }
73 
createDigest(const std::string & path)74 Result<std::vector<uint8_t>> createDigest(const std::string& path) {
75     struct stat filestat;
76     unique_fd fd(TEMP_FAILURE_RETRY(open(path.c_str(), O_RDONLY | O_CLOEXEC)));
77     if (fd < 0) {
78         return ErrnoError() << "Failed to open " << path;
79     }
80 
81     int ret = stat(path.c_str(), &filestat);
82     if (ret < 0) {
83         return ErrnoError() << "Failed to stat " << path;
84     }
85     struct libfsverity_merkle_tree_params params = {
86         .version = 1,
87         .hash_algorithm = FS_VERITY_HASH_ALG_SHA256,
88         .file_size = static_cast<uint64_t>(filestat.st_size),
89         .block_size = 4096,
90     };
91 
92     struct libfsverity_digest* digest;
93     ret = libfsverity_compute_digest(&fd, &read_callback, &params, &digest);
94     if (ret < 0) {
95         return ErrnoError() << "Failed to compute fs-verity digest for " << path;
96     }
97     int expected_digest_size = libfsverity_get_digest_size(FS_VERITY_HASH_ALG_SHA256);
98     if (digest->digest_size != expected_digest_size) {
99         return Error() << "Digest does not have expected size: " << expected_digest_size
100                        << " actual: " << digest->digest_size;
101     }
102     std::vector<uint8_t> digestVector(&digest->digest[0], &digest->digest[expected_digest_size]);
103     free(digest);
104     return digestVector;
105 }
106 
107 namespace {
108 template <typename T> struct DeleteAsPODArray {
operator ()__anon786956d70111::DeleteAsPODArray109     void operator()(T* x) {
110         if (x) {
111             x->~T();
112             delete[](uint8_t*) x;
113         }
114     }
115 };
116 }  // namespace
117 
118 template <typename T> using trailing_unique_ptr = std::unique_ptr<T, DeleteAsPODArray<T>>;
119 
120 template <typename T>
makeUniqueWithTrailingData(size_t trailing_data_size)121 static trailing_unique_ptr<T> makeUniqueWithTrailingData(size_t trailing_data_size) {
122     uint8_t* memory = new uint8_t[sizeof(T) + trailing_data_size];
123     T* ptr = new (memory) T;
124     return trailing_unique_ptr<T>{ptr};
125 }
126 
signDigest(const SigningKey & key,const std::vector<uint8_t> & digest)127 static Result<std::vector<uint8_t>> signDigest(const SigningKey& key,
128                                                const std::vector<uint8_t>& digest) {
129     auto d = makeUniqueWithTrailingData<fsverity_signed_digest>(digest.size());
130 
131     memcpy(d->magic, "FSVerity", 8);
132     d->digest_algorithm = cpu_to_le16(FS_VERITY_HASH_ALG_SHA256);
133     d->digest_size = cpu_to_le16(digest.size());
134     memcpy(d->digest, digest.data(), digest.size());
135 
136     auto signed_digest = key.sign(std::string((char*)d.get(), sizeof(*d) + digest.size()));
137     if (!signed_digest.ok()) {
138         return signed_digest.error();
139     }
140 
141     return std::vector<uint8_t>(signed_digest->begin(), signed_digest->end());
142 }
143 
enableFsVerity(const std::string & path,const SigningKey & key)144 Result<std::string> enableFsVerity(const std::string& path, const SigningKey& key) {
145     auto digest = createDigest(path);
146     if (!digest.ok()) {
147         return digest.error();
148     }
149 
150     auto signed_digest = signDigest(key, digest.value());
151     if (!signed_digest.ok()) {
152         return signed_digest.error();
153     }
154 
155     auto pkcs7_data = createPkcs7(signed_digest.value());
156 
157     struct fsverity_enable_arg arg = {.version = 1};
158 
159     arg.sig_ptr = (uint64_t)pkcs7_data->data();
160     arg.sig_size = pkcs7_data->size();
161     arg.hash_algorithm = FS_VERITY_HASH_ALG_SHA256;
162     arg.block_size = 4096;
163 
164     unique_fd fd(TEMP_FAILURE_RETRY(open(path.c_str(), O_RDONLY | O_CLOEXEC)));
165     int ret = ioctl(fd, FS_IOC_ENABLE_VERITY, &arg);
166 
167     if (ret != 0) {
168         return ErrnoError() << "Failed to call FS_IOC_ENABLE_VERITY on " << path;
169     }
170 
171     // Return the root hash as a hex string
172     return toHex(digest.value());
173 }
174 
addFilesToVerityRecursive(const std::string & path,const SigningKey & key)175 Result<std::map<std::string, std::string>> addFilesToVerityRecursive(const std::string& path,
176                                                                      const SigningKey& key) {
177     std::map<std::string, std::string> digests;
178     std::error_code ec;
179 
180     auto it = std::filesystem::recursive_directory_iterator(path, ec);
181     auto end = std::filesystem::recursive_directory_iterator();
182 
183     while (!ec && it != end) {
184         if (it->is_regular_file()) {
185             LOG(INFO) << "Adding " << it->path() << " to fs-verity...";
186             auto result = enableFsVerity(it->path(), key);
187             if (!result.ok()) {
188                 return result.error();
189             }
190             digests[it->path()] = *result;
191         }
192         ++it;
193     }
194     if (ec) {
195         return Error() << "Failed to iterate " << path << ": " << ec;
196     }
197 
198     return digests;
199 }
200 
isFileInVerity(const std::string & path)201 Result<std::string> isFileInVerity(const std::string& path) {
202     unsigned int flags;
203 
204     unique_fd fd(TEMP_FAILURE_RETRY(open(path.c_str(), O_RDONLY | O_CLOEXEC)));
205     if (fd < 0) {
206         return ErrnoError() << "Failed to open " << path;
207     }
208 
209     int ret = ioctl(fd, FS_IOC_GETFLAGS, &flags);
210     if (ret < 0) {
211         return ErrnoError() << "Failed to FS_IOC_GETFLAGS for " << path;
212     }
213     if (!(flags & FS_VERITY_FL)) {
214         return Error() << "File is not in fs-verity: " << path;
215     }
216 
217     auto d = makeUniqueWithTrailingData<fsverity_digest>(FS_VERITY_MAX_DIGEST_SIZE);
218     d->digest_size = FS_VERITY_MAX_DIGEST_SIZE;
219     ret = ioctl(fd, FS_IOC_MEASURE_VERITY, d.get());
220     if (ret < 0) {
221         return ErrnoError() << "Failed to FS_IOC_MEASURE_VERITY for " << path;
222     }
223     return toHex({&d->digest[0], &d->digest[d->digest_size]});
224 }
225 
verifyAllFilesInVerity(const std::string & path)226 Result<std::map<std::string, std::string>> verifyAllFilesInVerity(const std::string& path) {
227     std::map<std::string, std::string> digests;
228     std::error_code ec;
229 
230     auto it = std::filesystem::recursive_directory_iterator(path, ec);
231     auto end = std::filesystem::recursive_directory_iterator();
232 
233     while (!ec && it != end) {
234         if (it->is_regular_file()) {
235             // Verify the file is in fs-verity
236             auto result = isFileInVerity(it->path());
237             if (!result.ok()) {
238                 return result.error();
239             }
240             digests[it->path()] = *result;
241         } else if (it->is_directory()) {
242             // These are fine to ignore
243         } else if (it->is_symlink()) {
244             return Error() << "Rejecting artifacts, symlink at " << it->path();
245         } else {
246             return Error() << "Rejecting artifacts, unexpected file type for " << it->path();
247         }
248         ++it;
249     }
250     if (ec) {
251         return Error() << "Failed to iterate " << path << ": " << ec;
252     }
253 
254     return digests;
255 }
256 
addCertToFsVerityKeyring(const std::string & path)257 Result<void> addCertToFsVerityKeyring(const std::string& path) {
258     const char* const argv[] = {kFsVerityInitPath, "--load-extra-key", "fsv_ods"};
259 
260     int fd = open(path.c_str(), O_RDONLY | O_CLOEXEC);
261     pid_t pid = fork();
262     if (pid == 0) {
263         dup2(fd, STDIN_FILENO);
264         close(fd);
265         int argc = arraysize(argv);
266         char* argv_child[argc + 1];
267         memcpy(argv_child, argv, argc * sizeof(char*));
268         argv_child[argc] = nullptr;
269         execvp(argv_child[0], const_cast<char**>(argv_child));
270         PLOG(ERROR) << "exec in ForkExecvp";
271         _exit(EXIT_FAILURE);
272     } else {
273         close(fd);
274     }
275     if (pid == -1) {
276         return ErrnoError() << "Failed to fork.";
277     }
278     int status;
279     if (waitpid(pid, &status, 0) == -1) {
280         return ErrnoError() << "waitpid() failed.";
281     }
282     if (!WIFEXITED(status)) {
283         return Error() << kFsVerityInitPath << ": abnormal process exit";
284     }
285     if (WEXITSTATUS(status)) {
286         if (status != 0) {
287             return Error() << kFsVerityInitPath << " exited with " << status;
288         }
289     }
290 
291     return {};
292 }
293