/* * Copyright (C) 2007 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include #include #include #include #include #include #include #include "common.h" #include "install.h" #include "mincrypt/rsa.h" #include "minui/minui.h" #include "minzip/SysUtil.h" #include "minzip/Zip.h" #include "mtdutils/mounts.h" #include "mtdutils/mtdutils.h" #include "roots.h" #include "verifier.h" #define ASSUMED_UPDATE_BINARY_NAME "META-INF/com/google/android/update-binary" #define PUBLIC_KEYS_FILE "/res/keys" // Flash dsp*.mbn files included in the zip /* [yangjihong] Update dsp image */ #define MAX_DSP_MBN_IMAGES 5 #define WLAN_MODEM_ZIP_PATH "firmware" #define WLAN_MODEM_DEST_PATH "/firmware" #define WLAN_MODEM_INFO_FILE "firmware/image/update_info" #define TMP_WLAN_MODEM_INFO_FILE "/firmware/image/update_info" #define MODEM_ENTRY_MAX_LINE_LEN 64 // Callback invoked by mzProcessZipEntryContents to write uncompressed data to flash static bool flash_mbn_data(const unsigned char *data, int dataLen, void *cookie) { ssize_t ret; MtdWriteContext *ctx = (MtdWriteContext *) cookie; if (cookie == NULL) { LOGI("unexpected null ptr\n"); return false; } ret = mtd_write_data(ctx, data, dataLen); if (ret != dataLen) { LOGI("error writing buffer: return value %d, expected %d\n", ret, dataLen); return false; } return true; } static bool update_wlan_modem(const ZipArchive *zip) { char* zip_path; char* dest_path; const ZipEntry *info_entry; char line[MODEM_ENTRY_MAX_LINE_LEN]; int i; info_entry = mzFindZipEntry(zip, WLAN_MODEM_INFO_FILE); if (info_entry == NULL) { LOGI("None wlan & modem imags found!\n"); return true; } // To create a consistent system image, never use the clock for timestamps. struct utimbuf timestamp = { 1217592000, 1217592000 }; // 8/1/2008 default // extract images to firmware dir bool success = mzExtractRecursive(zip, WLAN_MODEM_ZIP_PATH, WLAN_MODEM_DEST_PATH, MZ_EXTRACT_FILES_ONLY, ×tamp, NULL, NULL); FILE* file = fopen(TMP_WLAN_MODEM_INFO_FILE, "r"); if (file == NULL) { LOGI("Can't open %s, do default set permission\n", TMP_WLAN_MODEM_INFO_FILE); system("chmod /firmware/image/* 664"); system("chmod /firmware/image/*.bin 775"); system("chmod /firmware/image/*.tlv 775"); system("chmod /firmware/image/*.txt 775"); return true; } memset(line, 0, MODEM_ENTRY_MAX_LINE_LEN); while(fgets(line, MODEM_ENTRY_MAX_LINE_LEN - 1, file)!=NULL) { for (i = 0; line[i] && isspace(line[i]); ++i); if (line[i] == '\0' || line[i] == '#') continue; char* original = strdup(line); char* image = strtok(line+i, " \t\n"); char* image_mode = strtok(NULL, " \t\n"); char* mode_end; if (image && image_mode) { int mode = strtoul(image_mode, &mode_end, 0); if (*mode_end != '\0' || image_mode[0] == 0) { LOGI("Unknown permission: %s\n", original); continue; } if (chmod(image, mode) < 0) { LOGI("%s: chmod to %o failed: %s\n", image, mode, strerror(errno)); }else { LOGI("Set permission: file %s (mode:%o)\n", image, mode); } }else { LOGI("skipping line: %s\n", original); } free(original); memset(line, 0, MODEM_ENTRY_MAX_LINE_LEN); } fclose(file); unlink(TMP_WLAN_MODEM_INFO_FILE); return success; } static bool install_mbns(const ZipArchive *zip) { int i; const char *mbn_names[MAX_DSP_MBN_IMAGES] = { "sbl1.mbn", "appsboot.mbn", "NON-HLOS.ubi", "boot.img", "rpm.mbn" }; const char *mbn_partitions[MAX_DSP_MBN_IMAGES] = {"sbl", "aboot", "modem", "boot", "rpm"}; const ZipEntry *mbn_entry; const MtdPartition *part; size_t total_size, erase_size, write_size; MtdWriteContext *ctx; if (mtd_scan_partitions() < 0) { LOGI("error scanning mtd partitions\n"); return false; } for (i = 0; i < MAX_DSP_MBN_IMAGES; i++) { mbn_entry = mzFindZipEntry(zip, mbn_names[i]); if (mbn_entry != NULL) { LOGI("found full mbn %s\n", mbn_names[i]); part = mtd_find_partition_by_name(mbn_partitions[i]); if (part == NULL) { LOGI("couldn't find partition %s\n", mbn_partitions[i]); return false; } if (mtd_partition_info(part, &total_size, &erase_size, &write_size) < 0) { LOGI("couldn't get partition info for %s\n", mbn_partitions[i]); return false; } if (total_size < mbn_entry->uncompLen) { LOGI("not enough room in partition %s for file of size %d " "(have %d bytes)!\n", mbn_partitions[i], total_size, mbn_entry->uncompLen); return false; } ctx = mtd_write_partition(part); if (ctx == NULL) { LOGI("couldn't open write context for %s\n", mbn_partitions[i]); return false; } if (!mzProcessZipEntryContents(zip, mbn_entry, flash_mbn_data, ctx)) { LOGI("error writing image\n"); (void) mtd_write_close(ctx); return false; } if (mtd_erase_blocks(ctx, -1) < 0) { LOGI("error finishing mtd write\n"); mtd_write_close(ctx); return false; } if (mtd_write_close(ctx) < 0) { LOGI("error closing write context"); return false; } LOGI("updating %s complete: wrote %d bytes\n", mbn_partitions[i], mbn_entry->uncompLen); } else { LOGI("No mbn %s image found\n", mbn_names[i]); } } return true; } // If the package contains an update binary, extract it and run it. static int try_update_binary(const char *path, ZipArchive *zip, int* wipe_cache) { const ZipEntry* binary_entry = mzFindZipEntry(zip, ASSUMED_UPDATE_BINARY_NAME); if (binary_entry == NULL) { mzCloseZipArchive(zip); LOGE("File corrupted %s; can't find %s\n", path, ASSUMED_UPDATE_BINARY_NAME); return INSTALL_CORRUPT; } char* binary = "/tmp/update_binary"; unlink(binary); int fd = creat(binary, 0755); if (fd < 0) { mzCloseZipArchive(zip); LOGE("Can't make %s\n", binary); return INSTALL_ERROR; } bool ok = mzExtractZipEntryToFile(zip, binary_entry, fd); close(fd); //mzCloseZipArchive(zip); if (!ok) { LOGE("Can't copy %s\n", ASSUMED_UPDATE_BINARY_NAME); return INSTALL_ERROR; } int pipefd[2]; pipe(pipefd); // When executing the update binary contained in the package, the // arguments passed are: // // - the version number for this interface // // - an fd to which the program can write in order to update the // progress bar. The program can write single-line commands: // // progress // fill up the next part of of the progress bar // over seconds. If is zero, use // set_progress commands to manually control the // progress of this segment of the bar // // set_progress // should be between 0.0 and 1.0; sets the // progress bar within the segment defined by the most // recent progress command. // // firmware <"hboot"|"radio"> // arrange to install the contents of in the // given partition on reboot. // // (API v2: may start with "PACKAGE:" to // indicate taking a file from the OTA package.) // // (API v3: this command no longer exists.) // // ui_print // display on the screen. // // - the name of the package zip file. // char *recovery_version = "3"; char** args = malloc(sizeof(char*) * 5); args[0] = binary; args[1] = recovery_version; // defined in Android.mk args[2] = malloc(10); sprintf(args[2], "%d", pipefd[1]); args[3] = (char*)path; args[4] = NULL; LOGI("Attempting to run %s\n", binary); pid_t pid = fork(); if (pid == 0) { close(pipefd[0]); execv(binary, args); fprintf(stdout, "E:Can't run %s (%s)\n", binary, strerror(errno)); LOGE("Can't run %s (%s)\n", binary, strerror(errno)); _exit(-1); } close(pipefd[1]); *wipe_cache = 0; char buffer[1024]; FILE* from_child = fdopen(pipefd[0], "r"); while (fgets(buffer, sizeof(buffer), from_child) != NULL) { char* command = strtok(buffer, " \n"); if (command == NULL) { continue; } else if (strcmp(command, "progress") == 0) { char* fraction_s = strtok(NULL, " \n"); char* seconds_s = strtok(NULL, " \n"); float fraction = strtof(fraction_s, NULL); int seconds = strtol(seconds_s, NULL, 10); ui_show_progress(fraction * (1-VERIFICATION_PROGRESS_FRACTION), seconds); } else if (strcmp(command, "set_progress") == 0) { char* fraction_s = strtok(NULL, " \n"); float fraction = strtof(fraction_s, NULL); ui_set_progress(fraction); } else if (strcmp(command, "ui_print") == 0) { char* str = strtok(NULL, "\n"); if (str) { ui_print("%s", str); } else { ui_print("\n"); } } else if (strcmp(command, "wipe_cache") == 0) { *wipe_cache = 1; } else { LOGE("unknown command [%s]\n", command); } } fclose(from_child); int status; waitpid(pid, &status, 0); if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) { LOGI("Error in %s\n(Status %d)\n", path, WEXITSTATUS(status)); return INSTALL_ERROR; } if (!install_mbns(zip)) { LOGI("Installing MBNs failed\n"); mzCloseZipArchive(zip); return INSTALL_ERROR; } else { LOGI("Installing MBNs success\n"); } #if 0 if (!update_wlan_modem(zip)) { LOGI("Update wlan & modem failed\n"); mzCloseZipArchive(zip); return INSTALL_ERROR; } else { LOGI("Update wlan & modem success\n"); } #endif mzCloseZipArchive(zip); return INSTALL_SUCCESS; } // Reads a file containing one or more public keys as produced by // DumpPublicKey: this is an RSAPublicKey struct as it would appear // as a C source literal, eg: // // "{64,0xc926ad21,{1795090719,...,-695002876},{-857949815,...,1175080310}}" // // (Note that the braces and commas in this example are actual // characters the parser expects to find in the file; the ellipses // indicate more numbers omitted from this example.) // // The file may contain multiple keys in this format, separated by // commas. The last key must not be followed by a comma. // // Returns NULL if the file failed to parse, or if it contain zero keys. static RSAPublicKey* load_keys(const char* filename, int* numKeys) { RSAPublicKey* out = NULL; *numKeys = 0; FILE* f = fopen(filename, "r"); if (f == NULL) { LOGE("opening %s: %s\n", filename, strerror(errno)); goto exit; } int i; bool done = false; while (!done) { ++*numKeys; out = realloc(out, *numKeys * sizeof(RSAPublicKey)); RSAPublicKey* key = out + (*numKeys - 1); if (fscanf(f, " { %i , 0x%x , { %u", &(key->len), &(key->n0inv), &(key->n[0])) != 3) { goto exit; } if (key->len != RSANUMWORDS) { LOGE("key length (%d) does not match expected size\n", key->len); goto exit; } for (i = 1; i < key->len; ++i) { if (fscanf(f, " , %u", &(key->n[i])) != 1) goto exit; } if (fscanf(f, " } , { %u", &(key->rr[0])) != 1) goto exit; for (i = 1; i < key->len; ++i) { if (fscanf(f, " , %u", &(key->rr[i])) != 1) goto exit; } fscanf(f, " } } "); // if the line ends in a comma, this file has more keys. switch (fgetc(f)) { case ',': // more keys to come. break; case EOF: done = true; break; default: LOGE("unexpected character between keys\n"); goto exit; } } fclose(f); return out; exit: if (f) fclose(f); free(out); *numKeys = 0; return NULL; } static int really_install_package(const char *path, int* wipe_cache) { ui_set_background(BACKGROUND_ICON_INSTALLING); ui_print("Finding update package...\n"); ui_show_indeterminate_progress(); LOGI("Update location: %s\n", path); if (ensure_path_mounted(path) != 0) { LOGE("Can't mount %s\n", path); return INSTALL_CORRUPT; } ui_print("Opening update package...\n"); #if ENABLE_SIGNATURE_CHECK int numKeys; RSAPublicKey* loadedKeys = load_keys(PUBLIC_KEYS_FILE, &numKeys); if (loadedKeys == NULL) { LOGE("Failed to load keys\n"); return INSTALL_CORRUPT; } LOGI("%d key(s) loaded from %s\n", numKeys, PUBLIC_KEYS_FILE); // Give verification half the progress bar... ui_print("Verifying update package...\n"); LOGI("Verifying update package...\n"); ui_show_progress( VERIFICATION_PROGRESS_FRACTION, VERIFICATION_PROGRESS_TIME); int err; err = verify_file(path, loadedKeys, numKeys); free(loadedKeys); LOGI("verify_file returned %d\n", err); if (err != VERIFY_SUCCESS) { LOGE("signature verification failed\n"); return INSTALL_CORRUPT; } #endif /* Try to open the package. */ int err; ZipArchive zip; err = mzOpenZipArchive(path, &zip); if (err != 0) { LOGE("Can't open %s\n(%s)\n", path, err != -1 ? strerror(err) : "bad"); return INSTALL_CORRUPT; } /*Do CRC check for files in zip package */ int zip_file_count; LOGI( "number of files in zip is %d \n",zip.numEntries); for(zip_file_count = 0; zip_file_count < zip.numEntries; zip_file_count++) { //LOGI("verifying file at index %d\n",zip_file_count); if(!mzIsZipEntryIntact(&zip, &zip.pEntries[zip_file_count])){ LOGI("Zip archive corrupt at entry %d\n",zip_file_count); return INSTALL_CORRUPT; } } /* Verify and install the contents of the package. */ ui_print("Installing update...\n"); LOGI("Installing update...\n"); return try_update_binary(path, &zip, wipe_cache); } int install_package(const char* path, int* wipe_cache, const char* install_file) { FILE* install_log = fopen_path(install_file, "w"); if (install_log) { fputs(path, install_log); fputc('\n', install_log); } else { LOGE("failed to open last_install: %s\n", strerror(errno)); } int result = really_install_package(path, wipe_cache); if (install_log) { fputc(result == INSTALL_SUCCESS ? '1' : '0', install_log); fputc('\n', install_log); fclose(install_log); } return result; }