blob: 967528e277b9f92c17ab7617910f37211756bc1c [file] [log] [blame]
Dees_Troy32c8eb82012-09-11 15:28:06 -04001/*
2 * Copyright (C) 2007 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 <ctype.h>
18#include <errno.h>
19#include <fcntl.h>
20#include <limits.h>
21#include <sys/stat.h>
22#include <sys/wait.h>
23#include <unistd.h>
24
25#include <string.h>
26#include <stdio.h>
27
28#include "common.h"
29#include "twmincrypt/twrsa.h"
30#include "twmincrypt/twsha.h"
31#include "minui/minui.h"
32#include "minzip/SysUtil.h"
33#include "minzip/Zip.h"
34#include "mtdutils/mounts.h"
35#include "mtdutils/mtdutils.h"
36#include "roots.h"
37#include "verifier.h"
38#include "ui.h"
39#include "variables.h"
40#include "data.hpp"
41#include "partitions.hpp"
Dees_Troy38bd7602012-09-14 13:33:53 -040042#include "twrp-functions.hpp"
Dees_Troy32c8eb82012-09-11 15:28:06 -040043
44extern "C" {
45#include "extra-functions.h"
46int __system(const char *command);
47};
48
49extern RecoveryUI* ui;
50
51#define ASSUMED_UPDATE_BINARY_NAME "META-INF/com/google/android/update-binary"
52#define PUBLIC_KEYS_FILE "/res/keys"
53
54// Default allocation of progress bar segments to operations
55static const int VERIFICATION_PROGRESS_TIME = 60;
56static const float VERIFICATION_PROGRESS_FRACTION = 0.25;
57static const float DEFAULT_FILES_PROGRESS_FRACTION = 0.4;
58static const float DEFAULT_IMAGE_PROGRESS_FRACTION = 0.1;
59
60enum { INSTALL_SUCCESS, INSTALL_ERROR, INSTALL_CORRUPT };
61
62// Look for an RSA signature embedded in the .ZIP file comment given
63// the path to the zip. Verify it matches one of the given public
64// keys.
65//
66// Return VERIFY_SUCCESS, VERIFY_FAILURE (if any error is encountered
67// or no key matches the signature).
68
69int TWverify_file(const char* path, const RSAPublicKey *pKeys, unsigned int numKeys) {
70 ui->SetProgress(0.0);
71
72 FILE* f = fopen(path, "rb");
73 if (f == NULL) {
74 LOGE("failed to open %s (%s)\n", path, strerror(errno));
75 return VERIFY_FAILURE;
76 }
77
78 // An archive with a whole-file signature will end in six bytes:
79 //
80 // (2-byte signature start) $ff $ff (2-byte comment size)
81 //
82 // (As far as the ZIP format is concerned, these are part of the
83 // archive comment.) We start by reading this footer, this tells
84 // us how far back from the end we have to start reading to find
85 // the whole comment.
86
87#define FOOTER_SIZE 6
88
89 if (fseek(f, -FOOTER_SIZE, SEEK_END) != 0) {
90 LOGE("failed to seek in %s (%s)\n", path, strerror(errno));
91 fclose(f);
92 return VERIFY_FAILURE;
93 }
94
95 unsigned char footer[FOOTER_SIZE];
96 if (fread(footer, 1, FOOTER_SIZE, f) != FOOTER_SIZE) {
97 LOGE("failed to read footer from %s (%s)\n", path, strerror(errno));
98 fclose(f);
99 return VERIFY_FAILURE;
100 }
101
102 if (footer[2] != 0xff || footer[3] != 0xff) {
103 fclose(f);
104 return VERIFY_FAILURE;
105 }
106
107 size_t comment_size = footer[4] + (footer[5] << 8);
108 size_t signature_start = footer[0] + (footer[1] << 8);
109 LOGI("comment is %d bytes; signature %d bytes from end\n",
110 comment_size, signature_start);
111
112 if (signature_start - FOOTER_SIZE < RSANUMBYTES) {
113 // "signature" block isn't big enough to contain an RSA block.
114 LOGE("signature is too short\n");
115 fclose(f);
116 return VERIFY_FAILURE;
117 }
118
119#define EOCD_HEADER_SIZE 22
120
121 // The end-of-central-directory record is 22 bytes plus any
122 // comment length.
123 size_t eocd_size = comment_size + EOCD_HEADER_SIZE;
124
125 if (fseek(f, -eocd_size, SEEK_END) != 0) {
126 LOGE("failed to seek in %s (%s)\n", path, strerror(errno));
127 fclose(f);
128 return VERIFY_FAILURE;
129 }
130
131 // Determine how much of the file is covered by the signature.
132 // This is everything except the signature data and length, which
133 // includes all of the EOCD except for the comment length field (2
134 // bytes) and the comment data.
135 size_t signed_len = ftell(f) + EOCD_HEADER_SIZE - 2;
136
137 unsigned char* eocd = (unsigned char*)malloc(eocd_size);
138 if (eocd == NULL) {
139 LOGE("malloc for EOCD record failed\n");
140 fclose(f);
141 return VERIFY_FAILURE;
142 }
143 if (fread(eocd, 1, eocd_size, f) != eocd_size) {
144 LOGE("failed to read eocd from %s (%s)\n", path, strerror(errno));
145 fclose(f);
146 return VERIFY_FAILURE;
147 }
148
149 // If this is really is the EOCD record, it will begin with the
150 // magic number $50 $4b $05 $06.
151 if (eocd[0] != 0x50 || eocd[1] != 0x4b ||
152 eocd[2] != 0x05 || eocd[3] != 0x06) {
153 LOGE("signature length doesn't match EOCD marker\n");
154 fclose(f);
155 return VERIFY_FAILURE;
156 }
157
158 size_t i;
159 for (i = 4; i < eocd_size-3; ++i) {
160 if (eocd[i ] == 0x50 && eocd[i+1] == 0x4b &&
161 eocd[i+2] == 0x05 && eocd[i+3] == 0x06) {
162 // if the sequence $50 $4b $05 $06 appears anywhere after
163 // the real one, minzip will find the later (wrong) one,
164 // which could be exploitable. Fail verification if
165 // this sequence occurs anywhere after the real one.
166 LOGE("EOCD marker occurs after start of EOCD\n");
167 fclose(f);
168 return VERIFY_FAILURE;
169 }
170 }
171
172#define BUFFER_SIZE 4096
173
174 SHA_CTX ctx;
175 SHA_init(&ctx);
176 unsigned char* buffer = (unsigned char*)malloc(BUFFER_SIZE);
177 if (buffer == NULL) {
178 LOGE("failed to alloc memory for sha1 buffer\n");
179 fclose(f);
180 return VERIFY_FAILURE;
181 }
182
183 double frac = -1.0;
184 size_t so_far = 0;
185 fseek(f, 0, SEEK_SET);
186 while (so_far < signed_len) {
187 size_t size = BUFFER_SIZE;
188 if (signed_len - so_far < size) size = signed_len - so_far;
189 if (fread(buffer, 1, size, f) != size) {
190 LOGE("failed to read data from %s (%s)\n", path, strerror(errno));
191 fclose(f);
192 return VERIFY_FAILURE;
193 }
194 SHA_update(&ctx, buffer, size);
195 so_far += size;
196 double f = so_far / (double)signed_len;
197 if (f > frac + 0.02 || size == so_far) {
198 ui->SetProgress(f);
199 frac = f;
200 }
201 }
202 fclose(f);
203 free(buffer);
204
205 const uint8_t* sha1 = SHA_final(&ctx);
206 for (i = 0; i < numKeys; ++i) {
207 // The 6 bytes is the "(signature_start) $ff $ff (comment_size)" that
208 // the signing tool appends after the signature itself.
209 if (RSA_verify(pKeys+i, eocd + eocd_size - 6 - RSANUMBYTES,
210 RSANUMBYTES, sha1)) {
211 LOGI("whole-file signature verified against key %d\n", i);
212 free(eocd);
213 return VERIFY_SUCCESS;
214 }
215 }
216 free(eocd);
217 LOGE("failed to verify whole-file signature\n");
218 return VERIFY_FAILURE;
219}
220
221// If the package contains an update binary, extract it and run it.
222static int
223try_update_binary(const char *path, ZipArchive *zip, int* wipe_cache) {
224 const ZipEntry* binary_entry =
225 mzFindZipEntry(zip, ASSUMED_UPDATE_BINARY_NAME);
226 if (binary_entry == NULL) {
227 mzCloseZipArchive(zip);
228 return INSTALL_CORRUPT;
229 }
230
231 const char* binary = "/tmp/update_binary";
232 unlink(binary);
233 int fd = creat(binary, 0755);
234 if (fd < 0) {
235 mzCloseZipArchive(zip);
236 LOGE("Can't make %s\n", binary);
237 return INSTALL_ERROR;
238 }
239 bool ok = mzExtractZipEntryToFile(zip, binary_entry, fd);
240 close(fd);
241 mzCloseZipArchive(zip);
242
243 if (!ok) {
244 LOGE("Can't copy %s\n", ASSUMED_UPDATE_BINARY_NAME);
245 return INSTALL_ERROR;
246 }
247
248 int pipefd[2];
249 pipe(pipefd);
250
251 // When executing the update binary contained in the package, the
252 // arguments passed are:
253 //
254 // - the version number for this interface
255 //
256 // - an fd to which the program can write in order to update the
257 // progress bar. The program can write single-line commands:
258 //
259 // progress <frac> <secs>
260 // fill up the next <frac> part of of the progress bar
261 // over <secs> seconds. If <secs> is zero, use
262 // set_progress commands to manually control the
263 // progress of this segment of the bar
264 //
265 // set_progress <frac>
266 // <frac> should be between 0.0 and 1.0; sets the
267 // progress bar within the segment defined by the most
268 // recent progress command.
269 //
270 // firmware <"hboot"|"radio"> <filename>
271 // arrange to install the contents of <filename> in the
272 // given partition on reboot.
273 //
274 // (API v2: <filename> may start with "PACKAGE:" to
275 // indicate taking a file from the OTA package.)
276 //
277 // (API v3: this command no longer exists.)
278 //
279 // ui_print <string>
280 // display <string> on the screen.
281 //
282 // - the name of the package zip file.
283 //
284
285 const char** args = (const char**)malloc(sizeof(char*) * 5);
286 args[0] = binary;
287 args[1] = EXPAND(RECOVERY_API_VERSION); // defined in Android.mk
288 char* temp = (char*)malloc(10);
289 sprintf(temp, "%d", pipefd[1]);
290 args[2] = temp;
291 args[3] = (char*)path;
292 args[4] = NULL;
293
294 pid_t pid = fork();
295 if (pid == 0) {
296 close(pipefd[0]);
297 execv(binary, (char* const*)args);
298 fprintf(stdout, "E:Can't run %s (%s)\n", binary, strerror(errno));
299 _exit(-1);
300 }
301 close(pipefd[1]);
302
303 *wipe_cache = 0;
304
305 char buffer[1024];
306 FILE* from_child = fdopen(pipefd[0], "r");
307 while (fgets(buffer, sizeof(buffer), from_child) != NULL) {
308 char* command = strtok(buffer, " \n");
309 if (command == NULL) {
310 continue;
311 } else if (strcmp(command, "progress") == 0) {
312 char* fraction_s = strtok(NULL, " \n");
313 char* seconds_s = strtok(NULL, " \n");
314
315 float fraction = strtof(fraction_s, NULL);
316 int seconds = strtol(seconds_s, NULL, 10);
317
318 ui->ShowProgress(fraction * (1-VERIFICATION_PROGRESS_FRACTION), seconds);
319 } else if (strcmp(command, "set_progress") == 0) {
320 char* fraction_s = strtok(NULL, " \n");
321 float fraction = strtof(fraction_s, NULL);
322 ui->SetProgress(fraction);
323 } else if (strcmp(command, "ui_print") == 0) {
324 char* str = strtok(NULL, "\n");
325 if (str) {
326 ui->Print("%s", str);
327 } else {
328 ui->Print("\n");
329 }
330 } else if (strcmp(command, "wipe_cache") == 0) {
331 *wipe_cache = 1;
332 } else if (strcmp(command, "clear_display") == 0) {
333 //ui->SetBackground(RecoveryUI::NONE);
334 } else {
335 LOGE("unknown command [%s]\n", command);
336 }
337 }
338 fclose(from_child);
339
340 int status;
341 waitpid(pid, &status, 0);
342 if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) {
343 LOGE("Error in %s\n(Status %d)\n", path, WEXITSTATUS(status));
344 return INSTALL_ERROR;
345 }
346
347 return INSTALL_SUCCESS;
348}
349
350// Reads a file containing one or more public keys as produced by
351// DumpPublicKey: this is an RSAPublicKey struct as it would appear
352// as a C source literal, eg:
353//
354// "{64,0xc926ad21,{1795090719,...,-695002876},{-857949815,...,1175080310}}"
355//
356// (Note that the braces and commas in this example are actual
357// characters the parser expects to find in the file; the ellipses
358// indicate more numbers omitted from this example.)
359//
360// The file may contain multiple keys in this format, separated by
361// commas. The last key must not be followed by a comma.
362//
363// Returns NULL if the file failed to parse, or if it contain zero keys.
364static RSAPublicKey*
365load_keys(const char* filename, int* numKeys) {
366 RSAPublicKey* out = NULL;
367 *numKeys = 0;
368
369 FILE* f = fopen(filename, "r");
370 if (f == NULL) {
371 LOGE("opening %s: %s\n", filename, strerror(errno));
372 goto exit;
373 }
374
375 {
376 int i;
377 bool done = false;
378 while (!done) {
379 ++*numKeys;
380 out = (RSAPublicKey*)realloc(out, *numKeys * sizeof(RSAPublicKey));
381 RSAPublicKey* key = out + (*numKeys - 1);
382 if (fscanf(f, " { %i , 0x%x , { %u",
383 &(key->len), &(key->n0inv), &(key->n[0])) != 3) {
384 goto exit;
385 }
386 if (key->len != RSANUMWORDS) {
387 LOGE("key length (%d) does not match expected size\n", key->len);
388 goto exit;
389 }
390 for (i = 1; i < key->len; ++i) {
391 if (fscanf(f, " , %u", &(key->n[i])) != 1) goto exit;
392 }
393 if (fscanf(f, " } , { %u", &(key->rr[0])) != 1) goto exit;
394 for (i = 1; i < key->len; ++i) {
395 if (fscanf(f, " , %u", &(key->rr[i])) != 1) goto exit;
396 }
397 fscanf(f, " } } ");
398
399 // if the line ends in a comma, this file has more keys.
400 switch (fgetc(f)) {
401 case ',':
402 // more keys to come.
403 break;
404
405 case EOF:
406 done = true;
407 break;
408
409 default:
410 LOGE("unexpected character between keys\n");
411 goto exit;
412 }
413 }
414 }
415
416 fclose(f);
417 return out;
418
419exit:
420 if (f) fclose(f);
421 free(out);
422 *numKeys = 0;
423 return NULL;
424}
425
Dees_Troy32c8eb82012-09-11 15:28:06 -0400426extern "C" int TWinstall_zip(const char* path, int* wipe_cache) {
427 int err, zip_verify, md5_return, md5_verify;
428
429 ui_print("Installing '%s'...\n", path);
430
431 if (!PartitionManager.Mount_By_Path(path, 0)) {
432 LOGE("Failed to mount '%s'\n", path);
433 return -1;
434 }
435
436 ui_print("Checking for MD5 file...\n");
Dees_Troy38bd7602012-09-14 13:33:53 -0400437 md5_return = TWFunc::Check_MD5(path);
Dees_Troy32c8eb82012-09-11 15:28:06 -0400438 if (md5_return == 0) {
439 // MD5 did not match.
440 LOGE("Zip MD5 does not match.\nUnable to install zip.\n");
441 return INSTALL_CORRUPT;
442 } else if (md5_return == -1) {
443 DataManager::GetValue(TW_FORCE_MD5_CHECK_VAR, md5_verify);
444 if (md5_verify == 1) {
445 // Forced MD5 checking is on and no MD5 file found.
446 LOGE("No MD5 file found for '%s'.\nDisable force MD5 check to avoid this error.\n", path);
447 return INSTALL_CORRUPT;
448 } else
449 ui_print("No MD5 file found, this is not an error.\n");
450 } else if (md5_return == 1)
451 ui_print("Zip MD5 matched.\n"); // MD5 found and matched.
452
453 DataManager::GetValue(TW_SIGNED_ZIP_VERIFY_VAR, zip_verify);
454 if (zip_verify) {
455 ui_print("Verifying zip signature...\n");
456 int numKeys;
457 RSAPublicKey* loadedKeys = load_keys(PUBLIC_KEYS_FILE, &numKeys);
458 if (loadedKeys == NULL) {
459 LOGE("Failed to load keys\n");
460 return -1;
461 }
462 LOGI("%d key(s) loaded from %s\n", numKeys, PUBLIC_KEYS_FILE);
463
464 // Give verification half the progress bar...
465 ui->Print("Verifying update package...\n");
466 ui->SetProgressType(RecoveryUI::DETERMINATE);
467 ui->ShowProgress(VERIFICATION_PROGRESS_FRACTION, VERIFICATION_PROGRESS_TIME);
468
469 err = TWverify_file(path, loadedKeys, numKeys);
470 free(loadedKeys);
471 LOGI("verify_file returned %d\n", err);
472 if (err != VERIFY_SUCCESS) {
473 LOGE("signature verification failed\n");
474 return -1;
475 }
476 }
477 /* Try to open the package.
478 */
479 ZipArchive zip;
480 err = mzOpenZipArchive(path, &zip);
481 if (err != 0) {
482 LOGE("Can't open %s\n(%s)\n", path, err != -1 ? strerror(err) : "bad");
483 return INSTALL_CORRUPT;
484 }
485
486 /* Verify and install the contents of the package.
487 */
488 return try_update_binary(path, &zip, wipe_cache);
489}