1 /* 2 * Copyright (C) 2014 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 // This module creates a special filesystem containing two files. 18 // 19 // "/sideload/package.zip" appears to be a normal file, but reading 20 // from it causes data to be fetched from the adb host. We can use 21 // this to sideload packages over an adb connection without having to 22 // store the entire package in RAM on the device. 23 // 24 // Because we may not trust the adb host, this filesystem maintains 25 // the following invariant: each read of a given position returns the 26 // same data as the first read at that position. That is, once a 27 // section of the file is read, future reads of that section return 28 // the same data. (Otherwise, a malicious adb host process could 29 // return one set of bits when the package is read for signature 30 // verification, and then different bits for when the package is 31 // accessed by the installer.) If the adb host returns something 32 // different than it did on the first read, the reader of the file 33 // will see their read fail with EINVAL. 34 // 35 // The other file, "/sideload/exit", is used to control the subprocess 36 // that creates this filesystem. Calling stat() on the exit file 37 // causes the filesystem to be unmounted and the adb process on the 38 // device shut down. 39 // 40 // Note that only the minimal set of file operations needed for these 41 // two files is implemented. In particular, you can't opendir() or 42 // readdir() on the "/sideload" directory; ls on it won't work. 43 44 #include <ctype.h> 45 #include <dirent.h> 46 #include <errno.h> 47 #include <fcntl.h> 48 #include <limits.h> 49 #include <linux/fuse.h> 50 #include <pthread.h> 51 #include <stdio.h> 52 #include <stdlib.h> 53 #include <string.h> 54 #include <sys/inotify.h> 55 #include <sys/mount.h> 56 #include <sys/resource.h> 57 #include <sys/stat.h> 58 #include <sys/statfs.h> 59 #include <sys/time.h> 60 #include <sys/uio.h> 61 #include <unistd.h> 62 63 #include "mincrypt/sha256.h" 64 #include "fuse_sideload.h" 65 66 #define PACKAGE_FILE_ID (FUSE_ROOT_ID+1) 67 #define EXIT_FLAG_ID (FUSE_ROOT_ID+2) 68 69 #define NO_STATUS 1 70 #define NO_STATUS_EXIT 2 71 72 struct fuse_data { 73 int ffd; // file descriptor for the fuse socket 74 75 struct provider_vtab* vtab; 76 void* cookie; 77 78 uint64_t file_size; // bytes 79 80 uint32_t block_size; // block size that the adb host is using to send the file to us 81 uint32_t file_blocks; // file size in block_size blocks 82 83 uid_t uid; 84 gid_t gid; 85 86 uint32_t curr_block; // cache the block most recently read from the host 87 uint8_t* block_data; 88 89 uint8_t* extra_block; // another block of storage for reads that 90 // span two blocks 91 92 uint8_t* hashes; // SHA-256 hash of each block (all zeros 93 // if block hasn't been read yet) 94 }; 95 96 static void fuse_reply(struct fuse_data* fd, __u64 unique, const void *data, size_t len) 97 { 98 struct fuse_out_header hdr; 99 struct iovec vec[2]; 100 int res; 101 102 hdr.len = len + sizeof(hdr); 103 hdr.error = 0; 104 hdr.unique = unique; 105 106 vec[0].iov_base = &hdr; 107 vec[0].iov_len = sizeof(hdr); 108 vec[1].iov_base = data; 109 vec[1].iov_len = len; 110 111 res = writev(fd->ffd, vec, 2); 112 if (res < 0) { 113 printf("*** REPLY FAILED *** %d\n", errno); 114 } 115 } 116 117 static int handle_init(void* data, struct fuse_data* fd, const struct fuse_in_header* hdr) { 118 const struct fuse_init_in* req = data; 119 struct fuse_init_out out; 120 121 out.major = FUSE_KERNEL_VERSION; 122 out.minor = FUSE_KERNEL_MINOR_VERSION; 123 out.max_readahead = req->max_readahead; 124 out.flags = 0; 125 out.max_background = 32; 126 out.congestion_threshold = 32; 127 out.max_write = 4096; 128 fuse_reply(fd, hdr->unique, &out, sizeof(out)); 129 130 return NO_STATUS; 131 } 132 133 static void fill_attr(struct fuse_attr* attr, struct fuse_data* fd, 134 uint64_t nodeid, uint64_t size, uint32_t mode) { 135 memset(attr, 0, sizeof(*attr)); 136 attr->nlink = 1; 137 attr->uid = fd->uid; 138 attr->gid = fd->gid; 139 attr->blksize = 4096; 140 141 attr->ino = nodeid; 142 attr->size = size; 143 attr->blocks = (size == 0) ? 0 : (((size-1) / attr->blksize) + 1); 144 attr->mode = mode; 145 } 146 147 static int handle_getattr(void* data, struct fuse_data* fd, const struct fuse_in_header* hdr) { 148 const struct fuse_getattr_in* req = data; 149 struct fuse_attr_out out; 150 memset(&out, 0, sizeof(out)); 151 out.attr_valid = 10; 152 153 if (hdr->nodeid == FUSE_ROOT_ID) { 154 fill_attr(&(out.attr), fd, hdr->nodeid, 4096, S_IFDIR | 0555); 155 } else if (hdr->nodeid == PACKAGE_FILE_ID) { 156 fill_attr(&(out.attr), fd, PACKAGE_FILE_ID, fd->file_size, S_IFREG | 0444); 157 } else if (hdr->nodeid == EXIT_FLAG_ID) { 158 fill_attr(&(out.attr), fd, EXIT_FLAG_ID, 0, S_IFREG | 0); 159 } else { 160 return -ENOENT; 161 } 162 163 fuse_reply(fd, hdr->unique, &out, sizeof(out)); 164 return (hdr->nodeid == EXIT_FLAG_ID) ? NO_STATUS_EXIT : NO_STATUS; 165 } 166 167 static int handle_lookup(void* data, struct fuse_data* fd, 168 const struct fuse_in_header* hdr) { 169 struct fuse_entry_out out; 170 memset(&out, 0, sizeof(out)); 171 out.entry_valid = 10; 172 out.attr_valid = 10; 173 174 if (strncmp(FUSE_SIDELOAD_HOST_FILENAME, data, 175 sizeof(FUSE_SIDELOAD_HOST_FILENAME)) == 0) { 176 out.nodeid = PACKAGE_FILE_ID; 177 out.generation = PACKAGE_FILE_ID; 178 fill_attr(&(out.attr), fd, PACKAGE_FILE_ID, fd->file_size, S_IFREG | 0444); 179 } else if (strncmp(FUSE_SIDELOAD_HOST_EXIT_FLAG, data, 180 sizeof(FUSE_SIDELOAD_HOST_EXIT_FLAG)) == 0) { 181 out.nodeid = EXIT_FLAG_ID; 182 out.generation = EXIT_FLAG_ID; 183 fill_attr(&(out.attr), fd, EXIT_FLAG_ID, 0, S_IFREG | 0); 184 } else { 185 return -ENOENT; 186 } 187 188 fuse_reply(fd, hdr->unique, &out, sizeof(out)); 189 return (out.nodeid == EXIT_FLAG_ID) ? NO_STATUS_EXIT : NO_STATUS; 190 } 191 192 static int handle_open(void* data, struct fuse_data* fd, const struct fuse_in_header* hdr) { 193 const struct fuse_open_in* req = data; 194 195 if (hdr->nodeid == EXIT_FLAG_ID) return -EPERM; 196 if (hdr->nodeid != PACKAGE_FILE_ID) return -ENOENT; 197 198 struct fuse_open_out out; 199 memset(&out, 0, sizeof(out)); 200 out.fh = 10; // an arbitrary number; we always use the same handle 201 fuse_reply(fd, hdr->unique, &out, sizeof(out)); 202 return NO_STATUS; 203 } 204 205 static int handle_flush(void* data, struct fuse_data* fd, const struct fuse_in_header* hdr) { 206 return 0; 207 } 208 209 static int handle_release(void* data, struct fuse_data* fd, const struct fuse_in_header* hdr) { 210 return 0; 211 } 212 213 // Fetch a block from the host into fd->curr_block and fd->block_data. 214 // Returns 0 on successful fetch, negative otherwise. 215 static int fetch_block(struct fuse_data* fd, uint32_t block) { 216 if (block == fd->curr_block) { 217 return 0; 218 } 219 220 if (block >= fd->file_blocks) { 221 memset(fd->block_data, 0, fd->block_size); 222 fd->curr_block = block; 223 return 0; 224 } 225 226 size_t fetch_size = fd->block_size; 227 if (block * fd->block_size + fetch_size > fd->file_size) { 228 // If we're reading the last (partial) block of the file, 229 // expect a shorter response from the host, and pad the rest 230 // of the block with zeroes. 231 fetch_size = fd->file_size - (block * fd->block_size); 232 memset(fd->block_data + fetch_size, 0, fd->block_size - fetch_size); 233 } 234 235 int result = fd->vtab->read_block(fd->cookie, block, fd->block_data, fetch_size); 236 if (result < 0) return result; 237 238 fd->curr_block = block; 239 240 // Verify the hash of the block we just got from the host. 241 // 242 // - If the hash of the just-received data matches the stored hash 243 // for the block, accept it. 244 // - If the stored hash is all zeroes, store the new hash and 245 // accept the block (this is the first time we've read this 246 // block). 247 // - Otherwise, return -EINVAL for the read. 248 249 uint8_t hash[SHA256_DIGEST_SIZE]; 250 SHA256_hash(fd->block_data, fd->block_size, hash); 251 uint8_t* blockhash = fd->hashes + block * SHA256_DIGEST_SIZE; 252 if (memcmp(hash, blockhash, SHA256_DIGEST_SIZE) == 0) { 253 return 0; 254 } 255 256 int i; 257 for (i = 0; i < SHA256_DIGEST_SIZE; ++i) { 258 if (blockhash[i] != 0) { 259 fd->curr_block = -1; 260 return -EIO; 261 } 262 } 263 264 memcpy(blockhash, hash, SHA256_DIGEST_SIZE); 265 return 0; 266 } 267 268 static int handle_read(void* data, struct fuse_data* fd, const struct fuse_in_header* hdr) { 269 const struct fuse_read_in* req = data; 270 struct fuse_out_header outhdr; 271 struct iovec vec[3]; 272 int vec_used; 273 int result; 274 275 if (hdr->nodeid != PACKAGE_FILE_ID) return -ENOENT; 276 277 uint64_t offset = req->offset; 278 uint32_t size = req->size; 279 280 // The docs on the fuse kernel interface are vague about what to 281 // do when a read request extends past the end of the file. We 282 // can return a short read -- the return structure does include a 283 // length field -- but in testing that caused the program using 284 // the file to segfault. (I speculate that this is due to the 285 // reading program accessing it via mmap; maybe mmap dislikes when 286 // you return something short of a whole page?) To fix this we 287 // zero-pad reads that extend past the end of the file so we're 288 // always returning exactly as many bytes as were requested. 289 // (Users of the mapped file have to know its real length anyway.) 290 291 outhdr.len = sizeof(outhdr) + size; 292 outhdr.error = 0; 293 outhdr.unique = hdr->unique; 294 vec[0].iov_base = &outhdr; 295 vec[0].iov_len = sizeof(outhdr); 296 297 uint32_t block = offset / fd->block_size; 298 result = fetch_block(fd, block); 299 if (result != 0) return result; 300 301 // Two cases: 302 // 303 // - the read request is entirely within this block. In this 304 // case we can reply immediately. 305 // 306 // - the read request goes over into the next block. Note that 307 // since we mount the filesystem with max_read=block_size, a 308 // read can never span more than two blocks. In this case we 309 // copy the block to extra_block and issue a fetch for the 310 // following block. 311 312 uint32_t block_offset = offset - (block * fd->block_size); 313 314 if (size + block_offset <= fd->block_size) { 315 // First case: the read fits entirely in the first block. 316 317 vec[1].iov_base = fd->block_data + block_offset; 318 vec[1].iov_len = size; 319 vec_used = 2; 320 } else { 321 // Second case: the read spills over into the next block. 322 323 memcpy(fd->extra_block, fd->block_data + block_offset, 324 fd->block_size - block_offset); 325 vec[1].iov_base = fd->extra_block; 326 vec[1].iov_len = fd->block_size - block_offset; 327 328 result = fetch_block(fd, block+1); 329 if (result != 0) return result; 330 vec[2].iov_base = fd->block_data; 331 vec[2].iov_len = size - vec[1].iov_len; 332 vec_used = 3; 333 } 334 335 if (writev(fd->ffd, vec, vec_used) < 0) { 336 printf("*** READ REPLY FAILED: %s ***\n", strerror(errno)); 337 } 338 return NO_STATUS; 339 } 340 341 int run_fuse_sideload(struct provider_vtab* vtab, void* cookie, 342 uint64_t file_size, uint32_t block_size) 343 { 344 int result; 345 346 // If something's already mounted on our mountpoint, try to remove 347 // it. (Mostly in case of a previous abnormal exit.) 348 umount2(FUSE_SIDELOAD_HOST_MOUNTPOINT, MNT_FORCE); 349 350 if (block_size < 1024) { 351 fprintf(stderr, "block size (%u) is too small\n", block_size); 352 return -1; 353 } 354 if (block_size > (1<<22)) { // 4 MiB 355 fprintf(stderr, "block size (%u) is too large\n", block_size); 356 return -1; 357 } 358 359 struct fuse_data fd; 360 memset(&fd, 0, sizeof(fd)); 361 fd.vtab = vtab; 362 fd.cookie = cookie; 363 fd.file_size = file_size; 364 fd.block_size = block_size; 365 fd.file_blocks = (file_size == 0) ? 0 : (((file_size-1) / block_size) + 1); 366 367 if (fd.file_blocks > (1<<18)) { 368 fprintf(stderr, "file has too many blocks (%u)\n", fd.file_blocks); 369 result = -1; 370 goto done; 371 } 372 373 fd.hashes = (uint8_t*)calloc(fd.file_blocks, SHA256_DIGEST_SIZE); 374 if (fd.hashes == NULL) { 375 fprintf(stderr, "failed to allocate %d bites for hashes\n", 376 fd.file_blocks * SHA256_DIGEST_SIZE); 377 result = -1; 378 goto done; 379 } 380 381 fd.uid = getuid(); 382 fd.gid = getgid(); 383 384 fd.curr_block = -1; 385 fd.block_data = (uint8_t*)malloc(block_size); 386 if (fd.block_data == NULL) { 387 fprintf(stderr, "failed to allocate %d bites for block_data\n", block_size); 388 result = -1; 389 goto done; 390 } 391 fd.extra_block = (uint8_t*)malloc(block_size); 392 if (fd.extra_block == NULL) { 393 fprintf(stderr, "failed to allocate %d bites for extra_block\n", block_size); 394 result = -1; 395 goto done; 396 } 397 398 fd.ffd = open("/dev/fuse", O_RDWR); 399 if (fd.ffd < 0) { 400 perror("open /dev/fuse"); 401 result = -1; 402 goto done; 403 } 404 405 char opts[256]; 406 snprintf(opts, sizeof(opts), 407 ("fd=%d,user_id=%d,group_id=%d,max_read=%zu," 408 "allow_other,rootmode=040000"), 409 fd.ffd, fd.uid, fd.gid, block_size); 410 411 result = mount("/dev/fuse", FUSE_SIDELOAD_HOST_MOUNTPOINT, 412 "fuse", MS_NOSUID | MS_NODEV | MS_RDONLY | MS_NOEXEC, opts); 413 if (result < 0) { 414 perror("mount"); 415 goto done; 416 } 417 uint8_t request_buffer[sizeof(struct fuse_in_header) + PATH_MAX*8]; 418 for (;;) { 419 ssize_t len = read(fd.ffd, request_buffer, sizeof(request_buffer)); 420 if (len < 0) { 421 if (errno != EINTR) { 422 perror("read request"); 423 if (errno == ENODEV) { 424 result = -1; 425 break; 426 } 427 } 428 continue; 429 } 430 431 if ((size_t)len < sizeof(struct fuse_in_header)) { 432 fprintf(stderr, "request too short: len=%zu\n", (size_t)len); 433 continue; 434 } 435 436 struct fuse_in_header* hdr = (struct fuse_in_header*) request_buffer; 437 void* data = request_buffer + sizeof(struct fuse_in_header); 438 439 result = -ENOSYS; 440 441 switch (hdr->opcode) { 442 case FUSE_INIT: 443 result = handle_init(data, &fd, hdr); 444 break; 445 446 case FUSE_LOOKUP: 447 result = handle_lookup(data, &fd, hdr); 448 break; 449 450 case FUSE_GETATTR: 451 result = handle_getattr(data, &fd, hdr); 452 break; 453 454 case FUSE_OPEN: 455 result = handle_open(data, &fd, hdr); 456 break; 457 458 case FUSE_READ: 459 result = handle_read(data, &fd, hdr); 460 break; 461 462 case FUSE_FLUSH: 463 result = handle_flush(data, &fd, hdr); 464 break; 465 466 case FUSE_RELEASE: 467 result = handle_release(data, &fd, hdr); 468 break; 469 470 default: 471 fprintf(stderr, "unknown fuse request opcode %d\n", hdr->opcode); 472 break; 473 } 474 475 if (result == NO_STATUS_EXIT) { 476 result = 0; 477 break; 478 } 479 480 if (result != NO_STATUS) { 481 struct fuse_out_header outhdr; 482 outhdr.len = sizeof(outhdr); 483 outhdr.error = result; 484 outhdr.unique = hdr->unique; 485 write(fd.ffd, &outhdr, sizeof(outhdr)); 486 } 487 } 488 489 done: 490 fd.vtab->close(fd.cookie); 491 492 result = umount2(FUSE_SIDELOAD_HOST_MOUNTPOINT, MNT_DETACH); 493 if (result < 0) { 494 printf("fuse_sideload umount failed: %s\n", strerror(errno)); 495 } 496 497 if (fd.ffd) close(fd.ffd); 498 free(fd.hashes); 499 free(fd.block_data); 500 free(fd.extra_block); 501 502 return result; 503 } 504