blob: 35ba6ca704eb6196127bfb3d2ff528e7b117a4ac [file] [log] [blame]
The Android Open Source Projectc24a8e62009-03-03 19:28:42 -08001/*
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
Doug Zongkerb2ee9202009-06-04 10:24:53 -070017#include <ctype.h>
The Android Open Source Projectc24a8e62009-03-03 19:28:42 -080018#include <errno.h>
19#include <fcntl.h>
20#include <limits.h>
21#include <sys/stat.h>
Doug Zongkerb2ee9202009-06-04 10:24:53 -070022#include <sys/wait.h>
23#include <unistd.h>
The Android Open Source Projectc24a8e62009-03-03 19:28:42 -080024
The Android Open Source Projectc24a8e62009-03-03 19:28:42 -080025#include "common.h"
26#include "install.h"
27#include "mincrypt/rsa.h"
28#include "minui/minui.h"
29#include "minzip/SysUtil.h"
30#include "minzip/Zip.h"
31#include "mtdutils/mounts.h"
32#include "mtdutils/mtdutils.h"
33#include "roots.h"
34#include "verifier.h"
35
Doug Zongkerb2ee9202009-06-04 10:24:53 -070036#define ASSUMED_UPDATE_BINARY_NAME "META-INF/com/google/android/update-binary"
Doug Zongkerd1b19b92009-04-01 15:48:46 -070037#define PUBLIC_KEYS_FILE "/res/keys"
The Android Open Source Projectc24a8e62009-03-03 19:28:42 -080038
Doug Zongkerb2ee9202009-06-04 10:24:53 -070039// If the package contains an update binary, extract it and run it.
40static int
41try_update_binary(const char *path, ZipArchive *zip) {
42 const ZipEntry* binary_entry =
43 mzFindZipEntry(zip, ASSUMED_UPDATE_BINARY_NAME);
44 if (binary_entry == NULL) {
45 return INSTALL_CORRUPT;
46 }
47
48 char* binary = "/tmp/update_binary";
49 unlink(binary);
50 int fd = creat(binary, 0755);
51 if (fd < 0) {
52 LOGE("Can't make %s\n", binary);
53 return 1;
54 }
55 bool ok = mzExtractZipEntryToFile(zip, binary_entry, fd);
56 close(fd);
57
58 if (!ok) {
59 LOGE("Can't copy %s\n", ASSUMED_UPDATE_BINARY_NAME);
60 return 1;
61 }
62
63 int pipefd[2];
64 pipe(pipefd);
65
66 // When executing the update binary contained in the package, the
67 // arguments passed are:
68 //
Doug Zongkerfb2e3af2009-06-17 17:29:40 -070069 // - the version number for this interface
Doug Zongkerb2ee9202009-06-04 10:24:53 -070070 //
71 // - an fd to which the program can write in order to update the
72 // progress bar. The program can write single-line commands:
73 //
74 // progress <frac> <secs>
Doug Zongkerfbf3c102009-06-24 09:36:20 -070075 // fill up the next <frac> part of of the progress bar
76 // over <secs> seconds. If <secs> is zero, use
77 // set_progress commands to manually control the
78 // progress of this segment of the bar
79 //
80 // set_progress <frac>
81 // <frac> should be between 0.0 and 1.0; sets the
82 // progress bar within the segment defined by the most
83 // recent progress command.
Doug Zongkerb2ee9202009-06-04 10:24:53 -070084 //
85 // firmware <"hboot"|"radio"> <filename>
86 // arrange to install the contents of <filename> in the
Doug Zongkere08991e2010-02-02 13:09:52 -080087 // given partition on reboot.
88 //
89 // (API v2: <filename> may start with "PACKAGE:" to
90 // indicate taking a file from the OTA package.)
91 //
92 // (API v3: this command no longer exists.)
Doug Zongkerb2ee9202009-06-04 10:24:53 -070093 //
Doug Zongkerd9c9d102009-06-12 12:24:39 -070094 // ui_print <string>
95 // display <string> on the screen.
96 //
Doug Zongkerb2ee9202009-06-04 10:24:53 -070097 // - the name of the package zip file.
98 //
99
100 char** args = malloc(sizeof(char*) * 5);
101 args[0] = binary;
Doug Zongkerfb2e3af2009-06-17 17:29:40 -0700102 args[1] = EXPAND(RECOVERY_API_VERSION); // defined in Android.mk
Doug Zongkerb2ee9202009-06-04 10:24:53 -0700103 args[2] = malloc(10);
104 sprintf(args[2], "%d", pipefd[1]);
105 args[3] = (char*)path;
106 args[4] = NULL;
107
108 pid_t pid = fork();
109 if (pid == 0) {
110 close(pipefd[0]);
111 execv(binary, args);
112 fprintf(stderr, "E:Can't run %s (%s)\n", binary, strerror(errno));
113 _exit(-1);
114 }
115 close(pipefd[1]);
116
Doug Zongker64893cc2009-07-14 16:31:56 -0700117 char buffer[1024];
Doug Zongkerb2ee9202009-06-04 10:24:53 -0700118 FILE* from_child = fdopen(pipefd[0], "r");
119 while (fgets(buffer, sizeof(buffer), from_child) != NULL) {
Doug Zongkerb2ee9202009-06-04 10:24:53 -0700120 char* command = strtok(buffer, " \n");
121 if (command == NULL) {
122 continue;
123 } else if (strcmp(command, "progress") == 0) {
124 char* fraction_s = strtok(NULL, " \n");
125 char* seconds_s = strtok(NULL, " \n");
126
127 float fraction = strtof(fraction_s, NULL);
128 int seconds = strtol(seconds_s, NULL, 10);
129
130 ui_show_progress(fraction * (1-VERIFICATION_PROGRESS_FRACTION),
131 seconds);
Doug Zongkerfbf3c102009-06-24 09:36:20 -0700132 } else if (strcmp(command, "set_progress") == 0) {
133 char* fraction_s = strtok(NULL, " \n");
134 float fraction = strtof(fraction_s, NULL);
135 ui_set_progress(fraction);
Doug Zongkerd9c9d102009-06-12 12:24:39 -0700136 } else if (strcmp(command, "ui_print") == 0) {
137 char* str = strtok(NULL, "\n");
138 if (str) {
Nick Kralevich21b97ed2010-06-24 16:11:17 -0700139 ui_print("%s", str);
Doug Zongkerd9c9d102009-06-12 12:24:39 -0700140 } else {
141 ui_print("\n");
142 }
Doug Zongkerb2ee9202009-06-04 10:24:53 -0700143 } else {
144 LOGE("unknown command [%s]\n", command);
145 }
146 }
147 fclose(from_child);
148
149 int status;
150 waitpid(pid, &status, 0);
151 if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) {
Doug Zongker9931f7f2009-06-10 14:11:53 -0700152 LOGE("Error in %s\n(Status %d)\n", path, WEXITSTATUS(status));
Doug Zongkerb2ee9202009-06-04 10:24:53 -0700153 return INSTALL_ERROR;
154 }
155
Doug Zongkere08991e2010-02-02 13:09:52 -0800156 return INSTALL_SUCCESS;
Doug Zongkerb2ee9202009-06-04 10:24:53 -0700157}
158
The Android Open Source Projectc24a8e62009-03-03 19:28:42 -0800159static int
Doug Zongker60151a22009-08-12 18:30:03 -0700160handle_update_package(const char *path, ZipArchive *zip)
The Android Open Source Projectc24a8e62009-03-03 19:28:42 -0800161{
The Android Open Source Projectc24a8e62009-03-03 19:28:42 -0800162 // Update should take the rest of the progress bar.
163 ui_print("Installing update...\n");
164
Doug Zongkerb2ee9202009-06-04 10:24:53 -0700165 int result = try_update_binary(path, zip);
The Android Open Source Projectc24a8e62009-03-03 19:28:42 -0800166 register_package_root(NULL, NULL); // Unregister package root
Doug Zongker64893cc2009-07-14 16:31:56 -0700167 return result;
The Android Open Source Projectc24a8e62009-03-03 19:28:42 -0800168}
169
Doug Zongkerd1b19b92009-04-01 15:48:46 -0700170// Reads a file containing one or more public keys as produced by
171// DumpPublicKey: this is an RSAPublicKey struct as it would appear
172// as a C source literal, eg:
173//
174// "{64,0xc926ad21,{1795090719,...,-695002876},{-857949815,...,1175080310}}"
175//
176// (Note that the braces and commas in this example are actual
177// characters the parser expects to find in the file; the ellipses
178// indicate more numbers omitted from this example.)
179//
180// The file may contain multiple keys in this format, separated by
181// commas. The last key must not be followed by a comma.
182//
183// Returns NULL if the file failed to parse, or if it contain zero keys.
184static RSAPublicKey*
185load_keys(const char* filename, int* numKeys) {
186 RSAPublicKey* out = NULL;
187 *numKeys = 0;
188
189 FILE* f = fopen(filename, "r");
190 if (f == NULL) {
191 LOGE("opening %s: %s\n", filename, strerror(errno));
192 goto exit;
193 }
194
195 int i;
196 bool done = false;
197 while (!done) {
198 ++*numKeys;
199 out = realloc(out, *numKeys * sizeof(RSAPublicKey));
200 RSAPublicKey* key = out + (*numKeys - 1);
Doug Zongkeraa062532010-01-28 16:47:20 -0800201 if (fscanf(f, " { %i , 0x%x , { %u",
Doug Zongkerd1b19b92009-04-01 15:48:46 -0700202 &(key->len), &(key->n0inv), &(key->n[0])) != 3) {
203 goto exit;
204 }
205 if (key->len != RSANUMWORDS) {
206 LOGE("key length (%d) does not match expected size\n", key->len);
207 goto exit;
208 }
209 for (i = 1; i < key->len; ++i) {
Doug Zongkeraa062532010-01-28 16:47:20 -0800210 if (fscanf(f, " , %u", &(key->n[i])) != 1) goto exit;
Doug Zongkerd1b19b92009-04-01 15:48:46 -0700211 }
Doug Zongkeraa062532010-01-28 16:47:20 -0800212 if (fscanf(f, " } , { %u", &(key->rr[0])) != 1) goto exit;
Doug Zongkerd1b19b92009-04-01 15:48:46 -0700213 for (i = 1; i < key->len; ++i) {
Doug Zongkeraa062532010-01-28 16:47:20 -0800214 if (fscanf(f, " , %u", &(key->rr[i])) != 1) goto exit;
Doug Zongkerd1b19b92009-04-01 15:48:46 -0700215 }
216 fscanf(f, " } } ");
217
218 // if the line ends in a comma, this file has more keys.
219 switch (fgetc(f)) {
220 case ',':
221 // more keys to come.
222 break;
223
224 case EOF:
225 done = true;
226 break;
227
228 default:
229 LOGE("unexpected character between keys\n");
230 goto exit;
231 }
232 }
233
234 fclose(f);
235 return out;
236
237exit:
238 if (f) fclose(f);
239 free(out);
240 *numKeys = 0;
241 return NULL;
242}
243
The Android Open Source Projectc24a8e62009-03-03 19:28:42 -0800244int
245install_package(const char *root_path)
246{
247 ui_set_background(BACKGROUND_ICON_INSTALLING);
248 ui_print("Finding update package...\n");
249 ui_show_indeterminate_progress();
250 LOGI("Update location: %s\n", root_path);
251
252 if (ensure_root_path_mounted(root_path) != 0) {
253 LOGE("Can't mount %s\n", root_path);
254 return INSTALL_CORRUPT;
255 }
256
257 char path[PATH_MAX] = "";
258 if (translate_root_path(root_path, path, sizeof(path)) == NULL) {
259 LOGE("Bad path %s\n", root_path);
260 return INSTALL_CORRUPT;
261 }
262
263 ui_print("Opening update package...\n");
264 LOGI("Update file path: %s\n", path);
265
Doug Zongkerd1b19b92009-04-01 15:48:46 -0700266 int numKeys;
267 RSAPublicKey* loadedKeys = load_keys(PUBLIC_KEYS_FILE, &numKeys);
268 if (loadedKeys == NULL) {
269 LOGE("Failed to load keys\n");
270 return INSTALL_CORRUPT;
271 }
272 LOGI("%d key(s) loaded from %s\n", numKeys, PUBLIC_KEYS_FILE);
273
Doug Zongker60151a22009-08-12 18:30:03 -0700274 // Give verification half the progress bar...
275 ui_print("Verifying update package...\n");
276 ui_show_progress(
277 VERIFICATION_PROGRESS_FRACTION,
278 VERIFICATION_PROGRESS_TIME);
279
280 int err;
281 err = verify_file(path, loadedKeys, numKeys);
282 free(loadedKeys);
283 LOGI("verify_file returned %d\n", err);
284 if (err != VERIFY_SUCCESS) {
285 LOGE("signature verification failed\n");
286 return INSTALL_CORRUPT;
287 }
288
The Android Open Source Projectc24a8e62009-03-03 19:28:42 -0800289 /* Try to open the package.
290 */
291 ZipArchive zip;
Doug Zongker60151a22009-08-12 18:30:03 -0700292 err = mzOpenZipArchive(path, &zip);
The Android Open Source Projectc24a8e62009-03-03 19:28:42 -0800293 if (err != 0) {
294 LOGE("Can't open %s\n(%s)\n", path, err != -1 ? strerror(err) : "bad");
295 return INSTALL_CORRUPT;
296 }
297
298 /* Verify and install the contents of the package.
299 */
Doug Zongker60151a22009-08-12 18:30:03 -0700300 int status = handle_update_package(path, &zip);
The Android Open Source Projectc24a8e62009-03-03 19:28:42 -0800301 mzCloseZipArchive(&zip);
The Android Open Source Projectc24a8e62009-03-03 19:28:42 -0800302 return status;
303}