diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index a573678f..3b7d0dc3 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -1,6 +1,10 @@ name: build -on: [push] +on: + push: + pull_request: + schedule: + - cron: '0 0 1 * *' jobs: build-linux-ubuntu: @@ -14,40 +18,47 @@ jobs: run: | echo "target_triplet=`gcc -dumpmachine`" >> $GITHUB_ENV - name: fetch libirecovery - uses: dawidd6/action-download-artifact@v2 + uses: dawidd6/action-download-artifact@v6 with: github_token: ${{secrets.GITHUB_TOKEN}} workflow: build.yml name: libirecovery-latest_${{env.target_triplet}} repo: libimobiledevice/libirecovery - name: fetch libplist - uses: dawidd6/action-download-artifact@v2 + uses: dawidd6/action-download-artifact@v6 with: github_token: ${{secrets.GITHUB_TOKEN}} workflow: build.yml name: libplist-latest_${{env.target_triplet}} repo: libimobiledevice/libplist - name: fetch libusbmuxd - uses: dawidd6/action-download-artifact@v2 + uses: dawidd6/action-download-artifact@v6 with: github_token: ${{secrets.GITHUB_TOKEN}} workflow: build.yml name: libusbmuxd-latest_${{env.target_triplet}} repo: libimobiledevice/libusbmuxd - name: fetch libimobiledevice-glue - uses: dawidd6/action-download-artifact@v2 + uses: dawidd6/action-download-artifact@v6 with: github_token: ${{secrets.GITHUB_TOKEN}} workflow: build.yml name: libimobiledevice-glue-latest_${{env.target_triplet}} repo: libimobiledevice/libimobiledevice-glue - name: fetch libimobiledevice - uses: dawidd6/action-download-artifact@v2 + uses: dawidd6/action-download-artifact@v6 with: github_token: ${{secrets.GITHUB_TOKEN}} workflow: build.yml name: libimobiledevice-latest_${{env.target_triplet}} repo: libimobiledevice/libimobiledevice + - name: fetch libtatsu + uses: dawidd6/action-download-artifact@v6 + with: + github_token: ${{secrets.GITHUB_TOKEN}} + workflow: build.yml + name: libtatsu-latest_${{env.target_triplet}} + repo: libimobiledevice/libtatsu - name: install external dependencies run: | mkdir extract @@ -57,7 +68,7 @@ jobs: rm -rf extract/lib sudo cp -r extract/* / sudo ldconfig - - uses: actions/checkout@v2 + - uses: actions/checkout@v4 with: fetch-depth: 0 - name: autogen @@ -72,7 +83,7 @@ jobs: DESTDIR=`pwd`/dest make install tar -C dest -cf idevicerestore.tar usr - name: publish artifact - uses: actions/upload-artifact@v2 + uses: actions/upload-artifact@v4 with: name: idevicerestore-latest_${{env.target_triplet}} path: idevicerestore.tar @@ -88,40 +99,47 @@ jobs: fi shell: bash - name: fetch libirecovery - uses: dawidd6/action-download-artifact@v2 + uses: dawidd6/action-download-artifact@v6 with: github_token: ${{secrets.GITHUB_TOKEN}} workflow: build.yml name: libirecovery-latest_macOS repo: libimobiledevice/libirecovery - name: fetch libplist - uses: dawidd6/action-download-artifact@v2 + uses: dawidd6/action-download-artifact@v6 with: github_token: ${{secrets.GITHUB_TOKEN}} workflow: build.yml name: libplist-latest_macOS repo: libimobiledevice/libplist - name: fetch libusbmuxd - uses: dawidd6/action-download-artifact@v2 + uses: dawidd6/action-download-artifact@v6 with: github_token: ${{secrets.GITHUB_TOKEN}} workflow: build.yml name: libusbmuxd-latest_macOS repo: libimobiledevice/libusbmuxd - name: fetch libimobiledevice-glue - uses: dawidd6/action-download-artifact@v2 + uses: dawidd6/action-download-artifact@v6 with: github_token: ${{secrets.GITHUB_TOKEN}} workflow: build.yml name: libimobiledevice-glue-latest_macOS repo: libimobiledevice/libimobiledevice-glue - name: fetch libimobiledevice - uses: dawidd6/action-download-artifact@v2 + uses: dawidd6/action-download-artifact@v6 with: github_token: ${{secrets.GITHUB_TOKEN}} workflow: build.yml name: libimobiledevice-latest_macOS repo: libimobiledevice/libimobiledevice + - name: fetch libtatsu + uses: dawidd6/action-download-artifact@v6 + with: + github_token: ${{secrets.GITHUB_TOKEN}} + workflow: build.yml + name: libtatsu-latest_macOS + repo: libimobiledevice/libtatsu - name: install external dependencies run: | mkdir extract @@ -129,7 +147,7 @@ jobs: tar -C extract -xvf $I done sudo cp -r extract/* / - - uses: actions/checkout@v2 + - uses: actions/checkout@v4 - name: install additional requirements run: | SDKDIR=`xcrun --sdk macosx --show-sdk-path 2>/dev/null` @@ -144,30 +162,18 @@ jobs: export CFLAGS="$USEARCHS -isysroot $SDKDIR" echo "Using CFLAGS: $CFLAGS" echo "BUILD_CFLAGS=$CFLAGS" >> $GITHUB_ENV - mkdir -p lib - curl -o lib/libcrypto.35.tbd -Ls \ - https://gist.github.com/nikias/94c99fd145a75a5104415e5117b0cafa/raw/5209dfbff5a871a14272afe4794e76eb4cf6f062/libcrypto.35.tbd - curl -o lib/libssl.35.tbd -Ls \ - https://gist.github.com/nikias/94c99fd145a75a5104415e5117b0cafa/raw/5209dfbff5a871a14272afe4794e76eb4cf6f062/libssl.35.tbd - LIBRESSL_VER=2.2.7 - FILENAME="libressl-$LIBRESSL_VER.tar.gz" - curl -o $FILENAME -Ls "https://ftp.openbsd.org/pub/OpenBSD/LibreSSL/$FILENAME" mkdir -p deps - tar -C deps -xzf $FILENAME - echo "LIBRESSL_CFLAGS=-I`pwd`/deps/libressl-$LIBRESSL_VER/include" >> $GITHUB_ENV - echo "LIBRESSL_LIBS=-Xlinker `pwd`/lib/libssl.35.tbd -Xlinker `pwd`/lib/libcrypto.35.tbd" >> $GITHUB_ENV FILENAME="libzip-static.tar.bz2" curl -o $FILENAME.b64 -Ls "https://gist.github.com/nikias/3da15d03120382f87b44029cd8495a02/raw/99cd8138fed99e8f6530b6f179f787342c698e1f/libzip-1.7.1_static_macOS.tar.bz2" base64 -D < $FILENAME.b64 > $FILENAME tar -C deps -xjf $FILENAME echo "LIBZIP_CFLAGS=-I`pwd`/deps/include" >> $GITHUB_ENV - echo "LIBZIP_LIBS=`pwd`/deps/lib/libzip.a -Xlinker /usr/lib/libbz2.dylib -Xlinker /usr/lib/liblzma.dylib -lz" >> $GITHUB_ENV + echo "LIBZIP_LIBS=`pwd`/deps/lib/libzip.a -Xlinker ${SDKDIR}/usr/lib/libbz2.tbd -Xlinker ${SDKDIR}/usr/lib/liblzma.tbd -lz" >> $GITHUB_ENV - name: autogen run: | export CFLAGS="${{env.BUILD_CFLAGS}} -Wno-nullability-completeness -Wno-expansion-to-defined" echo "Using CFLAGS: $CFLAGS" ./autogen.sh PKG_CONFIG_PATH=/usr/local/lib/pkgconfig \ - openssl_CFLAGS="$LIBRESSL_CFLAGS" openssl_LIBS="$LIBRESSL_LIBS" \ libcurl_CFLAGS="-I${{env.SDKDIR}}/usr/include" libcurl_LIBS="-lcurl" \ libzip_CFLAGS="$LIBZIP_CFLAGS" libzip_LIBS="$LIBZIP_LIBS" \ zlib_CFLAGS="-I${{env.SDKDIR}}/usr/include" zlib_LIBS="-lz" \ @@ -182,7 +188,7 @@ jobs: DESTDIR=`pwd`/dest make install tar -C dest -cf idevicerestore.tar usr - name: publish artifact - uses: actions/upload-artifact@v2 + uses: actions/upload-artifact@v4 with: name: idevicerestore-latest_macOS path: idevicerestore.tar @@ -219,40 +225,47 @@ jobs: echo "dest=$dest" >> $GITHUB_ENV echo "target_triplet=`gcc -dumpmachine`" >> $GITHUB_ENV - name: fetch libirecovery - uses: dawidd6/action-download-artifact@v2 + uses: dawidd6/action-download-artifact@v6 with: github_token: ${{secrets.GITHUB_TOKEN}} workflow: build.yml name: libirecovery-latest_${{ matrix.arch }}-${{ env.dest }} repo: libimobiledevice/libirecovery - name: fetch libplist - uses: dawidd6/action-download-artifact@v2 + uses: dawidd6/action-download-artifact@v6 with: github_token: ${{secrets.GITHUB_TOKEN}} workflow: build.yml name: libplist-latest_${{ matrix.arch }}-${{ env.dest }} repo: libimobiledevice/libplist - name: fetch libusbmuxd - uses: dawidd6/action-download-artifact@v2 + uses: dawidd6/action-download-artifact@v6 with: github_token: ${{secrets.GITHUB_TOKEN}} workflow: build.yml name: libusbmuxd-latest_${{ matrix.arch }}-${{ env.dest }} repo: libimobiledevice/libusbmuxd - name: fetch libimobiledevice-glue - uses: dawidd6/action-download-artifact@v2 + uses: dawidd6/action-download-artifact@v6 with: github_token: ${{secrets.GITHUB_TOKEN}} workflow: build.yml name: libimobiledevice-glue-latest_${{ matrix.arch }}-${{ env.dest }} repo: libimobiledevice/libimobiledevice-glue - name: fetch libimobiledevice - uses: dawidd6/action-download-artifact@v2 + uses: dawidd6/action-download-artifact@v6 with: github_token: ${{secrets.GITHUB_TOKEN}} workflow: build.yml name: libimobiledevice-latest_${{ matrix.arch }}-${{ env.dest }} repo: libimobiledevice/libimobiledevice + - name: fetch libtatsu + uses: dawidd6/action-download-artifact@v6 + with: + github_token: ${{secrets.GITHUB_TOKEN}} + workflow: build.yml + name: libtatsu-latest_${{ matrix.arch }}-${{ env.dest }} + repo: libimobiledevice/libtatsu - name: install external dependencies run: | mkdir extract @@ -260,7 +273,7 @@ jobs: tar -C extract -xvf $I done cp -r extract/* / - - uses: actions/checkout@v2 + - uses: actions/checkout@v4 - name: install additional requirements run: | FILENAME="libzip-1.7.1-static.tar.bz2" @@ -270,8 +283,17 @@ jobs: tar -C deps -xjf $FILENAME echo "LIBZIP_CFLAGS=-I`pwd`/deps/include" >> $GITHUB_ENV echo "LIBZIP_LIBS=`pwd`/deps/lib/libzip.a /${{env.dest}}/lib/libbz2.a /${{env.dest}}/lib/liblzma.a " >> $GITHUB_ENV + FILENAME="libcurl-8.1.0-static.tar.bz2" + curl -o $FILENAME.b64 -Ls "https://gist.github.com/nikias/6c397a0a2f4f4eafd91b81cccd22b761/raw/85216e60af6787f3b351291165eb91bd585ff09a/libcurl-8.1.0-static-${{matrix.arch}}-${{env.dest}}.tar.bz2" + base64 -d < $FILENAME.b64 > $FILENAME + tar -C deps -xjf $FILENAME + echo "LIBCURL_CFLAGS=-I`pwd`/deps/include -DCURL_STATICLIB" >> $GITHUB_ENV + echo "LIBCURL_LIBS=`pwd`/deps/lib/libcurl.a /${{env.dest}}/lib/libzstd.a -lws2_32 -lcrypt32 -lwldap32 -lbcrypt -lssl -lcrypto" >> $GITHUB_ENV - name: autogen - run: ./autogen.sh CC=gcc CXX=g++ --without-openssl libzip_CFLAGS="${{env.LIBZIP_CFLAGS}}" libzip_LIBS="${{env.LIBZIP_LIBS}}" zlib_LIBS="/${{env.dest}}/lib/libz.a" + run: | + ./autogen.sh CC=gcc CXX=g++ \ + libzip_VERSION=1.7.1 libzip_CFLAGS="${{env.LIBZIP_CFLAGS}}" libzip_LIBS="${{env.LIBZIP_LIBS}}" \ + zlib_LIBS="/${{env.dest}}/lib/libz.a" libcurl_CFLAGS="${{env.LIBCURL_CFLAGS}}" libcurl_LIBS="$LIBCURL_LIBS" - name: make run: make - name: make install @@ -282,7 +304,7 @@ jobs: DESTDIR=`pwd`/dest make install tar -C dest -cf idevicerestore.tar ${{ env.dest }} - name: publish artifact - uses: actions/upload-artifact@v2 + uses: actions/upload-artifact@v4 with: name: idevicerestore-latest_${{ matrix.arch }}-${{ env.dest }} path: idevicerestore.tar diff --git a/.github/workflows/curl.yml b/.github/workflows/curl.yml new file mode 100644 index 00000000..a1a80167 --- /dev/null +++ b/.github/workflows/curl.yml @@ -0,0 +1,60 @@ +name: build-libcurl-static + +on: workflow_dispatch + +jobs: + build-windows: + runs-on: windows-2019 + defaults: + run: + shell: msys2 {0} + strategy: + fail-fast: false + matrix: + include: [ + { msystem: UCRT64, arch: x86_64, flavor: w64-ucrt }, + { msystem: MINGW64, arch: x86_64, flavor: w64 }, + { msystem: MINGW32, arch: i686, flavor: w64 } + ] + steps: + - uses: msys2/setup-msys2@v2 + with: + msystem: ${{ matrix.msystem }} + release: false + update: false + install: >- + base-devel + git + mingw-${{ matrix.flavor }}-${{ matrix.arch }}-gcc + make + libtool + autoconf + automake-wrapper + liblzma + mingw-${{ matrix.flavor }}-${{ matrix.arch }}-openssl + - name: prepare environment + run: | + dest=`echo ${{ matrix.msystem }} |tr [:upper:] [:lower:]` + echo "dest=$dest" >> $GITHUB_ENV + echo "target_triplet=`gcc -dumpmachine`" >> $GITHUB_ENV + - name: fetch curl source + run: | + curl -Ls -o curl-8.10.1.tar.bz2 https://github.com/curl/curl/releases/download/curl-8_10_1/curl-8.10.1.tar.bz2 + tar xjf curl-8.10.1.tar.bz2 + - name: configure curl + run: | + cd curl-8.10.1 + ./configure --disable-ldap --disable-ldaps --disable-rtsp --disable-dict --disable-telnet --disable-tftp --disable-pop3 --disable-imap --disable-smb --disable-smtp --disable-gopher --disable-mqtt --disable-manual --disable-threaded-resolver --disable-pthreads --disable-sspi --disable-aws --disable-ntlm --disable-ntlm-wb --disable-tls-srp --disable-unix-sockets --disable-doh --disable-mime --disable-dnsshuffle --disable-alt-svc --disable-hsts --disable-websockets --with-openssl --without-brotli --without-libidn2 --without-ngtcp2 --without-quiche --without-msh3 --without-nghttp2 --without-libpsl + - name: build libcurl + run: | + CURDIR=`pwd` + cd curl-8.10.1/lib + make + cp .libs/libcurl.a . + cd .. + tar cf $CURDIR/libcurl-static.tar lib/libcurl.a include/curl/*.h + - name: publish artifact + uses: actions/upload-artifact@v4 + with: + name: libcurl_${{ matrix.arch }}-${{ env.dest }} + path: libcurl-static.tar diff --git a/.gitignore b/.gitignore index a826b421..bdfaa099 100644 --- a/.gitignore +++ b/.gitignore @@ -35,3 +35,4 @@ py-compile stamp-h1 src/.libs src/idevicerestore +src/idevicerestore.exe diff --git a/Makefile.am b/Makefile.am index 435cb17b..efaae8e2 100644 --- a/Makefile.am +++ b/Makefile.am @@ -4,4 +4,9 @@ SUBDIRS = src docs EXTRA_DIST = \ docs \ - README.md + README.md \ + git-version-gen + +dist-hook: + @if ! git diff --quiet; then echo "Uncommitted changes present; not releasing"; exit 1; fi + echo $(VERSION) > $(distdir)/.tarball-version diff --git a/README.md b/README.md index 3b525678..c7d22d6c 100644 --- a/README.md +++ b/README.md @@ -4,6 +4,21 @@ ![](https://github.com/libimobiledevice/idevicerestore/actions/workflows/build.yml/badge.svg) +## Table of Contents +- [Features](#features) +- [Building](#building) + - [Prerequisites](#prerequisites) + - [Linux (Debian/Ubuntu based)](#linux-debianubuntu-based) + - [macOS](#macos) + - [Windows](#windows) + - [Configuring the source tree](#configuring-the-source-tree) + - [Building and installation](#building-and-installation) +- [Usage](#usage) +- [Contributing](#contributing) +- [Links](#links) +- [License](#license) +- [Credits](#credits) + ## Features The idevicerestore application is a full reimplementation of all granular steps @@ -33,56 +48,179 @@ Use with caution and make sure to backup your data before trying to restore. **In any case, usage is at your own risk.** -## Installation / Getting started - -### Debian / Ubuntu Linux +## Building + +### Prerequisites + +You need to have a working compiler (gcc/clang) and development environent +available. This project uses autotools for the build process, allowing to +have common build steps across different platforms. +Only the prerequisites differ and they are described in this section. + +#### Linux (Debian/Ubuntu based) + +* Install all required dependencies and build tools: + ```shell + sudo apt-get install \ + build-essential \ + pkg-config \ + checkinstall \ + git \ + autoconf \ + automake \ + libtool-bin \ + libreadline-dev \ + libusb-1.0-0-dev \ + libplist-dev \ + libimobiledevice-dev \ + libimobiledevice-glue-dev \ + libtatsu-dev \ + libcurl4-openssl-dev \ + libssl-dev \ + libzip-dev \ + zlib1g-dev + ``` + NOTE: [libtatsu](https://github.com/libimobiledevice/libtatsu) (and thus `libtatsu-dev`) + is a new library that was just published recently, you have to + [build it from source](https://github.com/libimobiledevice/libtatsu?tab=readme-ov-file#building). + Also, other `*-dev` packages might not be available for your distribution, + so you will have to build these packages on your own as well. + +#### macOS + +* Make sure the Xcode command line tools are installed. + + **Option 1**: + The easiest way to build and install `idevicerestore` for macOS is using + the following build script which will do the work for you, it will build + and install all required dependencies: + ```bash + mkdir -p limd-build + cd limd-build + curl -o ./limd-build-macos.sh -L https://is.gd/limdmacos + bash ./limd-build-macos.sh + ``` + Follow the prompts of the script and you should have a working `idevicerestore` + available. + + **Option 2**: + Use either [MacPorts](https://www.macports.org/) + or [Homebrew](https://brew.sh/) to install `automake`, `autoconf`, and `libtool`. + + Using MacPorts: + ```shell + sudo port install libtool autoconf automake + ``` + + Using Homebrew: + ```shell + brew install libtool autoconf automake + ``` + + `idevicerestore` has a few dependencies from the libimobiledevice project. + You will have to build and install the following: + * [libplist](https://github.com/libimobiledevice/libplist) + * [libimobiledevice-glue](https://github.com/libimobiledevice/libimobiledevice-glue) + * [libusbmuxd](https://github.com/libimobiledevice/libusbmuxd) + * [libimobiledevice](https://github.com/libimobiledevice/libimobiledevice) + * [libirecovery](https://github.com/libimobiledevice/libirecovery) + * [libtatsu](https://github.com/libimobiledevice/libtatsu) + + Check their `README.md` for building and installation instructions. + +#### Windows + +* Using [MSYS2](https://www.msys2.org/) is the official way of compiling this project on Windows. Download the MSYS2 installer + and follow the installation steps. + + It is recommended to use the _MSYS2 MinGW 64-bit_ shell. Run it and make sure the required dependencies are installed: + + ```shell + pacman -S base-devel \ + git \ + mingw-w64-x86_64-gcc \ + make \ + libtool \ + autoconf \ + automake-wrapper \ + pkg-config \ + libcurl-devel \ + mingw-w64-x86_64-libzip + ``` + NOTE: You can use a different shell and different compiler according to your needs. Adapt the above command accordingly. + + `idevicerestore` has a few dependencies from the libimobiledevice project. + You will have to build and install the following: + * [libplist](https://github.com/libimobiledevice/libplist) + * [libimobiledevice-glue](https://github.com/libimobiledevice/libimobiledevice-glue) + * [libusbmuxd](https://github.com/libimobiledevice/libusbmuxd) + * [libimobiledevice](https://github.com/libimobiledevice/libimobiledevice) + * [libirecovery](https://github.com/libimobiledevice/libirecovery) + * [libtatsu](https://github.com/libimobiledevice/libtatsu) + + Check their `README.md` for building and installation instructions. + + +### Configuring the source tree + +You can build the source code from a git checkout, or from a `.tar.bz2` release tarball from [Releases](https://github.com/libimobiledevice/idevicerestore/releases). +Before we can build it, the source tree has to be configured for building. The steps depend on where you got the source from. + +* **From git** + + If you haven't done already, clone the actual project repository and change into the directory. + ```shell + git clone https://github.com/libimobiledevice/idevicerestore.git + cd idevicerestore + ``` + + Configure the source tree for building: + ```shell + ./autogen.sh + ``` + +* **From release tarball (.tar.bz2)** + + When using an official [release tarball](https://github.com/libimobiledevice/idevicerestore/releases) (`idevicerestore-x.y.z.tar.bz2`) + the procedure is slightly different. + + Extract the tarball: + ```shell + tar xjf idevicerestore-x.y.z.tar.bz2 + cd idevicerestore-x.y.z + ``` + + Configure the source tree for building: + ```shell + ./configure + ``` + +Both `./configure` and `./autogen.sh` (which generates and calls `configure`) accept a few options, for example `--prefix` to allow +building for a different target folder. You can simply pass them like this: -First install all required dependencies and build tools: ```shell -sudo apt-get install \ - build-essential \ - pkg-config \ - checkinstall \ - git \ - autoconf \ - automake \ - libtool-bin \ - libreadline-dev \ - libusb-1.0-0-dev \ - libplist-dev \ - libimobiledevice-dev \ - libimobiledevice-glue-dev \ - libcurl4-openssl-dev \ - libssl-dev \ - libzip-dev \ - zlib1g-dev +./autogen.sh --prefix=/usr/local ``` - -Then clone, build and install [libirecovery](https://github.com/libimobiledevice/libirecovery.git) which is not yet packaged: +or ```shell -git clone https://github.com/libimobiledevice/libirecovery.git -cd libirecovery -./autogen.sh -make -sudo make install -cd .. +./configure --prefix=/usr/local ``` -If the configure processes indicates old or missing libraries, your distribution -might not have yet packaged the latest versions. In that case you will have to -clone [these libraries](https://github.com/libimobiledevice/) separately and repeat the process in order to proceed. - -Continue with cloning the actual project repository: -```shell -git clone https://github.com/libimobiledevice/idevicerestore.git -cd idevicerestore +Once the command is successful, the last few lines of output will look like this: ``` +[...] +config.status: creating config.h +config.status: config.h is unchanged +config.status: executing depfiles commands +config.status: executing libtool commands -Now you can build and install it: -```shell -./autogen.sh -make -sudo make install +Configuration for idevicerestore 1.1.0: +------------------------------------------- + + Install prefix: .........: /usr/local + + Now type 'make' to build idevicerestore 1.1.0, + and then 'make install' for installation. ``` **Important** @@ -142,13 +280,11 @@ Please make sure your contribution adheres to: * Try to split larger changes into individual commits of a common domain * Use your real name and a valid email address for your commits -We are still working on the guidelines so bear with us! - ## Links * Homepage: https://libimobiledevice.org/ -* Repository: https://git.libimobiledevice.org/idevicerestore.git -* Repository (Mirror): https://github.com/libimobiledevice/idevicerestore.git +* Repository: https://github.com/libimobiledevice/idevicerestore.git +* Repository (Mirror): https://git.libimobiledevice.org/idevicerestore.git * Issue Tracker: https://github.com/libimobiledevice/idevicerestore/issues * Mailing List: https://lists.libimobiledevice.org/mailman/listinfo/libimobiledevice-devel * Twitter: https://twitter.com/libimobiledev @@ -166,4 +302,4 @@ iPadOS, tvOS, watchOS, and macOS are trademarks of Apple Inc. This project is an independent software application and has not been authorized, sponsored, or otherwise approved by Apple Inc. -README Updated on: 2022-04-04 +README Updated on: 2024-10-22 diff --git a/configure.ac b/configure.ac index 38152756..9c27a7be 100644 --- a/configure.ac +++ b/configure.ac @@ -15,21 +15,23 @@ if test -z $PACKAGE_VERSION; then fi # Minimum package versions -LIBIRECOVERY_VERSION=1.0.1 +LIBIRECOVERY_VERSION=1.2.1 LIBIMOBILEDEVICE_VERSION=1.3.0 -LIBPLIST_VERSION=2.2.0 -LIMD_GLUE_VERSION=1.0.0 +LIBUSBMUXD_VERSION=2.0.2 +LIBPLIST_VERSION=2.6.0 +LIMD_GLUE_VERSION=1.3.0 +LIBTATSU_VERSION=1.0.4 LIBZIP_VERSION=1.0 LIBCURL_VERSION=7.0 -OPENSSL_VERSION=0.9.8 AC_SUBST(LIBIRECOVERY_VERSION) AC_SUBST(LIBIMOBILEDEVICE_VERSION) +AC_SUBST(LIBUSBMUXD_VERSION) AC_SUBST(LIBPLIST_VERSION) AC_SUBST(LIMD_GLUE_VERSION) +AC_SUBST(LIBTATSU_VERSION) AC_SUBST(LIBZIP_VERSION) AC_SUBST(LIBCURL_VERSION) -AC_SUBST(OPENSSL_VERSION) # Checks for programs. AC_PROG_CC @@ -39,15 +41,14 @@ LT_INIT # Checks for libraries. PKG_CHECK_MODULES(libirecovery, libirecovery-1.0 >= $LIBIRECOVERY_VERSION) PKG_CHECK_MODULES(libimobiledevice, libimobiledevice-1.0 >= $LIBIMOBILEDEVICE_VERSION) +PKG_CHECK_MODULES(libusbmuxd, libusbmuxd-2.0 >= $LIBUSBMUXD_VERSION) PKG_CHECK_MODULES(libplist, libplist-2.0 >= $LIBPLIST_VERSION) PKG_CHECK_MODULES(limd_glue, libimobiledevice-glue-1.0 >= $LIMD_GLUE_VERSION) +PKG_CHECK_MODULES(libtatsu, libtatsu-1.0 >= $LIBTATSU_VERSION) PKG_CHECK_MODULES(libzip, libzip >= $LIBZIP_VERSION) PKG_CHECK_MODULES(libcurl, libcurl >= $LIBCURL_VERSION) PKG_CHECK_MODULES(zlib, zlib) -# optional -PKG_CHECK_MODULES(openssl, openssl >= $OPENSSL_VERSION, have_openssl=yes, have_openssl=no) - AC_CHECK_FUNCS([strsep strcspn mkstemp realpath]) if test x$ac_cv_func_strsep != xyes; then if test x$ac_cv_func_strcspn != xyes; then @@ -55,6 +56,7 @@ if test x$ac_cv_func_strsep != xyes; then fi fi +AC_SEARCH_LIBS([ceil], [m]) AC_CHECK_HEADER(endian.h, [ac_cv_have_endian_h="yes"], [ac_cv_have_endian_h="no"]) if test "x$ac_cv_have_endian_h" = "xno"; then @@ -150,32 +152,6 @@ fi CFLAGS="$CACHED_CFLAGS" -AC_ARG_WITH([openssl], - [AS_HELP_STRING([--without-openssl], - [Do not use OpenSSL])], - [use_openssl=$withval], - [use_openssl=$have_openssl]) - -if test "x$use_openssl" == "xyes"; then - if test "x$have_openssl" != "xyes"; then - echo "*** NOTE: --with-openssl passed but OpenSSL is not available ***" - use_openssl=no - fi -fi -if test "x$use_openssl" != "xyes"; then - echo "*** NOTE: Using internal SHA1 implementation ***" - have_openssl=no - openssl_CFLAGS= - openssl_LIBS= -fi -if test "x$have_openssl" == "xyes"; then - AC_DEFINE(HAVE_OPENSSL, [1], [Define if you have OpenSSL]) -fi -AC_SUBST(openssl_CFLAGS) -AC_SUBST(openssl_LIBS) - -AM_CONDITIONAL(USE_INTERNAL_SHA, test x$use_openssl != xyes) - AC_SUBST(GLOBAL_CFLAGS) AC_SUBST(AC_LDFLAGS) AC_SUBST(AC_LDADD) diff --git a/docs/idevicerestore.1 b/docs/idevicerestore.1 index 8e0a8845..d1415482 100644 --- a/docs/idevicerestore.1 +++ b/docs/idevicerestore.1 @@ -1,66 +1,111 @@ .TH "idevicerestore" 1 .SH NAME -idevicerestore \- Restore IPSW firmware FILE to an iOS device +idevicerestore \- Restore IPSW firmware at PATH to an iOS device .SH SYNOPSIS .B idevicerestore -[OPTIONS] FILE +[OPTIONS] PATH .SH DESCRIPTION Restore firmware files to iOS devices while either erasing the device or updating to preserve content and settings. +\f[B]PATH\f[] can be a compressed .ipsw file or a directory containing all files extracted from an IPSW. + .SH OPTIONS .TP .B \-i, \-\-ecid ECID -target specific device by its hexadecimal ECID e.g. 0xaabb123456 or 00000012AABBCCDD. +Target specific device by its ECID, e.g. 0xaabb123456 (hex) or 1234567890 (decimal). .TP .B \-u, \-\-udid UDID -target specific device by its 40-digit device UDID. -NOTE: only works with devices in normal mode. +Target specific device by its device UDID. + +\f[B]NOTE\f[]: only works with devices in normal mode. .TP +.B \-l, \-\-latest +Use latest available firmware (with download on demand). +Before performing any action it will interactively ask +to select one of the currently signed firmware versions, +unless \f[B]\-y\f[] has been given too. + +The PATH argument is ignored when using this option. + +\f[B]DO NOT USE\f[] if you need to preserve the baseband/unlock! + +\f[B]USE WITH CARE\f[] if you want to keep a jailbreakable firmware! +.TP .B \-e, \-\-erase -perform a full restore, erasing all data (defaults to update). +Perform full restore instead of update, erasing all data + +\f[B]DO NOT USE\f[] if you want to preserve user data on the device! +.TP +.B \-y, \-\-no\-input +Non-interactive mode, do not ask for any input. + +\f[B]WARNING\f[]: This will disable certain checks/prompts that are supposed +to prevent DATA LOSS. \f[B]Use with caution\f[]. +.TP +.B \-n, \-\-no\-action +Do not perform any restore action. If combined with \f[B]\-l\f[] option +the on-demand ipsw download is performed before exiting. +.TP +.B \-\-ipsw\-info +Print information about the IPSW at PATH and exit. +.TP +.B \-h, \-\-help +Prints usage information. +.TP +.B \-C, \-\-cache\-path DIR +Use specified directory for caching extracted or other reused files. +.TP +.B \-d, \-\-debug +Enable communication debugging. +.TP +.B \-v, \-\-version +Prints version information. + +.SH ADVANCED/EXPERIMENTAL OPTIONS .TP .B \-c, \-\-custom -restore with a custom firmware. -.TP -.B \-l, \-\-latest -use latest available firmware (with download on demand). \ -DO NOT USE if you need to preserve the baseband (unlock)! \ -USE WITH CARE if you want to keep a jailbreakable firmware! \ -The FILE argument is ignored when using this option. +Restore with a custom firmware (requires bootrom exploit) .TP -.B \-s, \-\-cydia -use Cydia's signature service instead of Apple's. +.B \-s, \-\-server URL +Override the default signing server request URL. If the URL doesn't contain +a path component, the default path \f[B]/TSS/controller?action=2\f[] will be added. .TP .B \-x, \-\-exclude -exclude nor/baseband upgrade. +Exclude nor/baseband upgrade. + +\f[B]NOTE\f[]: This option only works with legacy devices and/or custom firmware. .TP .B \-t, \-\-shsh -fetch TSS record and save to .shsh file, then exit. +Fetch TSS record and save to .shsh file, then exit. .TP -.B \-p, \-\-pwn -put device in pwned DFU mode and exit (limera1n devices only). +.B \-z, \-\-no\-restore +Do not restore and end after booting to the ramdisk. .TP -.B \-n, \-\-no\-action -do not perform any restore action. If combined with -l option the on demand -IPSW download is performed before exiting. +.B \-k, \-\-keep\-pers +Write personalized components to files for debugging. .TP -.B \-\-ipsw\-info -Print information about the IPSW at PATH and exit. +.B \-p, \-\-pwn +Put device in pwned DFU mode and exit (limera1n devices only). .TP -.B \-C, \-\-cache\-path DIR -use specified directory for caching extracted or other reused files. +.B \-P, --plain-progress +Print progress as plain step and progress .TP -.B \-d, \-\-debug -enable communication debugging. +.B \-R, \-\-restore\-mode +Allow restoring from Restore mode .TP -.B \-h, \-\-help -prints usage information. +.B \-T, \-\-ticket PATH +Use file at PATH to send as AP ticket .TP -.B \-v, \-\-version -prints version information. +.B \-\-variant VARIANT +Use given VARIANT to match the build identity to use, e.g. 'Customer Erase Install (IPSW)' +.TP +.B \-\-ignore\-errors +Try to continue the restore process after certain errors (like a failed baseband update). + +\f[B]WARNING\f[]: This might render the device unable to boot or only partially functioning. \f[B]Use with caution\f[]. .SH AUTHORS Martin Szulecki diff --git a/git-version-gen b/git-version-gen index 3eb6a42e..d8689526 100755 --- a/git-version-gen +++ b/git-version-gen @@ -3,7 +3,7 @@ SRCDIR=`dirname $0` if test -n "$1"; then VER=$1 else - if test -d "${SRCDIR}/.git" && test -x "`which git`" ; then + if test -r "${SRCDIR}/.git" && test -x "`which git`" ; then git update-index -q --refresh if ! VER=`git describe --tags --dirty 2>/dev/null`; then COMMIT=`git rev-parse --short HEAD` @@ -16,4 +16,5 @@ else fi fi fi +VER=`printf %s "$VER" | head -n1` printf %s "$VER" diff --git a/src/Makefile.am b/src/Makefile.am index a89a9c66..80f02f20 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -3,22 +3,24 @@ AM_CFLAGS = \ $(LFS_CFLAGS) \ $(libirecovery_CFLAGS) \ $(libimobiledevice_CFLAGS) \ + $(libusbmuxd_CFLAGS) \ $(libplist_CFLAGS) \ $(limd_glue_CFLAGS) \ + $(libtatsu_CFLAGS) \ $(libzip_CFLAGS) \ $(zlib_CFLAGS) \ - $(openssl_CFLAGS) \ $(libcurl_CFLAGS) AM_LDFLAGS = \ $(AC_LDFLAGS) \ $(libirecovery_LIBS) \ $(libimobiledevice_LIBS) \ + $(libusbmuxd_LIBS) \ $(libplist_LIBS) \ $(limd_glue_LIBS) \ + $(libtatsu_LIBS) \ $(libzip_LIBS) \ $(zlib_LIBS) \ - $(openssl_LIBS) \ $(libcurl_LIBS) AM_LDADD = $(AC_LDADD) @@ -29,7 +31,6 @@ idevicerestore_SOURCES = \ idevicerestore.c idevicerestore.h \ endianness.h \ common.c common.h \ - tss.c tss.h \ fls.c fls.h \ mbn.c mbn.h \ img3.c img3.h \ @@ -42,13 +43,11 @@ idevicerestore_SOURCES = \ restore.c restore.h \ asr.c asr.h \ fdr.c fdr.h \ + ace3.c ace3.h \ limera1n_payload.h \ limera1n.c limera1n.h \ download.c download.h \ locking.c locking.h -if USE_INTERNAL_SHA -idevicerestore_SOURCES += sha1.c sha1.h sha512.c sha512.h fixedint.h -endif idevicerestore_CFLAGS = $(AM_CFLAGS) idevicerestore_LDFLAGS = $(AM_LDFLAGS) idevicerestore_LDADD = $(AM_LDADD) diff --git a/src/ace3.c b/src/ace3.c new file mode 100644 index 00000000..a482f6f3 --- /dev/null +++ b/src/ace3.c @@ -0,0 +1,278 @@ +/* + * ace3.c + * Functions to handle Ace3/uarp firmware format + * + * Copyright (c) 2024 Nikias Bassen, All Rights Reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include +#include +#include +#include + +#include "common.h" +#include "ace3.h" +#include "endianness.h" + +static uint32_t crc_buffer(const unsigned char* buffer, unsigned int bufsize, unsigned int salt) +{ + uint32_t result; + unsigned int i; + unsigned int j; + + if ( !buffer ) + return 0xFFFFFFFF; + result = salt; + for (i = 0; i < bufsize; ++i) { + for (j = 0; j != 8; ++j) { + unsigned int tmp0 = 2 * result; + unsigned int tmp1 = *(unsigned char*)(buffer + i); + unsigned int tmp2 = ((unsigned int)result >> 31) ^ ((tmp1 >> j) & 1); + result = (tmp2 + 2 * result) ^ 0x4C11DB6; + if (!tmp2) + result = tmp0; + } + } + return result; +} + +static int uarp_version_convert(uint32_t* version_data, uint32_t* version_out) +{ + if (version_out) *version_out = 0; + if (!version_data) { + return -1; + } + uint32_t part1 = (version_data[0] < 0xE00) ? version_data[0] : version_data[0] - 0xE00; + if (part1 > 0x63) { + return 0; + } + uint32_t part2 = (version_data[0] < 0xE00) ? 0 : 0xE00; + uint32_t part3 = version_data[1]; + if (part3 > 0x3E7) { + return 0; + } + uint32_t part4 = version_data[2]; + if (part4 > 0x63) { + return 0; + } + if (version_out) { + *version_out = ((((0x147B * (unsigned int)((uint16_t)part3 >> 2)) >> 9) & 0x3FF00 | (0x10 * (((uint8_t)((uint16_t)part3 / 0xA) % 0xA) & 0xF)) | ((uint16_t)part3 % 0xA)) << 8) + | ((((uint8_t)part1 % 0xA) | (0x10 * ((uint8_t)part1 / 0xA)) | part2) << 20) + | ((uint8_t)part4 % 0xA) + | ((0xCD * (unsigned int)(uint8_t)part4) >> 7) & 0xF0; + } + return 0; +} + +int ace3_create_binary(const unsigned char* uarp_fw, size_t uarp_size, uint64_t bdid, unsigned int prev, plist_t tss, unsigned char** bin_out, size_t* bin_size) +{ + struct ace3bin_header { + uint32_t magic; // 0xACE00003 + uint32_t version; // ace3 version, e.g. 0x00203400 + uint32_t unk8; // 0x00002800 + uint32_t header_size; // 0x00000040 + uint32_t data1_size; + uint32_t data2_size; + uint32_t im4m_offset; + uint32_t im4m_dl_size; + uint32_t content_size; + uint32_t crc; + uint64_t fill1; // 0xFFFFFFFFFFFFFFFF + uint64_t fill2; // 0xFFFFFFFFFFFFFFFF + uint64_t fill3; // 0xFFFFFFFFFFFFFFFF + }; + + struct uarp_header { + uint32_t unk_00; // BE 0x00000002 + uint32_t header_size; // BE usually 0x0000002C + uint32_t plist_offset; // BE + uint32_t unk_0c; // 0 + uint32_t unk_10; // 0 + uint32_t unk_14; // 0 + uint32_t unk_18; // 0 + uint32_t c_offset; // BE + uint32_t unk_20; // 0 + uint32_t toc_offset; // BE usually 0x0000002c + uint32_t toc_size; // BE + }; + struct uarp_toc_entry { + uint32_t this_size; // BE usually 0x28 + uint32_t fourcc; // 'PT01' or similar + uint32_t version[4]; // BE values + uint32_t unk_18; // BE other offset, not sure + uint32_t unk_1c; // BE usually 0 + uint32_t offset; // BE + uint32_t size; // + }; + + plist_t p_im4m = plist_dict_get_item(tss, "USBPortController1,Ticket"); + uint64_t im4m_size = 0; + const char* im4m = plist_get_data_ptr(p_im4m, &im4m_size); + + struct uarp_header* uarp_hdr = (struct uarp_header*)uarp_fw; + uint32_t uarp_hdr_size = be32toh(uarp_hdr->header_size); + uint32_t plist_offset = be32toh(uarp_hdr->plist_offset); + uint32_t plist_size = uarp_size - plist_offset; + nskeyedarchive_t ka = nskeyedarchive_new_from_data(uarp_fw + plist_offset, plist_size); + if (!ka) { + return -1; + } + plist_t uarp_dict = nskeyedarchive_to_plist(ka); + nskeyedarchive_free(ka); + + // find the corresponding entries for given BoardID+PREV + + char* payload_4cc = NULL; + char* data_payload_4ccs = NULL; + + plist_t sb_payloads = plist_dict_get_item(uarp_dict, "SuperBinary Payloads"); + if (PLIST_IS_ARRAY(sb_payloads)) { + plist_array_iter iter = NULL; + plist_array_new_iter(sb_payloads, &iter); + plist_t payload = NULL; + do { + plist_array_next_item(sb_payloads, iter, &payload); + if (!payload) { + break; + } + plist_t meta = plist_dict_get_item(payload, "Payload MetaData"); + if (!PLIST_IS_DICT(meta)) { + continue; + } + plist_t prefix = plist_dict_get_item(meta, "Personalization Manifest Prefix"); + if (!PLIST_IS_STRING(prefix)) { + continue; + } + if (strcmp(plist_get_string_ptr(prefix, NULL), "USBPortController") != 0) { + continue; + } + plist_t p_boardid = plist_dict_get_item(meta, "Personalization Board ID (64 bits)"); + if (!PLIST_IS_INT(p_boardid)) { + continue; + } + uint64_t boardid = 0; + plist_get_uint_val(p_boardid, &boardid); + if (boardid == bdid) { + debug("DEBUG: %s: Found Board ID 0x%" PRIx64 "\n", __func__, bdid); + plist_t p4cc = plist_dict_get_item(payload, "Payload 4CC"); + plist_get_string_val(p4cc, &payload_4cc); + plist_t matching = plist_dict_get_item(meta, "Personalization Matching Data"); + if (PLIST_IS_ARRAY(matching)) { + plist_array_iter iter2 = NULL; + plist_array_new_iter(matching, &iter2); + plist_t match = NULL; + do { + plist_array_next_item(matching, iter2, &match); + if (!PLIST_IS_DICT(match)) { + break; + } + uint64_t minrev = 0; + plist_t p_min = plist_dict_get_item(match, "Personalization Matching Data Product Revision Minimum"); + plist_get_uint_val(p_min, &minrev); + uint64_t maxrev = 0; + plist_t p_max = plist_dict_get_item(match, "Personalization Matching Data Product Revision Maximum"); + plist_get_uint_val(p_max, &maxrev); + if (prev >= minrev && prev <= maxrev) { + plist_t tags = plist_dict_get_item(match, "Personalization Matching Data Payload Tags"); + plist_get_string_val(tags, &data_payload_4ccs); + debug("DEBUG: %s: Found matching tags %s\n", __func__, data_payload_4ccs); + break; + } + } while (match); + plist_mem_free(iter2); + } + break; + } + } while (payload); + plist_mem_free(iter); + } + if (!payload_4cc) { + printf("Failed to get payload 4cc\n"); + return -1; + } + if (!data_payload_4ccs) { + printf("Failed to get data payload 4ccs\n"); + return -1; + } + + // now find the blobs in UARP data + uint32_t dl_offset = 0; + uint32_t dl_size = 0; + uint32_t data1_offset = 0; + uint32_t data1_size = 0; + uint32_t data1_version = 0; + uint32_t data2_offset = 0; + uint32_t data2_size = 0; + uint32_t toc_offset = be32toh(uarp_hdr->toc_offset); + uint32_t toc_size = be32toh(uarp_hdr->toc_size); + const unsigned char* p = uarp_fw + uarp_hdr_size; + while (p < uarp_fw + toc_size) { + struct uarp_toc_entry* entry = (struct uarp_toc_entry*)p; + uint32_t te_size = be32toh(entry->this_size); + if (strncmp((char*)&(entry->fourcc), payload_4cc, 4) == 0) { + dl_offset = be32toh(entry->offset); + dl_size = be32toh(entry->size); + } else if (strncmp((char*)&(entry->fourcc), data_payload_4ccs, 4) == 0) { + uint32_t version_data[4]; + version_data[0] = be32toh(entry->version[0]); + version_data[1] = be32toh(entry->version[1]); + version_data[2] = be32toh(entry->version[2]); + version_data[3] = be32toh(entry->version[3]); + data1_offset = be32toh(entry->offset); + data1_size = be32toh(entry->size); + uarp_version_convert(version_data, &data1_version); + } else if (strncmp((char*)&(entry->fourcc), data_payload_4ccs+5, 4) == 0) { + data2_offset = be32toh(entry->offset); + data2_size = be32toh(entry->size); + } + p += te_size; + } + + uint32_t content_size = data1_size + data2_size + im4m_size + dl_size; + + *bin_out = (unsigned char*)malloc(0x40 + content_size); + struct ace3bin_header* hdr = (struct ace3bin_header*)(*bin_out); + hdr->magic = htole32(0xACE00003); + hdr->version = htole32(data1_version); + hdr->unk8 = htole32(0x00002800); + hdr->header_size = htole32(0x40); + hdr->data1_size = htole32(data1_size); + hdr->data2_size = htole32(data2_size);; + hdr->im4m_offset = htole32(0x40 + data1_size + data2_size); + hdr->im4m_dl_size = htole32(im4m_size + dl_size); + hdr->content_size = htole32(content_size); + hdr->crc = 0; + hdr->fill1 = 0xFFFFFFFFFFFFFFFFLL; + hdr->fill2 = 0xFFFFFFFFFFFFFFFFLL; + hdr->fill3 = 0xFFFFFFFFFFFFFFFFLL; + + // write data1 payload + memcpy(*bin_out + 0x40, uarp_fw + data1_offset, data1_size); + // write data2 payload + memcpy(*bin_out + 0x40 + data1_size, uarp_fw + data2_offset, data2_size); + // write IM4M + memcpy(*bin_out + 0x40 + data1_size + data2_size, im4m, im4m_size); + // write dl payload + memcpy(*bin_out + 0x40 + data1_size + data2_size + im4m_size, uarp_fw + dl_offset, dl_size); + + // calculate CRC and update header + hdr->crc = htole32(crc_buffer(*bin_out + 0x40, content_size, 0xFFFFFFFF)); + + *bin_size = 0x40 + content_size; + + return 0; +} diff --git a/src/ace3.h b/src/ace3.h new file mode 100644 index 00000000..2ef65a7c --- /dev/null +++ b/src/ace3.h @@ -0,0 +1,17 @@ +#ifndef IDEVICERESTORE_ACE3_H +#define IDEVICERESTORE_ACE3_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include +#include + +int ace3_create_binary(const unsigned char* uarp_fw, size_t uarp_size, uint64_t bdid, unsigned int prev, plist_t tss, unsigned char** bin_out, size_t* bin_size); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/src/asr.c b/src/asr.c index a1aba768..15396c4a 100644 --- a/src/asr.c +++ b/src/asr.c @@ -30,23 +30,16 @@ #include #include #include -#ifdef HAVE_OPENSSL -#include -#else -#include "sha1.h" -#define SHA_CTX SHA1_CTX -#define SHA1_Init SHA1Init -#define SHA1_Update SHA1Update -#define SHA1_Final SHA1Final -#endif + +#include #include "asr.h" #include "idevicerestore.h" #include "common.h" +#include "ipsw.h" #define ASR_VERSION 1 #define ASR_STREAM_ID 1 -#define ASR_PORT 12345 #define ASR_BUFFER_SIZE 65536 #define ASR_FEC_SLICE_STRIDE 40 #define ASR_PACKETS_PER_FEC 25 @@ -54,7 +47,7 @@ #define ASR_PAYLOAD_CHUNK_SIZE 131072 #define ASR_CHECKSUM_CHUNK_SIZE 131072 -int asr_open_with_timeout(idevice_t device, asr_client_t* asr) +int asr_open_with_timeout(idevice_t device, asr_client_t* asr, uint16_t port) { int i = 0; int attempts = 10; @@ -67,9 +60,13 @@ int asr_open_with_timeout(idevice_t device, asr_client_t* asr) return -1; } - debug("Connecting to ASR\n"); + if (port == 0) { + port = ASR_DEFAULT_PORT; + } + debug("Connecting to ASR on port %u\n", port); + for (i = 1; i <= attempts; i++) { - device_error = idevice_connect(device, ASR_PORT, &connection); + device_error = idevice_connect(device, port, &connection); if (device_error == IDEVICE_E_SUCCESS) { break; } @@ -205,37 +202,13 @@ void asr_free(asr_client_t asr) } } -int asr_perform_validation(asr_client_t asr, const char* filesystem) +int asr_send_validation_packet_info(asr_client_t asr, uint64_t ipsw_size) { - FILE* file = NULL; - uint64_t length = 0; - char* command = NULL; - plist_t node = NULL; - plist_t packet = NULL; - plist_t packet_info = NULL; - plist_t payload_info = NULL; - int attempts = 0; - - file = fopen(filesystem, "rb"); - if (file == NULL) { - return -1; - } - -#ifdef WIN32 - length = _lseeki64(fileno(file), 0, SEEK_END); - _lseeki64(fileno(file), 0, SEEK_SET); - rewind(file); -#else - fseeko(file, 0, SEEK_END); - length = ftello(file); - fseeko(file, 0, SEEK_SET); -#endif - - payload_info = plist_new_dict(); + plist_t payload_info = plist_new_dict(); plist_dict_set_item(payload_info, "Port", plist_new_uint(1)); - plist_dict_set_item(payload_info, "Size", plist_new_uint(length)); + plist_dict_set_item(payload_info, "Size", plist_new_uint(ipsw_size)); - packet_info = plist_new_dict(); + plist_t packet_info = plist_new_dict(); if (asr->checksum_chunks) { plist_dict_set_item(packet_info, "Checksum Chunk Size", plist_new_uint(ASR_CHECKSUM_CHUNK_SIZE)); } @@ -247,12 +220,30 @@ int asr_perform_validation(asr_client_t asr, const char* filesystem) plist_dict_set_item(packet_info, "Version", plist_new_uint(ASR_VERSION)); if (asr_send(asr, packet_info)) { - error("ERROR: Unable to sent packet information to ASR\n"); plist_free(packet_info); return -1; } plist_free(packet_info); + return 0; +} + +int asr_perform_validation(asr_client_t asr, ipsw_file_handle_t file) +{ + uint64_t length = 0; + char* command = NULL; + plist_t node = NULL; + plist_t packet = NULL; + int attempts = 0; + + length = ipsw_file_size(file); + + // Expected by device after every initiate + if (asr_send_validation_packet_info(asr, length) < 0) { + error("ERROR: Unable to send validation packet info to ASR\n"); + return -1; + } + while (1) { if (asr_receive(asr, &packet) < 0) { error("ERROR: Unable to receive validation packet\n"); @@ -277,6 +268,25 @@ int asr_perform_validation(asr_client_t asr, const char* filesystem) } plist_get_string_val(node, &command); + // Added for iBridgeOS 9.0 - second initiate request to change to checksum chunks + if (!strcmp(command, "Initiate")) { + // This might switch on the second Initiate + node = plist_dict_get_item(packet, "Checksum Chunks"); + if (node && (plist_get_node_type(node) == PLIST_BOOLEAN)) { + plist_get_bool_val(node, &(asr->checksum_chunks)); + } + plist_free(packet); + + // Expected by device after every Initiate + if (asr_send_validation_packet_info(asr, length) < 0) { + error("ERROR: Unable to send validation packet info to ASR\n"); + return -1; + } + + // A OOBData request should follow + continue; + } + if (!strcmp(command, "OOBData")) { int ret = asr_handle_oob_data_request(asr, packet, file); plist_free(packet); @@ -296,7 +306,7 @@ int asr_perform_validation(asr_client_t asr, const char* filesystem) return 0; } -int asr_handle_oob_data_request(asr_client_t asr, plist_t packet, FILE* file) +int asr_handle_oob_data_request(asr_client_t asr, plist_t packet, ipsw_file_handle_t file) { char* oob_data = NULL; uint64_t oob_offset = 0; @@ -324,14 +334,14 @@ int asr_handle_oob_data_request(asr_client_t asr, plist_t packet, FILE* file) return -1; } -#ifdef WIN32 - rewind(file); - _lseeki64(fileno(file), oob_offset, SEEK_SET); -#else - fseeko(file, oob_offset, SEEK_SET); -#endif - if (fread(oob_data, 1, oob_length, file) != oob_length) { - error("ERROR: Unable to read OOB data from filesystem offset: %s\n", strerror(errno)); + if (ipsw_file_seek(file, oob_offset, SEEK_SET) < 0) { + error("ERROR: Unable to seek to OOB offset 0x%" PRIx64 "\n", oob_offset); + free(oob_data); + return -1; + } + int64_t ir = ipsw_file_read(file, oob_data, oob_length); + if (ir != oob_length) { + error("ERROR: Unable to read OOB data from filesystem offset 0x%" PRIx64 ", oob_length %" PRIu64 ", read returned %" PRIi64"\n", oob_offset, oob_length, ir); free(oob_data); return -1; } @@ -345,58 +355,40 @@ int asr_handle_oob_data_request(asr_client_t asr, plist_t packet, FILE* file) return 0; } -int asr_send_payload(asr_client_t asr, const char* filesystem) +int asr_send_payload(asr_client_t asr, ipsw_file_handle_t file) { char *data = NULL; - FILE* file = NULL; uint64_t i, length, bytes = 0; double progress = 0; - file = fopen(filesystem, "rb"); - if (file == NULL) { - error("ERROR: Unable to open filesystem image %s: %s\n", filesystem, strerror(errno)); - return -1; - } - -#ifdef WIN32 - length = _lseeki64(fileno(file), 0, SEEK_END); - _lseeki64(fileno(file), 0, SEEK_SET); - rewind(file); -#else - fseeko(file, 0, SEEK_END); - length = ftello(file); - fseeko(file, 0, SEEK_SET); -#endif + length = ipsw_file_size(file); + ipsw_file_seek(file, 0, SEEK_SET); data = (char*)malloc(ASR_PAYLOAD_CHUNK_SIZE + 20); - SHA_CTX sha1; - - if (asr->checksum_chunks) { - SHA1_Init(&sha1); - } - - int size = 0; i = length; int retry = 3; while(i > 0 && retry >= 0) { - size = ASR_PAYLOAD_CHUNK_SIZE; + uint32_t size = ASR_PAYLOAD_CHUNK_SIZE; + uint32_t sendsize = 0; + if (i < ASR_PAYLOAD_CHUNK_SIZE) { size = i; } - if (fread(data, 1, size, file) != (size_t)size) { + if (ipsw_file_read(file, data, size) != (int64_t)size) { error("Error reading filesystem\n"); retry--; continue; } + sendsize = size; if (asr->checksum_chunks) { - SHA1((unsigned char*)data, size, (unsigned char*)(data+size)); + sha1((unsigned char*)data, size, (unsigned char*)(data+size)); + sendsize += 20; } - - if (asr_send_buffer(asr, data, size+20) < 0) { - error("ERROR: Unable to send filesystem payload\n"); + if (asr_send_buffer(asr, data, sendsize) < 0) { + error("Unable to send filesystem payload chunk, retrying...\n"); retry--; continue; } @@ -412,6 +404,5 @@ int asr_send_payload(asr_client_t asr, const char* filesystem) } free(data); - fclose(file); - return 0; + return (i == 0) ? 0 : -1; } diff --git a/src/asr.h b/src/asr.h index b3336a57..4473fbb4 100644 --- a/src/asr.h +++ b/src/asr.h @@ -30,6 +30,8 @@ extern "C" { #include +#define ASR_DEFAULT_PORT 12345 + typedef void (*asr_progress_cb_t)(double, void*); struct asr_client { @@ -41,15 +43,18 @@ struct asr_client { }; typedef struct asr_client *asr_client_t; -int asr_open_with_timeout(idevice_t device, asr_client_t* asr); +struct ipsw_file_handle; +typedef struct ipsw_file_handle* ipsw_file_handle_t; + +int asr_open_with_timeout(idevice_t device, asr_client_t* asr, uint16_t port); void asr_set_progress_callback(asr_client_t asr, asr_progress_cb_t, void* userdata); int asr_send(asr_client_t asr, plist_t data); int asr_receive(asr_client_t asr, plist_t* data); int asr_send_buffer(asr_client_t asr, const char* data, uint32_t size); void asr_free(asr_client_t asr); -int asr_perform_validation(asr_client_t asr, const char* filesystem); -int asr_send_payload(asr_client_t asr, const char* filesystem); -int asr_handle_oob_data_request(asr_client_t asr, plist_t packet, FILE* file); +int asr_perform_validation(asr_client_t asr, ipsw_file_handle_t file); +int asr_send_payload(asr_client_t asr, ipsw_file_handle_t file); +int asr_handle_oob_data_request(asr_client_t asr, plist_t packet, ipsw_file_handle_t file); #ifdef __cplusplus diff --git a/src/common.c b/src/common.c index 068f2ddb..c7be4f66 100644 --- a/src/common.c +++ b/src/common.c @@ -35,6 +35,7 @@ #include #include #include +#include #ifdef WIN32 #include @@ -63,6 +64,7 @@ struct idevicerestore_mode_t idevicerestore_modes[] = { { 3, "Recovery" }, { 4, "Restore" }, { 5, "Normal" }, + { 6, "Port DFU" }, }; int idevicerestore_debug = 0; @@ -78,17 +80,31 @@ static int info_disabled = 0; static int error_disabled = 0; static int debug_disabled = 0; +static mutex_t log_mutex; +static thread_once_t init_once = THREAD_ONCE_INIT; + +static void _log_init(void) +{ + mutex_init(&log_mutex); +} + void info(const char* format, ...) { if (info_disabled) return; + thread_once(&init_once, _log_init); + mutex_lock(&log_mutex); va_list vargs; va_start(vargs, format); vfprintf((info_stream) ? info_stream : stdout, format, vargs); va_end(vargs); + fflush(info_stream?info_stream:stdout); + mutex_unlock(&log_mutex); } void error(const char* format, ...) { + thread_once(&init_once, _log_init); + mutex_lock(&log_mutex); va_list vargs, vargs2; va_start(vargs, format); va_copy(vargs2, vargs); @@ -98,6 +114,8 @@ void error(const char* format, ...) vfprintf((error_stream) ? error_stream : stderr, format, vargs2); } va_end(vargs2); + fflush(error_stream?error_stream:stderr); + mutex_unlock(&log_mutex); } void debug(const char* format, ...) @@ -106,10 +124,14 @@ void debug(const char* format, ...) if (!idevicerestore_debug) { return; } + thread_once(&init_once, _log_init); + mutex_lock(&log_mutex); va_list vargs; va_start(vargs, format); vfprintf((debug_stream) ? debug_stream : stderr, format, vargs); va_end(vargs); + fflush(debug_stream?debug_stream:stderr); + mutex_unlock(&log_mutex); } void idevicerestore_set_info_stream(FILE* strm) @@ -226,9 +248,9 @@ void debug_plist(plist_t plist) { char* data = NULL; plist_to_xml(plist, &data, &size); if (size <= MAX_PRINT_LEN) - info("%s:printing %i bytes plist:\n%s", __FILE__, size, data); + info("printing %i bytes plist:\n%s", size, data); else - info("%s:supressed printing %i bytes plist...\n", __FILE__, size); + info("supressed printing %i bytes plist...\n", size); free(data); } @@ -238,13 +260,13 @@ void print_progress_bar(double progress) { int i = 0; if(progress < 0) return; if(progress > 100) progress = 100; - info("\r["); + fprintf((info_stream) ? info_stream : stdout, "\r["); for(i = 0; i < 50; i++) { - if(i < progress / 2) info("="); - else info(" "); + if(i < progress / 2) fprintf((info_stream) ? info_stream : stdout, "="); + else fprintf((info_stream) ? info_stream : stdout, " "); } - info("] %5.1f%%", progress); - if(progress >= 100) info("\n"); + fprintf((info_stream) ? info_stream : stdout, "] %5.1f%%", progress); + if(progress >= 100) fprintf((info_stream) ? info_stream : stdout, "\n"); fflush((info_stream) ? info_stream : stdout); #endif } @@ -463,14 +485,17 @@ char *get_temp_filename(const char *prefix) void idevicerestore_progress(struct idevicerestore_client_t* client, int step, double progress) { + thread_once(&init_once, _log_init); + mutex_lock(&log_mutex); if(client && client->progress_cb) { client->progress_cb(step, progress, client->progress_cb_data); } else { // we don't want to be too verbose in regular idevicerestore. - if ((step == RESTORE_STEP_UPLOAD_FS) || (step == RESTORE_STEP_VERIFY_FS) || (step == RESTORE_STEP_FLASH_FW)) { + if ((step == RESTORE_STEP_UPLOAD_FS) || (step == RESTORE_STEP_VERIFY_FS) || (step == RESTORE_STEP_FLASH_FW) || (step == RESTORE_STEP_UPLOAD_IMG)) { print_progress_bar(100.0 * progress); } } + mutex_unlock(&log_mutex); } #ifndef HAVE_STRSEP @@ -557,141 +582,19 @@ void get_user_input(char *buf, int maxlen, int secure) buf[len] = 0; } -uint64_t _plist_dict_get_uint(plist_t dict, const char *key) +const char* path_get_basename(const char* path) { - uint64_t uintval = 0; - char *strval = NULL; - uint64_t strsz = 0; - plist_t node = plist_dict_get_item(dict, key); - if (!node) { - return uintval; - } - switch (plist_get_node_type(node)) { - case PLIST_UINT: - plist_get_uint_val(node, &uintval); - break; - case PLIST_STRING: - plist_get_string_val(node, &strval); - if (strval) { - uintval = strtoull(strval, NULL, 0); - free(strval); - } - break; - case PLIST_DATA: - plist_get_data_val(node, &strval, &strsz); - if (strval) { - if (strsz == 8) { - uintval = le64toh(*(uint64_t*)strval); - } else if (strsz == 4) { - uintval = le32toh(*(uint32_t*)strval); - } else if (strsz == 2) { - uintval = le16toh(*(uint16_t*)strval); - } else if (strsz == 1) { - uintval = strval[0]; - } else { - error("%s: ERROR: invalid size %" PRIu64 " for data to integer conversion\n", __func__, strsz); - } - free(strval); - } - break; - default: - break; - } - return uintval; -} - -uint8_t _plist_dict_get_bool(plist_t dict, const char *key) -{ - uint8_t bval = 0; - uint64_t uintval = 0; - char *strval = NULL; - uint64_t strsz = 0; - plist_t node = plist_dict_get_item(dict, key); - if (!node) { - return 0; - } - switch (plist_get_node_type(node)) { - case PLIST_BOOLEAN: - plist_get_bool_val(node, &bval); - break; - case PLIST_UINT: - plist_get_uint_val(node, &uintval); - bval = (uint8_t)uintval; - break; - case PLIST_STRING: - plist_get_string_val(node, &strval); - if (strval) { - if (strcmp(strval, "true")) { - bval = 1; - } else if (strcmp(strval, "false")) { - bval = 0; - } - free(strval); - } - break; - case PLIST_DATA: - plist_get_data_val(node, &strval, &strsz); - if (strval) { - if (strsz == 1) { - bval = strval[0]; - } else { - error("%s: ERROR: invalid size %" PRIu64 " for data to boolean conversion\n", __func__, strsz); - } - free(strval); +#ifdef WIN32 + const char *p = path + strlen(path); + while (p > path) { + if ((*p == '/') || (*p == '\\')) { + return p+1; } - break; - default: - break; - } - return bval; -} - -int _plist_dict_copy_uint(plist_t target_dict, plist_t source_dict, const char *key, const char *alt_source_key) -{ - if (plist_dict_get_item(source_dict, (alt_source_key) ? alt_source_key : key) == NULL) { - return -1; + p--; } - uint64_t u64val = _plist_dict_get_uint(source_dict, (alt_source_key) ? alt_source_key : key); - plist_dict_set_item(target_dict, key, plist_new_uint(u64val)); - return 0; -} - -int _plist_dict_copy_bool(plist_t target_dict, plist_t source_dict, const char *key, const char *alt_source_key) -{ - if (plist_dict_get_item(source_dict, (alt_source_key) ? alt_source_key : key) == NULL) { - return -1; - } - uint64_t bval = _plist_dict_get_bool(source_dict, (alt_source_key) ? alt_source_key : key); - plist_dict_set_item(target_dict, key, plist_new_bool(bval)); - return 0; -} - -int _plist_dict_copy_data(plist_t target_dict, plist_t source_dict, const char *key, const char *alt_source_key) -{ - plist_t node = plist_dict_get_item(source_dict, (alt_source_key) ? alt_source_key : key); - if (!PLIST_IS_DATA(node)) { - return -1; - } - plist_dict_set_item(target_dict, key, plist_copy(node)); - return 0; -} - -int _plist_dict_copy_string(plist_t target_dict, plist_t source_dict, const char *key, const char *alt_source_key) -{ - plist_t node = plist_dict_get_item(source_dict, (alt_source_key) ? alt_source_key : key); - if (!PLIST_IS_STRING(node)) { - return -1; - } - plist_dict_set_item(target_dict, key, plist_copy(node)); - return 0; -} - -int _plist_dict_copy_item(plist_t target_dict, plist_t source_dict, const char *key, const char *alt_source_key) -{ - plist_t node = plist_dict_get_item(source_dict, (alt_source_key) ? alt_source_key : key); - if (!node) { - return -1; - } - plist_dict_set_item(target_dict, key, plist_copy(node)); - return 0; + return p; +#else + const char *p = strrchr(path, '/'); + return p ? p + 1 : path; +#endif } diff --git a/src/common.h b/src/common.h index 97d5f964..872d2f92 100644 --- a/src/common.h +++ b/src/common.h @@ -47,6 +47,7 @@ extern "C" { #define _MODE_RECOVERY 3 #define _MODE_RESTORE 4 #define _MODE_NORMAL 5 +#define _MODE_PORTDFU 6 #define MODE_UNKNOWN &idevicerestore_modes[_MODE_UNKNOWN] #define MODE_WTF &idevicerestore_modes[_MODE_WTF] @@ -54,6 +55,7 @@ extern "C" { #define MODE_RECOVERY &idevicerestore_modes[_MODE_RECOVERY] #define MODE_RESTORE &idevicerestore_modes[_MODE_RESTORE] #define MODE_NORMAL &idevicerestore_modes[_MODE_NORMAL] +#define MODE_PORTDFU &idevicerestore_modes[_MODE_PORTDFU] #define FLAG_QUIT 1 @@ -70,6 +72,9 @@ struct dfu_client_t; struct normal_client_t; struct restore_client_t; struct recovery_client_t; +struct ipsw_archive; + +typedef struct ipsw_archive* ipsw_archive_t; struct idevicerestore_mode_t { int index; @@ -88,6 +93,7 @@ struct idevicerestore_entry_t { struct idevicerestore_client_t { int flags; + int debug_level; plist_t tss; plist_t tss_localpolicy; plist_t tss_recoveryos_root_ticket; @@ -98,11 +104,12 @@ struct idevicerestore_client_t { int nonce_size; int image4supported; plist_t build_manifest; + plist_t firmware_preflight_info; plist_t preflight_info; + plist_t parameters; char* udid; char* srnm; - char* ipsw; - const char* filesystem; + ipsw_archive_t ipsw; struct dfu_client_t* dfu; struct restore_client_t* restore; struct recovery_client_t* recovery; @@ -111,6 +118,8 @@ struct idevicerestore_client_t { struct idevicerestore_mode_t* mode; char* version; char* build; + char* device_version; + char* device_build; int build_major; char* restore_boot_args; char* cache_dir; @@ -124,7 +133,11 @@ struct idevicerestore_client_t { cond_t device_event_cond; int ignore_device_add_events; plist_t macos_variant; + plist_t recovery_variant; char* restore_variant; + char* filesystem; + int delete_fs; + int async_err; }; extern struct idevicerestore_mode_t idevicerestore_modes[]; @@ -182,13 +195,7 @@ char* realpath(const char *filename, char *resolved_name); void get_user_input(char *buf, int maxlen, int secure); -uint8_t _plist_dict_get_bool(plist_t dict, const char *key); -uint64_t _plist_dict_get_uint(plist_t dict, const char *key); -int _plist_dict_copy_uint(plist_t target_dict, plist_t source_dict, const char *key, const char *alt_source_key); -int _plist_dict_copy_bool(plist_t target_dict, plist_t source_dict, const char *key, const char *alt_source_key); -int _plist_dict_copy_data(plist_t target_dict, plist_t source_dict, const char *key, const char *alt_source_key); -int _plist_dict_copy_string(plist_t target_dict, plist_t source_dict, const char *key, const char *alt_source_key); -int _plist_dict_copy_item(plist_t target_dict, plist_t source_dict, const char *key, const char *alt_source_key); +const char* path_get_basename(const char* path); #ifdef __cplusplus } diff --git a/src/dfu.c b/src/dfu.c index cb4d2b33..508b4f61 100644 --- a/src/dfu.c +++ b/src/dfu.c @@ -27,8 +27,9 @@ #include #include +#include + #include "dfu.h" -#include "tss.h" #include "recovery.h" #include "idevicerestore.h" #include "common.h" @@ -83,7 +84,6 @@ irecv_device_t dfu_get_irecv_device(struct idevicerestore_client_t* client) irecv_error_t dfu_error = IRECV_E_SUCCESS; irecv_device_t device = NULL; - irecv_init(); if (irecv_open_with_ecid_and_attempts(&dfu, client->ecid, 10) != IRECV_E_SUCCESS) { return NULL; } @@ -103,13 +103,13 @@ irecv_device_t dfu_get_irecv_device(struct idevicerestore_client_t* client) return device; } -int dfu_send_buffer(struct idevicerestore_client_t* client, unsigned char* buffer, unsigned int size) +int dfu_send_buffer_with_options(struct idevicerestore_client_t* client, unsigned char* buffer, unsigned int size, unsigned int irecv_options) { irecv_error_t err = 0; info("Sending data (%d bytes)...\n", size); - err = irecv_send_buffer(client->dfu->client, buffer, size, 1); + err = irecv_send_buffer(client->dfu->client, buffer, size, irecv_options); if (err != IRECV_E_SUCCESS) { error("ERROR: Unable to send data: %s\n", irecv_strerror(err)); return -1; @@ -118,6 +118,11 @@ int dfu_send_buffer(struct idevicerestore_client_t* client, unsigned char* buffe return 0; } +int dfu_send_buffer(struct idevicerestore_client_t* client, unsigned char* buffer, unsigned int size) +{ + return dfu_send_buffer_with_options(client, buffer, size, IRECV_SEND_OPT_DFU_NOTIFY_FINISH); +} + int dfu_send_component(struct idevicerestore_client_t* client, plist_t build_identity, const char* component) { char* path = NULL; @@ -162,7 +167,7 @@ int dfu_send_component(struct idevicerestore_client_t* client, plist_t build_ide unsigned char* data = NULL; uint32_t size = 0; - if (personalize_component(component, component_data, component_size, tss, &data, &size) < 0) { + if (personalize_component(client, component, component_data, component_size, tss, &data, &size) < 0) { error("ERROR: Unable to get personalized component: %s\n", component); free(component_data); return -1; @@ -193,7 +198,7 @@ int dfu_send_component(struct idevicerestore_client_t* client, plist_t build_ide info("Sending %s (%d bytes)...\n", component, size); - irecv_error_t err = irecv_send_buffer(client->dfu->client, data, size, 1); + irecv_error_t err = irecv_send_buffer(client->dfu->client, data, size, IRECV_SEND_OPT_DFU_NOTIFY_FINISH); if (err != IRECV_E_SUCCESS) { error("ERROR: Unable to send %s component: %s\n", component, irecv_strerror(err)); free(data); @@ -204,6 +209,24 @@ int dfu_send_component(struct idevicerestore_client_t* client, plist_t build_ide return 0; } +int dfu_get_bdid(struct idevicerestore_client_t* client, unsigned int* bdid) +{ + if(client->dfu == NULL) { + if (dfu_client_new(client) < 0) { + return -1; + } + } + + const struct irecv_device_info *device_info = irecv_get_device_info(client->dfu->client); + if (!device_info) { + return -1; + } + + *bdid = device_info->bdid; + + return 0; +} + int dfu_get_cpid(struct idevicerestore_client_t* client, unsigned int* cpid) { if(client->dfu == NULL) { @@ -222,6 +245,27 @@ int dfu_get_cpid(struct idevicerestore_client_t* client, unsigned int* cpid) return 0; } +int dfu_get_prev(struct idevicerestore_client_t* client, unsigned int* prev) +{ + if(client->dfu == NULL) { + if (dfu_client_new(client) < 0) { + return -1; + } + } + + const struct irecv_device_info *device_info = irecv_get_device_info(client->dfu->client); + if (!device_info) { + return -1; + } + char* ptr = strstr(device_info->serial_string, "PREV:"); + if (ptr) { + sscanf(ptr, "PREV:%x", prev); + return 0; + } + return -1; +} + + int dfu_is_image4_supported(struct idevicerestore_client_t* client) { if(client->dfu == NULL) { @@ -238,7 +282,36 @@ int dfu_is_image4_supported(struct idevicerestore_client_t* client) return (device_info->ibfl & IBOOT_FLAG_IMAGE4_AWARE); } -int dfu_get_ap_nonce(struct idevicerestore_client_t* client, unsigned char** nonce, int* nonce_size) +int dfu_get_portdfu_nonce(struct idevicerestore_client_t* client, unsigned char** nonce, unsigned int* nonce_size) +{ + if(client->dfu == NULL) { + if (dfu_client_new(client) < 0) { + return -1; + } + } + + const struct irecv_device_info *device_info = irecv_get_device_info(client->dfu->client); + if (!device_info) { + return -1; + } + + if (device_info->ap_nonce && device_info->ap_nonce_size > 0) { + *nonce = (unsigned char*)malloc(device_info->ap_nonce_size); + if (!*nonce) { + return -1; + } + *nonce_size = device_info->ap_nonce_size; + // The nonce is backwards, so we have to swap the bytes + unsigned int i = 0; + for (i = 0; i < *nonce_size; i++) { + (*nonce)[(*nonce_size)-1-i] = device_info->ap_nonce[i]; + } + } + + return 0; +} + +int dfu_get_ap_nonce(struct idevicerestore_client_t* client, unsigned char** nonce, unsigned int* nonce_size) { if(client->dfu == NULL) { if (dfu_client_new(client) < 0) { @@ -263,7 +336,7 @@ int dfu_get_ap_nonce(struct idevicerestore_client_t* client, unsigned char** non return 0; } -int dfu_get_sep_nonce(struct idevicerestore_client_t* client, unsigned char** nonce, int* nonce_size) +int dfu_get_sep_nonce(struct idevicerestore_client_t* client, unsigned char** nonce, unsigned int* nonce_size) { if(client->dfu == NULL) { if (dfu_client_new(client) < 0) { @@ -349,11 +422,13 @@ int dfu_send_iboot_stage1_components(struct idevicerestore_client_t* client, pli uint8_t b = 0; plist_get_bool_val(iboot_node, &b); if (b) { - debug("DEBUG: %s is loaded by iBoot Stage 1.\n", key); - if (dfu_send_component_and_command(client, build_identity, key, "firmware") < 0) { - error("ERROR: Unable to send component '%s' to device.\n", key); - err++; - } + debug("DEBUG: %s is loaded by iBoot Stage 1 and iBoot.\n", key); + } else { + debug("DEBUG: %s is loaded by iBoot Stage 1 but not iBoot...\n", key); + } + if (dfu_send_component_and_command(client, build_identity, key, "firmware") < 0) { + error("ERROR: Unable to send component '%s' to device.\n", key); + err++; } } free(key); @@ -415,7 +490,7 @@ int dfu_enter_recovery(struct idevicerestore_client_t* client, plist_t build_ide /* get nonce */ unsigned char* nonce = NULL; - int nonce_size = 0; + unsigned int nonce_size = 0; int nonce_changed = 0; if (dfu_get_ap_nonce(client, &nonce, &nonce_size) < 0) { error("ERROR: Unable to get ApNonce from device!\n"); @@ -471,6 +546,21 @@ int dfu_enter_recovery(struct idevicerestore_client_t* client, plist_t build_ide return -1; } + char *value = NULL; + unsigned long boot_stage = 0; + irecv_getenv(client->dfu->client, "boot-stage", &value); + if (value) { + boot_stage = strtoul(value, NULL, 0); + } + if (boot_stage > 0) { + info("iBoot boot-stage=%s\n", value); + free(value); + value = NULL; + if (boot_stage != 1) { + error("ERROR: iBoot should be at boot stage 1, continuing anyway...\n"); + } + } + if (dfu_send_iboot_stage1_components(client, build_identity) < 0) { mutex_unlock(&client->device_event_mutex); error("ERROR: Unable to send iBoot stage 1 components to device\n"); diff --git a/src/dfu.h b/src/dfu.h index 67124429..4590dbd7 100644 --- a/src/dfu.h +++ b/src/dfu.h @@ -41,11 +41,15 @@ int dfu_client_new(struct idevicerestore_client_t* client); void dfu_client_free(struct idevicerestore_client_t* client); irecv_device_t dfu_get_irecv_device(struct idevicerestore_client_t* client); int dfu_send_buffer(struct idevicerestore_client_t* client, unsigned char* buffer, unsigned int size); +int dfu_send_buffer_with_options(struct idevicerestore_client_t* client, unsigned char* buffer, unsigned int size, unsigned int irecv_options); int dfu_send_component(struct idevicerestore_client_t* client, plist_t build_identity, const char* component); +int dfu_get_bdid(struct idevicerestore_client_t* client, unsigned int* bdid); int dfu_get_cpid(struct idevicerestore_client_t* client, unsigned int* cpid); +int dfu_get_prev(struct idevicerestore_client_t* client, unsigned int* prev); int dfu_is_image4_supported(struct idevicerestore_client_t* client); -int dfu_get_ap_nonce(struct idevicerestore_client_t* client, unsigned char** nonce, int* nonce_size); -int dfu_get_sep_nonce(struct idevicerestore_client_t* client, unsigned char** nonce, int* nonce_size); +int dfu_get_portdfu_nonce(struct idevicerestore_client_t* client, unsigned char** nonce, unsigned int* nonce_size); +int dfu_get_ap_nonce(struct idevicerestore_client_t* client, unsigned char** nonce, unsigned int* nonce_size); +int dfu_get_sep_nonce(struct idevicerestore_client_t* client, unsigned char** nonce, unsigned int* nonce_size); int dfu_enter_recovery(struct idevicerestore_client_t* client, plist_t build_identity); diff --git a/src/fdr.c b/src/fdr.c index e3703fe8..ca9b7c75 100644 --- a/src/fdr.c +++ b/src/fdr.c @@ -575,7 +575,7 @@ static int fdr_handle_proxy_cmd(fdr_client_t fdr) break; } if (bytes) { - debug("FDR %p got payload of %u bytes, now try to proxy it\n", fdr, bytes); + debug("FDR %p got payload of %u bytes, now trying to proxy it\n", fdr, bytes); debug("Sending %u bytes of data\n", bytes); sent = 0; while (sent < bytes) { diff --git a/src/fixedint.h b/src/fixedint.h deleted file mode 100644 index 1a8745b1..00000000 --- a/src/fixedint.h +++ /dev/null @@ -1,72 +0,0 @@ -/* - Portable header to provide the 32 and 64 bits type. - - Not a compatible replacement for , do not blindly use it as such. -*/ - -#if ((defined(__STDC__) && __STDC__ && __STDC_VERSION__ >= 199901L) || (defined(__WATCOMC__) && (defined(_STDINT_H_INCLUDED) || __WATCOMC__ >= 1250)) || (defined(__GNUC__) && (defined(_STDINT_H) || defined(_STDINT_H_) || defined(__UINT_FAST64_TYPE__)) )) && !defined(FIXEDINT_H_INCLUDED) - #include - #define FIXEDINT_H_INCLUDED - - #if defined(__WATCOMC__) && __WATCOMC__ >= 1250 && !defined(UINT64_C) - #include - #define UINT64_C(x) (x + (UINT64_MAX - UINT64_MAX)) - #endif -#endif - - -#ifndef FIXEDINT_H_INCLUDED - #define FIXEDINT_H_INCLUDED - - #include - - /* (u)int32_t */ - #ifndef uint32_t - #if (ULONG_MAX == 0xffffffffUL) - typedef unsigned long uint32_t; - #elif (UINT_MAX == 0xffffffffUL) - typedef unsigned int uint32_t; - #elif (USHRT_MAX == 0xffffffffUL) - typedef unsigned short uint32_t; - #endif - #endif - - - #ifndef int32_t - #if (LONG_MAX == 0x7fffffffL) - typedef signed long int32_t; - #elif (INT_MAX == 0x7fffffffL) - typedef signed int int32_t; - #elif (SHRT_MAX == 0x7fffffffL) - typedef signed short int32_t; - #endif - #endif - - - /* (u)int64_t */ - #if (defined(__STDC__) && defined(__STDC_VERSION__) && __STDC__ && __STDC_VERSION__ >= 199901L) - typedef long long int64_t; - typedef unsigned long long uint64_t; - - #define UINT64_C(v) v ##ULL - #define INT64_C(v) v ##LL - #elif defined(__GNUC__) - __extension__ typedef long long int64_t; - __extension__ typedef unsigned long long uint64_t; - - #define UINT64_C(v) v ##ULL - #define INT64_C(v) v ##LL - #elif defined(__MWERKS__) || defined(__SUNPRO_C) || defined(__SUNPRO_CC) || defined(__APPLE_CC__) || defined(_LONG_LONG) || defined(_CRAYC) - typedef long long int64_t; - typedef unsigned long long uint64_t; - - #define UINT64_C(v) v ##ULL - #define INT64_C(v) v ##LL - #elif (defined(__WATCOMC__) && defined(__WATCOM_INT64__)) || (defined(_MSC_VER) && _INTEGRAL_MAX_BITS >= 64) || (defined(__BORLANDC__) && __BORLANDC__ > 0x460) || defined(__alpha) || defined(__DECC) - typedef __int64 int64_t; - typedef unsigned __int64 uint64_t; - - #define UINT64_C(v) v ##UI64 - #define INT64_C(v) v ##I64 - #endif -#endif diff --git a/src/idevicerestore.c b/src/idevicerestore.c index 23c48d5f..b8bb1d06 100644 --- a/src/idevicerestore.c +++ b/src/idevicerestore.c @@ -38,15 +38,12 @@ #include -#ifdef HAVE_OPENSSL -#include -#else -#include "sha512.h" -#define SHA384 sha384 -#endif +#include +#include +#include +#include "ace3.h" #include "dfu.h" -#include "tss.h" #include "img3.h" #include "img4.h" #include "ipsw.h" @@ -72,7 +69,7 @@ static struct option longopts[] = { { "erase", no_argument, NULL, 'e' }, { "custom", no_argument, NULL, 'c' }, { "latest", no_argument, NULL, 'l' }, - { "cydia", no_argument, NULL, 's' }, + { "server", required_argument, NULL, 's' }, { "exclude", no_argument, NULL, 'x' }, { "shsh", no_argument, NULL, 't' }, { "keep-pers", no_argument, NULL, 'k' }, @@ -132,8 +129,8 @@ static void usage(int argc, char* argv[], int err) "\n" \ "Advanced/experimental options:\n" " -c, --custom Restore with a custom firmware (requires bootrom exploit)\n" \ - " -s, --cydia Use Cydia's signature service instead of Apple's\n" \ - " -x, --exclude Exclude nor/baseband upgrade\n" \ + " -s, --server URL Override default signing server request URL\n" \ + " -x, --exclude Exclude nor/baseband upgrade (legacy devices)\n" \ " -t, --shsh Fetch TSS record and save to .shsh file, then exit\n" \ " -z, --no-restore Do not restore and end after booting to the ramdisk\n" \ " -k, --keep-pers Write personalized components to files for debugging\n" \ @@ -296,6 +293,9 @@ static void irecv_event_cb(const irecv_device_event_t* event, void *userdata) case IRECV_K_DFU_MODE: client->mode = MODE_DFU; break; + case IRECV_K_PORT_DFU_MODE: + client->mode = MODE_PORTDFU; + break; case IRECV_K_RECOVERY_MODE_1: case IRECV_K_RECOVERY_MODE_2: case IRECV_K_RECOVERY_MODE_3: @@ -314,12 +314,20 @@ static void irecv_event_cb(const irecv_device_event_t* event, void *userdata) mutex_lock(&client->device_event_mutex); client->mode = MODE_UNKNOWN; debug("%s: device %016" PRIx64 " (udid: %s) disconnected\n", __func__, client->ecid, (client->udid) ? client->udid : "N/A"); + if (event->mode == IRECV_K_PORT_DFU_MODE) { + // We have to reset the ECID here if a port DFU device disconnects, + // because when the device reconnects in a different mode, it will + // have the actual device ECID and wouldn't get detected. + client->ecid = 0; + } cond_signal(&client->device_event_cond); mutex_unlock(&client->device_event_mutex); } } } +int build_identity_check_components_in_ipsw(plist_t build_identity, ipsw_archive_t ipsw); + int idevicerestore_start(struct idevicerestore_client_t* client) { int tss_enabled = 0; @@ -339,10 +347,15 @@ int idevicerestore_start(struct idevicerestore_client_t* client) return -1; } - if (client->flags & FLAG_DEBUG) { - idevice_set_debug_level(1); - irecv_set_debug_level(1); + if (client->debug_level > 0) { idevicerestore_debug = 1; + if (client->debug_level > 1) { + irecv_set_debug_level(1); + } + if (client->debug_level > 2) { + idevice_set_debug_level(1); + } + tss_set_debug_level(client->debug_level); } idevicerestore_progress(client, RESTORE_STEP_DETECT, 0.0); @@ -380,7 +393,7 @@ int idevicerestore_start(struct idevicerestore_client_t* client) } char wtfname[256]; - sprintf(wtfname, "Firmware/dfu/WTF.s5l%04xxall.RELEASE.dfu", cpid); + snprintf(wtfname, sizeof(wtfname), "Firmware/dfu/WTF.s5l%04xxall.RELEASE.dfu", cpid); unsigned char* wtftmp = NULL; unsigned int wtfsize = 0; @@ -424,7 +437,9 @@ int idevicerestore_start(struct idevicerestore_client_t* client) download_to_file(s_wtfurl, wtfipsw, 0); } - ipsw_extract_to_memory(wtfipsw, wtfname, &wtftmp, &wtfsize); + ipsw_archive_t wtf_ipsw = ipsw_open(wtfipsw); + ipsw_extract_to_memory(wtf_ipsw, wtfname, &wtftmp, &wtfsize); + ipsw_close(wtf_ipsw); if (!wtftmp) { error("ERROR: Could not extract WTF\n"); } @@ -470,6 +485,21 @@ int idevicerestore_start(struct idevicerestore_client_t* client) return -1; } + if (client->mode == MODE_NORMAL) { + plist_t pver = normal_get_lockdown_value(client, NULL, "ProductVersion"); + if (pver) { + plist_get_string_val(pver, &client->device_version); + plist_free(pver); + } + pver = normal_get_lockdown_value(client, NULL, "BuildVersion"); + if (pver) { + plist_get_string_val(pver, &client->device_build); + plist_free(pver); + } + } + info("Device Product Version: %s\n", (client->device_version) ? client->device_version : "N/A"); + info("Device Product Build: %s\n", (client->device_build) ? client->device_build : "N/A"); + if (client->flags & FLAG_PWN) { recovery_client_free(client); @@ -482,17 +512,24 @@ int idevicerestore_start(struct idevicerestore_client_t* client) if (dfu_client_new(client) < 0) { return -1; } - info("exploiting with limera1n...\n"); - // TODO: check for non-limera1n device and fail - if (limera1n_exploit(client->device, &client->dfu->client) != 0) { - error("ERROR: limera1n exploit failed\n"); + + if (limera1n_is_supported(client->device)) { + info("exploiting with limera1n...\n"); + if (limera1n_exploit(client->device, &client->dfu->client) != 0) { + error("ERROR: limera1n exploit failed\n"); + dfu_client_free(client); + return -1; + } + dfu_client_free(client); + info("Device should be in pwned DFU state now.\n"); + + return 0; + } + else { dfu_client_free(client); + error("ERROR: This device is not supported by the limera1n exploit"); return -1; } - dfu_client_free(client); - info("Device should be in pwned DFU state now.\n"); - - return 0; } if (client->flags & FLAG_LATEST) { @@ -594,12 +631,16 @@ int idevicerestore_start(struct idevicerestore_client_t* client) char* ipsw = NULL; res = ipsw_download_fw(fwurl, p_fwsha1, client->cache_dir, &ipsw); if (res != 0) { - if (ipsw) { - free(ipsw); - } + free(ipsw); return res; } else { - client->ipsw = ipsw; + client->ipsw = ipsw_open(ipsw); + if (!client->ipsw) { + error("ERROR: Failed to open ipsw '%s'\n", ipsw); + free(ipsw); + return -1; + } + free(ipsw); } } idevicerestore_progress(client, RESTORE_STEP_DETECT, 0.6); @@ -608,14 +649,29 @@ int idevicerestore_start(struct idevicerestore_client_t* client) return 0; } + // extract buildmanifest + if (client->flags & FLAG_CUSTOM) { + info("Extracting Restore.plist from IPSW\n"); + if (ipsw_extract_restore_plist(client->ipsw, &client->build_manifest) < 0) { + error("ERROR: Unable to extract Restore.plist from %s. Firmware file might be corrupt.\n", client->ipsw->path); + return -1; + } + } else { + info("Extracting BuildManifest from IPSW\n"); + if (ipsw_extract_build_manifest(client->ipsw, &client->build_manifest, &tss_enabled) < 0) { + error("ERROR: Unable to extract BuildManifest from %s. Firmware file might be corrupt.\n", client->ipsw->path); + return -1; + } + } + + if (client->flags & FLAG_CUSTOM) { + // prevent attempt to sign custom firmware + tss_enabled = 0; + info("Custom firmware requested; TSS has been disabled.\n"); + } + if (client->mode == MODE_RESTORE) { - if (client->flags & FLAG_ALLOW_RESTORE_MODE) { - tss_enabled = 0; - if (!client->root_ticket) { - client->root_ticket = (void*)strdup(""); - client->root_ticket_len = 0; - } - } else { + if (!(client->flags & FLAG_ALLOW_RESTORE_MODE)) { if (restore_reboot(client) < 0) { error("ERROR: Unable to exit restore mode\n"); return -2; @@ -634,26 +690,153 @@ int idevicerestore_start(struct idevicerestore_client_t* client) } } - // verify if ipsw file exists - if (access(client->ipsw, F_OK) < 0) { - error("ERROR: Firmware file %s does not exist.\n", client->ipsw); - return -1; - } + if (client->mode == MODE_PORTDFU) { + unsigned int pdfu_bdid = 0; + unsigned int pdfu_cpid = 0; + unsigned int prev = 0; - // extract buildmanifest - if (client->flags & FLAG_CUSTOM) { - info("Extracting Restore.plist from IPSW\n"); - if (ipsw_extract_restore_plist(client->ipsw, &client->build_manifest) < 0) { - error("ERROR: Unable to extract Restore.plist from %s. Firmware file might be corrupt.\n", client->ipsw); + if (dfu_get_bdid(client, &pdfu_bdid) < 0) { + error("ERROR: Failed to get bdid for Port DFU device!\n"); return -1; } - } else { - info("Extracting BuildManifest from IPSW\n"); - if (ipsw_extract_build_manifest(client->ipsw, &client->build_manifest, &tss_enabled) < 0) { - error("ERROR: Unable to extract BuildManifest from %s. Firmware file might be corrupt.\n", client->ipsw); + if (dfu_get_cpid(client, &pdfu_cpid) < 0) { + error("ERROR: Failed to get cpid for Port DFU device!\n"); return -1; } + if (dfu_get_prev(client, &prev) < 0) { + error("ERROR: Failed to get PREV for Port DFU device!\n"); + return -1; + } + + unsigned char* pdfu_nonce = NULL; + unsigned int pdfu_nsize = 0; + if (dfu_get_portdfu_nonce(client, &pdfu_nonce, &pdfu_nsize) < 0) { + error("ERROR: Failed to get nonce for Port DFU device!\n"); + return -1; + } + + plist_t build_identity = build_manifest_get_build_identity_for_model_with_variant(client->build_manifest, client->device->hardware_model, RESTORE_VARIANT_ERASE_INSTALL, 0); + if (!build_identity) { + error("ERORR: Failed to get build identity\n"); + return -1; + } + + unsigned int b_pdfu_cpid = (unsigned int)plist_dict_get_uint(build_identity, "USBPortController1,ChipID"); + if (b_pdfu_cpid != pdfu_cpid) { + error("ERROR: cpid 0x%02x doesn't match USBPortController1,ChipID in build identity (0x%02x)\n", pdfu_cpid, b_pdfu_cpid); + return -1; + } + unsigned int b_pdfu_bdid = (unsigned int)plist_dict_get_uint(build_identity, "USBPortController1,BoardID"); + if (b_pdfu_bdid != pdfu_bdid) { + error("ERROR: bdid 0x%x doesn't match USBPortController1,BoardID in build identity (0x%x)\n", pdfu_bdid, b_pdfu_bdid); + return -1; + } + + plist_t parameters = plist_new_dict(); + plist_dict_set_item(parameters, "@USBPortController1,Ticket", plist_new_bool(1)); + plist_dict_set_item(parameters, "USBPortController1,ECID", plist_new_int(client->ecid)); + plist_dict_copy_item(parameters, build_identity, "USBPortController1,BoardID", NULL); + plist_dict_copy_item(parameters, build_identity, "USBPortController1,ChipID", NULL); + plist_dict_copy_item(parameters, build_identity, "USBPortController1,SecurityDomain", NULL); + plist_dict_set_item(parameters, "USBPortController1,SecurityMode", plist_new_bool(1)); + plist_dict_set_item(parameters, "USBPortController1,ProductionMode", plist_new_bool(1)); + plist_t usbf = plist_access_path(build_identity, 2, "Manifest", "USBPortController1,USBFirmware"); + if (!usbf) { + plist_free(parameters); + error("ERROR: Unable to find USBPortController1,USBFirmware in build identity\n"); + return -1; + } + plist_t p_fwpath = plist_access_path(usbf, 2, "Info", "Path"); + if (!p_fwpath) { + plist_free(parameters); + error("ERROR: Unable to find path of USBPortController1,USBFirmware component\n"); + return -1; + } + const char* fwpath = plist_get_string_ptr(p_fwpath, NULL); + if (!fwpath) { + plist_free(parameters); + error("ERROR: Unable to get path of USBPortController1,USBFirmware component\n"); + return -1; + } + unsigned char* uarp_buf = NULL; + unsigned int uarp_size = 0; + if (ipsw_extract_to_memory(client->ipsw, fwpath, &uarp_buf, &uarp_size) < 0) { + plist_free(parameters); + error("ERROR: Unable to extract '%s' from IPSW\n", fwpath); + return -1; + } + usbf = plist_copy(usbf); + plist_dict_remove_item(usbf, "Info"); + plist_dict_set_item(parameters, "USBPortController1,USBFirmware", usbf); + plist_dict_set_item(parameters, "USBPortController1,Nonce", plist_new_data((const char*)pdfu_nonce, pdfu_nsize)); + + plist_t request = tss_request_new(NULL); + if (request == NULL) { + plist_free(parameters); + error("ERROR: Unable to create TSS request\n"); + return -1; + } + plist_dict_merge(&request, parameters); + plist_free(parameters); + + // send request and grab response + plist_t response = tss_request_send(request, client->tss_url); + plist_free(request); + if (response == NULL) { + error("ERROR: Unable to send TSS request\n"); + return -1; + } + info("Received USBPortController1,Ticket\n"); + + info("Creating Ace3Binary\n"); + unsigned char* ace3bin = NULL; + size_t ace3bin_size = 0; + if (ace3_create_binary(uarp_buf, uarp_size, pdfu_bdid, prev, response, &ace3bin, &ace3bin_size) < 0) { + error("ERROR: Could not create Ace3Binary\n"); + return -1; + } + plist_free(response); + free(uarp_buf); + + if (idevicerestore_keep_pers) { + write_file("Ace3Binary", (const char*)ace3bin, ace3bin_size); + } + + if (dfu_send_buffer_with_options(client, ace3bin, ace3bin_size, IRECV_SEND_OPT_DFU_NOTIFY_FINISH | IRECV_SEND_OPT_DFU_SMALL_PKT) < 0) { + error("ERROR: Could not send Ace3Buffer to device\n"); + return -1; + } + + debug("Waiting for device to disconnect...\n"); + cond_wait_timeout(&client->device_event_cond, &client->device_event_mutex, 5000); + if (client->mode != MODE_UNKNOWN || (client->flags & FLAG_QUIT)) { + mutex_unlock(&client->device_event_mutex); + + if (!(client->flags & FLAG_QUIT)) { + error("ERROR: Device did not disconnect. Port DFU failed.\n"); + } + return -2; + } + dfu_client_free(client); + debug("Waiting for device to reconnect in DFU mode...\n"); + cond_wait_timeout(&client->device_event_cond, &client->device_event_mutex, 20000); + if (client->mode != MODE_DFU || (client->flags & FLAG_QUIT)) { + mutex_unlock(&client->device_event_mutex); + if (!(client->flags & FLAG_QUIT)) { + error("ERROR: Device did not reconnect in DFU mode. Port DFU failed.\n"); + } + return -2; + } + mutex_unlock(&client->device_event_mutex); + + if (client->flags & FLAG_NOACTION) { + info("Port DFU restore successful.\n"); + return 0; + } else { + info("Port DFU restore successful. Continuing.\n"); + } } + idevicerestore_progress(client, RESTORE_STEP_DETECT, 0.8); /* check if device type is supported by the given build manifest */ @@ -665,18 +848,12 @@ int idevicerestore_start(struct idevicerestore_client_t* client) /* print iOS information from the manifest */ build_manifest_get_version_information(client->build_manifest, client); - info("Product Version: %s\n", client->version); - info("Product Build: %s Major: %d\n", client->build, client->build_major); + info("IPSW Product Version: %s\n", client->version); + info("IPSW Product Build: %s Major: %d\n", client->build, client->build_major); client->image4supported = is_image4_supported(client); info("Device supports Image4: %s\n", (client->image4supported) ? "true" : "false"); - if (client->flags & FLAG_CUSTOM) { - /* prevent signing custom firmware */ - tss_enabled = 0; - info("Custom firmware requested. Disabled TSS request.\n"); - } - // choose whether this is an upgrade or a restore (default to upgrade) client->tss = NULL; plist_t build_identity = NULL; @@ -700,7 +877,7 @@ int idevicerestore_start(struct idevicerestore_client_t* client) x++; } - sprintf(p_all_flash, "Firmware/all_flash/all_flash.%s.%s", lcmodel, "production"); + snprintf(p_all_flash, sizeof(p_all_flash), "Firmware/all_flash/all_flash.%s.%s", lcmodel, "production"); strcpy(tmpstr, p_all_flash); strcat(tmpstr, "/manifest"); @@ -750,7 +927,7 @@ int idevicerestore_start(struct idevicerestore_client_t* client) } // add iBSS - sprintf(tmpstr, "Firmware/dfu/iBSS.%s.%s.dfu", lcmodel, "RELEASE"); + snprintf(tmpstr, sizeof(tmpstr), "Firmware/dfu/iBSS.%s.%s.dfu", lcmodel, "RELEASE"); inf = plist_new_dict(); plist_dict_set_item(inf, "Path", plist_new_string(tmpstr)); comp = plist_new_dict(); @@ -758,7 +935,7 @@ int idevicerestore_start(struct idevicerestore_client_t* client) plist_dict_set_item(manifest, "iBSS", comp); // add iBEC - sprintf(tmpstr, "Firmware/dfu/iBEC.%s.%s.dfu", lcmodel, "RELEASE"); + snprintf(tmpstr, sizeof(tmpstr), "Firmware/dfu/iBEC.%s.%s.dfu", lcmodel, "RELEASE"); inf = plist_new_dict(); plist_dict_set_item(inf, "Path", plist_new_string(tmpstr)); comp = plist_new_dict(); @@ -859,17 +1036,11 @@ int idevicerestore_start(struct idevicerestore_client_t* client) } if (client->mode == MODE_NORMAL && !(client->flags & FLAG_ERASE) && !(client->flags & FLAG_SHSHONLY)) { - plist_t pver = normal_get_lockdown_value(client, NULL, "ProductVersion"); - char *device_version = NULL; - if (pver) { - plist_get_string_val(pver, &device_version); - plist_free(pver); - } - if (device_version && (compare_versions(device_version, client->version) > 0)) { + if (client->device_version && (compare_versions(client->device_version, client->version) > 0)) { if (client->flags & FLAG_INTERACTIVE) { char input[64]; char spaces[16]; - int num_spaces = 13 - strlen(client->version) - strlen(device_version); + int num_spaces = 13 - strlen(client->version) - strlen(client->device_version); memset(spaces, ' ', num_spaces); spaces[num_spaces] = '\0'; printf("################################ [ WARNING ] #################################\n" @@ -880,7 +1051,7 @@ int idevicerestore_start(struct idevicerestore_client_t* client) "# If you want to take the risk (and have a backup of your important data!) #\n" "# type YES and press ENTER to continue. You have been warned. #\n" "##############################################################################\n", - device_version, client->version, spaces); + client->device_version, client->version, spaces); while (1) { printf("> "); fflush(stdout); @@ -899,7 +1070,6 @@ int idevicerestore_start(struct idevicerestore_client_t* client) } } } - free(device_version); } if (client->flags & FLAG_ERASE && client->flags & FLAG_INTERACTIVE) { @@ -934,112 +1104,74 @@ int idevicerestore_start(struct idevicerestore_client_t* client) /* check if all components we need are actually there */ info("Checking IPSW for required components...\n"); if (build_identity_check_components_in_ipsw(build_identity, client->ipsw) < 0) { - error("ERROR: Could not find all required components in IPSW %s\n", client->ipsw); + error("ERROR: Could not find all required components in IPSW %s\n", client->ipsw->path); return -1; } info("All required components found in IPSW\n"); - // Get filesystem name from build identity - char* fsname = NULL; - if (build_identity_get_component_path(build_identity, "OS", &fsname) < 0) { + /* Get OS (filesystem) name from build identity */ + char* os_path = NULL; + if (build_identity_get_component_path(build_identity, "OS", &os_path) < 0) { error("ERROR: Unable to get path for filesystem component\n"); return -1; } - // check if we already have an extracted filesystem - int delete_fs = 0; - char* filesystem = NULL; - struct stat st; - memset(&st, '\0', sizeof(struct stat)); - char tmpf[1024]; - if (client->cache_dir) { - if (stat(client->cache_dir, &st) < 0) { - mkdir_with_parents(client->cache_dir, 0755); + /* check if IPSW has OS component 'stored' in ZIP archive, otherwise we need to extract it */ + int needs_os_extraction = 0; + if (client->ipsw->zip) { + ipsw_file_handle_t zfile = ipsw_file_open(client->ipsw, os_path); + if (zfile) { + if (!zfile->seekable) { + needs_os_extraction = 1; + } + ipsw_file_close(zfile); } - strcpy(tmpf, client->cache_dir); - strcat(tmpf, "/"); - char *ipswtmp = strdup(client->ipsw); - strcat(tmpf, basename(ipswtmp)); - free(ipswtmp); - } else { - strcpy(tmpf, client->ipsw); } - if (!ipsw_is_directory(client->ipsw)) { - // strip off file extension if given ipsw is not a directory - char* s = tmpf + strlen(tmpf) - 1; - char* p = s; - while (*p != '\0' && *p != '.' && *p != '/' && *p != '\\') p--; - if (s - p < 6) { - if (*p == '.') { + if (needs_os_extraction && !(client->flags & FLAG_SHSHONLY)) { + char* tmpf = NULL; + struct stat st; + if (client->cache_dir) { + memset(&st, '\0', sizeof(struct stat)); + if (stat(client->cache_dir, &st) < 0) { + mkdir_with_parents(client->cache_dir, 0755); + } + char* ipsw_basename = strdup(path_get_basename(client->ipsw->path)); + char* p = strrchr(ipsw_basename, '.'); + if (p && isalpha(*(p+1))) { *p = '\0'; } - } - } - - if (stat(tmpf, &st) < 0) { - __mkdir(tmpf, 0755); - } - strcat(tmpf, "/"); - strcat(tmpf, fsname); - - memset(&st, '\0', sizeof(struct stat)); - if (stat(tmpf, &st) == 0) { - uint64_t fssize = 0; - ipsw_get_file_size(client->ipsw, fsname, &fssize); - if ((fssize > 0) && ((uint64_t)st.st_size == fssize)) { - info("Using cached filesystem from '%s'\n", tmpf); - filesystem = strdup(tmpf); - } - } - - if (!filesystem && !(client->flags & FLAG_SHSHONLY)) { - char extfn[1024]; - strcpy(extfn, tmpf); - strcat(extfn, ".extract"); - char lockfn[1024]; - strcpy(lockfn, tmpf); - strcat(lockfn, ".lock"); - lock_info_t li; - - lock_file(lockfn, &li); - FILE* extf = NULL; - if (access(extfn, F_OK) != 0) { - extf = fopen(extfn, "wb"); - } - unlock_file(&li); - if (!extf) { - // use temp filename - filesystem = get_temp_filename("ipsw_"); - if (!filesystem) { - error("WARNING: Could not get temporary filename, using '%s' in current directory\n", fsname); - filesystem = strdup(fsname); - } - delete_fs = 1; + tmpf = string_build_path(client->cache_dir, ipsw_basename, NULL); + mkdir_with_parents(tmpf, 0755); + free(tmpf); + tmpf = string_build_path(client->cache_dir, ipsw_basename, os_path, NULL); + free(ipsw_basename); } else { - // use .extract as filename - filesystem = strdup(extfn); - fclose(extf); + tmpf = get_temp_filename(NULL); + client->delete_fs = 1; } - remove(lockfn); - // Extract filesystem from IPSW - info("Extracting filesystem from IPSW: %s\n", fsname); - if (ipsw_extract_to_file_with_progress(client->ipsw, fsname, filesystem, 1) < 0) { - error("ERROR: Unable to extract filesystem from IPSW\n"); - if (client->tss) - plist_free(client->tss); - info("Removing %s\n", filesystem); - unlink(filesystem); - return -1; + /* check if we already have it extracted */ + uint64_t fssize = 0; + ipsw_get_file_size(client->ipsw, os_path, &fssize); + memset(&st, '\0', sizeof(struct stat)); + if (stat(tmpf, &st) == 0) { + if ((fssize > 0) && ((uint64_t)st.st_size == fssize)) { + info("Using cached filesystem from '%s'\n", tmpf); + client->filesystem = tmpf; + } } - if (strstr(filesystem, ".extract")) { - // rename .extract to - remove(tmpf); - rename(filesystem, tmpf); - free(filesystem); - filesystem = strdup(tmpf); + if (!client->filesystem) { + info("Extracting filesystem from IPSW: %s\n", os_path); + if (ipsw_extract_to_file_with_progress(client->ipsw, os_path, tmpf, 1) < 0) { + error("ERROR: Unable to extract filesystem from IPSW\n"); + info("Removing %s\n", tmpf); + unlink(tmpf); + free(tmpf); + return -1; + } + client->filesystem = tmpf; } } @@ -1080,7 +1212,7 @@ int idevicerestore_start(struct idevicerestore_client_t* client) if (client->build_major > 8) { unsigned char* nonce = NULL; - int nonce_size = 0; + unsigned int nonce_size = 0; if (get_ap_nonce(client, &nonce, &nonce_size) < 0) { /* the first nonce request with older firmware releases can fail and it's OK */ info("NOTE: Unable to get nonce from device\n"); @@ -1095,26 +1227,76 @@ int idevicerestore_start(struct idevicerestore_client_t* client) } else { free(nonce); } + if (client->mode == MODE_NORMAL) { + plist_t ap_params = normal_get_lockdown_value(client, NULL, "ApParameters"); + if (ap_params) { + if (!client->parameters) { + client->parameters = plist_new_dict(); + } + plist_dict_merge(&client->parameters, ap_params); + plist_t p_sep_nonce = plist_dict_get_item(ap_params, "SepNonce"); + uint64_t sep_nonce_size = 0; + const char* sep_nonce = plist_get_data_ptr(p_sep_nonce, &sep_nonce_size); + info("Getting SepNonce in normal mode... "); + int i = 0; + for (i = 0; i < sep_nonce_size; i++) { + info("%02x ", (unsigned char)sep_nonce[i]); + } + info("\n"); + plist_free(ap_params); + } + plist_t req_nonce_slot = plist_access_path(build_identity, 2, "Info", "RequiresNonceSlot"); + if (req_nonce_slot) { + plist_dict_set_item(client->parameters, "RequiresNonceSlot", plist_copy(req_nonce_slot)); + } + } } if (client->flags & FLAG_QUIT) { return -1; } - if (get_tss_response(client, build_identity, &client->tss) < 0) { - error("ERROR: Unable to get SHSH blobs for this device\n"); - return -1; - } - if (client->macos_variant) { - if (get_local_policy_tss_response(client, build_identity, &client->tss_localpolicy) < 0) { - error("ERROR: Unable to get SHSH blobs for this device (local policy)\n"); + if (client->mode == MODE_RESTORE && client->root_ticket) { + plist_t ap_ticket = plist_new_data((char*)client->root_ticket, client->root_ticket_len); + if (!ap_ticket) { + error("ERROR: Failed to create ApImg4Ticket node value.\n"); return -1; } - if (get_recoveryos_root_ticket_tss_response(client, build_identity, &client->tss_recoveryos_root_ticket) < - 0) { - error("ERROR: Unable to get SHSH blobs for this device (recovery OS Root Ticket)\n"); + client->tss = plist_new_dict(); + if (!client->tss) { + error("ERROR: Failed to create ApImg4Ticket node.\n"); return -1; } + plist_dict_set_item(client->tss, "ApImg4Ticket", ap_ticket); + } else { + if (get_tss_response(client, build_identity, &client->tss) < 0) { + error("ERROR: Unable to get SHSH blobs for this device\n"); + return -1; + } + if (client->macos_variant) { + if (get_local_policy_tss_response(client, build_identity, &client->tss_localpolicy) < 0) { + error("ERROR: Unable to get SHSH blobs for this device (local policy)\n"); + return -1; + } + if (get_recoveryos_root_ticket_tss_response(client, build_identity, &client->tss_recoveryos_root_ticket) < 0) { + error("ERROR: Unable to get SHSH blobs for this device (recovery OS Root Ticket)\n"); + return -1; + } + } else { + plist_t recovery_variant = plist_access_path(build_identity, 2, "Info", "RecoveryVariant"); + if (recovery_variant) { + const char* recovery_variant_str = plist_get_string_ptr(recovery_variant, NULL); + client->recovery_variant = build_manifest_get_build_identity_for_model_with_variant(client->build_manifest, client->device->hardware_model, recovery_variant_str, 1); + if (!client->recovery_variant) { + error("ERROR: Variant '%s' not found in BuildManifest\n", recovery_variant_str); + return -1; + } + if (get_tss_response(client, client->recovery_variant, &client->tss_recoveryos_root_ticket) < 0) { + error("ERROR: Unable to get SHSH blobs for this device (%s)\n", recovery_variant_str); + return -1; + } + } + } } if (stashbag_commit_required) { @@ -1133,8 +1315,6 @@ int idevicerestore_start(struct idevicerestore_client_t* client) } if (client->flags & FLAG_QUIT) { - if (delete_fs && filesystem) - unlink(filesystem); return -1; } if (client->flags & FLAG_SHSHONLY) { @@ -1158,7 +1338,7 @@ int idevicerestore_start(struct idevicerestore_client_t* client) strcpy(zfn, "shsh"); } mkdir_with_parents(zfn, 0755); - sprintf(zfn+strlen(zfn), "/%" PRIu64 "-%s-%s.shsh", client->ecid, client->device->product_type, client->version); + snprintf(&zfn[0]+strlen(zfn), sizeof(zfn)-strlen(zfn), "/%" PRIu64 "-%s-%s.shsh", client->ecid, client->device->product_type, client->version); struct stat fst; if (stat(zfn, &fst) != 0) { gzFile zf = gzopen(zfn, "wb"); @@ -1189,8 +1369,6 @@ int idevicerestore_start(struct idevicerestore_client_t* client) } idevicerestore_progress(client, RESTORE_STEP_PREPARE, 0.25); if (client->flags & FLAG_QUIT) { - if (delete_fs && filesystem) - unlink(filesystem); return -1; } @@ -1207,8 +1385,6 @@ int idevicerestore_start(struct idevicerestore_client_t* client) idevicerestore_progress(client, RESTORE_STEP_PREPARE, 0.3); if (client->flags & FLAG_QUIT) { - if (delete_fs && filesystem) - unlink(filesystem); return -1; } @@ -1219,17 +1395,12 @@ int idevicerestore_start(struct idevicerestore_client_t* client) if ((client->flags & FLAG_CUSTOM) && limera1n_is_supported(client->device)) { info("connecting to DFU\n"); if (dfu_client_new(client) < 0) { - if (delete_fs && filesystem) - unlink(filesystem); return -1; } info("exploiting with limera1n\n"); - // TODO: check for non-limera1n device and fail if (limera1n_exploit(client->device, &client->dfu->client) != 0) { error("ERROR: limera1n exploit failed\n"); dfu_client_free(client); - if (delete_fs && filesystem) - unlink(filesystem); return -1; } dfu_client_free(client); @@ -1239,8 +1410,6 @@ int idevicerestore_start(struct idevicerestore_client_t* client) error("ERROR: Unable to place device into recovery mode from DFU mode\n"); if (client->tss) plist_free(client->tss); - if (delete_fs && filesystem) - unlink(filesystem); return -2; } } else if (client->mode == MODE_RECOVERY) { @@ -1250,8 +1419,6 @@ int idevicerestore_start(struct idevicerestore_client_t* client) /* send ApTicket */ if (recovery_send_ticket(client) < 0) { error("ERROR: Unable to send APTicket\n"); - if (delete_fs && filesystem) - unlink(filesystem); return -2; } } @@ -1263,8 +1430,6 @@ int idevicerestore_start(struct idevicerestore_client_t* client) if (recovery_send_ibec(client, build_identity) < 0) { mutex_unlock(&client->device_event_mutex); error("ERROR: Unable to send iBEC\n"); - if (delete_fs && filesystem) - unlink(filesystem); return -2; } recovery_client_free(client); @@ -1277,10 +1442,9 @@ int idevicerestore_start(struct idevicerestore_client_t* client) if (!(client->flags & FLAG_QUIT)) { error("ERROR: Device did not disconnect. Possibly invalid iBEC. Reset device and try again.\n"); } - if (delete_fs && filesystem) - unlink(filesystem); return -2; } + recovery_client_free(client); debug("Waiting for device to reconnect in recovery mode...\n"); cond_wait_timeout(&client->device_event_cond, &client->device_event_mutex, 60000); if (client->mode != MODE_RECOVERY || (client->flags & FLAG_QUIT)) { @@ -1288,29 +1452,23 @@ int idevicerestore_start(struct idevicerestore_client_t* client) if (!(client->flags & FLAG_QUIT)) { error("ERROR: Device did not reconnect in recovery mode. Possibly invalid iBEC. Reset device and try again.\n"); } - if (delete_fs && filesystem) - unlink(filesystem); return -2; } mutex_unlock(&client->device_event_mutex); } idevicerestore_progress(client, RESTORE_STEP_PREPARE, 0.5); if (client->flags & FLAG_QUIT) { - if (delete_fs && filesystem) - unlink(filesystem); return -1; } if (!client->image4supported && (client->build_major > 8)) { // we need another tss request with nonce. unsigned char* nonce = NULL; - int nonce_size = 0; + unsigned int nonce_size = 0; int nonce_changed = 0; if (get_ap_nonce(client, &nonce, &nonce_size) < 0) { error("ERROR: Unable to get nonce from device!\n"); recovery_send_reset(client); - if (delete_fs && filesystem) - unlink(filesystem); return -2; } @@ -1330,14 +1488,10 @@ int idevicerestore_start(struct idevicerestore_client_t* client) plist_free(client->tss); if (get_tss_response(client, build_identity, &client->tss) < 0) { error("ERROR: Unable to get SHSH blobs for this device\n"); - if (delete_fs && filesystem) - unlink(filesystem); return -1; } if (!client->tss) { error("ERROR: can't continue without TSS\n"); - if (delete_fs && filesystem) - unlink(filesystem); return -1; } fixup_tss(client->tss); @@ -1345,8 +1499,6 @@ int idevicerestore_start(struct idevicerestore_client_t* client) } idevicerestore_progress(client, RESTORE_STEP_PREPARE, 0.7); if (client->flags & FLAG_QUIT) { - if (delete_fs && filesystem) - unlink(filesystem); return -1; } @@ -1356,8 +1508,6 @@ int idevicerestore_start(struct idevicerestore_client_t* client) error("ERROR: Unable to place device into restore mode\n"); if (client->tss) plist_free(client->tss); - if (delete_fs && filesystem) - unlink(filesystem); return -2; } recovery_client_free(client); @@ -1371,9 +1521,11 @@ int idevicerestore_start(struct idevicerestore_client_t* client) if (client->mode != MODE_RESTORE || (client->flags & FLAG_QUIT)) { mutex_unlock(&client->device_event_mutex); error("ERROR: Device failed to enter restore mode.\n"); - error("Please make sure that usbmuxd is running.\n"); - if (delete_fs && filesystem) - unlink(filesystem); + if (client->mode == MODE_UNKNOWN) { + error("Make sure that usbmuxd is running.\n"); + } else if (client->mode == MODE_RECOVERY) { + error("Device reconnected in recovery mode, most likely image personalization failed.\n"); + } return -1; } mutex_unlock(&client->device_event_mutex); @@ -1382,24 +1534,18 @@ int idevicerestore_start(struct idevicerestore_client_t* client) // device is finally in restore mode, let's do this if (client->mode == MODE_RESTORE) { if ((client->flags & FLAG_NO_RESTORE) != 0) { - info("Device is now in restore mode. Exiting as requested."); + info("Device is now in restore mode. Exiting as requested.\n"); return 0; } client->ignore_device_add_events = 1; info("About to restore device... \n"); - result = restore_device(client, build_identity, filesystem); + result = restore_device(client, build_identity); if (result < 0) { error("ERROR: Unable to restore device\n"); - if (delete_fs && filesystem) - unlink(filesystem); return result; } } - info("Cleaning up...\n"); - if (delete_fs && filesystem) - unlink(filesystem); - /* special handling of older AppleTVs as they enter Recovery mode on boot when plugged in to USB */ if ((strncmp(client->device->product_type, "AppleTV", 7) == 0) && (client->device->product_type[7] < '5')) { if (recovery_client_new(client) == 0) { @@ -1413,10 +1559,11 @@ int idevicerestore_start(struct idevicerestore_client_t* client) } } - info("DONE\n"); - if (result == 0) { + info("DONE\n"); idevicerestore_progress(client, RESTORE_NUM_STEPS-1, 1.0); + } else { + info("RESTORE FAILED\n"); } if (build_identity_needs_free) @@ -1470,14 +1617,18 @@ void idevicerestore_client_free(struct idevicerestore_client_t* client) free(client->srnm); } if (client->ipsw) { - free(client->ipsw); - } - if (client->version) { - free(client->version); + ipsw_close(client->ipsw); } - if (client->build) { - free(client->build); + if (client->filesystem) { + if (client->delete_fs) { + unlink(client->filesystem); + } + free(client->filesystem); } + free(client->version); + free(client->build); + free(client->device_version); + free(client->device_build); if (client->restore_boot_args) { free(client->restore_boot_args); } @@ -1490,6 +1641,9 @@ void idevicerestore_client_free(struct idevicerestore_client_t* client) if (client->build_manifest) { plist_free(client->build_manifest); } + if (client->firmware_preflight_info) { + plist_free(client->firmware_preflight_info); + } if (client->preflight_info) { plist_free(client->preflight_info); } @@ -1529,11 +1683,11 @@ void idevicerestore_set_ipsw(struct idevicerestore_client_t* client, const char* if (!client) return; if (client->ipsw) { - free(client->ipsw); + ipsw_close(client->ipsw); client->ipsw = NULL; } if (path) { - client->ipsw = strdup(path); + client->ipsw = ipsw_open(path); } } @@ -1611,7 +1765,7 @@ int main(int argc, char* argv[]) { client->flags |= FLAG_INTERACTIVE; } - while ((opt = getopt_long(argc, argv, "dhcesxtpli:u:nC:kyPRT:zv", longopts, &optindex)) > 0) { + while ((opt = getopt_long(argc, argv, "dhces:xtpli:u:nC:kyPRT:zv", longopts, &optindex)) > 0) { switch (opt) { case 'h': usage(argc, argv, 0); @@ -1619,6 +1773,7 @@ int main(int argc, char* argv[]) { case 'd': client->flags |= FLAG_DEBUG; + client->debug_level++; break; case 'e': @@ -1629,8 +1784,36 @@ int main(int argc, char* argv[]) { client->flags |= FLAG_CUSTOM; break; - case 's': - client->tss_url = strdup("http://cydia.saurik.com/TSS/controller?action=2"); + case 's': { + if (!*optarg) { + error("ERROR: URL argument for --server must not be empty!\n"); + usage(argc, argv, 1); + return EXIT_FAILURE; + } + char *baseurl = NULL; + if (!strncmp(optarg, "http://", 7) && (strlen(optarg) > 7) && (optarg[7] != '/')) { + baseurl = optarg+7; + } else if (!strncmp(optarg, "https://", 8) && (strlen(optarg) > 8) && (optarg[8] != '/')) { + baseurl = optarg+8; + } + if (baseurl) { + char *p = strchr(baseurl, '/'); + if (!p || *(p+1) == '\0') { + // no path component, add default path + const char default_path[] = "/TSS/controller?action=2"; + size_t usize = strlen(optarg)+sizeof(default_path); + char* newurl = malloc(usize); + snprintf(newurl, usize, "%s%s", optarg, (p) ? default_path+1 : default_path); + client->tss_url = newurl; + } else { + client->tss_url = strdup(optarg); + } + } else { + error("ERROR: URL argument for --server is invalid, must start with http:// or https://\n"); + usage(argc, argv, 1); + return EXIT_FAILURE; + } + } break; case 'x': @@ -1701,7 +1884,7 @@ int main(int argc, char* argv[]) { break; case 'v': - info("%s %s\n", PACKAGE_NAME, PACKAGE_VERSION); + info("%s %s (libirecovery %s, libtatsu %s)\n", PACKAGE_NAME, PACKAGE_VERSION, irecv_version(), libtatsu_version()); return EXIT_SUCCESS; case 'T': { @@ -1759,10 +1942,15 @@ int main(int argc, char* argv[]) { return EXIT_FAILURE; } - info("%s %s\n", PACKAGE_NAME, PACKAGE_VERSION); + info("%s %s (libirecovery %s, libtatsu %s)\n", PACKAGE_NAME, PACKAGE_VERSION, irecv_version(), libtatsu_version()); if (ipsw) { - client->ipsw = strdup(ipsw); + // verify if ipsw file exists + client->ipsw = ipsw_open(ipsw); + if (!client->ipsw) { + error("ERROR: Firmware file %s cannot be opened.\n", ipsw); + return -1; + } } curl_global_init(CURL_GLOBAL_ALL); @@ -1793,6 +1981,7 @@ irecv_device_t get_irecv_device(struct idevicerestore_client_t *client) return normal_get_irecv_device(client); case _MODE_DFU: + case _MODE_PORTDFU: case _MODE_RECOVERY: return dfu_get_irecv_device(client); @@ -1830,7 +2019,7 @@ int is_image4_supported(struct idevicerestore_client_t* client) return res; } -int get_ap_nonce(struct idevicerestore_client_t* client, unsigned char** nonce, int* nonce_size) +int get_ap_nonce(struct idevicerestore_client_t* client, unsigned char** nonce, unsigned int* nonce_size) { int mode = _MODE_UNKNOWN; @@ -1881,7 +2070,7 @@ int get_ap_nonce(struct idevicerestore_client_t* client, unsigned char** nonce, return 0; } -int get_sep_nonce(struct idevicerestore_client_t* client, unsigned char** nonce, int* nonce_size) +int get_sep_nonce(struct idevicerestore_client_t* client, unsigned char** nonce, unsigned int* nonce_size) { int mode = _MODE_UNKNOWN; @@ -2001,7 +2190,7 @@ int get_preboard_manifest(struct idevicerestore_client_t* client, plist_t build_ plist_t overrides = plist_new_dict(); plist_dict_set_item(overrides, "@APTicket", plist_new_bool(1)); plist_dict_set_item(overrides, "ApProductionMode", plist_new_uint(0)); - plist_dict_set_item(overrides, "ApSecurityDomain", plist_new_uint(0)); + plist_dict_set_item(overrides, "ApSecurityDomain", plist_new_uint(1)); plist_dict_set_item(parameters, "ApProductionMode", plist_new_bool(0)); plist_dict_set_item(parameters, "ApSecurityMode", plist_new_bool(0)); @@ -2060,9 +2249,9 @@ int get_tss_response(struct idevicerestore_client_t* client, plist_t build_ident char zfn[1024]; if (client->version) { if (client->cache_dir) { - sprintf(zfn, "%s/shsh/%" PRIu64 "-%s-%s.shsh", client->cache_dir, client->ecid, client->device->product_type, client->version); + snprintf(zfn, sizeof(zfn), "%s/shsh/%" PRIu64 "-%s-%s.shsh", client->cache_dir, client->ecid, client->device->product_type, client->version); } else { - sprintf(zfn, "shsh/%" PRIu64 "-%s-%s.shsh", client->ecid, client->device->product_type, client->version); + snprintf(zfn, sizeof(zfn), "shsh/%" PRIu64 "-%s-%s.shsh", client->ecid, client->device->product_type, client->version); } struct stat fst; if (stat(zfn, &fst) == 0) { @@ -2117,17 +2306,21 @@ int get_tss_response(struct idevicerestore_client_t* client, plist_t build_ident /* populate parameters */ plist_t parameters = plist_new_dict(); + plist_dict_merge(¶meters, client->parameters); + plist_dict_set_item(parameters, "ApECID", plist_new_uint(client->ecid)); if (client->nonce) { plist_dict_set_item(parameters, "ApNonce", plist_new_data((const char*)client->nonce, client->nonce_size)); } - unsigned char* sep_nonce = NULL; - int sep_nonce_size = 0; - get_sep_nonce(client, &sep_nonce, &sep_nonce_size); - if (sep_nonce) { - plist_dict_set_item(parameters, "ApSepNonce", plist_new_data((const char*)sep_nonce, sep_nonce_size)); - free(sep_nonce); + if (!plist_dict_get_item(parameters, "SepNonce")) { + unsigned char* sep_nonce = NULL; + unsigned int sep_nonce_size = 0; + get_sep_nonce(client, &sep_nonce, &sep_nonce_size); + if (sep_nonce) { + plist_dict_set_item(parameters, "ApSepNonce", plist_new_data((const char*)sep_nonce, sep_nonce_size)); + free(sep_nonce); + } } plist_dict_set_item(parameters, "ApProductionMode", plist_new_bool(1)); @@ -2185,27 +2378,31 @@ int get_tss_response(struct idevicerestore_client_t* client, plist_t build_ident if (client->mode == MODE_NORMAL) { /* normal mode; request baseband ticket aswell */ plist_t pinfo = NULL; - normal_get_preflight_info(client, &pinfo); + normal_get_firmware_preflight_info(client, &pinfo); if (pinfo) { - _plist_dict_copy_data(parameters, pinfo, "BbNonce", "Nonce"); - _plist_dict_copy_uint(parameters, pinfo, "BbChipID", "ChipID"); - _plist_dict_copy_uint(parameters, pinfo, "BbGoldCertId", "CertID"); - _plist_dict_copy_data(parameters, pinfo, "BbSNUM", "ChipSerialNo"); + plist_dict_copy_data(parameters, pinfo, "BbNonce", "Nonce"); + plist_dict_copy_uint(parameters, pinfo, "BbChipID", "ChipID"); + plist_dict_copy_uint(parameters, pinfo, "BbGoldCertId", "CertID"); + plist_dict_copy_data(parameters, pinfo, "BbSNUM", "ChipSerialNo"); /* add baseband parameters */ tss_request_add_baseband_tags(request, parameters, NULL); - _plist_dict_copy_uint(parameters, pinfo, "eUICC,ChipID", "EUICCChipID"); - if (_plist_dict_get_uint(parameters, "eUICC,ChipID") >= 5) { - _plist_dict_copy_data(parameters, pinfo, "eUICC,EID", "EUICCCSN"); - _plist_dict_copy_data(parameters, pinfo, "eUICC,RootKeyIdentifier", "EUICCCertIdentifier"); - _plist_dict_copy_data(parameters, pinfo, "EUICCGoldNonce", NULL); - _plist_dict_copy_data(parameters, pinfo, "EUICCMainNonce", NULL); + plist_dict_copy_uint(parameters, pinfo, "eUICC,ChipID", "EUICCChipID"); + if (plist_dict_get_uint(parameters, "eUICC,ChipID") >= 5) { + plist_dict_copy_data(parameters, pinfo, "eUICC,EID", "EUICCCSN"); + plist_dict_copy_data(parameters, pinfo, "eUICC,RootKeyIdentifier", "EUICCCertIdentifier"); + plist_dict_copy_data(parameters, pinfo, "EUICCGoldNonce", NULL); + plist_dict_copy_data(parameters, pinfo, "EUICCMainNonce", NULL); /* add vinyl parameters */ tss_request_add_vinyl_tags(request, parameters, NULL); } } + client->firmware_preflight_info = pinfo; + pinfo = NULL; + + normal_get_preflight_info(client, &pinfo); client->preflight_info = pinfo; } @@ -2246,7 +2443,7 @@ int get_recoveryos_root_ticket_tss_response(struct idevicerestore_client_t* clie plist_dict_set_item(parameters, "ApNonce", plist_new_data((const char*)client->nonce, client->nonce_size)); } unsigned char* sep_nonce = NULL; - int sep_nonce_size = 0; + unsigned int sep_nonce_size = 0; get_sep_nonce(client, &sep_nonce, &sep_nonce_size); /* ApSepNonce */ @@ -2269,7 +2466,7 @@ int get_recoveryos_root_ticket_tss_response(struct idevicerestore_client_t* clie tss_parameters_add_from_manifest(parameters, build_identity, true); /* create basic request */ - /* Adds @BBTicket, @HostPlatformInfo, @VersionInfo, @UUID */ + /* Adds @HostPlatformInfo, @VersionInfo, @UUID */ request = tss_request_new(NULL); if (request == NULL) { error("ERROR: Unable to create TSS request\n"); @@ -2278,7 +2475,7 @@ int get_recoveryos_root_ticket_tss_response(struct idevicerestore_client_t* clie } /* add common tags from manifest */ - /* Adds Ap,OSLongVersion, AppNonce, @ApImg4Ticket */ + /* Adds Ap,OSLongVersion, ApNonce, @ApImg4Ticket */ if (tss_request_add_ap_img4_tags(request, parameters) < 0) { error("ERROR: Unable to add AP IMG4 tags to TSS request\n"); plist_free(request); @@ -2350,14 +2547,14 @@ int get_recovery_os_local_policy_tss_response( // Add Ap,LocalPolicy uint8_t digest[SHA384_DIGEST_LENGTH]; - SHA384(lpol_file, lpol_file_length, digest); + sha384(lpol_file, lpol_file_length, digest); plist_t lpol = plist_new_dict(); plist_dict_set_item(lpol, "Digest", plist_new_data((char*)digest, SHA384_DIGEST_LENGTH)); plist_dict_set_item(lpol, "Trusted", plist_new_bool(1)); plist_dict_set_item(parameters, "Ap,LocalPolicy", lpol); - _plist_dict_copy_data(parameters, args, "Ap,NextStageIM4MHash", NULL); - _plist_dict_copy_data(parameters, args, "Ap,RecoveryOSPolicyNonceHash", NULL); + plist_dict_copy_data(parameters, args, "Ap,NextStageIM4MHash", NULL); + plist_dict_copy_data(parameters, args, "Ap,RecoveryOSPolicyNonceHash", NULL); plist_t vol_uuid_node = plist_dict_get_item(args, "Ap,VolumeUUID"); char* vol_uuid_str = NULL; @@ -2425,7 +2622,7 @@ int get_local_policy_tss_response(struct idevicerestore_client_t* client, plist_ plist_dict_set_item(parameters, "ApNonce", plist_new_data((const char*)client->nonce, client->nonce_size)); } unsigned char* sep_nonce = NULL; - int sep_nonce_size = 0; + unsigned int sep_nonce_size = 0; get_sep_nonce(client, &sep_nonce, &sep_nonce_size); if (sep_nonce) { @@ -2445,7 +2642,7 @@ int get_local_policy_tss_response(struct idevicerestore_client_t* client, plist_ // Add Ap,LocalPolicy uint8_t digest[SHA384_DIGEST_LENGTH]; - SHA384(lpol_file, lpol_file_length, digest); + sha384(lpol_file, lpol_file_length, digest); plist_t lpol = plist_new_dict(); plist_dict_set_item(lpol, "Digest", plist_new_data((char*)digest, SHA384_DIGEST_LENGTH)); plist_dict_set_item(lpol, "Trusted", plist_new_bool(1)); @@ -2458,7 +2655,7 @@ int get_local_policy_tss_response(struct idevicerestore_client_t* client, plist_ tss_response_get_ap_img4_ticket(client->tss, &ticket, &ticket_length); // Hash it and add it as Ap,NextStageIM4MHash uint8_t hash[SHA384_DIGEST_LENGTH]; - SHA384(ticket, ticket_length, hash); + sha384(ticket, ticket_length, hash); plist_dict_set_item(parameters, "Ap,NextStageIM4MHash", plist_new_data((char*)hash, SHA384_DIGEST_LENGTH)); /* create basic request */ @@ -2537,7 +2734,7 @@ int build_manifest_get_identity_count(plist_t build_manifest) return plist_array_get_size(build_identities_array); } -int extract_component(const char* ipsw, const char* path, unsigned char** component_data, unsigned int* component_size) +int extract_component(ipsw_archive_t ipsw, const char* path, unsigned char** component_data, unsigned int* component_size) { char* component_name = NULL; if (!ipsw || !path || !component_data || !component_size) { @@ -2552,23 +2749,23 @@ int extract_component(const char* ipsw, const char* path, unsigned char** compon info("Extracting %s (%s)...\n", component_name, path); if (ipsw_extract_to_memory(ipsw, path, component_data, component_size) < 0) { - error("ERROR: Unable to extract %s from %s\n", component_name, ipsw); + error("ERROR: Unable to extract %s from %s\n", component_name, ipsw->path); return -1; } return 0; } -int personalize_component(const char *component_name, const unsigned char* component_data, unsigned int component_size, plist_t tss_response, unsigned char** personalized_component, unsigned int* personalized_component_size) +int personalize_component(struct idevicerestore_client_t* client, const char *component_name, const unsigned char* component_data, unsigned int component_size, plist_t tss_response, unsigned char** personalized_component, unsigned int* personalized_component_size) { unsigned char* component_blob = NULL; unsigned int component_blob_size = 0; unsigned char* stitched_component = NULL; unsigned int stitched_component_size = 0; - if (tss_response && tss_response_get_ap_img4_ticket(tss_response, &component_blob, &component_blob_size) == 0) { + if (tss_response && plist_dict_get_item(tss_response, "ApImg4Ticket")) { /* stitch ApImg4Ticket into IMG4 file */ - img4_stitch_component(component_name, component_data, component_size, component_blob, component_blob_size, &stitched_component, &stitched_component_size); + img4_stitch_component(component_name, component_data, component_size, client->parameters, tss_response, &stitched_component, &stitched_component_size); } else { /* try to get blob for current component from tss response */ if (tss_response && tss_response_get_blob_by_entry(tss_response, component_name, &component_blob) < 0) { @@ -2685,7 +2882,7 @@ void build_identity_print_information(plist_t build_identity) node = NULL; } -int build_identity_check_components_in_ipsw(plist_t build_identity, const char *ipsw) +int build_identity_check_components_in_ipsw(plist_t build_identity, ipsw_archive_t ipsw) { plist_t manifest_node = plist_dict_get_item(build_identity, "Manifest"); if (!manifest_node || plist_get_node_type(manifest_node) != PLIST_DICT) { diff --git a/src/idevicerestore.h b/src/idevicerestore.h index 8a5736ff..fe9d11f2 100644 --- a/src/idevicerestore.h +++ b/src/idevicerestore.h @@ -52,6 +52,8 @@ extern "C" { #define RESTORE_VARIANT_MACOS_RECOVERY_OS "macOS Customer" struct idevicerestore_client_t; +struct ipsw_archive; +typedef struct ipsw_archive* ipsw_archive_t; enum { RESTORE_STEP_DETECT = 0, @@ -61,6 +63,7 @@ enum { RESTORE_STEP_FLASH_FW, RESTORE_STEP_FLASH_BB, RESTORE_STEP_FUD, + RESTORE_STEP_UPLOAD_IMG, RESTORE_NUM_STEPS }; @@ -90,8 +93,8 @@ const char* idevicerestore_get_error(void); irecv_device_t get_irecv_device(struct idevicerestore_client_t* client); int get_ecid(struct idevicerestore_client_t* client, uint64_t* ecid); int is_image4_supported(struct idevicerestore_client_t* client); -int get_ap_nonce(struct idevicerestore_client_t* client, unsigned char** nonce, int* nonce_size); -int get_sep_nonce(struct idevicerestore_client_t* client, unsigned char** nonce, int* nonce_size); +int get_ap_nonce(struct idevicerestore_client_t* client, unsigned char** nonce, unsigned int* nonce_size); +int get_sep_nonce(struct idevicerestore_client_t* client, unsigned char** nonce, unsigned int* nonce_size); int get_tss_response(struct idevicerestore_client_t* client, plist_t build_identity, plist_t* tss); int get_local_policy_tss_response(struct idevicerestore_client_t* client, plist_t build_identity, plist_t* tss); int get_recoveryos_root_ticket_tss_response(struct idevicerestore_client_t* client, plist_t build_identity, plist_t* tss); @@ -108,12 +111,11 @@ plist_t build_manifest_get_build_identity_for_model(plist_t build_manifest, cons plist_t build_manifest_get_build_identity_for_model_with_variant(plist_t build_manifest, const char *hardware_model, const char *variant, int exact); int build_manifest_get_build_count(plist_t build_manifest); void build_identity_print_information(plist_t build_identity); -int build_identity_check_components_in_ipsw(plist_t build_identity, const char* ipsw); int build_identity_has_component(plist_t build_identity, const char* component); int build_identity_get_component_path(plist_t build_identity, const char* component, char** path); -int ipsw_extract_filesystem(const char* ipsw, plist_t build_identity, char** filesystem); -int extract_component(const char* ipsw, const char* path, unsigned char** component_data, unsigned int* component_size); -int personalize_component(const char *component, const unsigned char* component_data, unsigned int component_size, plist_t tss_response, unsigned char** personalized_component, unsigned int* personalized_component_size); +int ipsw_extract_filesystem(ipsw_archive_t ipsw, plist_t build_identity, char** filesystem); +int extract_component(ipsw_archive_t ipsw, const char* path, unsigned char** component_data, unsigned int* component_size); +int personalize_component(struct idevicerestore_client_t* client, const char *component, const unsigned char* component_data, unsigned int component_size, plist_t tss_response, unsigned char** personalized_component, unsigned int* personalized_component_size); int get_preboard_manifest(struct idevicerestore_client_t* client, plist_t build_identity, plist_t* manifest); const char* get_component_name(const char* filename); diff --git a/src/img4.c b/src/img4.c index 609e80be..e9d4ccac 100644 --- a/src/img4.c +++ b/src/img4.c @@ -22,8 +22,11 @@ #include #include +#include + #include "common.h" #include "img4.h" +#include "endianness.h" #define ASN1_PRIVATE 0xc0 #define ASN1_PRIMITIVE_TAG 0x1f @@ -201,7 +204,7 @@ static void asn1_write_element(unsigned char **p, unsigned int *length, unsigned } } break; default: - fprintf(stderr, "ERROR: %s: type %02x is not implemented", __func__, type); + fprintf(stderr, "ERROR: %s: type %02x is not implemented\n", __func__, type); return; } } @@ -275,28 +278,37 @@ static const char *_img4_get_component_tag(const char *compname) { "ACIBT", "acib" }, { "ACIBTLPEM", "lpbt" }, { "ACIWIFI", "aciw" }, - { "Alamo", "almo" }, { "ANE", "anef" }, { "ANS", "ansf" }, { "AOP", "aopf" }, + { "AVE", "avef" }, + { "Alamo", "almo" }, + { "Ap,ANE1", "ane1" }, + { "Ap,ANE2", "ane2" }, + { "Ap,ANE3", "ane3" }, { "Ap,AudioAccessibilityBootChime", "auac" }, { "Ap,AudioBootChime", "aubt" }, { "Ap,AudioPowerAttachChime", "aupr" }, + { "Ap,BootabilityBrainTrustCache", "trbb" }, { "Ap,CIO", "ciof" }, { "Ap,HapticAssets", "hpas" }, { "Ap,LocalBoot", "lobo" }, { "Ap,LocalPolicy", "lpol" }, { "Ap,NextStageIM4MHash", "nsih" }, { "Ap,RecoveryOSPolicyNonceHash", "ronh" }, + { "Ap,RestoreANE1", "ran1" }, + { "Ap,RestoreANE2", "ran2" }, + { "Ap,RestoreANE3", "ran3" }, { "Ap,RestoreCIO", "rcio" }, { "Ap,RestoreTMU", "rtmu" }, { "Ap,Scorpius", "scpf" }, { "Ap,SystemVolumeCanonicalMetadata", "msys" }, { "Ap,TMU", "tmuf" }, { "Ap,VolumeUUID", "vuid" }, + { "Ap,rOSLogo1", "rlg1" }, + { "Ap,rOSLogo2", "rlg2" }, { "AppleLogo", "logo" }, { "AudioCodecFirmware", "acfw" }, - { "AVE", "avef" }, { "BatteryCharging", "glyC" }, { "BatteryCharging0", "chg0" }, { "BatteryCharging1", "chg1" }, @@ -305,28 +317,22 @@ static const char *_img4_get_component_tag(const char *compname) { "BatteryLow1", "bat1" }, { "BatteryPlugin", "glyP" }, { "CFELoader", "cfel" }, - { "Dali", "dali" }, + { "CrownFirmware", "crwn" }, { "DCP", "dcpf" }, + { "Dali", "dali" }, { "DeviceTree", "dtre" }, { "Diags", "diag" }, { "EngineeringTrustCache", "dtrs" }, { "ExtDCP", "edcp" }, - { "ftap", "ftap" }, - { "ftsp", "ftsp" }, { "GFX", "gfxf" }, { "Hamm", "hamf" }, { "Homer", "homr" }, - { "iBEC", "ibec" }, - { "iBoot", "ibot" }, - { "iBootData", "ibdt" }, - { "iBootTest", "itst" }, - { "iBSS", "ibss" }, - { "InputDevice", "ipdf" }, { "ISP", "ispf" }, + { "InputDevice", "ipdf" }, { "KernelCache", "krnl" }, + { "LLB", "illb" }, { "LeapHaptics", "lphp" }, { "Liquid", "liqd" }, - { "LLB", "illb" }, { "LoadableTrustCache", "ltrs" }, { "LowPowerWallet0", "lpw0" }, { "LowPowerWallet1", "lpw1" }, @@ -337,12 +343,13 @@ static const char *_img4_get_component_tag(const char *compname) { "NeedService", "nsrv" }, { "OS", "OS\0\0" }, { "OSRamdisk", "osrd" }, - { "PersonalizedDMG", "pdmg" }, { "PEHammer", "hmmr" }, { "PERTOS", "pert" }, { "PHLEET", "phlt" }, { "PMP", "pmpf" }, + { "PersonalizedDMG", "pdmg" }, { "RBM", "rmbt" }, + { "RTP", "rtpf" }, { "Rap,SoftwareBinaryDsp1", "sbd1" }, { "Rap,RTKitOS", "rkos" }, { "Rap,RestoreRTKitOS", "rrko" }, @@ -353,12 +360,10 @@ static const char *_img4_get_component_tag(const char *compname) { "RestoreExtDCP", "recp" }, { "RestoreKernelCache", "rkrn" }, { "RestoreLogo", "rlgo" }, + { "RestoreRTP", "rrtp" }, { "RestoreRamDisk", "rdsk" }, { "RestoreSEP", "rsep" }, { "RestoreTrustCache", "rtsc" }, - { "rfta", "rfta" }, - { "rfts", "rfts" }, - { "RTP", "rtpf" }, { "SCE", "scef" }, { "SCE1Firmware", "sc1f" }, { "SEP", "sepi" }, @@ -367,6 +372,16 @@ static const char *_img4_get_component_tag(const char *compname) { "SystemLocker", "lckr" }, { "SystemVolume", "isys" }, { "WCHFirmwareUpdater", "wchf" }, + { "ftap", "ftap" }, + { "ftsp", "ftsp" }, + { "iBEC", "ibec" }, + { "iBSS", "ibss" }, + { "iBoot", "ibot" }, + { "iBootData", "ibdt" }, + { "iBootDataStage1", "ibd1" }, + { "iBootTest", "itst" }, + { "rfta", "rfta" }, + { "rfts", "rfts" }, { NULL, NULL } }; int i = 0; @@ -381,7 +396,7 @@ static const char *_img4_get_component_tag(const char *compname) return NULL; } -int img4_stitch_component(const char* component_name, const unsigned char* component_data, unsigned int component_size, const unsigned char* blob, unsigned int blob_size, unsigned char** img4_data, unsigned int *img4_size) +int img4_stitch_component(const char* component_name, const unsigned char* component_data, unsigned int component_size, plist_t parameters, plist_t tss_response, unsigned char** img4_data, unsigned int *img4_size) { unsigned char* magic_header = NULL; unsigned int magic_header_size = 0; @@ -392,8 +407,15 @@ int img4_stitch_component(const char* component_name, const unsigned char* compo unsigned int content_size; unsigned char* outbuf; unsigned char* p; + unsigned char* blob = NULL; + unsigned int blob_size = 0; - if (!component_name || !component_data || component_size == 0 || !blob || blob_size == 0 || !img4_data || !img4_size) { + if (!component_name || !component_data || component_size == 0 || !tss_response || !img4_data || !img4_size) { + return -1; + } + + if (tss_response_get_ap_img4_ticket(tss_response, &blob, &blob_size) != 0) { + error("ERROR: %s: Failed to get ApImg4Ticket from TSS response\n", __func__); return -1; } @@ -420,16 +442,231 @@ int img4_stitch_component(const char* component_name, const unsigned char* compo memcpy((void*)tag, "rcio", 4); } else if (strcmp(component_name, "Ap,DCP2") == 0) { memcpy((void*)tag, "dcp2", 4); + } else if (strcmp(component_name, "Ap,RestoreSecureM3Firmware") == 0) { + memcpy((void*)tag, "rsm3", 4); + } else if (strcmp(component_name, "Ap,RestoreSecurePageTableMonitor") == 0) { + memcpy((void*)tag, "rspt", 4); + } else if (strcmp(component_name, "Ap,RestoreTrustedExecutionMonitor") == 0) { + memcpy((void*)tag, "rtrx", 4); + } else if (strcmp(component_name, "Ap,RestorecL4") == 0) { + memcpy((void*)tag, "rxcl", 4); } } + // check if we have a *-TBM entry for the given component + unsigned char *additional_data = NULL; + unsigned int additional_size = 0; + char *tbm_key = malloc(strlen(component_name) + 5); + snprintf(tbm_key, strlen(component_name)+5, "%s-TBM", component_name); + plist_t tbm_dict = plist_dict_get_item(tss_response, tbm_key); + free(tbm_key); + uint64_t ucon_size = 0; + const char* ucon_data = NULL; + uint64_t ucer_size = 0; + const char* ucer_data = NULL; + if (tbm_dict) { + plist_t dt = plist_dict_get_item(tbm_dict, "ucon"); + if (!dt) { + error("ERROR: %s: Missing ucon node in %s-TBM dictionary\n", __func__, component_name); + return -1; + } + ucon_data = plist_get_data_ptr(dt, &ucon_size); + if (!ucon_data) { + error("ERROR: %s: Missing ucon data in %s-TBM dictionary\n", __func__, component_name); + return -1; + } + dt = plist_dict_get_item(tbm_dict, "ucer"); + if (!dt) { + error("ERROR: %s: Missing ucer data node in %s-TBM dictionary\n", __func__, component_name); + return -1; + } + ucer_data = plist_get_data_ptr(dt, &ucer_size); + if (!ucer_data) { + error("ERROR: %s: Missing ucer data in %s-TBM dictionary\n", __func__, component_name); + return -1; + } + } + + int nonce_slot_required = plist_dict_get_bool(parameters, "RequiresNonceSlot") && (!strcmp(component_name, "SEP") || !strcmp(component_name, "SepStage1") || !strcmp(component_name, "LLB")); + + if (ucon_data || ucer_data || nonce_slot_required) { + size_t im4r_size = 16; + if (ucon_data) { + im4r_size += 8 + 8 + ucon_size + 16; + } + if (ucer_data) { + im4r_size += 8 + 8 + ucer_size + 16; + } + if (nonce_slot_required) { + im4r_size += 16; + } + unsigned char *im4rset = (unsigned char*)malloc(im4r_size); + unsigned char *p_im4rset = im4rset; + unsigned int im4rlen = 0; + + // ----------- anid/snid ------- + if (nonce_slot_required) { + const char* tag_name = NULL; + uint64_t tag_value = 0; + if (!strcmp(component_name, "SEP") || !strcmp(component_name, "SepStage1")) { + tag_name = "snid"; + tag_value = 2; + if (plist_dict_get_item(parameters, "SepNonceSlotID")) { + tag_value = plist_dict_get_uint(parameters, "SepNonceSlotID"); + } + } else { + tag_name = "anid"; + tag_value = 0; + if (plist_dict_get_item(parameters, "ApNonceSlotID")) { + tag_value = plist_dict_get_uint(parameters, "ApNonceSlotID"); + } + } + // write priv anid/snid element + asn1_write_priv_element(&p_im4rset, &im4rlen, __bswap_32(*(uint32_t*)tag_name)); + // write anid/snid IA5STRING and anid/snid value + unsigned char inner_seq[16]; + unsigned char *p_inner_seq = &inner_seq[0]; + unsigned int inner_seq_hdr_len = 0; + asn1_write_element(&p_inner_seq, &inner_seq_hdr_len, ASN1_IA5_STRING, (void*)tag_name, -1); + asn1_write_element(&p_inner_seq, &inner_seq_hdr_len, ASN1_INTEGER, (void*)&tag_value, -1); + + // write anid/snid sequence + unsigned char elem_seq[8]; + unsigned char *p = &elem_seq[0]; + unsigned int seq_hdr_len = 0; + asn1_write_element_header(ASN1_SEQUENCE | ASN1_CONSTRUCTED, inner_seq_hdr_len, &p, &seq_hdr_len); + + // add size to priv anid/snid element + asn1_write_size(inner_seq_hdr_len + seq_hdr_len, &p_im4rset, &im4rlen); + + // put it together + memcpy(p_im4rset, elem_seq, seq_hdr_len); + p_im4rset += seq_hdr_len; + im4rlen += seq_hdr_len; + memcpy(p_im4rset, inner_seq, inner_seq_hdr_len); + p_im4rset += inner_seq_hdr_len; + im4rlen += inner_seq_hdr_len; + } + + // ----------- ucon ------------ + if (ucon_data) { + // write priv ucon element + asn1_write_priv_element(&p_im4rset, &im4rlen, *(uint32_t*)"nocu"); + + // write ucon IA5STRING and ucon data header + unsigned char inner_seq[16]; + unsigned char *p_inner_seq = &inner_seq[0]; + unsigned int inner_seq_hdr_len = 0; + asn1_write_element(&p_inner_seq, &inner_seq_hdr_len, ASN1_IA5_STRING, (void*)"ucon", -1); + asn1_write_element_header(ASN1_OCTET_STRING, ucon_size, &p_inner_seq, &inner_seq_hdr_len); + + // write ucon sequence + unsigned char elem_seq[8]; + unsigned char *p = &elem_seq[0]; + unsigned int seq_hdr_len = 0; + asn1_write_element_header(ASN1_SEQUENCE | ASN1_CONSTRUCTED, inner_seq_hdr_len + ucon_size, &p, &seq_hdr_len); + + // add size to priv ucon element + asn1_write_size(inner_seq_hdr_len + ucon_size + seq_hdr_len, &p_im4rset, &im4rlen); + + // put it together + memcpy(p_im4rset, elem_seq, seq_hdr_len); + p_im4rset += seq_hdr_len; + im4rlen += seq_hdr_len; + memcpy(p_im4rset, inner_seq, inner_seq_hdr_len); + p_im4rset += inner_seq_hdr_len; + im4rlen += inner_seq_hdr_len; + // write ucon data + memcpy(p_im4rset, ucon_data, ucon_size); + p_im4rset += ucon_size; + im4rlen += ucon_size; + } + + // ----------- ucer ------------ + if (ucer_data) { + // write priv ucer element + asn1_write_priv_element(&p_im4rset, &im4rlen, *(uint32_t*)"recu"); + + // write ucer IA5STRING and ucer data header + unsigned char inner_seq[16]; + unsigned char *p_inner_seq = &inner_seq[0]; + unsigned int inner_seq_hdr_len = 0; + asn1_write_element(&p_inner_seq, &inner_seq_hdr_len, ASN1_IA5_STRING, (void*)"ucer", -1); + asn1_write_element_header(ASN1_OCTET_STRING, ucer_size, &p_inner_seq, &inner_seq_hdr_len); + + // write ucer sequence + unsigned char elem_seq[8]; + unsigned char *p = &elem_seq[0]; + unsigned int seq_hdr_len = 0; + asn1_write_element_header(ASN1_SEQUENCE | ASN1_CONSTRUCTED, inner_seq_hdr_len + ucer_size, &p, &seq_hdr_len); + + // add size to priv ucer element + asn1_write_size(inner_seq_hdr_len + ucer_size + seq_hdr_len, &p_im4rset, &im4rlen); + + // put it together + memcpy(p_im4rset, elem_seq, seq_hdr_len); + p_im4rset += seq_hdr_len; + im4rlen += seq_hdr_len; + memcpy(p_im4rset, inner_seq, inner_seq_hdr_len); + p_im4rset += inner_seq_hdr_len; + im4rlen += inner_seq_hdr_len; + // write ucer data + memcpy(p_im4rset, ucer_data, ucer_size); + p_im4rset += ucer_size; + im4rlen += ucer_size; + } + + // now construct IM4R + + /* write inner set */ + unsigned char inner_set_[8]; + unsigned char *inner_set = &inner_set_[0]; + unsigned int inner_set_len = 0; + asn1_write_element_header(ASN1_SET | ASN1_CONSTRUCTED, im4rlen, &inner_set, &inner_set_len); + + /* write header values */ + unsigned char hdrdata_[16]; + unsigned char *hdrdata = &hdrdata_[0]; + unsigned int hdrdata_len = 0; + asn1_write_element(&hdrdata, &hdrdata_len, ASN1_IA5_STRING, (void*)"IM4R", -1); + + /* write sequence now that we know the entire size */ + unsigned char seq_[8]; + unsigned char *seq = &seq_[0]; + unsigned int seq_len = 0; + asn1_write_element_header(ASN1_SEQUENCE | ASN1_CONSTRUCTED, im4rlen + inner_set_len + hdrdata_len, &seq, &seq_len); + + /* write outer cont[1] */ + unsigned char cont_[8]; + unsigned char *cont = &cont_[0]; + unsigned int cont_len = 0; + asn1_write_element_header(ASN1_CONTEXT_SPECIFIC | ASN1_CONSTRUCTED | 1, im4rlen + inner_set_len + hdrdata_len + seq_len, &cont, &cont_len); + + // now put everything together + additional_data = malloc(im4rlen + inner_set_len + hdrdata_len + seq_len + cont_len); + p = additional_data; + memcpy(p, cont_, cont_len); + p += cont_len; + memcpy(p, seq_, seq_len); + p += seq_len; + memcpy(p, hdrdata_, hdrdata_len); + p += hdrdata_len; + memcpy(p, inner_set_, inner_set_len); + p += inner_set_len; + memcpy(p, im4rset, im4rlen); + p += im4rlen; + additional_size = (unsigned int)(p - additional_data); + + free(im4rset); + } + // create element header for the "IMG4" magic asn1_create_element_header(ASN1_IA5_STRING, IMG4_MAGIC_SIZE, &magic_header, &magic_header_size); // create element header for the blob (ApImg4Ticket) asn1_create_element_header(ASN1_CONTEXT_SPECIFIC|ASN1_CONSTRUCTED, blob_size, &blob_header, &blob_header_size); // calculate the size for the final IMG4 file (asn1 sequence) - content_size = magic_header_size + IMG4_MAGIC_SIZE + component_size + blob_header_size + blob_size; + content_size = magic_header_size + IMG4_MAGIC_SIZE + component_size + blob_header_size + blob_size + additional_size; // create element header for the final IMG4 asn1 blob asn1_create_element_header(ASN1_SEQUENCE|ASN1_CONSTRUCTED, content_size, &img4header, &img4header_size); @@ -445,6 +682,7 @@ int img4_stitch_component(const char* component_name, const unsigned char* compo if (img4header) { free(img4header); } + free(additional_data); error("ERROR: out of memory when personalizing IMG4 component %s\n", component_name); return -1; } @@ -463,6 +701,10 @@ int img4_stitch_component(const char* component_name, const unsigned char* compo p += blob_header_size; memcpy(p, blob, blob_size); p += blob_size; + if (additional_size) { + memcpy(p, additional_data, additional_size); + p += additional_size; + } *img4_data = outbuf; *img4_size = (p - outbuf); @@ -476,6 +718,7 @@ int img4_stitch_component(const char* component_name, const unsigned char* compo if (img4header) { free(img4header); } + free(additional_data); return 0; } @@ -540,13 +783,11 @@ static void _manifest_write_component(unsigned char **p, unsigned int *length, c node = plist_dict_get_item(comp, "Digest"); if (node) { - char *digest = NULL; uint64_t digest_len = 0; - plist_get_data_val(node, &digest, &digest_len); + const char *digest = plist_get_data_ptr(node, &digest_len); if (digest_len > 0) { - _manifest_write_key_value(&tmp, &tmp_len, "DGST", ASN1_OCTET_STRING, digest, digest_len); + _manifest_write_key_value(&tmp, &tmp_len, "DGST", ASN1_OCTET_STRING, (void*)digest, digest_len); } - free(digest); } node = plist_dict_get_item(comp, "Trusted"); @@ -575,9 +816,8 @@ static void _manifest_write_component(unsigned char **p, unsigned int *length, c node = plist_dict_get_item(comp, "TBMDigests"); if (node) { - char *data = NULL; uint64_t datalen = 0; - plist_get_data_val(node, &data, &datalen); + const char *data = plist_get_data_ptr(node, &datalen); const char *tbmtag = NULL; if (!strcmp(tag, "sepi")) { tbmtag = "tbms"; @@ -587,9 +827,8 @@ static void _manifest_write_component(unsigned char **p, unsigned int *length, c if (!tbmtag) { error("ERROR: Unexpected TMBDigests for comp '%s'\n", tag); } else { - _manifest_write_key_value(&tmp, &tmp_len, tbmtag, ASN1_OCTET_STRING, data, datalen); + _manifest_write_key_value(&tmp, &tmp_len, tbmtag, ASN1_OCTET_STRING, (void*)data, datalen); } - free(data); } asn1_write_element_header(ASN1_SET | ASN1_CONSTRUCTED, tmp_len, &inner_start, &inner_length); @@ -633,22 +872,22 @@ int img4_create_local_manifest(plist_t request, plist_t build_identity, plist_t* unsigned int tmp_len = 0; /* write manifest properties */ - uintval = _plist_dict_get_uint(request, "ApBoardID"); + uintval = plist_dict_get_uint(request, "ApBoardID"); _manifest_write_key_value(&tmp, &tmp_len, "BORD", ASN1_INTEGER, &uintval, -1); uintval = 0; _manifest_write_key_value(&tmp, &tmp_len, "CEPO", ASN1_INTEGER, &uintval, -1); - uintval = _plist_dict_get_uint(request, "ApChipID"); + uintval = plist_dict_get_uint(request, "ApChipID"); _manifest_write_key_value(&tmp, &tmp_len, "CHIP", ASN1_INTEGER, &uintval, -1); - boolval = _plist_dict_get_bool(request, "ApProductionMode"); + boolval = plist_dict_get_bool(request, "ApProductionMode"); _manifest_write_key_value(&tmp, &tmp_len, "CPRO", ASN1_BOOLEAN, &boolval, -1); boolval = 0; _manifest_write_key_value(&tmp, &tmp_len, "CSEC", ASN1_BOOLEAN, &boolval, -1); - uintval = _plist_dict_get_uint(request, "ApSecurityDomain"); + uintval = plist_dict_get_uint(request, "ApSecurityDomain"); _manifest_write_key_value(&tmp, &tmp_len, "SDOM", ASN1_INTEGER, &uintval, -1); /* create manifest properties set */ @@ -679,13 +918,12 @@ int img4_create_local_manifest(plist_t request, plist_t build_identity, plist_t* comp = _img4_get_component_tag(key); } if (!comp) { - error("ERROR: %s: Unhandled component '%s' - can't create manifest\n", __func__, key); - free(iter); - free(buf); - return -1; + debug("DEBUG: %s: Unhandled component '%s'\n", __func__, key); + _manifest_write_component(&p, &length, key, val); + } else { + debug("DEBUG: found component %s (%s)\n", comp, key); + _manifest_write_component(&p, &length, comp, val); } - debug("DEBUG: found component %s (%s)\n", comp, key); - _manifest_write_component(&p, &length, comp, val); } free(key); } while (val); diff --git a/src/img4.h b/src/img4.h index 37dea569..19c1c84e 100644 --- a/src/img4.h +++ b/src/img4.h @@ -26,7 +26,7 @@ extern "C" { #endif -int img4_stitch_component(const char* component_name, const unsigned char* component_data, unsigned int component_size, const unsigned char* blob, unsigned int blob_size, unsigned char** img4_data, unsigned int *img4_size); +int img4_stitch_component(const char* component_name, const unsigned char* component_data, unsigned int component_size, plist_t parameters, plist_t tss_response, unsigned char** img4_data, unsigned int *img4_size); int img4_create_local_manifest(plist_t request, plist_t build_identity, plist_t* manifest); #ifdef __cplusplus diff --git a/src/ipsw.c b/src/ipsw.c index 639ecf58..da7528d0 100644 --- a/src/ipsw.c +++ b/src/ipsw.c @@ -34,16 +34,8 @@ #include #include #include -#ifdef HAVE_OPENSSL -#include -#else -#include "sha1.h" -#define SHA_CTX SHA1_CTX -#define SHA1_Init SHA1Init -#define SHA1_Update SHA1Update -#define SHA1_Final SHA1Final -#endif +#include #include #include @@ -55,16 +47,8 @@ #define BUFSIZE 0x100000 -typedef struct { - struct zip* zip; - char *path; -} ipsw_archive; - static int cancel_flag = 0; -ipsw_archive* ipsw_open(const char* ipsw); -void ipsw_close(ipsw_archive* archive); - static char* build_path(const char* path, const char* file) { size_t plen = strlen(path); @@ -92,13 +76,13 @@ int ipsw_print_info(const char* path) char thepath[PATH_MAX]; if (S_ISDIR(fst.st_mode)) { - sprintf(thepath, "%s/BuildManifest.plist", path); + snprintf(thepath, sizeof(thepath), "%s/BuildManifest.plist", path); if (stat(thepath, &fst) != 0) { error("ERROR: '%s': %s\n", thepath, strerror(errno)); return -1; } } else { - sprintf(thepath, "%s", path); + snprintf(thepath, sizeof(thepath), "%s", path); } FILE* f = fopen(thepath, "r"); @@ -118,11 +102,14 @@ int ipsw_print_info(const char* path) uint32_t plist_len = 0; if (memcmp(&magic, "PK\x03\x04", 4) == 0) { + ipsw_archive_t ipsw = ipsw_open(thepath); unsigned int rlen = 0; - if (ipsw_extract_to_memory(thepath, "BuildManifest.plist", (unsigned char**)&plist_buf, &rlen) < 0) { + if (ipsw_extract_to_memory(ipsw, "BuildManifest.plist", (unsigned char**)&plist_buf, &rlen) < 0) { + ipsw_close(ipsw); error("ERROR: Failed to extract BuildManifest.plist from IPSW!\n"); return -1; } + ipsw_close(ipsw); plist_len = (uint32_t)rlen; } else { size_t rlen = 0; @@ -134,7 +121,7 @@ int ipsw_print_info(const char* path) } plist_t manifest = NULL; - plist_from_memory(plist_buf, plist_len, &manifest); + plist_from_memory(plist_buf, plist_len, &manifest, NULL); free(plist_buf); plist_t val; @@ -310,10 +297,10 @@ int ipsw_print_info(const char* path) return 0; } -ipsw_archive* ipsw_open(const char* ipsw) +ipsw_archive_t ipsw_open(const char* ipsw) { int err = 0; - ipsw_archive* archive = (ipsw_archive*) malloc(sizeof(ipsw_archive)); + ipsw_archive_t archive = (ipsw_archive_t)calloc(1, sizeof(struct ipsw_archive)); if (archive == NULL) { error("ERROR: Out of memory\n"); return NULL; @@ -324,18 +311,27 @@ ipsw_archive* ipsw_open(const char* ipsw) error("ERROR: ipsw_open %s: %s\n", ipsw, strerror(errno)); return NULL; } - archive->path = strdup(ipsw); if (S_ISDIR(fst.st_mode)) { - archive->zip = NULL; + archive->zip = 0; } else { - archive->zip = zip_open(ipsw, 0, &err); - if (archive->zip == NULL) { + struct zip *zip = zip_open(ipsw, 0, &err); + if (zip == NULL) { error("ERROR: zip_open: %s: %d\n", ipsw, err); free(archive); return NULL; } + archive->zip = 1; + } + archive->path = strdup(ipsw); + return (ipsw_archive_t)archive; +} + +void ipsw_close(ipsw_archive_t ipsw) +{ + if (ipsw != NULL) { + free(ipsw->path); + free(ipsw); } - return archive; } int ipsw_is_directory(const char* ipsw) @@ -348,37 +344,45 @@ int ipsw_is_directory(const char* ipsw) return S_ISDIR(fst.st_mode); } -int ipsw_get_file_size(const char* ipsw, const char* infile, uint64_t* size) +int ipsw_get_file_size(ipsw_archive_t ipsw, const char* infile, uint64_t* size) { - ipsw_archive* archive = ipsw_open(ipsw); - if (archive == NULL) { + if (ipsw == NULL) { error("ERROR: Invalid archive\n"); return -1; } - if (archive->zip) { - int zindex = zip_name_locate(archive->zip, infile, 0); + if (ipsw->zip) { + int err = 0; + struct zip *zip = zip_open(ipsw->path, 0, &err); + if (zip == NULL) { + error("ERROR: zip_open: %s: %d\n", ipsw->path, err); + return -1; + } + int zindex = zip_name_locate(zip, infile, 0); if (zindex < 0) { error("ERROR: zip_name_locate: %s\n", infile); - ipsw_close(archive); + zip_unchange_all(zip); + zip_close(zip); return -1; } struct zip_stat zstat; zip_stat_init(&zstat); - if (zip_stat_index(archive->zip, zindex, 0, &zstat) != 0) { + if (zip_stat_index(zip, zindex, 0, &zstat) != 0) { error("ERROR: zip_stat_index: %s\n", infile); - ipsw_close(archive); + zip_unchange_all(zip); + zip_close(zip); return -1; } + zip_unchange_all(zip); + zip_close(zip); *size = zstat.size; } else { - char *filepath = build_path(archive->path, infile); + char *filepath = build_path(ipsw->path, infile); struct stat fst; if (stat(filepath, &fst) != 0) { free(filepath); - ipsw_close(archive); return -1; } free(filepath); @@ -386,51 +390,67 @@ int ipsw_get_file_size(const char* ipsw, const char* infile, uint64_t* size) *size = fst.st_size; } - ipsw_close(archive); return 0; } -int ipsw_extract_to_file_with_progress(const char* ipsw, const char* infile, const char* outfile, int print_progress) +int ipsw_extract_to_file_with_progress(ipsw_archive_t ipsw, const char* infile, const char* outfile, int print_progress) { int ret = 0; - ipsw_archive* archive = ipsw_open(ipsw); - if (archive == NULL) { - error("ERROR: Invalid archive\n"); + + if (!ipsw || !infile || !outfile) { + error("ERROR: Invalid argument\n"); return -1; } cancel_flag = 0; - if (archive->zip) { - int zindex = zip_name_locate(archive->zip, infile, 0); + if (ipsw->zip) { + int err = 0; + struct zip *zip = zip_open(ipsw->path, 0, &err); + if (zip == NULL) { + error("ERROR: zip_open: %s: %d\n", ipsw->path, err); + return -1; + } + + int zindex = zip_name_locate(zip, infile, 0); if (zindex < 0) { + zip_unchange_all(zip); + zip_close(zip); error("ERROR: zip_name_locate: %s\n", infile); return -1; } struct zip_stat zstat; zip_stat_init(&zstat); - if (zip_stat_index(archive->zip, zindex, 0, &zstat) != 0) { + if (zip_stat_index(zip, zindex, 0, &zstat) != 0) { + zip_unchange_all(zip); + zip_close(zip); error("ERROR: zip_stat_index: %s\n", infile); return -1; } char* buffer = (char*) malloc(BUFSIZE); if (buffer == NULL) { + zip_unchange_all(zip); + zip_close(zip); error("ERROR: Unable to allocate memory\n"); return -1; } - struct zip_file* zfile = zip_fopen_index(archive->zip, zindex, 0); + struct zip_file* zfile = zip_fopen_index(zip, zindex, 0); if (zfile == NULL) { + zip_unchange_all(zip); + zip_close(zip); error("ERROR: zip_fopen_index: %s\n", infile); return -1; } FILE* fd = fopen(outfile, "wb"); if (fd == NULL) { - error("ERROR: Unable to open output file: %s\n", outfile); zip_fclose(zfile); + zip_unchange_all(zip); + zip_close(zip); + error("ERROR: Unable to open output file: %s\n", outfile); return -1; } @@ -445,7 +465,10 @@ int ipsw_extract_to_file_with_progress(const char* ipsw, const char* infile, con size = i; count = zip_fread(zfile, buffer, size); if (count < 0) { - error("ERROR: zip_fread: %s\n", infile); + int zep = 0; + int sep = 0; + zip_file_error_get(zfile, &zep, &sep); + error("ERROR: zip_fread: %s %d %d\n", infile, zep, sep); ret = -1; break; } @@ -464,10 +487,16 @@ int ipsw_extract_to_file_with_progress(const char* ipsw, const char* infile, con free(buffer); fclose(fd); zip_fclose(zfile); + zip_unchange_all(zip); + zip_close(zip); } else { - char *filepath = build_path(archive->path, infile); + char *filepath = build_path(ipsw->path, infile); char actual_filepath[PATH_MAX+1]; char actual_outfile[PATH_MAX+1]; + if (!filepath) { + ret = -1; + goto leave; + } if (!realpath(filepath, actual_filepath)) { error("ERROR: realpath failed on %s: %s\n", filepath, strerror(errno)); ret = -1; @@ -542,76 +571,87 @@ int ipsw_extract_to_file_with_progress(const char* ipsw, const char* infile, con leave: free(filepath); } - ipsw_close(archive); if (cancel_flag) { ret = -2; } return ret; } -int ipsw_extract_to_file(const char* ipsw, const char* infile, const char* outfile) +int ipsw_extract_to_file(ipsw_archive_t ipsw, const char* infile, const char* outfile) { return ipsw_extract_to_file_with_progress(ipsw, infile, outfile, 0); } -int ipsw_file_exists(const char* ipsw, const char* infile) +int ipsw_file_exists(ipsw_archive_t ipsw, const char* infile) { - ipsw_archive* archive = ipsw_open(ipsw); - if (archive == NULL) { + if (!ipsw) { return 0; } - if (archive->zip) { - int zindex = zip_name_locate(archive->zip, infile, 0); + if (ipsw->zip) { + int err = 0; + struct zip *zip = zip_open(ipsw->path, 0, &err); + if (zip == NULL) { + error("ERROR: zip_open: %s: %d\n", ipsw->path, err); + return 0; + } + int zindex = zip_name_locate(zip, infile, 0); + zip_unchange_all(zip); + zip_close(zip); if (zindex < 0) { - ipsw_close(archive); return 0; } } else { - char *filepath = build_path(archive->path, infile); + char *filepath = build_path(ipsw->path, infile); if (access(filepath, R_OK) != 0) { free(filepath); - ipsw_close(archive); return 0; } free(filepath); } - ipsw_close(archive); - return 1; } -int ipsw_extract_to_memory(const char* ipsw, const char* infile, unsigned char** pbuffer, unsigned int* psize) +int ipsw_extract_to_memory(ipsw_archive_t ipsw, const char* infile, unsigned char** pbuffer, unsigned int* psize) { size_t size = 0; unsigned char* buffer = NULL; - ipsw_archive* archive = ipsw_open(ipsw); - if (archive == NULL) { + if (ipsw == NULL) { error("ERROR: Invalid archive\n"); return -1; } - if (archive->zip) { - int zindex = zip_name_locate(archive->zip, infile, 0); + if (ipsw->zip) { + int err = 0; + struct zip *zip = zip_open(ipsw->path, 0, &err); + if (zip == NULL) { + error("ERROR: zip_open: %s: %d\n", ipsw->path, err); + return -1; + } + + int zindex = zip_name_locate(zip, infile, 0); if (zindex < 0) { + zip_unchange_all(zip); + zip_close(zip); debug("NOTE: zip_name_locate: '%s' not found in archive.\n", infile); - ipsw_close(archive); return -1; } struct zip_stat zstat; zip_stat_init(&zstat); - if (zip_stat_index(archive->zip, zindex, 0, &zstat) != 0) { + if (zip_stat_index(zip, zindex, 0, &zstat) != 0) { + zip_unchange_all(zip); + zip_close(zip); error("ERROR: zip_stat_index: %s\n", infile); - ipsw_close(archive); return -1; } - struct zip_file* zfile = zip_fopen_index(archive->zip, zindex, 0); + struct zip_file* zfile = zip_fopen_index(zip, zindex, 0); if (zfile == NULL) { + zip_unchange_all(zip); + zip_close(zip); error("ERROR: zip_fopen_index: %s\n", infile); - ipsw_close(archive); return -1; } @@ -620,23 +660,31 @@ int ipsw_extract_to_memory(const char* ipsw, const char* infile, unsigned char** if (buffer == NULL) { error("ERROR: Out of memory\n"); zip_fclose(zfile); - ipsw_close(archive); + zip_unchange_all(zip); + zip_close(zip); return -1; } - if (zip_fread(zfile, buffer, size) != size) { - error("ERROR: zip_fread: %s\n", infile); - zip_fclose(zfile); + zip_int64_t zr = zip_fread(zfile, buffer, size); + zip_fclose(zfile); + zip_unchange_all(zip); + zip_close(zip); + if (zr < 0) { + int zep = 0; + int sep = 0; + zip_file_error_get(zfile, &zep, &sep); + error("ERROR: zip_fread: %s %d %d\n", infile, zep, sep); + free(buffer); + return -1; + } else if (zr != size) { + error("ERROR: zip_fread: %s got only %lld of %zu\n", infile, zr, size); free(buffer); - ipsw_close(archive); return -1; } buffer[size] = '\0'; - - zip_fclose(zfile); } else { - char *filepath = build_path(archive->path, infile); + char *filepath = build_path(ipsw->path, infile); struct stat fst; #ifdef WIN32 if (stat(filepath, &fst) != 0) { @@ -645,7 +693,6 @@ int ipsw_extract_to_memory(const char* ipsw, const char* infile, unsigned char** #endif error("ERROR: %s: stat failed for %s: %s\n", __func__, filepath, strerror(errno)); free(filepath); - ipsw_close(archive); return -1; } size = fst.st_size; @@ -653,7 +700,6 @@ int ipsw_extract_to_memory(const char* ipsw, const char* infile, unsigned char** if (buffer == NULL) { error("ERROR: Out of memory\n"); free(filepath); - ipsw_close(archive); return -1; } @@ -663,7 +709,6 @@ int ipsw_extract_to_memory(const char* ipsw, const char* infile, unsigned char** error("ERROR: %s: readlink failed for %s: %s\n", __func__, filepath, strerror(errno)); free(filepath); free(buffer); - ipsw_close(archive); return -1; } } else { @@ -673,7 +718,6 @@ int ipsw_extract_to_memory(const char* ipsw, const char* infile, unsigned char** error("ERROR: %s: fopen failed for %s: %s\n", __func__, filepath, strerror(errno)); free(filepath); free(buffer); - ipsw_close(archive); return -2; } if (fread(buffer, 1, size, f) != size) { @@ -681,7 +725,6 @@ int ipsw_extract_to_memory(const char* ipsw, const char* infile, unsigned char** error("ERROR: %s: fread failed for %s: %s\n", __func__, filepath, strerror(errno)); free(filepath); free(buffer); - ipsw_close(archive); return -1; } fclose(f); @@ -692,14 +735,161 @@ int ipsw_extract_to_memory(const char* ipsw, const char* infile, unsigned char** free(filepath); } - ipsw_close(archive); *pbuffer = buffer; *psize = size; return 0; } -int ipsw_extract_build_manifest(const char* ipsw, plist_t* buildmanifest, int *tss_enabled) +int ipsw_extract_send(ipsw_archive_t ipsw, const char* infile, int blocksize, ipsw_send_cb send_callback, void* ctx) +{ + unsigned char* buffer = NULL; + size_t done = 0; + size_t total_size = 0; + + if (ipsw == NULL) { + error("ERROR: Invalid archive\n"); + return -1; + } + + if (ipsw->zip) { + int err = 0; + struct zip *zip = zip_open(ipsw->path, 0, &err); + if (zip == NULL) { + error("ERROR: zip_open: %s: %d\n", ipsw->path, err); + return -1; + } + + int zindex = zip_name_locate(zip, infile, 0); + if (zindex < 0) { + zip_unchange_all(zip); + zip_close(zip); + debug("NOTE: zip_name_locate: '%s' not found in archive.\n", infile); + return -1; + } + + struct zip_stat zstat; + zip_stat_init(&zstat); + if (zip_stat_index(zip, zindex, 0, &zstat) != 0) { + zip_unchange_all(zip); + zip_close(zip); + error("ERROR: zip_stat_index: %s\n", infile); + return -1; + } + + struct zip_file* zfile = zip_fopen_index(zip, zindex, 0); + if (zfile == NULL) { + zip_unchange_all(zip); + zip_close(zip); + error("ERROR: zip_fopen_index: %s\n", infile); + return -1; + } + + total_size = zstat.size; + buffer = (unsigned char*) malloc(blocksize); + if (buffer == NULL) { + zip_fclose(zfile); + zip_unchange_all(zip); + zip_close(zip); + error("ERROR: Out of memory\n"); + return -1; + } + + while (done < total_size) { + size_t size = total_size-done; + if (size > blocksize) size = blocksize; + zip_int64_t zr = zip_fread(zfile, buffer, size); + if (zr < 0) { + error("ERROR: %s: zip_fread: %s\n", __func__, infile); + break; + } else if (zr == 0) { + // EOF + break; + } + if (send_callback(ctx, buffer, zr, done, total_size) < 0) { + error("ERROR: %s: send failed\n", __func__); + break; + } + done += zr; + } + free(buffer); + zip_fclose(zfile); + zip_unchange_all(zip); + zip_close(zip); + } else { + char *filepath = build_path(ipsw->path, infile); + struct stat fst; +#ifdef WIN32 + if (stat(filepath, &fst) != 0) { +#else + if (lstat(filepath, &fst) != 0) { +#endif + error("ERROR: %s: stat failed for %s: %s\n", __func__, filepath, strerror(errno)); + free(filepath); + return -1; + } + total_size = fst.st_size; + buffer = (unsigned char*)malloc(blocksize); + if (buffer == NULL) { + error("ERROR: Out of memory\n"); + free(filepath); + return -1; + } + +#ifndef WIN32 + if (S_ISLNK(fst.st_mode)) { + ssize_t rl = readlink(filepath, (char*)buffer, (total_size > blocksize) ? blocksize : total_size); + if (rl < 0) { + error("ERROR: %s: readlink failed for %s: %s\n", __func__, filepath, strerror(errno)); + free(filepath); + free(buffer); + return -1; + } + send_callback(ctx, buffer, (size_t)rl, 0, 0); + } else { +#endif + FILE *f = fopen(filepath, "rb"); + if (!f) { + error("ERROR: %s: fopen failed for %s: %s\n", __func__, filepath, strerror(errno)); + free(filepath); + free(buffer); + return -2; + } + + while (done < total_size) { + size_t size = total_size-done; + if (size > blocksize) size = blocksize; + size_t fr = fread(buffer, 1, size, f); + if (fr != size) { + error("ERROR: %s: fread failed for %s: %s\n", __func__, filepath, strerror(errno)); + break; + } + if (send_callback(ctx, buffer, fr, done, total_size) < 0) { + error("ERROR: %s: send failed\n", __func__); + break; + } + done += fr; + } + fclose(f); +#ifndef WIN32 + } +#endif + free(filepath); + free(buffer); + } + + if (done < total_size) { + error("ERROR: %s: Sending file data for %s failed (sent %" PRIu64 "/%" PRIu64 ")\n", __func__, infile, (uint64_t)done, (uint64_t)total_size); + return -1; + } + + // send a NULL buffer to mark end of transfer + send_callback(ctx, NULL, 0, done, total_size); + + return 0; +} + +int ipsw_extract_build_manifest(ipsw_archive_t ipsw, plist_t* buildmanifest, int *tss_enabled) { unsigned int size = 0; unsigned char* data = NULL; @@ -729,7 +919,7 @@ int ipsw_extract_build_manifest(const char* ipsw, plist_t* buildmanifest, int *t return -1; } -int ipsw_extract_restore_plist(const char* ipsw, plist_t* restore_plist) +int ipsw_extract_restore_plist(ipsw_archive_t ipsw, plist_t* restore_plist) { unsigned int size = 0; unsigned char* data = NULL; @@ -743,10 +933,10 @@ int ipsw_extract_restore_plist(const char* ipsw, plist_t* restore_plist) return -1; } -static int ipsw_list_contents_recurse(ipsw_archive *archive, const char *path, ipsw_list_cb cb, void *ctx) +static int ipsw_list_contents_recurse(ipsw_archive_t ipsw, const char *path, ipsw_list_cb cb, void *ctx) { int ret = 0; - char *base = build_path(archive->path, path); + char *base = build_path(ipsw->path, path); DIR *dirp = opendir(base); @@ -784,10 +974,10 @@ static int ipsw_list_contents_recurse(ipsw_archive *archive, const char *path, i break; } - ret = cb(ctx, archive->path, subpath, &st); + ret = cb(ctx, ipsw, subpath, &st); if (ret >= 0 && S_ISDIR(st.st_mode)) - ipsw_list_contents_recurse(archive, subpath, cb, ctx); + ipsw_list_contents_recurse(ipsw, subpath, cb, ctx); free(fpath); free(subpath); @@ -798,21 +988,26 @@ static int ipsw_list_contents_recurse(ipsw_archive *archive, const char *path, i return ret; } -int ipsw_list_contents(const char* ipsw, ipsw_list_cb cb, void *ctx) +int ipsw_list_contents(ipsw_archive_t ipsw, ipsw_list_cb cb, void *ctx) { int ret = 0; - ipsw_archive* archive = ipsw_open(ipsw); - if (archive == NULL) { - error("ERROR: Invalid archive\n"); + if (ipsw == NULL) { + error("ERROR: Invalid IPSW archive\n"); return -1; } - if (archive->zip) { - int64_t entries = zip_get_num_entries(archive->zip, 0); + if (ipsw->zip) { + int err = 0; + struct zip *zip = zip_open(ipsw->path, 0, &err); + if (zip == NULL) { + error("ERROR: zip_open: %s: %d\n", ipsw->path, err); + return -1; + } + + int64_t entries = zip_get_num_entries(zip, 0); if (entries < 0) { error("ERROR: zip_get_num_entries failed\n"); - ipsw_close(archive); return -1; } @@ -820,7 +1015,7 @@ int ipsw_list_contents(const char* ipsw, ipsw_list_cb cb, void *ctx) zip_stat_t stat; zip_stat_init(&stat); - if (zip_stat_index(archive->zip, index, 0, &stat) < 0) { + if (zip_stat_index(zip, index, 0, &stat) < 0) { error("ERROR: zip_stat_index failed for %s\n", stat.name); ret = -1; continue; @@ -828,7 +1023,7 @@ int ipsw_list_contents(const char* ipsw, ipsw_list_cb cb, void *ctx) uint8_t opsys; uint32_t attributes; - if (zip_file_get_external_attributes(archive->zip, index, 0, &opsys, &attributes) < 0) { + if (zip_file_get_external_attributes(zip, index, 0, &opsys, &attributes) < 0) { error("ERROR: zip_file_get_external_attributes failed for %s\n", stat.name); ret = -1; continue; @@ -858,25 +1053,12 @@ int ipsw_list_contents(const char* ipsw, ipsw_list_cb cb, void *ctx) break; } } else { - ret = ipsw_list_contents_recurse(archive, "", cb, ctx); + ret = ipsw_list_contents_recurse(ipsw, "", cb, ctx); } - ipsw_close(archive); return ret; } -void ipsw_close(ipsw_archive* archive) -{ - if (archive != NULL) { - free(archive->path); - if (archive->zip) { - zip_unchange_all(archive->zip); - zip_close(archive->zip); - } - free(archive); - } -} - int ipsw_get_signed_firmwares(const char* product, plist_t* firmwares) { char url[256]; @@ -987,7 +1169,7 @@ int ipsw_get_latest_fw(plist_t version_data, const char* product, char** fwurl, } char majstr[32]; // should be enough for a uint64_t value - sprintf(majstr, "%"PRIu64, (uint64_t)major); + snprintf(majstr, sizeof(majstr), "%"PRIu64, (uint64_t)major); n1 = plist_access_path(version_data, 7, "MobileDeviceSoftwareVersionsByVersion", majstr, "MobileDeviceSoftwareVersions", product, "Unknown", "Universal", "Restore"); if (!n1) { error("%s: ERROR: Can't get Unknown/Universal/Restore node?!\n", __func__); @@ -1073,14 +1255,14 @@ static int sha1_verify_fp(FILE* f, unsigned char* expected_sha1) unsigned char tsha1[20]; char buf[8192]; if (!f) return 0; - SHA_CTX sha1ctx; - SHA1_Init(&sha1ctx); + sha1_context sha1ctx; + sha1_init(&sha1ctx); rewind(f); while (!feof(f)) { size_t sz = fread(buf, 1, 8192, f); - SHA1_Update(&sha1ctx, (const void*)buf, sz); + sha1_update(&sha1ctx, buf, sz); } - SHA1_Final(tsha1, &sha1ctx); + sha1_final(&sha1ctx, tsha1); return (memcmp(expected_sha1, tsha1, 20) == 0) ? 1 : 0; } @@ -1095,13 +1277,13 @@ int ipsw_download_fw(const char *fwurl, unsigned char* isha1, const char* todir, char fwlfn[PATH_MAX - 5]; if (todir) { - sprintf(fwlfn, "%s/%s", todir, fwfn); + snprintf(fwlfn, sizeof(fwlfn), "%s/%s", todir, fwfn); } else { - sprintf(fwlfn, "%s", fwfn); + snprintf(fwlfn, sizeof(fwlfn), "%s", fwfn); } char fwlock[PATH_MAX]; - sprintf(fwlock, "%s.lock", fwlfn); + snprintf(fwlock, sizeof(fwlock), "%s.lock", fwlfn); lock_info_t lockinfo; @@ -1200,3 +1382,121 @@ void ipsw_cancel(void) { cancel_flag++; } + +ipsw_file_handle_t ipsw_file_open(ipsw_archive_t ipsw, const char* path) +{ + ipsw_file_handle_t handle = (ipsw_file_handle_t)calloc(1, sizeof(struct ipsw_file_handle)); + if (ipsw->zip) { + int err = 0; + struct zip *zip = zip_open(ipsw->path, 0, &err); + if (zip == NULL) { + error("ERROR: zip_open: %s: %d\n", ipsw->path, err); + return NULL; + } + + zip_stat_t zst; + zip_int64_t zindex = zip_name_locate(zip, path, 0); + if (zindex < 0) { + error("ERROR: zip_name_locate: %s not found\n", path); + zip_unchange_all(zip); + zip_close(zip); + free(handle); + return NULL; + } + handle->zfile = zip_fopen_index(zip, zindex, 0); + if (handle->zfile == NULL) { + error("ERROR: zip_fopen_index: %s could not be opened\n", path); + zip_unchange_all(zip); + zip_close(zip); + free(handle); + return NULL; + } + zip_stat_init(&zst); + zip_stat(zip, path, 0, &zst); + handle->size = zst.size; + handle->seekable = (zst.comp_method == ZIP_CM_STORE); + handle->zip = zip; + } else { + struct stat st; + char *filepath = build_path(ipsw->path, path); + handle->file = fopen(filepath, "rb"); + free(filepath); + if (!handle->file) { + error("ERROR: fopen: %s could not be opened\n", path); + free(handle); + return NULL; + } + fstat(fileno(handle->file), &st); + handle->size = st.st_size; + handle->seekable = 1; + } + return handle; +} + +void ipsw_file_close(ipsw_file_handle_t handle) +{ + if (handle && handle->zfile) { + zip_fclose(handle->zfile); + zip_unchange_all(handle->zip); + zip_close(handle->zip); + } else if (handle && handle->file) { + fclose(handle->file); + } + free(handle); +} + +uint64_t ipsw_file_size(ipsw_file_handle_t handle) +{ + if (handle) { + return handle->size; + } + return 0; +} + +int64_t ipsw_file_read(ipsw_file_handle_t handle, void* buffer, size_t size) +{ + if (handle && handle->zfile) { + zip_int64_t zr = zip_fread(handle->zfile, buffer, size); + return (int64_t)zr; + } else if (handle && handle->file) { + return fread(buffer, 1, size, handle->file); + } else { + error("ERROR: %s: Invalid file handle\n", __func__); + return -1; + } +} + +int ipsw_file_seek(ipsw_file_handle_t handle, int64_t offset, int whence) +{ + if (handle && handle->zfile) { + return zip_fseek(handle->zfile, offset, whence); + } else if (handle && handle->file) { +#ifdef WIN32 + if (whence == SEEK_SET) { + rewind(handle->file); + } + return (_lseeki64(fileno(handle->file), offset, whence) < 0) ? -1 : 0; +#else + return fseeko(handle->file, offset, whence); +#endif + } else { + error("ERROR: %s: Invalid file handle\n", __func__); + return -1; + } +} + +int64_t ipsw_file_tell(ipsw_file_handle_t handle) +{ + if (handle && handle->zfile) { + return zip_ftell(handle->zfile); + } else if (handle && handle->file) { +#ifdef WIN32 + return _lseeki64(fileno(handle->file), 0, SEEK_CUR); +#else + return ftello(handle->file); +#endif + } else { + error("ERROR: %s: Invalid file handle\n", __func__); + return -1; + } +} diff --git a/src/ipsw.h b/src/ipsw.h index 3b5da80e..8cb25612 100644 --- a/src/ipsw.h +++ b/src/ipsw.h @@ -32,19 +32,48 @@ extern "C" { #include #include +struct ipsw_archive { + int zip; + char *path; +}; +typedef struct ipsw_archive* ipsw_archive_t; + +ipsw_archive_t ipsw_open(const char* ipsw); +void ipsw_close(ipsw_archive_t ipsw); + int ipsw_print_info(const char* ipsw); -typedef int (*ipsw_list_cb)(void *ctx, const char* ipsw, const char *name, struct stat *stat); +typedef int (*ipsw_list_cb)(void *ctx, ipsw_archive_t ipsw, const char *name, struct stat *stat); +typedef int (*ipsw_send_cb)(void *ctx, void *data, size_t size, size_t done, size_t total_size); + +struct ipsw_file_handle { + FILE* file; + struct zip* zip; + struct zip_file* zfile; + uint64_t size; + int seekable; +}; +typedef struct ipsw_file_handle* ipsw_file_handle_t; + +ipsw_file_handle_t ipsw_file_open(ipsw_archive_t, const char* path); +void ipsw_file_close(ipsw_file_handle_t handle); + +uint64_t ipsw_file_size(ipsw_file_handle_t handle); +int64_t ipsw_file_read(ipsw_file_handle_t handle, void* buffer, size_t size); +int ipsw_file_seek(ipsw_file_handle_t handle, int64_t offset, int whence); +int64_t ipsw_file_tell(ipsw_file_handle_t handle); int ipsw_is_directory(const char* ipsw); -int ipsw_file_exists(const char* ipsw, const char* infile); -int ipsw_get_file_size(const char* ipsw, const char* infile, uint64_t* size); -int ipsw_extract_to_file(const char* ipsw, const char* infile, const char* outfile); -int ipsw_extract_to_file_with_progress(const char* ipsw, const char* infile, const char* outfile, int print_progress); -int ipsw_extract_to_memory(const char* ipsw, const char* infile, unsigned char** pbuffer, unsigned int* psize); -int ipsw_extract_build_manifest(const char* ipsw, plist_t* buildmanifest, int *tss_enabled); -int ipsw_extract_restore_plist(const char* ipsw, plist_t* restore_plist); -int ipsw_list_contents(const char* ipsw, ipsw_list_cb cb, void *ctx); + +int ipsw_file_exists(ipsw_archive_t ipsw, const char* infile); +int ipsw_get_file_size(ipsw_archive_t ipsw, const char* infile, uint64_t* size); +int ipsw_extract_to_file(ipsw_archive_t ipsw, const char* infile, const char* outfile); +int ipsw_extract_to_file_with_progress(ipsw_archive_t ipsw, const char* infile, const char* outfile, int print_progress); +int ipsw_extract_to_memory(ipsw_archive_t ipsw, const char* infile, unsigned char** pbuffer, unsigned int* psize); +int ipsw_extract_send(ipsw_archive_t ipsw, const char* infile, int blocksize, ipsw_send_cb send_callback, void* ctx); +int ipsw_extract_build_manifest(ipsw_archive_t ipsw, plist_t* buildmanifest, int *tss_enabled); +int ipsw_extract_restore_plist(ipsw_archive_t ipsw, plist_t* restore_plist); +int ipsw_list_contents(ipsw_archive_t ipsw, ipsw_list_cb cb, void *ctx); int ipsw_get_signed_firmwares(const char* product, plist_t* firmwares); int ipsw_download_fw(const char *fwurl, unsigned char* isha1, const char* todir, char** ipswfile); diff --git a/src/normal.c b/src/normal.c index 7f570dab..9e460803 100644 --- a/src/normal.c +++ b/src/normal.c @@ -29,6 +29,7 @@ #include #include #include +#include #include "common.h" #include "normal.h" @@ -233,12 +234,6 @@ int normal_enter_recovery(struct idevicerestore_client_t* client) return -1; } - /* unpair the device */ - lockdown_error = lockdownd_unpair(lockdown, NULL); - if (lockdown_error != LOCKDOWN_E_SUCCESS) { - error("WARNING: Could not unpair device\n"); - } - lockdown_error = lockdownd_enter_recovery(lockdown); if (lockdown_error == LOCKDOWN_E_SESSION_INACTIVE) { lockdownd_client_free(lockdown); @@ -285,6 +280,9 @@ int normal_enter_recovery(struct idevicerestore_client_t* client) return -1; } + /* remove pair record for given device */ + usbmuxd_delete_pair_record(client->udid); + return 0; } @@ -325,7 +323,7 @@ plist_t normal_get_lockdown_value(struct idevicerestore_client_t* client, const return node; } -static int normal_get_nonce_by_key(struct idevicerestore_client_t* client, const char* key, unsigned char** nonce, int* nonce_size) +static int normal_get_nonce_by_key(struct idevicerestore_client_t* client, const char* key, unsigned char** nonce, unsigned int* nonce_size) { plist_t nonce_node = normal_get_lockdown_value(client, NULL, key); @@ -336,18 +334,30 @@ static int normal_get_nonce_by_key(struct idevicerestore_client_t* client, const uint64_t n_size = 0; plist_get_data_val(nonce_node, (char**)nonce, &n_size); - *nonce_size = (int)n_size; + *nonce_size = (unsigned int)n_size; plist_free(nonce_node); return 0; } -int normal_get_sep_nonce(struct idevicerestore_client_t* client, unsigned char** nonce, int* nonce_size) +int normal_get_sep_nonce(struct idevicerestore_client_t* client, unsigned char** nonce, unsigned int* nonce_size) { + plist_t node = normal_get_lockdown_value(client, NULL, "ApParameters"); + if (PLIST_IS_DICT(node)) { + plist_t nonce_node = plist_dict_get_item(node, "SepNonce"); + if (nonce_node) { + uint64_t n_size = 0; + plist_get_data_val(nonce_node, (char**)nonce, &n_size); + *nonce_size = (unsigned int)n_size; + plist_free(node); + return 0; + } + } + plist_free(node); return normal_get_nonce_by_key(client, "SEPNonce", nonce, nonce_size); } -int normal_get_ap_nonce(struct idevicerestore_client_t* client, unsigned char** nonce, int* nonce_size) +int normal_get_ap_nonce(struct idevicerestore_client_t* client, unsigned char** nonce, unsigned int* nonce_size) { return normal_get_nonce_by_key(client, "ApNonce", nonce, nonce_size); } @@ -367,7 +377,7 @@ int normal_is_image4_supported(struct idevicerestore_client_t* client) return bval; } -int normal_get_preflight_info(struct idevicerestore_client_t* client, plist_t *preflight_info) +int normal_get_firmware_preflight_info(struct idevicerestore_client_t* client, plist_t *preflight_info) { uint8_t has_telephony_capability = 0; plist_t node; @@ -391,6 +401,18 @@ int normal_get_preflight_info(struct idevicerestore_client_t* client, plist_t *p return 0; } +int normal_get_preflight_info(struct idevicerestore_client_t* client, plist_t *preflight_info) +{ + plist_t node = normal_get_lockdown_value(client, NULL, "PreflightInfo"); + if (PLIST_IS_DICT(node)) { + *preflight_info = node; + } else { + debug("DEBUG: No PreflightInfo available.\n"); + *preflight_info = NULL; + } + return 0; +} + int normal_handle_create_stashbag(struct idevicerestore_client_t* client, plist_t manifest) { int result = -1; @@ -464,13 +486,13 @@ int normal_handle_create_stashbag(struct idevicerestore_client_t* client, plist_ } else { plist_t node; - if (_plist_dict_get_bool(pl, "Skip")) { + if (plist_dict_get_bool(pl, "Skip")) { result = 0; info("Device does not require stashbag.\n"); break; } - if (_plist_dict_get_bool(pl, "ShowDialog")) { + if (plist_dict_get_bool(pl, "ShowDialog")) { info("Device requires stashbag.\n"); printf("******************************************************************************\n" "* Please enter your passcode on the device. The device will store a token *\n" @@ -493,13 +515,13 @@ int normal_handle_create_stashbag(struct idevicerestore_client_t* client, plist_ plist_free(pl); break; } - if (_plist_dict_get_bool(pl, "Timeout")) { + if (plist_dict_get_bool(pl, "Timeout")) { error("ERROR: Timeout while waiting for user to enter passcode.\n"); result = -2; plist_free(pl); break; } - if (_plist_dict_get_bool(pl, "HideDialog")) { + if (plist_dict_get_bool(pl, "HideDialog")) { plist_free(pl); /* hide dialog */ result = 1; @@ -590,7 +612,7 @@ int normal_handle_commit_stashbag(struct idevicerestore_client_t* client, plist_ } error("ERROR: Could not commit stashbag: %s\n", (strval) ? strval : "(Unknown error)"); free(strval); - } else if (_plist_dict_get_bool(pl, "StashbagCommitComplete")) { + } else if (plist_dict_get_bool(pl, "StashbagCommitComplete")) { info("Stashbag committed!\n"); result = 0; } else { diff --git a/src/normal.h b/src/normal.h index 85464dbc..d3d7b0cd 100644 --- a/src/normal.h +++ b/src/normal.h @@ -36,8 +36,9 @@ int normal_check_mode(struct idevicerestore_client_t* client); irecv_device_t normal_get_irecv_device(struct idevicerestore_client_t* client); int normal_enter_recovery(struct idevicerestore_client_t* client); int normal_is_image4_supported(struct idevicerestore_client_t* client); -int normal_get_ap_nonce(struct idevicerestore_client_t* client, unsigned char** nonce, int* nonce_size); -int normal_get_sep_nonce(struct idevicerestore_client_t* client, unsigned char** nonce, int* nonce_size); +int normal_get_ap_nonce(struct idevicerestore_client_t* client, unsigned char** nonce, unsigned int* nonce_size); +int normal_get_sep_nonce(struct idevicerestore_client_t* client, unsigned char** nonce, unsigned int* nonce_size); +int normal_get_firmware_preflight_info(struct idevicerestore_client_t* client, plist_t *preflight_info); int normal_get_preflight_info(struct idevicerestore_client_t* client, plist_t *preflight_info); plist_t normal_get_lockdown_value(struct idevicerestore_client_t* client, const char* domain, const char* key); int normal_handle_create_stashbag(struct idevicerestore_client_t* client, plist_t manifest); diff --git a/src/recovery.c b/src/recovery.c index 1a9c9709..c4513502 100644 --- a/src/recovery.c +++ b/src/recovery.c @@ -29,8 +29,9 @@ #include #include +#include + #include "idevicerestore.h" -#include "tss.h" #include "img3.h" #include "restore.h" #include "recovery.h" @@ -158,6 +159,20 @@ int recovery_enter_restore(struct idevicerestore_client_t* client, plist_t build free(value); value = NULL; + unsigned long boot_stage = 0; + irecv_getenv(client->recovery->client, "boot-stage", &value); + if (value) { + boot_stage = strtoul(value, NULL, 0); + } + if (boot_stage > 0) { + info("iBoot boot-stage=%s\n", value); + free(value); + value = NULL; + if (boot_stage != 2) { + error("ERROR: iBoot should be at boot stage 2, continuing anyway...\n"); + } + } + unsigned long radio_error = 0; irecv_getenv(client->recovery->client, "radio-error", &value); if (value) { @@ -290,7 +305,7 @@ int recovery_send_component(struct idevicerestore_client_t* client, plist_t buil return -1; } - ret = personalize_component(component, component_data, component_size, client->tss, &data, &size); + ret = personalize_component(client, component, component_data, component_size, client->tss, &data, &size); free(component_data); if (ret < 0) { error("ERROR: Unable to get personalized component: %s\n", component); @@ -344,7 +359,7 @@ int recovery_send_ibec(struct idevicerestore_client_t* client, plist_t build_ide return -1; } - recovery_error = irecv_send_command(client->recovery->client, "go"); + recovery_error = irecv_send_command_breq(client->recovery->client, "go", 1); if (recovery_error != IRECV_E_SUCCESS) { error("ERROR: Unable to execute %s\n", component); return -1; @@ -498,7 +513,7 @@ int recovery_send_kernelcache(struct idevicerestore_client_t* client, plist_t bu recovery_error = irecv_send_command(client->recovery->client, setba); } - recovery_error = irecv_send_command(client->recovery->client, "bootx"); + recovery_error = irecv_send_command_breq(client->recovery->client, "bootx", 1); if (recovery_error != IRECV_E_SUCCESS) { error("ERROR: Unable to execute %s\n", component); return -1; @@ -523,7 +538,7 @@ int recovery_is_image4_supported(struct idevicerestore_client_t* client) return (device_info->ibfl & IBOOT_FLAG_IMAGE4_AWARE); } -int recovery_get_ap_nonce(struct idevicerestore_client_t* client, unsigned char** nonce, int* nonce_size) +int recovery_get_ap_nonce(struct idevicerestore_client_t* client, unsigned char** nonce, unsigned int* nonce_size) { if(client->recovery == NULL) { if (recovery_client_new(client) < 0) { @@ -548,7 +563,7 @@ int recovery_get_ap_nonce(struct idevicerestore_client_t* client, unsigned char* return 0; } -int recovery_get_sep_nonce(struct idevicerestore_client_t* client, unsigned char** nonce, int* nonce_size) +int recovery_get_sep_nonce(struct idevicerestore_client_t* client, unsigned char** nonce, unsigned int* nonce_size) { if(client->recovery == NULL) { if (recovery_client_new(client) < 0) { @@ -575,7 +590,7 @@ int recovery_get_sep_nonce(struct idevicerestore_client_t* client, unsigned char int recovery_send_reset(struct idevicerestore_client_t* client) { - irecv_send_command(client->recovery->client, "reset"); + irecv_send_command_breq(client->recovery->client, "reset", 1); return 0; } diff --git a/src/recovery.h b/src/recovery.h index 92214af3..d7413e0c 100644 --- a/src/recovery.h +++ b/src/recovery.h @@ -55,8 +55,8 @@ int recovery_send_reset(struct idevicerestore_client_t* client); int recovery_send_ticket(struct idevicerestore_client_t* client); int recovery_set_autoboot(struct idevicerestore_client_t* client, int enable); int recovery_is_image4_supported(struct idevicerestore_client_t* client); -int recovery_get_ap_nonce(struct idevicerestore_client_t* client, unsigned char** nonce, int* nonce_size); -int recovery_get_sep_nonce(struct idevicerestore_client_t* client, unsigned char** nonce, int* nonce_size); +int recovery_get_ap_nonce(struct idevicerestore_client_t* client, unsigned char** nonce, unsigned int* nonce_size); +int recovery_get_sep_nonce(struct idevicerestore_client_t* client, unsigned char** nonce, unsigned int* nonce_size); #ifdef __cplusplus diff --git a/src/restore.c b/src/restore.c index d51de03c..2daeea2b 100644 --- a/src/restore.c +++ b/src/restore.c @@ -29,7 +29,11 @@ #include #include #include +#include +#include #include +#include +#include #ifdef HAVE_REVERSE_PROXY #include #else @@ -37,6 +41,8 @@ #endif #include #include +#include +#include #include "idevicerestore.h" #include "asr.h" @@ -44,7 +50,6 @@ #include "fls.h" #include "mbn.h" #include "ftab.h" -#include "tss.h" #include "ipsw.h" #include "restore.h" #include "common.h" @@ -632,6 +637,107 @@ const char* restore_progress_string(unsigned int operation) } } +struct restored_service_client { + +}; + +#define SERVICE_TYPE_RESTORED 1 +#define SERVICE_TYPE_PLIST 2 + +typedef struct restore_service_client { + void* client; + int type; +} *restore_service_client_t; + +static void* _restore_get_service_client_for_data_request(struct idevicerestore_client_t *client, plist_t message) +{ + if (!client || !client->restore || !client->restore->client) return NULL; + restore_service_client_t service = (restore_service_client_t)malloc(sizeof(struct restore_service_client)); + if (!PLIST_IS_DICT(message) || !plist_dict_get_item(message, "DataPort")) { + service->client = client->restore->client; + service->type = SERVICE_TYPE_RESTORED; + return service; + } + plist_t data_type = plist_dict_get_item(message, "DataType"); + uint16_t data_port = plist_dict_get_uint(message, "DataPort"); + const char* data_type_str = plist_get_string_ptr(data_type, NULL); + + struct lockdownd_service_descriptor svcdesc = { + data_port, + 0, + (char*)data_type_str + }; + property_list_service_client_t plclient = NULL; + info("Connecting to %s data port %u\n", data_type_str, data_port); + if (property_list_service_client_new(client->restore->device, &svcdesc, &plclient) != PROPERTY_LIST_SERVICE_E_SUCCESS) { + error("ERROR: Failed to start service connection for %s on port %u\n", data_type_str, data_port); + free(service); + return NULL; + } + service->client = plclient; + service->type = SERVICE_TYPE_PLIST; + + return service; +} + +static int _restore_service_send(restore_service_client_t service, plist_t plist, plist_format_t fmt) +{ + if (!service) { + return -1; + } + switch (service->type) { + case SERVICE_TYPE_RESTORED: + return restored_send((restored_client_t)service->client, plist); + case SERVICE_TYPE_PLIST: + if (fmt == PLIST_FORMAT_BINARY) { + return property_list_service_send_binary_plist((property_list_service_client_t)service->client, plist); + } + return property_list_service_send_xml_plist((property_list_service_client_t)service->client, plist); + default: + break; + } + return -1; +} + +static int _restore_service_recv_timeout(restore_service_client_t service, plist_t *plist, unsigned int timeout) +{ + struct restored_client_private { + property_list_service_client_t parent; + char *udid; + char *label; + plist_t info; + }; + if (!service) { + return -1; + } + switch (service->type) { + case SERVICE_TYPE_RESTORED: + return property_list_service_receive_plist_with_timeout(((struct restored_client_private*)service->client)->parent, plist, timeout); + case SERVICE_TYPE_PLIST: + return property_list_service_receive_plist_with_timeout((property_list_service_client_t)service->client, plist, timeout); + default: + break; + } + return -1; +} + +static void _restore_service_free(restore_service_client_t service) +{ + if (!service) { + return; + } + switch (service->type) { + case SERVICE_TYPE_RESTORED: + break; + case SERVICE_TYPE_PLIST: + property_list_service_client_free((property_list_service_client_t)service->client); + break; + default: + break; + } + free(service); +} + static int lastop = 0; static int restore_handle_previous_restore_log_msg(restored_client_t client, plist_t msg) @@ -685,6 +791,9 @@ int restore_handle_progress_msg(struct idevicerestore_client_t* client, plist_t info("%s (%d)\n", restore_progress_string(adapted_operation), (int)operation); } switch (adapted_operation) { + case RESTORE_IMAGE: + idevicerestore_progress(client, RESTORE_STEP_UPLOAD_FS, progress / 100.0); + break; case VERIFY_RESTORE: idevicerestore_progress(client, RESTORE_STEP_VERIFY_FS, progress / 100.0); break; @@ -714,7 +823,7 @@ int restore_handle_progress_msg(struct idevicerestore_client_t* client, plist_t return 0; } -int restore_handle_status_msg(restored_client_t client, plist_t msg) +int restore_handle_status_msg(struct idevicerestore_client_t* client, plist_t msg) { int result = 0; uint64_t value = 0; @@ -742,6 +851,7 @@ int restore_handle_status_msg(restored_client_t client, plist_t msg) case 27: info("Status: Failed to mount filesystems.\n"); break; + case 50: case 51: info("Status: Failed to load SEP Firmware.\n"); break; @@ -779,10 +889,10 @@ int restore_handle_status_msg(restored_client_t client, plist_t msg) return result; } -static int restore_handle_baseband_updater_output_data(restored_client_t restore, struct idevicerestore_client_t* client, idevice_t device, plist_t msg) +static int restore_handle_baseband_updater_output_data(struct idevicerestore_client_t* client, plist_t message) { int result = -1; - plist_t node = plist_dict_get_item(msg, "DataPort"); + plist_t node = plist_dict_get_item(message, "DataPort"); uint64_t u64val = 0; plist_get_uint_val(node, &u64val); uint16_t data_port = (uint16_t)u64val; @@ -791,9 +901,14 @@ static int restore_handle_baseband_updater_output_data(restored_client_t restore idevice_connection_t connection = NULL; idevice_error_t device_error = IDEVICE_E_SUCCESS; + if (!client || !client->restore || !client->restore->build_identity || !client->restore->device) { + error("ERROR: %s: idevicerestore client not initialized?!\n", __func__); + return -1; + } + debug("Connecting to baseband updater data port\n"); while (--attempts > 0) { - device_error = idevice_connect(device, data_port, &connection); + device_error = idevice_connect(client->restore->device, data_port, &connection); if (device_error == IDEVICE_E_SUCCESS) { break; } @@ -848,10 +963,10 @@ static int restore_handle_baseband_updater_output_data(restored_client_t restore return result; } -static int restore_handle_bb_update_status_msg(restored_client_t client, plist_t msg) +static int restore_handle_bb_update_status_msg(struct idevicerestore_client_t* client, plist_t message) { int result = -1; - plist_t node = plist_dict_get_item(msg, "Accepted"); + plist_t node = plist_dict_get_item(message, "Accepted"); uint8_t accepted = 0; plist_get_bool_val(node, &accepted); @@ -861,14 +976,14 @@ static int restore_handle_bb_update_status_msg(restored_client_t client, plist_t } uint8_t done = 0; - node = plist_access_path(msg, 2, "Output", "done"); + node = plist_access_path(message, 2, "Output", "done"); if (node && plist_get_node_type(node) == PLIST_BOOLEAN) { plist_get_bool_val(node, &done); } if (done) { info("Updating Baseband completed.\n"); - plist_t provisioning = plist_access_path(msg, 2, "Output", "provisioning"); + plist_t provisioning = plist_access_path(message, 2, "Output", "provisioning"); if (provisioning && plist_get_node_type(provisioning) == PLIST_DICT) { char* sval = NULL; node = plist_dict_get_item(provisioning, "IMEI"); @@ -896,24 +1011,61 @@ static void restore_asr_progress_cb(double progress, void* userdata) } } -int restore_send_filesystem(struct idevicerestore_client_t* client, idevice_t device, const char* filesystem) +int restore_send_filesystem(struct idevicerestore_client_t* client, plist_t message) { asr_client_t asr = NULL; + ipsw_archive_t ipsw_dummy = NULL; + ipsw_file_handle_t file = NULL; + char* fsname = NULL; + + if (!client || !client->restore || !client->restore->build_identity || !client->restore->device) { + error("ERROR: %s: idevicerestore client not initialized?!\n", __func__); + return -1; + } info("About to send filesystem...\n"); - if (asr_open_with_timeout(device, &asr) < 0) { + if (build_identity_get_component_path(client->restore->build_identity, "OS", &fsname) < 0) { + error("ERROR: Unable to get path for filesystem component\n"); + return -1; + } + if (client->filesystem) { + char* path = strdup(client->filesystem); + const char* fsname_base = path_get_basename(path); + char* parent_dir = dirname(path); + ipsw_dummy = ipsw_open(parent_dir); + file = ipsw_file_open(ipsw_dummy, fsname_base); + free(path); + } else { + file = ipsw_file_open(client->ipsw, fsname); + } + if (!file) { + error("ERROR: Unable to open '%s' in ipsw\n", fsname); + free(fsname); + } + + uint16_t asr_port = (uint16_t)plist_dict_get_uint(message, "DataPort"); + if (asr_port == 0) { + asr_port = ASR_DEFAULT_PORT; + } + if (asr_open_with_timeout(client->restore->device, &asr, asr_port) < 0) { + ipsw_file_close(file); + ipsw_close(ipsw_dummy); error("ERROR: Unable to connect to ASR\n"); return -1; } info("Connected to ASR\n"); - asr_set_progress_callback(asr, restore_asr_progress_cb, (void*)client); + if (asr_port == ASR_DEFAULT_PORT) { + asr_set_progress_callback(asr, restore_asr_progress_cb, (void*)client); + } // this step sends requested chunks of data from various offsets to asr so // it can validate the filesystem before installing it info("Validating the filesystem\n"); - if (asr_perform_validation(asr, filesystem) < 0) { + if (asr_perform_validation(asr, file) < 0) { + ipsw_file_close(file); + ipsw_close(ipsw_dummy); error("ERROR: ASR was unable to validate the filesystem\n"); asr_free(asr); return -1; @@ -923,18 +1075,23 @@ int restore_send_filesystem(struct idevicerestore_client_t* client, idevice_t de // once the target filesystem has been validated, ASR then requests the // entire filesystem to be sent. info("Sending filesystem now...\n"); - if (asr_send_payload(asr, filesystem) < 0) { + if (asr_send_payload(asr, file) < 0) { + ipsw_file_close(file); + ipsw_close(ipsw_dummy); error("ERROR: Unable to send payload to ASR\n"); asr_free(asr); return -1; } + ipsw_file_close(file); + ipsw_close(ipsw_dummy); + info("Done sending filesystem\n"); asr_free(asr); return 0; } -int restore_send_recovery_os_root_ticket(restored_client_t restore, struct idevicerestore_client_t* client) +int restore_send_recovery_os_root_ticket(struct idevicerestore_client_t* client, plist_t message) { restored_error_t restore_error; plist_t dict; @@ -974,9 +1131,16 @@ int restore_send_recovery_os_root_ticket(restored_client_t restore, struct idevi free(data); } + restore_service_client_t service = _restore_get_service_client_for_data_request(client, message); + if (!service) { + error("ERROR: %s: Unable to connect to service client\n", __func__); + return -1; + } + info("Sending RecoveryOSRootTicket now...\n"); - restore_error = restored_send(restore, dict); + restore_error = _restore_service_send(service, dict, 0); plist_free(dict); + _restore_service_free(service); if (restore_error != RESTORE_E_SUCCESS) { error("ERROR: Unable to send RootTicket (%d)\n", restore_error); return -1; @@ -987,7 +1151,7 @@ int restore_send_recovery_os_root_ticket(restored_client_t restore, struct idevi } -int restore_send_root_ticket(restored_client_t restore, struct idevicerestore_client_t* client) +int restore_send_root_ticket(struct idevicerestore_client_t* client, plist_t message) { restored_error_t restore_error; plist_t dict; @@ -1027,9 +1191,16 @@ int restore_send_root_ticket(restored_client_t restore, struct idevicerestore_cl free(data); } + restore_service_client_t service = _restore_get_service_client_for_data_request(client, message); + if (!service) { + error("ERROR: %s: Unable to connect to service client\n", __func__); + return -1; + } + info("Sending RootTicket now...\n"); - restore_error = restored_send(restore, dict); + restore_error = _restore_service_send(service, dict, 0); plist_free(dict); + _restore_service_free(service); if (restore_error != RESTORE_E_SUCCESS) { error("ERROR: Unable to send RootTicket (%d)\n", restore_error); return -1; @@ -1039,7 +1210,246 @@ int restore_send_root_ticket(restored_client_t restore, struct idevicerestore_cl return 0; } -int restore_send_component(restored_client_t restore, struct idevicerestore_client_t* client, plist_t build_identity, const char* component, const char* component_name) +typedef struct { + int length; + char* content; +} query_response; + +static size_t _curl_write_callback(char* data, size_t size, size_t nmemb, query_response* response) +{ + size_t total = size * nmemb; + if (total != 0) { + response->content = realloc(response->content, response->length + total + 1); + memcpy(response->content + response->length, data, total); + response->content[response->length + total] = '\0'; + response->length += total; + } + + return total; +} + +static size_t _curl_header_callback(char* buffer, size_t size, size_t nitems, void* userdata) +{ + plist_t header_dict = (plist_t)userdata; + size_t len = nitems*size; + char* key = NULL; + char* val = NULL; + size_t i = 0; + while (i < len) { + if (buffer[i] == ':') { + key = malloc(i+1); + strncpy(key, buffer, i); + key[i] = '\0'; + i++; + while (i < len && buffer[i] == ' ' || buffer[i] == '\t') i++; + val = malloc(len-i+1); + strncpy(val, buffer+i, len-i); + val[len-i] = '\0'; + break; + } + i++; + } + if (key && val) { + plist_dict_set_item(header_dict, key, plist_new_string(val)); + } + free(key); + free(val); + return len; +} + +int restore_send_url_asset(struct idevicerestore_client_t* client, plist_t message) +{ + debug("DEBUG: %s\n", __func__); + plist_t arguments = plist_dict_get_item(message, "Arguments"); + if (!PLIST_IS_DICT(arguments)) { + error("ERROR: %s: Unexpected arguments\n", __func__); + debug_plist(arguments); + return -1; + } + + const char* request_method = plist_get_string_ptr(plist_dict_get_item(arguments, "RequestMethod"), NULL); + if (!request_method) { + error("ERROR: %s: Unable to extract RequestMethod from Arguments\n", __func__); + return -1; + } + if (strcmp(request_method, "GET")) { + error("ERROR: %s: Unexpected RequestMethod '%s' in message\n", __func__, request_method); + return -1; + } + const char* request_url = plist_get_string_ptr(plist_dict_get_item(arguments, "RequestURL"), NULL); + if (!request_url) { + error("ERROR: %s: Unable to extract RequestURL from Arguments\n", __func__); + return -1; + } + info("Requesting URLAsset from %s\n", request_url); + + char curl_error_message[CURL_ERROR_SIZE]; + CURL* handle = curl_easy_init(); + /* disable SSL verification to allow download from untrusted https locations */ + curl_easy_setopt(handle, CURLOPT_SSL_VERIFYPEER, 0); + + query_response* response = malloc(sizeof(query_response)); + if (response == NULL) { + error("ERROR: %s: Unable to allocate sufficient memory\n", __func__); + return -1; + } + + response->length = 0; + response->content = malloc(1); + response->content[0] = '\0'; + + curl_easy_setopt(handle, CURLOPT_HTTPGET, 1L); + curl_easy_setopt(handle, CURLOPT_ERRORBUFFER, curl_error_message); + curl_easy_setopt(handle, CURLOPT_WRITEFUNCTION, (curl_write_callback)&_curl_write_callback); + curl_easy_setopt(handle, CURLOPT_HEADERFUNCTION, &_curl_header_callback); + plist_t response_headers = plist_new_dict(); + curl_easy_setopt(handle, CURLOPT_HEADERDATA, response_headers); + curl_easy_setopt(handle, CURLOPT_WRITEDATA, response); + if (idevicerestore_debug) { + curl_easy_setopt(handle, CURLOPT_VERBOSE, 1L); + } + curl_easy_setopt(handle, CURLOPT_FOLLOWLOCATION, 1); + curl_easy_setopt(handle, CURLOPT_URL, request_url); + curl_easy_perform(handle); + + long http_response = 0; + curl_easy_getinfo(handle, CURLINFO_RESPONSE_CODE, &http_response); + + curl_easy_cleanup(handle); + + plist_t dict = plist_new_dict(); + plist_dict_set_item(dict, "ResponseBody", plist_new_data(response->content, response->length)); + plist_dict_set_item(dict, "ResponseBodyDone", plist_new_bool(1)); + plist_dict_set_item(dict, "ResponseHeaders", response_headers); + plist_dict_set_item(dict, "ResponseStatus", plist_new_uint(http_response)); + + free(response); + + restore_service_client_t service = _restore_get_service_client_for_data_request(client, message); + if (!service) { + error("ERROR: %s: Unable to connect to service client\n", __func__); + return -1; + } + + _restore_service_send(service, dict, PLIST_FORMAT_BINARY); + _restore_service_free(service); + + return 0; +} + +int restore_send_streamed_image_decryption_key(struct idevicerestore_client_t* client, plist_t message) +{ + debug("DEBUG: %s\n", __func__); + plist_t arguments = plist_dict_get_item(message, "Arguments"); + if (!PLIST_IS_DICT(arguments)) { + error("ERROR: %s: Unexpected arguments\n", __func__); + debug_plist(arguments); + return -1; + } + + const char* request_method = plist_get_string_ptr(plist_dict_get_item(arguments, "RequestMethod"), NULL); + if (!request_method) { + error("ERROR: %s: Unable to extract RequestMethod from Arguments\n", __func__); + return -1; + } + if (strcmp(request_method, "POST")) { + error("ERROR: %s: Unexpected RequestMethod '%s' in message\n", __func__, request_method); + return -1; + } + const char* request_url = plist_get_string_ptr(plist_dict_get_item(arguments, "RequestURL"), NULL); + if (!request_url) { + error("ERROR: %s: Unable to extract RequestURL from Arguments\n", __func__); + return -1; + } + + struct curl_slist* header = NULL; + + plist_t headers = plist_dict_get_item(arguments, "RequestAdditionalHeaders"); + if (!headers) { + error("ERROR: %s: Missing 'RequestAdditionalHeaders'\n", __func__); + return -1; + } + + uint64_t request_body_size = 0; + const char* request_body = plist_get_data_ptr(plist_dict_get_item(arguments, "RequestBody"), &request_body_size); + if (!request_body) { + error("ERROR: %s: Missing 'RequestBody'\n", __func__); + return -1; + } + + info("Requesting image decryption key from %s\n", request_url); + + char curl_error_message[CURL_ERROR_SIZE]; + char header_tmp[1024]; + plist_dict_iter iter = NULL; + plist_dict_new_iter(headers, &iter); + plist_t node = NULL; + do { + char *key = NULL; + plist_dict_next_item(headers, iter, &key, &node); + if (!node) break; + snprintf(header_tmp, sizeof(header_tmp), "%s: %s", key, plist_get_string_ptr(node, NULL)); + curl_slist_append(header, header_tmp); + } while (node); + plist_mem_free(iter); + + CURL* handle = curl_easy_init(); + /* disable SSL verification to allow download from untrusted https locations */ + curl_easy_setopt(handle, CURLOPT_SSL_VERIFYPEER, 0); + + query_response* response = malloc(sizeof(query_response)); + if (response == NULL) { + error("ERROR: %s: Unable to allocate sufficient memory\n", __func__); + return -1; + } + + response->length = 0; + response->content = malloc(1); + response->content[0] = '\0'; + + curl_easy_setopt(handle, CURLOPT_ERRORBUFFER, curl_error_message); + curl_easy_setopt(handle, CURLOPT_WRITEFUNCTION, (curl_write_callback)&_curl_write_callback); + curl_easy_setopt(handle, CURLOPT_HEADERFUNCTION, &_curl_header_callback); + plist_t response_headers = plist_new_dict(); + curl_easy_setopt(handle, CURLOPT_HEADERDATA, response_headers); + curl_easy_setopt(handle, CURLOPT_WRITEDATA, response); + curl_easy_setopt(handle, CURLOPT_HTTPHEADER, header); + curl_easy_setopt(handle, CURLOPT_POSTFIELDS, request_body); + curl_easy_setopt(handle, CURLOPT_POSTFIELDSIZE, request_body_size); + if (idevicerestore_debug) { + curl_easy_setopt(handle, CURLOPT_VERBOSE, 1L); + } + curl_easy_setopt(handle, CURLOPT_FOLLOWLOCATION, 1); + curl_easy_setopt(handle, CURLOPT_URL, request_url); + curl_easy_perform(handle); + curl_slist_free_all(header); + + long http_response = 0; + curl_easy_getinfo(handle, CURLINFO_RESPONSE_CODE, &http_response); + + curl_easy_cleanup(handle); + + plist_t dict = plist_new_dict(); + plist_dict_set_item(dict, "ResponseBody", plist_new_data(response->content, response->length)); + plist_dict_set_item(dict, "ResponseBodyDone", plist_new_bool(1)); + plist_dict_set_item(dict, "ResponseHeaders", response_headers); + plist_dict_set_item(dict, "ResponseStatus", plist_new_uint(http_response)); + + free(response); + + restore_service_client_t service = _restore_get_service_client_for_data_request(client, message); + if (!service) { + error("ERROR: %s: Unable to connect to service client\n", __func__); + return -1; + } + + _restore_service_send(service, dict, PLIST_FORMAT_BINARY); + _restore_service_free(service); + + return 0; +} + +int restore_send_component(struct idevicerestore_client_t* client, plist_t message, const char* component, const char* component_name) { unsigned int size = 0; unsigned char* data = NULL; @@ -1048,6 +1458,11 @@ int restore_send_component(restored_client_t restore, struct idevicerestore_clie plist_t dict = NULL; restored_error_t restore_error = RESTORE_E_SUCCESS; + if (!client || !client->restore || !client->restore->build_identity) { + error("ERROR: %s: idevicerestore client not initialized?!\n", __func__); + return -1; + } + if (component_name == NULL) { component_name = component; } @@ -1060,7 +1475,7 @@ int restore_send_component(restored_client_t restore, struct idevicerestore_clie } } if (!path) { - if (build_identity_get_component_path(build_identity, component, &path) < 0) { + if (build_identity_get_component_path(client->restore->build_identity, component, &path) < 0) { error("ERROR: Unable to find %s path from build identity\n", component); return -1; } @@ -1076,7 +1491,7 @@ int restore_send_component(restored_client_t restore, struct idevicerestore_clie return -1; } - ret = personalize_component(component, component_data, component_size, client->tss, &data, &size); + ret = personalize_component(client, component, component_data, component_size, client->tss, &data, &size); free(component_data); component_data = NULL; if (ret < 0) { @@ -1087,13 +1502,20 @@ int restore_send_component(restored_client_t restore, struct idevicerestore_clie dict = plist_new_dict(); blob = plist_new_data((char*)data, size); char compkeyname[256]; - sprintf(compkeyname, "%sFile", component_name); + snprintf(compkeyname, sizeof(compkeyname), "%sFile", component_name); plist_dict_set_item(dict, compkeyname, blob); free(data); + restore_service_client_t service = _restore_get_service_client_for_data_request(client, message); + if (!service) { + error("ERROR: %s: Unable to connect to service client\n", __func__); + return -1; + } + info("Sending %s now...\n", component_name); - restore_error = restored_send(restore, dict); + restore_error = _restore_service_send(service, dict, 0); plist_free(dict); + _restore_service_free(service); if (restore_error != RESTORE_E_SUCCESS) { error("ERROR: Unable to send component %s data\n", component_name); return -1; @@ -1103,7 +1525,7 @@ int restore_send_component(restored_client_t restore, struct idevicerestore_clie return 0; } -int restore_send_nor(restored_client_t restore, struct idevicerestore_client_t* client, plist_t build_identity, plist_t message) +int restore_send_nor(struct idevicerestore_client_t* client, plist_t message) { char* llb_path = NULL; char* llb_filename = NULL; @@ -1123,6 +1545,11 @@ int restore_send_nor(restored_client_t restore, struct idevicerestore_client_t* plist_t firmware_files = NULL; int flash_version_1 = 0; + if (!client || !client->restore || !client->restore->build_identity) { + error("ERROR: %s: idevicerestore client not initialized?!\n", __func__); + return -1; + } + info("About to send NORData...\n"); plist_t arguments = plist_dict_get_item(message, "Arguments"); @@ -1136,7 +1563,7 @@ int restore_send_nor(restored_client_t restore, struct idevicerestore_client_t* } } if (llb_path == NULL) { - if (build_identity_get_component_path(build_identity, "LLB", &llb_path) < 0) { + if (build_identity_get_component_path(client->restore->build_identity, "LLB", &llb_path) < 0) { error("ERROR: Unable to get component path for LLB\n"); return -1; } @@ -1176,7 +1603,7 @@ int restore_send_nor(restored_client_t restore, struct idevicerestore_client_t* } else { info("Getting firmware manifest from build identity\n"); plist_dict_iter iter = NULL; - plist_t build_id_manifest = plist_dict_get_item(build_identity, "Manifest"); + plist_t build_id_manifest = plist_dict_get_item(client->restore->build_identity, "Manifest"); if (build_id_manifest) { plist_dict_new_iter(build_id_manifest, &iter); } @@ -1236,7 +1663,7 @@ int restore_send_nor(restored_client_t restore, struct idevicerestore_client_t* return -1; } - ret = personalize_component(component, component_data, component_size, client->tss, &llb_data, &llb_size); + ret = personalize_component(client, component, component_data, component_size, client->tss, &llb_data, &llb_size); free(component_data); component_data = NULL; component_size = 0; @@ -1246,7 +1673,7 @@ int restore_send_nor(restored_client_t restore, struct idevicerestore_client_t* } dict = plist_new_dict(); - plist_dict_set_item(dict, "LlbImageData", plist_new_data((char*)llb_data, (uint64_t) llb_size)); + plist_dict_set_item(dict, "LlbImageData", plist_new_data((char*)llb_data, llb_size)); free(llb_data); if (flash_version_1) { @@ -1292,7 +1719,7 @@ int restore_send_nor(restored_client_t restore, struct idevicerestore_client_t* return -1; } - if (personalize_component(component, component_data, component_size, client->tss, &nor_data, &nor_size) < 0) { + if (personalize_component(client, component, component_data, component_size, client->tss, &nor_data, &nor_size) < 0) { free(iter); free(comp); free(comppath); @@ -1306,13 +1733,13 @@ int restore_send_nor(restored_client_t restore, struct idevicerestore_client_t* component_size = 0; if (flash_version_1) { - plist_dict_set_item(norimage, component, plist_new_data((char*)nor_data, (uint64_t)nor_size)); + plist_dict_set_item(norimage, component, plist_new_data((char*)nor_data, nor_size)); } else { /* make sure iBoot is the first entry in the array */ if (!strncmp("iBoot", component, 5)) { - plist_array_insert_item(norimage, plist_new_data((char*)nor_data, (uint64_t)nor_size), 0); + plist_array_insert_item(norimage, plist_new_data((char*)nor_data, nor_size), 0); } else { - plist_array_append_item(norimage, plist_new_data((char*)nor_data, (uint64_t)nor_size)); + plist_array_append_item(norimage, plist_new_data((char*)nor_data, nor_size)); } } @@ -1329,8 +1756,8 @@ int restore_send_nor(restored_client_t restore, struct idevicerestore_client_t* unsigned char* personalized_data = NULL; unsigned int personalized_size = 0; - if (build_identity_has_component(build_identity, "RestoreSEP") && - build_identity_get_component_path(build_identity, "RestoreSEP", &restore_sep_path) == 0) { + if (build_identity_has_component(client->restore->build_identity, "RestoreSEP") && + build_identity_get_component_path(client->restore->build_identity, "RestoreSEP", &restore_sep_path) == 0) { component = "RestoreSEP"; ret = extract_component(client->ipsw, restore_sep_path, &component_data, &component_size); free(restore_sep_path); @@ -1339,7 +1766,7 @@ int restore_send_nor(restored_client_t restore, struct idevicerestore_client_t* return -1; } - ret = personalize_component(component, component_data, component_size, client->tss, &personalized_data, &personalized_size); + ret = personalize_component(client, component, component_data, component_size, client->tss, &personalized_data, &personalized_size); free(component_data); component_data = NULL; component_size = 0; @@ -1348,14 +1775,14 @@ int restore_send_nor(restored_client_t restore, struct idevicerestore_client_t* return -1; } - plist_dict_set_item(dict, "RestoreSEPImageData", plist_new_data((char*)personalized_data, (uint64_t) personalized_size)); + plist_dict_set_item(dict, "RestoreSEPImageData", plist_new_data((char*)personalized_data, personalized_size)); free(personalized_data); personalized_data = NULL; personalized_size = 0; } - if (build_identity_has_component(build_identity, "SEP") && - build_identity_get_component_path(build_identity, "SEP", &sep_path) == 0) { + if (build_identity_has_component(client->restore->build_identity, "SEP") && + build_identity_get_component_path(client->restore->build_identity, "SEP", &sep_path) == 0) { component = "SEP"; ret = extract_component(client->ipsw, sep_path, &component_data, &component_size); free(sep_path); @@ -1364,7 +1791,32 @@ int restore_send_nor(restored_client_t restore, struct idevicerestore_client_t* return -1; } - ret = personalize_component(component, component_data, component_size, client->tss, &personalized_data, &personalized_size); + ret = personalize_component(client, component, component_data, component_size, client->tss, &personalized_data, &personalized_size); + free(component_data); + component_data = NULL; + component_size = 0; + if (ret < 0) { + error("ERROR: Unable to get personalized component: %s\n", component); + return -1; + } + + plist_dict_set_item(dict, "SEPImageData", plist_new_data((char*)personalized_data, personalized_size)); + free(personalized_data); + personalized_data = NULL; + personalized_size = 0; + } + + if (build_identity_has_component(client->restore->build_identity, "SepStage1") && + build_identity_get_component_path(client->restore->build_identity, "SepStage1", &sep_path) == 0) { + component = "SepStage1"; + ret = extract_component(client->ipsw, sep_path, &component_data, &component_size); + free(sep_path); + if (ret < 0) { + error("ERROR: Unable to extract component: %s\n", component); + return -1; + } + + ret = personalize_component(client, component, component_data, component_size, client->tss, &personalized_data, &personalized_size); free(component_data); component_data = NULL; component_size = 0; @@ -1373,7 +1825,7 @@ int restore_send_nor(restored_client_t restore, struct idevicerestore_client_t* return -1; } - plist_dict_set_item(dict, "SEPImageData", plist_new_data((char*)personalized_data, (uint64_t) personalized_size)); + plist_dict_set_item(dict, "SEPPatchImageData", plist_new_data((char*)personalized_data, personalized_size)); free(personalized_data); personalized_data = NULL; personalized_size = 0; @@ -1382,15 +1834,22 @@ int restore_send_nor(restored_client_t restore, struct idevicerestore_client_t* if (idevicerestore_debug) debug_plist(dict); + restore_service_client_t service = _restore_get_service_client_for_data_request(client, message); + if (!service) { + error("ERROR: %s: Unable to connect to service client\n", __func__); + return -1; + } + info("Sending NORData now...\n"); - if (restored_send(restore, dict) != RESTORE_E_SUCCESS) { + restored_error_t restore_error = _restore_service_send(service, dict, 0); + plist_free(dict); + _restore_service_free(service); + if (restore_error != RESTORE_E_SUCCESS) { error("ERROR: Unable to send NORData\n"); - plist_free(dict); return -1; } info("Done sending NORData\n"); - plist_free(dict); return 0; } @@ -1452,12 +1911,12 @@ static int restore_sign_bbfw(const char* bbfwtmp, plist_t bbtss, const unsigned } unsigned char* buffer = NULL; - unsigned char* blob = NULL; + const unsigned char* blob = NULL; unsigned char* fdata = NULL; uint64_t fsize = 0; uint64_t blob_size = 0; int zerr = 0; - int zindex = -1; + int64_t zindex = -1; struct zip_stat zstat; struct zip_file* zfile = NULL; struct zip* za = NULL; @@ -1479,7 +1938,7 @@ static int restore_sign_bbfw(const char* bbfwtmp, plist_t bbtss, const unsigned } int is_fls = 0; - int signed_file_idxs[16]; + int64_t signed_file_idxs[16]; int signed_file_count = 0; char* key = NULL; plist_t node = NULL; @@ -1508,13 +1967,13 @@ static int restore_sign_bbfw(const char* bbfwtmp, plist_t bbtss, const unsigned zip_stat_init(&zstat); if (zip_stat_index(za, zindex, 0, &zstat) != 0) { - error("ERROR: zip_stat_index failed for index %d\n", zindex); + error("ERROR: zip_stat_index failed for index %" PRIi64 "\n", zindex); goto leave; } zfile = zip_fopen_index(za, zindex, 0); if (zfile == NULL) { - error("ERROR: zip_fopen_index failed for index %d\n", zindex); + error("ERROR: zip_fopen_index failed for index %" PRIi64 "\n", zindex); goto leave; } @@ -1549,9 +2008,8 @@ static int restore_sign_bbfw(const char* bbfwtmp, plist_t bbtss, const unsigned free(buffer); buffer = NULL; - blob = NULL; blob_size = 0; - plist_get_data_val(node, (char**)&blob, &blob_size); + blob = (const unsigned char*)plist_get_data_ptr(node, &blob_size); if (!blob) { error("ERROR: could not get %s-Blob data\n", key); goto leave; @@ -1568,8 +2026,6 @@ static int restore_sign_bbfw(const char* bbfwtmp, plist_t bbtss, const unsigned goto leave; } } - free(blob); - blob = NULL; fsize = (is_fls ? fls->size : mbn->size); fdata = (unsigned char*)malloc(fsize); @@ -1594,7 +2050,7 @@ static int restore_sign_bbfw(const char* bbfwtmp, plist_t bbtss, const unsigned goto leave; } - if (zip_replace(za, zindex, zs) == -1) { + if (zip_file_replace(za, zindex, zs, 0) == -1) { error("ERROR: could not update signed '%s' in archive\n", signfn); goto leave; } @@ -1612,9 +2068,10 @@ static int restore_sign_bbfw(const char* bbfwtmp, plist_t bbtss, const unsigned free(iter); // remove everything but required files - int i, j, keep, numf = zip_get_num_files(za); + int64_t i, numf = zip_get_num_entries(za, 0); for (i = 0; i < numf; i++) { - keep = 0; + int j; + int keep = 0; // check for signed file index for (j = 0; j < signed_file_count; j++) { if (i == signed_file_idxs[j]) { @@ -1648,13 +2105,13 @@ static int restore_sign_bbfw(const char* bbfwtmp, plist_t bbtss, const unsigned zip_stat_init(&zstat); if (zip_stat_index(za, zindex, 0, &zstat) != 0) { - error("ERROR: zip_stat_index failed for index %d\n", zindex); + error("ERROR: zip_stat_index failed for index %" PRIi64 "\n", zindex); goto leave; } zfile = zip_fopen_index(za, zindex, 0); if (zfile == NULL) { - error("ERROR: zip_fopen_index failed for index %d\n", zindex); + error("ERROR: zip_fopen_index failed for index %" PRIi64 "\n", zindex); goto leave; } @@ -1681,9 +2138,8 @@ static int restore_sign_bbfw(const char* bbfwtmp, plist_t bbtss, const unsigned goto leave; } - blob = NULL; blob_size = 0; - plist_get_data_val(bbticket, (char**)&blob, &blob_size); + blob = (const unsigned char*)plist_get_data_ptr(bbticket, &blob_size); if (!blob) { error("ERROR: could not get BBTicket data\n"); goto leave; @@ -1693,8 +2149,6 @@ static int restore_sign_bbfw(const char* bbfwtmp, plist_t bbtss, const unsigned error("ERROR: could not insert BBTicket to ebl.fls\n"); goto leave; } - free(blob); - blob = NULL; fsize = fls->size; fdata = (unsigned char*)malloc(fsize); @@ -1713,28 +2167,26 @@ static int restore_sign_bbfw(const char* bbfwtmp, plist_t bbtss, const unsigned goto leave; } - if (zip_replace(za, zindex, zs) == -1) { + if (zip_file_replace(za, zindex, zs, 0) == -1) { error("ERROR: could not update archive with ticketed ebl.fls\n"); goto leave; } } else { // add BBTicket as bbticket.der - blob = NULL; blob_size = 0; - plist_get_data_val(bbticket, (char**)&blob, &blob_size); + blob = (const unsigned char*)plist_get_data_ptr(bbticket, &blob_size); if (!blob) { error("ERROR: could not get BBTicket data\n"); goto leave; } - zs = zip_source_buffer(za, blob, blob_size, 1); + zs = zip_source_buffer(za, blob, blob_size, 0); if (!zs) { error("ERROR: out of memory\n"); goto leave; } - blob = NULL; - if (zip_add(za, "bbticket.der", zs) == -1) { + if (zip_file_add(za, "bbticket.der", zs, ZIP_FL_OVERWRITE) == -1) { error("ERROR: could not add bbticket.der to archive\n"); goto leave; } @@ -1765,12 +2217,11 @@ static int restore_sign_bbfw(const char* bbfwtmp, plist_t bbtss, const unsigned mbn_free(mbn); fls_free(fls); free(buffer); - free(blob); return res; } -static int restore_send_baseband_data(restored_client_t restore, struct idevicerestore_client_t* client, plist_t build_identity, plist_t message) +static int restore_send_baseband_data(struct idevicerestore_client_t* client, plist_t message) { int res = -1; uint64_t bb_cert_id = 0; @@ -1784,6 +2235,11 @@ static int restore_send_baseband_data(restored_client_t restore, struct idevicer char* bbfwtmp = NULL; plist_t dict = NULL; + if (!client || !client->restore || !client->restore->build_identity) { + error("ERROR: %s: idevicerestore client not initialized?!\n", __func__); + return -1; + } + info("About to send BasebandData...\n"); // NOTE: this function is called 2 or 3 times! @@ -1820,7 +2276,7 @@ static int restore_send_baseband_data(restored_client_t restore, struct idevicer plist_dict_set_item(parameters, "BbGoldCertId", plist_new_uint(bb_cert_id)); plist_dict_set_item(parameters, "BbSNUM", plist_new_data((const char*)bb_snum, bb_snum_size)); - tss_parameters_add_from_manifest(parameters, build_identity, true); + tss_parameters_add_from_manifest(parameters, client->restore->build_identity, true); /* create baseband request */ plist_t request = tss_request_new(NULL); @@ -1834,7 +2290,7 @@ static int restore_send_baseband_data(restored_client_t restore, struct idevicer tss_request_add_common_tags(request, parameters, NULL); tss_request_add_baseband_tags(request, parameters, NULL); - plist_t node = plist_access_path(build_identity, 2, "Info", "FDRSupport"); + plist_t node = plist_access_path(client->restore->build_identity, 2, "Info", "FDRSupport"); if (node && plist_get_node_type(node) == PLIST_BOOLEAN) { uint8_t b = 0; plist_get_bool_val(node, &b); @@ -1861,7 +2317,7 @@ static int restore_send_baseband_data(restored_client_t restore, struct idevicer } // get baseband firmware file path from build identity - plist_t bbfw_path = plist_access_path(build_identity, 4, "Manifest", "BasebandFirmware", "Info", "Path"); + plist_t bbfw_path = plist_access_path(client->restore->build_identity, 4, "Manifest", "BasebandFirmware", "Info", "Path"); if (!bbfw_path || plist_get_node_type(bbfw_path) != PLIST_STRING) { error("ERROR: Unable to get BasebandFirmware/Info/Path node\n"); plist_free(response); @@ -1911,16 +2367,24 @@ static int restore_send_baseband_data(restored_client_t restore, struct idevicer // send file dict = plist_new_dict(); - plist_dict_set_item(dict, "BasebandData", plist_new_data(buffer, (uint64_t)sz)); + plist_dict_set_item(dict, "BasebandData", plist_new_data(buffer, sz)); free(buffer); buffer = NULL; + restore_service_client_t service = _restore_get_service_client_for_data_request(client, message); + if (!service) { + error("ERROR: %s: Unable to connect to service client\n", __func__); + return -1; + } + info("Sending BasebandData now...\n"); - if (restored_send(restore, dict) != RESTORE_E_SUCCESS) { + if (_restore_service_send(service, dict, 0) != RESTORE_E_SUCCESS) { error("ERROR: Unable to send BasebandData data\n"); goto leave; } + _restore_service_free(service); + info("Done sending BasebandData\n"); res = 0; @@ -1936,7 +2400,7 @@ static int restore_send_baseband_data(restored_client_t restore, struct idevicer return res; } -int restore_send_fdr_trust_data(restored_client_t restore, idevice_t device) +int restore_send_fdr_trust_data(struct idevicerestore_client_t* client, plist_t message) { restored_error_t restore_error; plist_t dict; @@ -1948,9 +2412,16 @@ int restore_send_fdr_trust_data(restored_client_t restore, idevice_t device) * and this is what iTunes seems to be doing too */ dict = plist_new_dict(); + restore_service_client_t service = _restore_get_service_client_for_data_request(client, message); + if (!service) { + error("ERROR: %s: Unable to connect to service client\n", __func__); + return -1; + } + info("Sending FDR Trust data now...\n"); - restore_error = restored_send(restore, dict); + restore_error = _restore_service_send(service, dict, 0); plist_free(dict); + _restore_service_free(service); if (restore_error != RESTORE_E_SUCCESS) { error("ERROR: During sending FDR Trust data (%d)\n", restore_error); return -1; @@ -1961,7 +2432,7 @@ int restore_send_fdr_trust_data(restored_client_t restore, idevice_t device) return 0; } -static int restore_send_image_data(restored_client_t restore, struct idevicerestore_client_t *client, plist_t build_identity, plist_t message, const char *image_list_k, const char *image_type_k, const char *image_data_k) +static int restore_send_image_data(struct idevicerestore_client_t *client, plist_t message, const char *image_list_k, const char *image_type_k, const char *image_data_k) { restored_error_t restore_error; plist_t arguments; @@ -1974,8 +2445,13 @@ static int restore_send_image_data(restored_client_t restore, struct idevicerest char *image_name = NULL; int want_image_list = 0; + if (!client || !client->restore || !client->restore->build_identity) { + error("ERROR: %s: idevicerestore client not initialized?!\n", __func__); + return -1; + } + arguments = plist_dict_get_item(message, "Arguments"); - want_image_list = _plist_dict_get_bool(arguments, image_list_k); + want_image_list = plist_dict_get_bool(arguments, image_list_k); node = plist_dict_get_item(arguments, "ImageName"); if (node) { plist_get_string_val(node, &image_name); @@ -2001,7 +2477,7 @@ static int restore_send_image_data(restored_client_t restore, struct idevicerest data_dict = plist_new_dict(); } - build_id_manifest = plist_dict_get_item(build_identity, "Manifest"); + build_id_manifest = plist_dict_get_item(client->restore->build_identity, "Manifest"); if (build_id_manifest) { plist_dict_new_iter(build_id_manifest, &iter); } @@ -2033,7 +2509,7 @@ static int restore_send_image_data(restored_client_t restore, struct idevicerest if (!image_name) { info("Found %s component '%s'\n", image_type_k, component); } - build_identity_get_component_path(build_identity, component, &path); + build_identity_get_component_path(client->restore->build_identity, component, &path); if (path) { ret = extract_component(client->ipsw, path, &component_data, &component_size); } @@ -2043,7 +2519,7 @@ static int restore_send_image_data(restored_client_t restore, struct idevicerest error("ERROR: Unable to extract component: %s\n", component); } - ret = personalize_component(component, component_data, component_size, client->tss, &data, &size); + ret = personalize_component(client, component, component_data, component_size, client->tss, &data, &size); free(component_data); component_data = NULL; if (ret < 0) { @@ -2060,6 +2536,12 @@ static int restore_send_image_data(restored_client_t restore, struct idevicerest free(iter); } + restore_service_client_t service = _restore_get_service_client_for_data_request(client, message); + if (!service) { + error("ERROR: %s: Unable to connect to service client\n", __func__); + return -1; + } + dict = plist_new_dict(); if (want_image_list) { plist_dict_set_item(dict, image_list_k, matched_images); @@ -2078,8 +2560,9 @@ static int restore_send_image_data(restored_client_t restore, struct idevicerest } } - restore_error = restored_send(restore, dict); + restore_error = _restore_service_send(service, dict, 0); plist_free(dict); + _restore_service_free(service); if (restore_error != RESTORE_E_SUCCESS) { if (want_image_list) { error("ERROR: Failed to send %s image list (%d)\n", image_type_k, restore_error); @@ -2105,7 +2588,29 @@ static int restore_send_image_data(restored_client_t restore, struct idevicerest return 0; } -static plist_t restore_get_se_firmware_data(restored_client_t restore, struct idevicerestore_client_t* client, plist_t build_identity, plist_t p_info) +static int _wants_firmware_data(plist_t arguments) +{ + int result = 0; + plist_t tags = plist_access_path(arguments, 2, "DeviceGeneratedTags", "ResponseTags"); + if (tags) { + plist_array_iter iter = NULL; + plist_array_new_iter(tags, &iter); + plist_t node = NULL; + do { + plist_array_next_item(tags, iter, &node); + if (node) { + const char* tag = plist_get_string_ptr(node, NULL); + if (tag && (strcmp(tag, "FirmwareData") == 0)) { + result = 1; + } + } + } while (node); + plist_mem_free(iter); + } + return result; +} + +static plist_t restore_get_se_firmware_data(struct idevicerestore_client_t* client, plist_t p_info, plist_t arguments) { const char *comp_name = NULL; char *comp_path = NULL; @@ -2114,21 +2619,28 @@ static plist_t restore_get_se_firmware_data(restored_client_t restore, struct id plist_t parameters = NULL; plist_t request = NULL; plist_t response = NULL; + plist_t p_dgr = NULL; int ret; uint64_t chip_id = 0; + + if (!client || !client->restore || !client->restore->build_identity) { + error("ERROR: %s: idevicerestore client not initialized?!\n", __func__); + return NULL; + } + plist_t node = plist_dict_get_item(p_info, "SE,ChipID"); if (node && plist_get_node_type(node) == PLIST_UINT) { plist_get_uint_val(node, &chip_id); } if (chip_id == 0x20211) { comp_name = "SE,Firmware"; - } else if (chip_id == 0x73 || chip_id == 0x64 || chip_id == 0xC8 || chip_id == 0xD2) { + } else if (chip_id == 0x73 || chip_id == 0x64 || chip_id == 0xC8 || chip_id == 0xD2 || chip_id == 0x2C || chip_id == 0x36) { comp_name = "SE,UpdatePayload"; } else { info("WARNING: Unknown SE,ChipID 0x%" PRIx64 " detected. Restore might fail.\n", (uint64_t)chip_id); - if (build_identity_has_component(build_identity, "SE,UpdatePayload")) + if (build_identity_has_component(client->restore->build_identity, "SE,UpdatePayload")) comp_name = "SE,UpdatePayload"; - else if (build_identity_has_component(build_identity, "SE,Firmware")) + else if (build_identity_has_component(client->restore->build_identity, "SE,Firmware")) comp_name = "SE,Firmware"; else { error("ERROR: Neither 'SE,Firmware' nor 'SE,UpdatePayload' found in build identity.\n"); @@ -2137,16 +2649,11 @@ static plist_t restore_get_se_firmware_data(restored_client_t restore, struct id debug("DEBUG: %s: using %s\n", __func__, comp_name); } - if (build_identity_get_component_path(build_identity, comp_name, &comp_path) < 0) { - error("ERROR: Unable to get path for '%s' component\n", comp_name); - return NULL; - } - - ret = extract_component(client->ipsw, comp_path, &component_data, &component_size); - free(comp_path); - comp_path = NULL; - if (ret < 0) { - error("ERROR: Unable to extract '%s' component\n", comp_name); + p_dgr = plist_dict_get_item(arguments, "DeviceGeneratedRequest"); + if (!p_dgr) { + info("NOTE: %s: No DeviceGeneratedRequest in firmware updater data request. Continuing anyway.\n", __func__); + } else if (!PLIST_IS_DICT(p_dgr)) { + error("ERROR: %s: DeviceGeneratedRequest has invalid type!\n", __func__); return NULL; } @@ -2161,13 +2668,13 @@ static plist_t restore_get_se_firmware_data(restored_client_t restore, struct id parameters = plist_new_dict(); /* add manifest for current build_identity to parameters */ - tss_parameters_add_from_manifest(parameters, build_identity, true); + tss_parameters_add_from_manifest(parameters, client->restore->build_identity, true); /* add SE,* tags from info dictionary to parameters */ plist_dict_merge(¶meters, p_info); /* add required tags for SE TSS request */ - tss_request_add_se_tags(request, parameters, NULL); + tss_request_add_se_tags(request, parameters, p_dgr); plist_free(parameters); @@ -2180,13 +2687,36 @@ static plist_t restore_get_se_firmware_data(restored_client_t restore, struct id return NULL; } - if (plist_dict_get_item(response, "SE,Ticket")) { - info("Received SE ticket\n"); + if (plist_dict_get_item(response, "SE2,Ticket")) { + info("Received SE2,Ticket\n"); + } else if (plist_dict_get_item(response, "SE,Ticket")) { + info("Received SE,Ticket\n"); } else { - error("ERROR: No 'SE,Ticket' in TSS response, this might not work\n"); + error("ERROR: No 'SE ticket' in TSS response, this might not work\n"); + } + + /* don't add FirmwareData if not requested via ResponseTags */ + if (!_wants_firmware_data(arguments)) { + debug("DEBUG: Not adding FirmwareData as it was not requested\n"); + return response; + } + + if (build_identity_get_component_path(client->restore->build_identity, comp_name, &comp_path) < 0) { + plist_free(response); + error("ERROR: Unable to get path for '%s' component\n", comp_name); + return NULL; + } + + ret = extract_component(client->ipsw, comp_path, &component_data, &component_size); + free(comp_path); + comp_path = NULL; + if (ret < 0) { + plist_free(response); + error("ERROR: Unable to extract '%s' component\n", comp_name); + return NULL; } - plist_dict_set_item(response, "FirmwareData", plist_new_data((char*)component_data, (uint64_t) component_size)); + plist_dict_set_item(response, "FirmwareData", plist_new_data((char*)component_data, component_size)); free(component_data); component_data = NULL; component_size = 0; @@ -2194,7 +2724,7 @@ static plist_t restore_get_se_firmware_data(restored_client_t restore, struct id return response; } -static plist_t restore_get_savage_firmware_data(restored_client_t restore, struct idevicerestore_client_t* client, plist_t build_identity, plist_t p_info) +static plist_t restore_get_savage_firmware_data(struct idevicerestore_client_t* client, plist_t p_info, plist_t arguments) { char *comp_name = NULL; char *comp_path = NULL; @@ -2206,6 +2736,17 @@ static plist_t restore_get_savage_firmware_data(restored_client_t restore, struc plist_t response = NULL; int ret; + if (!client || !client->restore || !client->restore->build_identity) { + error("ERROR: %s: idevicerestore client not initialized?!\n", __func__); + return NULL; + } + + plist_t device_generated_request = plist_dict_get_item(arguments, "DeviceGeneratedRequest"); + if (device_generated_request && !PLIST_IS_DICT(device_generated_request)) { + error("ERROR: %s: DeviceGeneratedRequest has invalid type!\n", __func__); + return NULL; + } + /* create Savage request */ request = tss_request_new(NULL); if (request == NULL) { @@ -2216,13 +2757,13 @@ static plist_t restore_get_savage_firmware_data(restored_client_t restore, struc parameters = plist_new_dict(); /* add manifest for current build_identity to parameters */ - tss_parameters_add_from_manifest(parameters, build_identity, true); + tss_parameters_add_from_manifest(parameters, client->restore->build_identity, true); /* add Savage,* tags from info dictionary to parameters */ plist_dict_merge(¶meters, p_info); /* add required tags for Savage TSS request */ - tss_request_add_savage_tags(request, parameters, NULL, &comp_name); + tss_request_add_savage_tags(request, parameters, device_generated_request, &comp_name); plist_free(parameters); @@ -2248,8 +2789,15 @@ static plist_t restore_get_savage_firmware_data(restored_client_t restore, struc error("ERROR: No 'Savage,Ticket' in TSS response, this might not work\n"); } + /* don't add FirmwareData if not requested via ResponseTags */ + if (!_wants_firmware_data(arguments)) { + debug("DEBUG: Not adding FirmwareData as it was not requested\n"); + return response; + } + /* now get actual component data */ - if (build_identity_get_component_path(build_identity, comp_name, &comp_path) < 0) { + if (build_identity_get_component_path(client->restore->build_identity, comp_name, &comp_path) < 0) { + plist_free(response); error("ERROR: Unable to get path for '%s' component\n", comp_name); free(comp_name); return NULL; @@ -2259,6 +2807,7 @@ static plist_t restore_get_savage_firmware_data(restored_client_t restore, struc free(comp_path); comp_path = NULL; if (ret < 0) { + plist_free(response); error("ERROR: Unable to extract '%s' component\n", comp_name); free(comp_name); return NULL; @@ -2269,6 +2818,7 @@ static plist_t restore_get_savage_firmware_data(restored_client_t restore, struc component_data_tmp = realloc(component_data, (size_t)component_size+16); if (!component_data_tmp) { free(component_data); + plist_free(response); return NULL; } component_data = component_data_tmp; @@ -2277,7 +2827,7 @@ static plist_t restore_get_savage_firmware_data(restored_client_t restore, struc *(uint32_t*)(component_data + 4) = htole32((uint32_t)component_size); component_size += 16; - plist_dict_set_item(response, "FirmwareData", plist_new_data((char*)component_data, (uint64_t) component_size)); + plist_dict_set_item(response, "FirmwareData", plist_new_data((char*)component_data, component_size)); free(component_data); component_data = NULL; component_size = 0; @@ -2285,7 +2835,7 @@ static plist_t restore_get_savage_firmware_data(restored_client_t restore, struc return response; } -static plist_t restore_get_yonkers_firmware_data(restored_client_t restore, struct idevicerestore_client_t* client, plist_t build_identity, plist_t p_info) +static plist_t restore_get_yonkers_firmware_data(struct idevicerestore_client_t* client, plist_t p_info, plist_t arguments) { char *comp_name = NULL; char *comp_path = NULL; @@ -2296,25 +2846,34 @@ static plist_t restore_get_yonkers_firmware_data(restored_client_t restore, stru plist_t response = NULL; int ret; + if (!client || !client->restore || !client->restore->build_identity) { + error("ERROR: %s: idevicerestore client not initialized?!\n", __func__); + return NULL; + } + + plist_t device_generated_request = plist_dict_get_item(arguments, "DeviceGeneratedRequest"); + if (device_generated_request && !PLIST_IS_DICT(device_generated_request)) { + error("ERROR: %s: DeviceGeneratedRequest has invalid type!\n", __func__); + return NULL; + } + /* create Yonkers request */ request = tss_request_new(NULL); if (request == NULL) { error("ERROR: Unable to create Yonkers TSS request\n"); - free(component_data); - free(comp_name); return NULL; } parameters = plist_new_dict(); /* add manifest for current build_identity to parameters */ - tss_parameters_add_from_manifest(parameters, build_identity, true); + tss_parameters_add_from_manifest(parameters, client->restore->build_identity, true); /* add Yonkers,* tags from info dictionary to parameters */ plist_dict_merge(¶meters, p_info); /* add required tags for Yonkers TSS request */ - tss_request_add_yonkers_tags(request, parameters, NULL, &comp_name); + tss_request_add_yonkers_tags(request, parameters, device_generated_request, &comp_name); plist_free(parameters); @@ -2330,7 +2889,7 @@ static plist_t restore_get_yonkers_firmware_data(restored_client_t restore, stru plist_free(request); if (response == NULL) { error("ERROR: Unable to fetch Yonkers ticket\n"); - free(component_data); + free(comp_name); return NULL; } @@ -2340,7 +2899,15 @@ static plist_t restore_get_yonkers_firmware_data(restored_client_t restore, stru error("ERROR: No 'Yonkers,Ticket' in TSS response, this might not work\n"); } - if (build_identity_get_component_path(build_identity, comp_name, &comp_path) < 0) { + /* don't add FirmwareData if not requested via ResponseTags */ + if (!_wants_firmware_data(arguments)) { + debug("DEBUG: Not adding FirmwareData as it was not requested\n"); + free(comp_name); + return response; + } + + if (build_identity_get_component_path(client->restore->build_identity, comp_name, &comp_path) < 0) { + plist_free(response); error("ERROR: Unable to get path for '%s' component\n", comp_name); free(comp_name); return NULL; @@ -2351,6 +2918,7 @@ static plist_t restore_get_yonkers_firmware_data(restored_client_t restore, stru free(comp_path); comp_path = NULL; if (ret < 0) { + plist_free(response); error("ERROR: Unable to extract '%s' component\n", comp_name); free(comp_name); return NULL; @@ -2359,7 +2927,7 @@ static plist_t restore_get_yonkers_firmware_data(restored_client_t restore, stru comp_name = NULL; plist_t firmware_data = plist_new_dict(); - plist_dict_set_item(firmware_data, "YonkersFirmware", plist_new_data((char *)component_data, (uint64_t)component_size)); + plist_dict_set_item(firmware_data, "YonkersFirmware", plist_new_data((char*)component_data, component_size)); plist_dict_set_item(response, "FirmwareData", firmware_data); free(component_data); @@ -2369,7 +2937,7 @@ static plist_t restore_get_yonkers_firmware_data(restored_client_t restore, stru return response; } -static plist_t restore_get_rose_firmware_data(restored_client_t restore, struct idevicerestore_client_t* client, plist_t build_identity, plist_t p_info) +static plist_t restore_get_rose_firmware_data(struct idevicerestore_client_t* client, plist_t p_info, plist_t arguments) { char *comp_name = NULL; char *comp_path = NULL; @@ -2383,18 +2951,22 @@ static plist_t restore_get_rose_firmware_data(restored_client_t restore, struct plist_t response = NULL; int ret; + if (!client || !client->restore || !client->restore->build_identity) { + error("ERROR: %s: idevicerestore client not initialized?!\n", __func__); + return NULL; + } + /* create Rose request */ request = tss_request_new(NULL); if (request == NULL) { error("ERROR: Unable to create Rose TSS request\n"); - free(component_data); return NULL; } parameters = plist_new_dict(); /* add manifest for current build_identity to parameters */ - tss_parameters_add_from_manifest(parameters, build_identity, true); + tss_parameters_add_from_manifest(parameters, client->restore->build_identity, true); plist_dict_set_item(parameters, "ApProductionMode", plist_new_bool(1)); if (client->image4supported) { @@ -2404,8 +2976,14 @@ static plist_t restore_get_rose_firmware_data(restored_client_t restore, struct plist_dict_set_item(parameters, "ApSupportsImg4", plist_new_bool(0)); } - /* add Rap,* tags from info dictionary to parameters */ - plist_dict_merge(¶meters, p_info); + plist_t device_generated_request = plist_dict_get_item(arguments, "DeviceGeneratedRequest"); + if (device_generated_request) { + /* use DeviceGeneratedRequest if present */ + plist_dict_merge(&request, device_generated_request); + } else { + /* add Rap,* tags from info dictionary to parameters */ + plist_dict_merge(¶meters, p_info); + } /* add required tags for Rose TSS request */ tss_request_add_rose_tags(request, parameters, NULL); @@ -2417,7 +2995,6 @@ static plist_t restore_get_rose_firmware_data(restored_client_t restore, struct plist_free(request); if (response == NULL) { error("ERROR: Unable to fetch Rose ticket\n"); - free(component_data); return NULL; } @@ -2427,8 +3004,15 @@ static plist_t restore_get_rose_firmware_data(restored_client_t restore, struct error("ERROR: No 'Rap,Ticket' in TSS response, this might not work\n"); } + /* don't add FirmwareData if not requested via ResponseTags */ + if (!_wants_firmware_data(arguments)) { + debug("DEBUG: Not adding FirmwareData as it was not requested\n"); + return response; + } + comp_name = "Rap,RTKitOS"; - if (build_identity_get_component_path(build_identity, comp_name, &comp_path) < 0) { + if (build_identity_get_component_path(client->restore->build_identity, comp_name, &comp_path) < 0) { + plist_free(response); error("ERROR: Unable to get path for '%s' component\n", comp_name); return NULL; } @@ -2436,10 +3020,12 @@ static plist_t restore_get_rose_firmware_data(restored_client_t restore, struct free(comp_path); comp_path = NULL; if (ret < 0) { + plist_free(response); error("ERROR: Unable to extract '%s' component\n", comp_name); return NULL; } if (ftab_parse(component_data, component_size, &ftab, &ftag) != 0) { + plist_free(response); free(component_data); error("ERROR: Failed to parse '%s' component data.\n", comp_name); return NULL; @@ -2452,9 +3038,10 @@ static plist_t restore_get_rose_firmware_data(restored_client_t restore, struct } comp_name = "Rap,RestoreRTKitOS"; - if (build_identity_has_component(build_identity, comp_name)) { - if (build_identity_get_component_path(build_identity, comp_name, &comp_path) < 0) { + if (build_identity_has_component(client->restore->build_identity, comp_name)) { + if (build_identity_get_component_path(client->restore->build_identity, comp_name, &comp_path) < 0) { ftab_free(ftab); + plist_free(response); error("ERROR: Unable to get path for '%s' component\n", comp_name); return NULL; } @@ -2463,6 +3050,7 @@ static plist_t restore_get_rose_firmware_data(restored_client_t restore, struct comp_path = NULL; if (ret < 0) { ftab_free(ftab); + plist_free(response); error("ERROR: Unable to extract '%s' component\n", comp_name); return NULL; } @@ -2471,6 +3059,7 @@ static plist_t restore_get_rose_firmware_data(restored_client_t restore, struct if (ftab_parse(component_data, component_size, &rftab, &ftag) != 0) { free(component_data); ftab_free(ftab); + plist_free(response); error("ERROR: Failed to parse '%s' component data.\n", comp_name); return NULL; } @@ -2496,7 +3085,7 @@ static plist_t restore_get_rose_firmware_data(restored_client_t restore, struct ftab_write(ftab, &component_data, &component_size); ftab_free(ftab); - plist_dict_set_item(response, "FirmwareData", plist_new_data((char *)component_data, (uint64_t)component_size)); + plist_dict_set_item(response, "FirmwareData", plist_new_data((char*)component_data, component_size)); free(component_data); component_data = NULL; component_size = 0; @@ -2504,7 +3093,7 @@ static plist_t restore_get_rose_firmware_data(restored_client_t restore, struct return response; } -static plist_t restore_get_veridian_firmware_data(restored_client_t restore, struct idevicerestore_client_t* client, plist_t build_identity, plist_t p_info) +static plist_t restore_get_veridian_firmware_data(struct idevicerestore_client_t* client, plist_t p_info, plist_t arguments) { char *comp_name = "BMU,FirmwareMap"; char *comp_path = NULL; @@ -2515,6 +3104,17 @@ static plist_t restore_get_veridian_firmware_data(restored_client_t restore, str plist_t response = NULL; int ret; + if (!client || !client->restore || !client->restore->build_identity) { + error("ERROR: %s: idevicerestore client not initialized?!\n", __func__); + return NULL; + } + + plist_t device_generated_request = plist_dict_get_item(arguments, "DeviceGeneratedRequest"); + if (device_generated_request && !PLIST_IS_DICT(device_generated_request)) { + error("ERROR: %s: DeviceGeneratedRequest has invalid type!\n", __func__); + return NULL; + } + /* create Veridian request */ request = tss_request_new(NULL); if (request == NULL) { @@ -2526,13 +3126,13 @@ static plist_t restore_get_veridian_firmware_data(restored_client_t restore, str parameters = plist_new_dict(); /* add manifest for current build_identity to parameters */ - tss_parameters_add_from_manifest(parameters, build_identity, true); + tss_parameters_add_from_manifest(parameters, client->restore->build_identity, true); /* add BMU,* tags from info dictionary to parameters */ plist_dict_merge(¶meters, p_info); /* add required tags for Veridian TSS request */ - tss_request_add_veridian_tags(request, parameters, NULL); + tss_request_add_veridian_tags(request, parameters, device_generated_request); plist_free(parameters); @@ -2551,7 +3151,14 @@ static plist_t restore_get_veridian_firmware_data(restored_client_t restore, str error("ERROR: No 'BMU,Ticket' in TSS response, this might not work\n"); } - if (build_identity_get_component_path(build_identity, comp_name, &comp_path) < 0) { + /* don't add FirmwareData if not requested via ResponseTags */ + if (!_wants_firmware_data(arguments)) { + debug("DEBUG: Not adding FirmwareData as it was not requested\n"); + return response; + } + + if (build_identity_get_component_path(client->restore->build_identity, comp_name, &comp_path) < 0) { + plist_free(response); error("ERROR: Unable to get path for '%s' component\n", comp_name); return NULL; } @@ -2561,6 +3168,7 @@ static plist_t restore_get_veridian_firmware_data(restored_client_t restore, str free(comp_path); comp_path = NULL; if (ret < 0) { + plist_free(response); error("ERROR: Unable to extract '%s' component\n", comp_name); return NULL; } @@ -2576,13 +3184,15 @@ static plist_t restore_get_veridian_firmware_data(restored_client_t restore, str component_size = 0; if (!fw_map) { + plist_free(response); error("ERROR: Unable to parse '%s' component data as plist\n", comp_name); return NULL; } - plist_t fw_map_digest = plist_access_path(build_identity, 3, "Manifest", comp_name, "Digest"); + plist_t fw_map_digest = plist_access_path(client->restore->build_identity, 3, "Manifest", comp_name, "Digest"); if (!fw_map_digest) { plist_free(fw_map); + plist_free(response); error("ERROR: Unable to get Digest for '%s' component\n", comp_name); return NULL; } @@ -2594,13 +3204,68 @@ static plist_t restore_get_veridian_firmware_data(restored_client_t restore, str plist_to_bin(fw_map, &bin_plist, &bin_size); plist_free(fw_map); - plist_dict_set_item(response, "FirmwareData", plist_new_data(bin_plist, (uint64_t)bin_size)); + plist_dict_set_item(response, "FirmwareData", plist_new_data(bin_plist, bin_size)); free(bin_plist); return response; } -static plist_t restore_get_tcon_firmware_data(restored_client_t restore, struct idevicerestore_client_t* client, plist_t build_identity, plist_t p_info) +static plist_t restore_get_generic_firmware_data(struct idevicerestore_client_t* client, plist_t p_info, plist_t arguments) +{ + plist_t request = NULL; + plist_t response = NULL; + + plist_t p_updater_name = plist_dict_get_item(arguments, "MessageArgUpdaterName"); + const char* s_updater_name = plist_get_string_ptr(p_updater_name, NULL); + + plist_t response_tags = plist_access_path(arguments, 2, "DeviceGeneratedTags", "ResponseTags"); + const char* response_ticket = NULL; + if (PLIST_IS_ARRAY(response_tags)) { + plist_t tag0 = plist_array_get_item(response_tags, 0); + if (tag0) { + response_ticket = plist_get_string_ptr(tag0, NULL); + } + } + if (response_ticket == NULL) { + error("ERROR: Unable to determine response ticket from device generated tags"); + return NULL; + } + + /* create TSS request */ + request = tss_request_new(NULL); + if (request == NULL) { + error("ERROR: Unable to create %s TSS request\n", s_updater_name); + return NULL; + } + + /* add device generated request data to request */ + plist_t device_generated_request = plist_dict_get_item(arguments, "DeviceGeneratedRequest"); + if (!device_generated_request) { + error("ERROR: Could not find DeviceGeneratedRequest in arguments dictionary\n"); + plist_free(request); + return NULL; + } + plist_dict_merge(&request, device_generated_request); + + info("Sending %s TSS request...\n", s_updater_name); + response = tss_request_send(request, client->tss_url); + plist_free(request); + if (response == NULL) { + error("ERROR: Unable to fetch %s ticket\n", s_updater_name); + return NULL; + } + + if (plist_dict_get_item(response, response_ticket)) { + info("Received %s\n", response_ticket); + } else { + error("ERROR: No '%s' in TSS response, this might not work\n", response_ticket); + debug_plist(response); + } + + return response; +} + +static plist_t restore_get_tcon_firmware_data(struct idevicerestore_client_t* client, plist_t p_info, plist_t arguments) { char *comp_name = "Baobab,TCON"; char *comp_path = NULL; @@ -2611,6 +3276,17 @@ static plist_t restore_get_tcon_firmware_data(restored_client_t restore, struct plist_t response = NULL; int ret; + if (!client || !client->restore || !client->restore->build_identity) { + error("ERROR: %s: idevicerestore client not initialized?!\n", __func__); + return NULL; + } + + plist_t device_generated_request = plist_dict_get_item(arguments, "DeviceGeneratedRequest"); + if (device_generated_request && !PLIST_IS_DICT(device_generated_request)) { + error("ERROR: %s: DeviceGeneratedRequest has invalid type!\n", __func__); + return NULL; + } + /* create Baobab request */ request = tss_request_new(NULL); if (request == NULL) { @@ -2622,13 +3298,13 @@ static plist_t restore_get_tcon_firmware_data(restored_client_t restore, struct parameters = plist_new_dict(); /* add manifest for current build_identity to parameters */ - tss_parameters_add_from_manifest(parameters, build_identity, true); + tss_parameters_add_from_manifest(parameters, client->restore->build_identity, true); /* add Baobab,* tags from info dictionary to parameters */ plist_dict_merge(¶meters, p_info); /* add required tags for Baobab TSS request */ - tss_request_add_tcon_tags(request, parameters, NULL); + tss_request_add_tcon_tags(request, parameters, device_generated_request); plist_free(parameters); @@ -2647,8 +3323,15 @@ static plist_t restore_get_tcon_firmware_data(restored_client_t restore, struct error("ERROR: No 'Baobab,Ticket' in TSS response, this might not work\n"); } - if (build_identity_get_component_path(build_identity, comp_name, &comp_path) < 0) { + /* don't add FirmwareData if not requested via ResponseTags */ + if (!_wants_firmware_data(arguments)) { + debug("DEBUG: Not adding FirmwareData as it was not requested\n"); + return response; + } + + if (build_identity_get_component_path(client->restore->build_identity, comp_name, &comp_path) < 0) { error("ERROR: Unable to get path for '%s' component\n", comp_name); + plist_free(response); return NULL; } @@ -2658,10 +3341,11 @@ static plist_t restore_get_tcon_firmware_data(restored_client_t restore, struct comp_path = NULL; if (ret < 0) { error("ERROR: Unable to extract '%s' component\n", comp_name); + plist_free(response); return NULL; } - plist_dict_set_item(response, "FirmwareData", plist_new_data((char *)component_data, (uint64_t)component_size)); + plist_dict_set_item(response, "FirmwareData", plist_new_data((char*)component_data, component_size)); free(component_data); component_data = NULL; component_size = 0; @@ -2669,7 +3353,7 @@ static plist_t restore_get_tcon_firmware_data(restored_client_t restore, struct return response; } -static plist_t restore_get_timer_firmware_data(restored_client_t restore, struct idevicerestore_client_t* client, plist_t build_identity, plist_t p_info) +static plist_t restore_get_timer_firmware_data(struct idevicerestore_client_t* client, plist_t p_info, plist_t arguments) { char comp_name[64]; char *comp_path = NULL; @@ -2685,6 +3369,17 @@ static plist_t restore_get_timer_firmware_data(restored_client_t restore, struct uint32_t tag = 0; int ret; + if (!client || !client->restore || !client->restore->build_identity) { + error("ERROR: %s: idevicerestore client not initialized?!\n", __func__); + return NULL; + } + + plist_t device_generated_request = plist_dict_get_item(arguments, "DeviceGeneratedRequest"); + if (device_generated_request && !PLIST_IS_DICT(device_generated_request)) { + error("ERROR: %s: DeviceGeneratedRequest has invalid type!\n", __func__); + return NULL; + } + /* create Timer request */ request = tss_request_new(NULL); if (request == NULL) { @@ -2695,7 +3390,7 @@ static plist_t restore_get_timer_firmware_data(restored_client_t restore, struct parameters = plist_new_dict(); /* add manifest for current build_identity to parameters */ - tss_parameters_add_from_manifest(parameters, build_identity, true); + tss_parameters_add_from_manifest(parameters, client->restore->build_identity, true); plist_dict_set_item(parameters, "ApProductionMode", plist_new_bool(1)); if (client->image4supported) { @@ -2714,7 +3409,7 @@ static plist_t restore_get_timer_firmware_data(restored_client_t restore, struct } else { plist_t info_dict = plist_array_get_item(info_array, 0); plist_t hwid = plist_dict_get_item(info_dict, "HardwareID"); - tag = (uint32_t)_plist_dict_get_uint(info_dict, "TagNumber"); + tag = (uint32_t)plist_dict_get_uint(info_dict, "TagNumber"); char key[64]; plist_dict_set_item(parameters, "TagNumber", plist_new_uint(tag)); @@ -2724,26 +3419,26 @@ static plist_t restore_get_timer_firmware_data(restored_client_t restore, struct plist_dict_set_item(parameters, "TicketName", plist_copy(node)); } - sprintf(key, "Timer,ChipID,%u", tag); - _plist_dict_copy_uint(parameters, hwid, key, "ChipID"); + snprintf(key, sizeof(key), "Timer,ChipID,%u", tag); + plist_dict_copy_uint(parameters, hwid, key, "ChipID"); - sprintf(key, "Timer,BoardID,%u", tag); - _plist_dict_copy_uint(parameters, hwid, key, "BoardID"); + snprintf(key, sizeof(key), "Timer,BoardID,%u", tag); + plist_dict_copy_uint(parameters, hwid, key, "BoardID"); - sprintf(key, "Timer,ECID,%u", tag); - _plist_dict_copy_uint(parameters, hwid, key, "ECID"); + snprintf(key, sizeof(key), "Timer,ECID,%u", tag); + plist_dict_copy_uint(parameters, hwid, key, "ECID"); - sprintf(key, "Timer,Nonce,%u", tag); - _plist_dict_copy_data(parameters, hwid, key, "Nonce"); + snprintf(key, sizeof(key), "Timer,Nonce,%u", tag); + plist_dict_copy_data(parameters, hwid, key, "Nonce"); - sprintf(key, "Timer,SecurityMode,%u", tag); - _plist_dict_copy_bool(parameters, hwid, key, "SecurityMode"); + snprintf(key, sizeof(key), "Timer,SecurityMode,%u", tag); + plist_dict_copy_bool(parameters, hwid, key, "SecurityMode"); - sprintf(key, "Timer,SecurityDomain,%u", tag); - _plist_dict_copy_uint(parameters, hwid, key, "SecurityDomain"); + snprintf(key, sizeof(key), "Timer,SecurityDomain,%u", tag); + plist_dict_copy_uint(parameters, hwid, key, "SecurityDomain"); - sprintf(key, "Timer,ProductionMode,%u", tag); - _plist_dict_copy_uint(parameters, hwid, key, "ProductionStatus"); + snprintf(key, sizeof(key), "Timer,ProductionMode,%u", tag); + plist_dict_copy_uint(parameters, hwid, key, "ProductionStatus"); } plist_t ap_info = plist_dict_get_item(p_info, "APInfo"); if (!ap_info) { @@ -2755,7 +3450,7 @@ static plist_t restore_get_timer_firmware_data(restored_client_t restore, struct } /* add required tags for Timer TSS request */ - tss_request_add_timer_tags(request, parameters, NULL); + tss_request_add_timer_tags(request, parameters, device_generated_request); plist_free(parameters); @@ -2773,9 +3468,16 @@ static plist_t restore_get_timer_firmware_data(restored_client_t restore, struct error("ERROR: No '%s' in TSS response, this might not work\n", ticket_name); } - sprintf(comp_name, "Timer,RTKitOS,%u", tag); - if (build_identity_has_component(build_identity, comp_name)) { - if (build_identity_get_component_path(build_identity, comp_name, &comp_path) < 0) { + /* don't add FirmwareData if not requested via ResponseTags */ + if (!_wants_firmware_data(arguments)) { + debug("DEBUG: Not adding FirmwareData as it was not requested\n"); + return response; + } + + snprintf(comp_name, sizeof(comp_name), "Timer,RTKitOS,%u", tag); + if (build_identity_has_component(client->restore->build_identity, comp_name)) { + if (build_identity_get_component_path(client->restore->build_identity, comp_name, &comp_path) < 0) { + plist_free(response); error("ERROR: Unable to get path for '%s' component\n", comp_name); return NULL; } @@ -2784,10 +3486,12 @@ static plist_t restore_get_timer_firmware_data(restored_client_t restore, struct comp_path = NULL; if (ret < 0) { error("ERROR: Unable to extract '%s' component\n", comp_name); + plist_free(response); return NULL; } if (ftab_parse(component_data, component_size, &ftab, &ftag) != 0) { free(component_data); + plist_free(response); error("ERROR: Failed to parse '%s' component data.\n", comp_name); return NULL; } @@ -2801,10 +3505,11 @@ static plist_t restore_get_timer_firmware_data(restored_client_t restore, struct info("NOTE: Build identity does not have a '%s' component.\n", comp_name); } - sprintf(comp_name, "Timer,RestoreRTKitOS,%u", tag); - if (build_identity_has_component(build_identity, comp_name)) { - if (build_identity_get_component_path(build_identity, comp_name, &comp_path) < 0) { + snprintf(comp_name, sizeof(comp_name), "Timer,RestoreRTKitOS,%u", tag); + if (build_identity_has_component(client->restore->build_identity, comp_name)) { + if (build_identity_get_component_path(client->restore->build_identity, comp_name, &comp_path) < 0) { ftab_free(ftab); + plist_free(response); error("ERROR: Unable to get path for '%s' component\n", comp_name); return NULL; } @@ -2813,6 +3518,7 @@ static plist_t restore_get_timer_firmware_data(restored_client_t restore, struct comp_path = NULL; if (ret < 0) { ftab_free(ftab); + plist_free(response); error("ERROR: Unable to extract '%s' component\n", comp_name); return NULL; } @@ -2821,6 +3527,7 @@ static plist_t restore_get_timer_firmware_data(restored_client_t restore, struct if (ftab_parse(component_data, component_size, &rftab, &ftag) != 0) { free(component_data); ftab_free(ftab); + plist_free(response); error("ERROR: Failed to parse '%s' component data.\n", comp_name); return NULL; } @@ -2846,7 +3553,7 @@ static plist_t restore_get_timer_firmware_data(restored_client_t restore, struct ftab_write(ftab, &component_data, &component_size); ftab_free(ftab); - plist_dict_set_item(response, "FirmwareData", plist_new_data((char *)component_data, (uint64_t)component_size)); + plist_dict_set_item(response, "FirmwareData", plist_new_data((char*)component_data, component_size)); free(component_data); component_data = NULL; component_size = 0; @@ -2854,64 +3561,102 @@ static plist_t restore_get_timer_firmware_data(restored_client_t restore, struct return response; } -static plist_t restore_get_cryptex1_firmware_data(restored_client_t restore, struct idevicerestore_client_t* client, plist_t build_identity, plist_t p_info, plist_t arguments) +static plist_t restore_get_cryptex1_firmware_data(struct idevicerestore_client_t* client, plist_t p_info, plist_t arguments) { plist_t parameters = NULL; plist_t request = NULL; plist_t response = NULL; - /* create Timer request */ + if (!client || !client->restore || !client->restore->build_identity) { + error("ERROR: %s: idevicerestore client not initialized?!\n", __func__); + return NULL; + } + + plist_t p_updater_name = plist_dict_get_item(arguments, "MessageArgUpdaterName"); + const char* s_updater_name = plist_get_string_ptr(p_updater_name, NULL); + + plist_t response_tags = plist_access_path(arguments, 2, "DeviceGeneratedTags", "ResponseTags"); + const char* response_ticket = "Cryptex1,Ticket"; + if (PLIST_IS_ARRAY(response_tags)) { + plist_t tag0 = plist_array_get_item(response_tags, 0); + if (tag0) { + response_ticket = plist_get_string_ptr(tag0, NULL); + } + } + + /* create Cryptex1 request */ request = tss_request_new(NULL); if (request == NULL) { - error("ERROR: Unable to create Cryptex1 TSS request\n"); + error("ERROR: Unable to create %s TSS request\n", s_updater_name); return NULL; } parameters = plist_new_dict(); - /* add manifest for current build_identity to parameters (Cryptex1 will require the manifest in a seperate message) */ - tss_parameters_add_from_manifest(parameters, build_identity, false); + /* merge data from MessageArgInfo */ + plist_dict_merge(¶meters, p_info); - plist_dict_set_item(parameters, "ApProductionMode", plist_new_bool(1)); - plist_dict_set_item(parameters, "ApSecurityMode", plist_new_bool(1)); + /* add tags from manifest to parameters */ + plist_t build_identity_tags = plist_access_path(arguments, 2, "DeviceGeneratedTags", "BuildIdentityTags"); + if (PLIST_IS_ARRAY(build_identity_tags)) { + uint32_t i = 0; + for (i = 0; i < plist_array_get_size(build_identity_tags); i++) { + plist_t node = plist_array_get_item(build_identity_tags, i); + const char* key = plist_get_string_ptr(node, NULL); + plist_t item = plist_dict_get_item(client->restore->build_identity, key); + if (item) { + plist_dict_set_item(parameters, key, plist_copy(item)); + } + } + } - /* add Timer,* tags from info dictionary to parameters */ + /* make sure we always have these required tags defined */ + if (!plist_dict_get_item(parameters, "ApProductionMode")) { + plist_dict_set_item(parameters, "ApProductionMode", plist_new_bool(1)); + } + if (!plist_dict_get_item(parameters, "ApSecurityMode")) { + plist_dict_set_item(parameters, "ApSecurityMode", plist_new_bool(1)); + } + if (!plist_dict_get_item(parameters, "ApChipID")) { + plist_dict_copy_uint(parameters, client->restore->build_identity, "ApChipID", NULL); + } + if (!plist_dict_get_item(parameters, "ApBoardID")) { + plist_dict_copy_uint(parameters, client->restore->build_identity, "ApBoardID", NULL); + } + + /* add device generated request data to parameters */ plist_t device_generated_request = plist_dict_get_item(arguments, "DeviceGeneratedRequest"); if (!device_generated_request) { error("ERROR: Could not find DeviceGeneratedRequest in arguments dictionary\n"); plist_free(parameters); return NULL; } - plist_dict_merge(¶meters, device_generated_request); - /* add common tags */ - tss_request_add_common_tags(request, p_info, NULL); - - /* add Cryptex1 tags */ - plist_dict_set_item(request, "@BBTicket", plist_new_bool(1)); - plist_dict_merge(&request, parameters); + /* add Cryptex1 tags to request */ + tss_request_add_cryptex_tags(request, parameters, NULL); plist_free(parameters); - info("Sending Cryptex1 TSS request...\n"); + info("Sending %s TSS request...\n", s_updater_name); response = tss_request_send(request, client->tss_url); plist_free(request); if (response == NULL) { - error("ERROR: Unable to fetch Cryptex1\n"); + error("ERROR: Unable to fetch %s ticket\n", s_updater_name); return NULL; } - if (plist_dict_get_item(response, "Cryptex1,Ticket")) { - info("Received Cryptex1,Ticket\n"); + if (plist_dict_get_item(response, response_ticket)) { + info("Received %s\n", response_ticket); } else { - error("ERROR: No 'Cryptex1,Ticket' in TSS response, this might not work\n"); + error("ERROR: No '%s' in TSS response, this might not work\n", response_ticket); + debug_plist(response); } return response; } -static int restore_send_firmware_updater_preflight(restored_client_t restore, struct idevicerestore_client_t* client, plist_t build_identity, plist_t message) +static int restore_send_firmware_updater_preflight(struct idevicerestore_client_t* client, plist_t message) { plist_t dict = NULL; int restore_error; @@ -2921,11 +3666,18 @@ static int restore_send_firmware_updater_preflight(restored_client_t restore, st debug_plist(message); } + restore_service_client_t service = _restore_get_service_client_for_data_request(client, message); + if (!service) { + error("ERROR: %s: Unable to connect to service client\n", __func__); + return -1; + } + dict = plist_new_dict(); info("Sending FirmwareResponsePreflight now...\n"); - restore_error = restored_send(restore, dict); + restore_error = _restore_service_send(service, dict, 0); plist_free(dict); + _restore_service_free(service); if (restore_error != RESTORE_E_SUCCESS) { error("ERROR: Couldn't send FirmwareResponsePreflight data (%d)\n", restore_error); return -1; @@ -2935,7 +3687,7 @@ static int restore_send_firmware_updater_preflight(restored_client_t restore, st return 0; } -static int restore_send_firmware_updater_data(restored_client_t restore, struct idevicerestore_client_t* client, plist_t build_identity, plist_t message) +static int restore_send_firmware_updater_data(struct idevicerestore_client_t* client, plist_t message) { plist_t arguments; plist_t p_type, p_updater_name, p_loop_count, p_info; @@ -2946,6 +3698,11 @@ static int restore_send_firmware_updater_data(restored_client_t restore, struct char *s_updater_name = NULL; int restore_error; + if (!client || !client->restore || !client->restore->build_identity) { + error("ERROR: %s: idevicerestore client not initialized?!\n", __func__); + return -1; + } + if (idevicerestore_debug) { debug("DEBUG: %s: Got FirmwareUpdaterData request:\n", __func__); debug_plist(message); @@ -2992,7 +3749,7 @@ static int restore_send_firmware_updater_data(restored_client_t restore, struct plist_get_string_val(p_updater_name, &s_updater_name); if (strcmp(s_updater_name, "SE") == 0) { - fwdict = restore_get_se_firmware_data(restore, client, build_identity, p_info); + fwdict = restore_get_se_firmware_data(client, p_info, arguments); if (fwdict == NULL) { error("ERROR: %s: Couldn't get SE firmware data\n", __func__); goto error_out; @@ -3002,57 +3759,80 @@ static int restore_send_firmware_updater_data(restored_client_t restore, struct plist_t p_info2 = plist_dict_get_item(p_info, "YonkersDeviceInfo"); if (p_info2 && plist_get_node_type(p_info2) == PLIST_DICT) { fwtype = "Yonkers"; - fwdict = restore_get_yonkers_firmware_data(restore, client, build_identity, p_info2); + fwdict = restore_get_yonkers_firmware_data(client, p_info2, arguments); } else { - fwdict = restore_get_savage_firmware_data(restore, client, build_identity, p_info); + fwdict = restore_get_savage_firmware_data(client, p_info, arguments); } if (fwdict == NULL) { error("ERROR: %s: Couldn't get %s firmware data\n", __func__, fwtype); goto error_out; } } else if (strcmp(s_updater_name, "Rose") == 0) { - fwdict = restore_get_rose_firmware_data(restore, client, build_identity, p_info); + fwdict = restore_get_rose_firmware_data(client, p_info, arguments); if (fwdict == NULL) { error("ERROR: %s: Couldn't get Rose firmware data\n", __func__); goto error_out; } } else if (strcmp(s_updater_name, "T200") == 0) { - fwdict = restore_get_veridian_firmware_data(restore, client, build_identity, p_info); + fwdict = restore_get_veridian_firmware_data(client, p_info, arguments); if (fwdict == NULL) { error("ERROR: %s: Couldn't get Veridian firmware data\n", __func__); goto error_out; } } else if (strcmp(s_updater_name, "AppleTCON") == 0) { - fwdict = restore_get_tcon_firmware_data(restore, client, build_identity, p_info); + fwdict = restore_get_tcon_firmware_data(client, p_info, arguments); if (fwdict == NULL) { error("ERROR: %s: Couldn't get AppleTCON firmware data\n", __func__); goto error_out; } + } else if (strcmp(s_updater_name, "PS190") == 0) { + fwdict = restore_get_generic_firmware_data(client, p_info, arguments); + if (fwdict == NULL) { + error("ERROR: %s: Couldn't get PCON1 firmware data\n", __func__); + goto error_out; + } } else if (strcmp(s_updater_name, "AppleTypeCRetimer") == 0) { - fwdict = restore_get_timer_firmware_data(restore, client, build_identity, p_info); + fwdict = restore_get_timer_firmware_data(client, p_info, arguments); if (fwdict == NULL) { error("ERROR: %s: Couldn't get AppleTypeCRetimer firmware data\n", __func__); goto error_out; } - } else if (strcmp(s_updater_name, "Cryptex1") == 0) { - fwdict = restore_get_cryptex1_firmware_data(restore, client, build_identity, p_info, arguments); + } else if ((strcmp(s_updater_name, "Cryptex1") == 0) || (strcmp(s_updater_name, "Cryptex1LocalPolicy") == 0)) { + fwdict = restore_get_cryptex1_firmware_data(client, p_info, arguments); if (fwdict == NULL) { - error("ERROR: %s: Couldn't get AppleTypeCRetimer firmware data\n", __func__); + error("ERROR: %s: Couldn't get %s firmware data\n", __func__, s_updater_name); + goto error_out; + } + } else if (strcmp(s_updater_name, "Ace3") == 0) { + fwdict = restore_get_generic_firmware_data(client, p_info, arguments); + if (fwdict == NULL) { + error("ERROR: %s: Couldn't get %s firmware data\n", __func__, s_updater_name); goto error_out; } } else { - error("ERROR: %s: Got unknown updater name '%s'.\n", __func__, s_updater_name); - goto error_out; + error("ERROR: %s: Got unknown updater name '%s', trying to discover from device generated request.\n", __func__, s_updater_name); + fwdict = restore_get_generic_firmware_data(client, p_info, arguments); + if (fwdict == NULL) { + error("ERROR: %s: Couldn't get %s firmware data\n", __func__, s_updater_name); + goto error_out; + } } free(s_updater_name); s_updater_name = NULL; + restore_service_client_t service = _restore_get_service_client_for_data_request(client, message); + if (!service) { + error("ERROR: %s: Unable to connect to service client\n", __func__); + return -1; + } + dict = plist_new_dict(); plist_dict_set_item(dict, "FirmwareResponseData", fwdict); info("Sending FirmwareResponse data now...\n"); - restore_error = restored_send(restore, dict); + restore_error = _restore_service_send(service, dict, 0); plist_free(dict); + _restore_service_free(service); if (restore_error != RESTORE_E_SUCCESS) { error("ERROR: Couldn't send FirmwareResponse data (%d)\n", restore_error); goto error_out; @@ -3069,23 +3849,35 @@ static int restore_send_firmware_updater_data(restored_client_t restore, struct return -1; } -static int restore_send_receipt_manifest(restored_client_t restore, struct idevicerestore_client_t* client, plist_t build_identity) +static int restore_send_receipt_manifest(struct idevicerestore_client_t* client, plist_t message) { plist_t dict; int restore_error; - plist_t manifest = plist_dict_get_item(build_identity, "Manifest"); + if (!client || !client->restore || !client->restore->build_identity) { + error("ERROR: %s: idevicerestore client not initialized?!\n", __func__); + return -1; + } + + plist_t manifest = plist_dict_get_item(client->restore->build_identity, "Manifest"); if (!manifest) { error("failed to get Manifest node from build_identity"); goto error_out; } + restore_service_client_t service = _restore_get_service_client_for_data_request(client, message); + if (!service) { + error("ERROR: %s: Unable to connect to service client\n", __func__); + return -1; + } + dict = plist_new_dict(); plist_dict_set_item(dict, "ReceiptManifest", plist_copy(manifest)); info("Sending ReceiptManifest data now...\n"); - restore_error = restored_send(restore, dict); + restore_error = _restore_service_send(service, dict, 0); plist_free(dict); + _restore_service_free(service); if (restore_error != RESTORE_E_SUCCESS) { error("ERROR: Couldn't send ReceiptManifest data (%d)\n", restore_error); goto error_out; @@ -3166,7 +3958,7 @@ static int cpio_send_file(idevice_connection_t connection, const char *name, str return 0; } -static int restore_bootability_send_one(void *ctx, const char *ipsw, const char *name, struct stat *stat) +static int restore_bootability_send_one(void *ctx, ipsw_archive_t ipsw, const char *name, struct stat *stat) { idevice_connection_t connection = (idevice_connection_t)ctx; const char *prefix = "BootabilityBundle/Restore/Bootability/"; @@ -3202,7 +3994,7 @@ static int restore_bootability_send_one(void *ctx, const char *ipsw, const char return ret; } -static int restore_send_bootability_bundle_data(restored_client_t restore, struct idevicerestore_client_t* client, plist_t build_identity, plist_t message, idevice_t device) +static int restore_send_bootability_bundle_data(struct idevicerestore_client_t* client, plist_t message) { if (idevicerestore_debug) { debug("DEBUG: %s: Got BootabilityBundle request:\n", __func__); @@ -3218,9 +4010,14 @@ static int restore_send_bootability_bundle_data(restored_client_t restore, struc idevice_connection_t connection = NULL; idevice_error_t device_error = IDEVICE_E_SUCCESS; + if (!client || !client->restore || !client->restore->build_identity || !client->restore->device) { + error("ERROR: %s: idevicerestore client not initialized?!\n", __func__); + return -1; + } + debug("Connecting to BootabilityBundle data port\n"); while (--attempts > 0) { - device_error = idevice_connect(device, data_port, &connection); + device_error = idevice_connect(client->restore->device, data_port, &connection); if (device_error == IDEVICE_E_SUCCESS) { break; } @@ -3264,15 +4061,18 @@ plist_t restore_get_build_identity(struct idevicerestore_client_t* client, uint8 variant, 0); plist_t unique_id_node = plist_dict_get_item(client->build_manifest, "UniqueBuildID"); - debug_plist(unique_id_node); + if (unique_id_node) { + info("UniqueBuildID: "); + plist_write_to_stream(unique_id_node, stdout, PLIST_FORMAT_PRINT, PLIST_OPT_NONE); + } return build_identity; } -plist_t restore_get_build_identity_from_request(struct idevicerestore_client_t* client, plist_t msg) +plist_t restore_get_build_identity_from_request(struct idevicerestore_client_t* client, plist_t message) { - plist_t args = plist_dict_get_item(msg, "Arguments"); - return restore_get_build_identity(client, _plist_dict_get_bool(args, "IsRecoveryOS")); + plist_t args = plist_dict_get_item(message, "Arguments"); + return restore_get_build_identity(client, plist_dict_get_bool(args, "IsRecoveryOS")); } int extract_macos_variant(plist_t build_identity, char** output) @@ -3293,37 +4093,53 @@ int extract_macos_variant(plist_t build_identity, char** output) return 0; } -int extract_global_manifest(struct idevicerestore_client_t* client, plist_t build_identity, unsigned char** pbuffer, unsigned int* psize) +static char* extract_global_manifest_path(plist_t build_identity, char *variant) { plist_t build_info = plist_dict_get_item(build_identity, "Info"); if (!build_info) { error("ERROR: build identity does not contain an 'Info' element\n"); - return -1; + return NULL; } plist_t device_class_node = plist_dict_get_item(build_info, "DeviceClass"); if (!device_class_node) { error("ERROR: build identity info does not contain a DeviceClass\n"); - return -1; + return NULL; } char *device_class = NULL; plist_get_string_val(device_class_node, &device_class); char *macos_variant = NULL; - int ret = extract_macos_variant(build_identity, &macos_variant); - if (ret != 0) { - free(device_class); - return -1; + int ret; + if (variant) { + macos_variant = variant; + } else { + ret = extract_macos_variant(build_identity, &macos_variant); + if (ret != 0) { + free(device_class); + return NULL; + } } // The path of the global manifest is hardcoded. There's no pointer to in the build manifest. - char *ticket_path = malloc((42+strlen(macos_variant)+strlen(device_class)+1)*sizeof(char)); - sprintf(ticket_path, "Firmware/Manifests/restore/%s/apticket.%s.im4m", macos_variant, device_class); + size_t psize = 42+strlen(macos_variant)+strlen(device_class)+1; + char *ticket_path = malloc(psize); + snprintf(ticket_path, psize, "Firmware/Manifests/restore/%s/apticket.%s.im4m", macos_variant, device_class); free(device_class); free(macos_variant); - ret = ipsw_extract_to_memory(client->ipsw, ticket_path, pbuffer, psize); + return ticket_path; +} + +int extract_global_manifest(struct idevicerestore_client_t* client, plist_t build_identity, char *variant, unsigned char** pbuffer, unsigned int* psize) +{ + char* ticket_path = extract_global_manifest_path(build_identity, variant); + if (!ticket_path) { + error("ERROR: failed to get global manifest path\n"); + return -1; + } + int ret = ipsw_extract_to_memory(client->ipsw, ticket_path, pbuffer, psize); if (ret != 0) { free(ticket_path); error("ERROR: failed to read global manifest\n"); @@ -3334,12 +4150,62 @@ int extract_global_manifest(struct idevicerestore_client_t* client, plist_t buil return 0; } -int restore_send_personalized_boot_object_v3(restored_client_t restore, struct idevicerestore_client_t* client, plist_t msg, plist_t build_identity) +struct _restore_send_file_data_ctx { + struct idevicerestore_client_t* client; + restore_service_client_t service; + int last_progress; +}; + +static int _restore_send_file_data(struct _restore_send_file_data_ctx* rctx, void* data, size_t size, size_t done, size_t total_size) +{ + plist_t dict = plist_new_dict(); + if (data != NULL) { + // Send a chunk of file data + plist_dict_set_item(dict, "FileData", plist_new_data((char*)data, size)); + } else { + // Send FileDataDone to mark end of transfer + plist_dict_set_item(dict, "FileDataDone", plist_new_bool(1)); + } + restored_error_t restore_error = _restore_service_send(rctx->service, dict, 0); + if (restore_error != RESTORE_E_SUCCESS) { + plist_free(dict); + error("ERROR: %s: Failed to send data (%d)\n", __func__, restore_error); + return -1; + } + plist_free(dict); + + /* special handling for AEA image format */ + if (done == 0 && (memcmp(data, "AEA1", 4) == 0)) { + info("Encountered First Chunk in AEA image\n"); + plist_t message = NULL; + property_list_service_error_t err = _restore_service_recv_timeout(rctx->service, &message, 3000); + if (err == PROPERTY_LIST_SERVICE_E_RECEIVE_TIMEOUT) { + info("NOTE: No URLAsset requested, assuming it is not necessary."); + } else if (err == PROPERTY_LIST_SERVICE_E_SUCCESS) { + restore_send_url_asset(rctx->client, message); + } + } + + if (total_size > 0x1000000) { + double progress = (double)done / (double)total_size; + int progress_int = (int)(progress*100.0); + if (progress_int > rctx->last_progress) { + idevicerestore_progress(rctx->client, RESTORE_STEP_UPLOAD_IMG, progress); + rctx->last_progress = progress_int; + } + } + return 0; +} + +int restore_send_personalized_boot_object_v3(struct idevicerestore_client_t* client, plist_t message) { - debug_plist(msg); + if (idevicerestore_debug) { + debug("DEBUG: %s: Got PersonalizedBootObjectV3 request:\n", __func__); + debug_plist(message); + } char *image_name = NULL; - plist_t node = plist_access_path(msg, 2, "Arguments", "ImageName"); + plist_t node = plist_access_path(message, 2, "Arguments", "ImageName"); if (!node || plist_get_node_type(node) != PLIST_STRING) { debug("Failed to parse arguments from PersonalizedBootObjectV3 plist\n"); return -1; @@ -3357,12 +4223,11 @@ int restore_send_personalized_boot_object_v3(restored_client_t restore, struct i plist_t blob = NULL; plist_t dict = NULL; restored_error_t restore_error = RESTORE_E_SUCCESS; - char *component_name = component; - info("About to send %s...\n", component_name); + info("About to send %s...\n", component); if (strcmp(image_name, "__GlobalManifest__") == 0) { - int ret = extract_global_manifest(client, build_identity, &data, &size); + int ret = extract_global_manifest(client, client->restore->build_identity, NULL, &data, &size); if (ret != 0) { return -1; } @@ -3386,7 +4251,7 @@ int restore_send_personalized_boot_object_v3(restored_client_t restore, struct i } } if (!path) { - plist_t build_identity = restore_get_build_identity_from_request(client, msg); + plist_t build_identity = restore_get_build_identity_from_request(client, message); if (!build_identity) { error("ERROR: Unable to find a matching build identity\n"); return -1; @@ -3408,8 +4273,8 @@ int restore_send_personalized_boot_object_v3(restored_client_t restore, struct i return -1; } - // Personalize IMG40 - ret = personalize_component(component, component_data, component_size, client->tss, &data, &size); + // Personalize IMG4 + ret = personalize_component(client, component, component_data, component_size, client->tss, &data, &size); free(component_data); component_data = NULL; if (ret < 0) { @@ -3418,52 +4283,49 @@ int restore_send_personalized_boot_object_v3(restored_client_t restore, struct i } } - // Make plist - info("Sending %s now...\n", component_name); + restore_service_client_t service = _restore_get_service_client_for_data_request(client, message); + if (!service) { + error("ERROR: %s: Unable to connect to service client\n", __func__); + return -1; + } + + info("Sending %s now (%" PRIu64 " bytes)...\n", component, (uint64_t)size); + + struct _restore_send_file_data_ctx rctx; + rctx.client = client; + rctx.service = service; + rctx.last_progress = 0; int64_t i = size; while (i > 0) { int blob_size = i > 8192 ? 8192 : i; - - dict = plist_new_dict(); - blob = plist_new_data((char *) (data + size - i), blob_size); - plist_dict_set_item(dict, "FileData", blob); - - restore_error = restored_send(restore, dict); - if (restore_error != RESTORE_E_SUCCESS) { - error("ERROR: Unable to send component %s data\n", component_name); + if (_restore_send_file_data(&rctx, (data + size - i), blob_size, size-i, size) < 0) { + free(data); + _restore_service_free(service); + error("ERROR: Unable to send component %s data\n", component); return -1; } - - plist_free(dict); - i -= blob_size; } - debug("\n"); - - // Send FileDataDone - dict = plist_new_dict(); - plist_dict_set_item(dict, "FileDataDone", plist_new_bool(1)); + free(data); - restore_error = restored_send(restore, dict); - if (restore_error != RESTORE_E_SUCCESS) { - error("ERROR: Unable to send component %s data\n", component_name); - return -1; - } + _restore_send_file_data(&rctx, NULL, 0, size-i, size); - plist_free(dict); - free(data); + _restore_service_free(service); - info("Done sending %s\n", component_name); + info("Done sending %s\n", component); return 0; } -int restore_send_source_boot_object_v4(restored_client_t restore, struct idevicerestore_client_t* client, plist_t msg, plist_t build_identity) +int restore_send_source_boot_object_v4(struct idevicerestore_client_t* client, plist_t message) { - debug_plist(msg); + if (idevicerestore_debug) { + debug("DEBUG: %s: Got SourceBootObjectV4 request:\n", __func__); + debug_plist(message); + } char *image_name = NULL; - plist_t node = plist_access_path(msg, 2, "Arguments", "ImageName"); + plist_t node = plist_access_path(message, 2, "Arguments", "ImageName"); if (!node || plist_get_node_type(node) != PLIST_STRING) { debug("Failed to parse arguments from SourceBootObjectV4 plist\n"); return -1; @@ -3483,27 +4345,27 @@ int restore_send_source_boot_object_v4(restored_client_t restore, struct idevice plist_t blob = NULL; plist_t dict = NULL; restored_error_t restore_error = RESTORE_E_SUCCESS; - char *component_name = component; - info("About to send %s...\n", component_name); + info("About to send %s...\n", component); if (strcmp(image_name, "__GlobalManifest__") == 0) { - int ret = extract_global_manifest(client, build_identity, &data, &size); - if (ret != 0) { + char *variant = NULL; + plist_t node = plist_access_path(message, 2, "Arguments", "Variant"); + if (!node || plist_get_node_type(node) != PLIST_STRING) { + debug("Failed to parse arguments from SourceBootObjectV4 plist\n"); return -1; } - } else if (strcmp(image_name, "__RestoreVersion__") == 0) { - int ret = ipsw_extract_to_memory(client->ipsw, "RestoreVersion.plist", &data, &size); - if (ret != 0) { - error("ERROR: failed to read global manifest\n"); + plist_get_string_val(node, &variant); + if (!variant) { + debug("Failed to parse arguments from SourceBootObjectV4 as string\n"); return -1; } + + path = extract_global_manifest_path(client->restore->build_identity, variant); + } else if (strcmp(image_name, "__RestoreVersion__") == 0) { + path = strdup("RestoreVersion.plist"); } else if (strcmp(image_name, "__SystemVersion__") == 0) { - int ret = ipsw_extract_to_memory(client->ipsw, "SystemVersion.plist", &data, &size); - if (ret != 0) { - error("ERROR: failed to read global manifest\n"); - return -1; - } + path = strdup("SystemVersion.plist"); } else { // Get component path if (client->tss) { @@ -3512,63 +4374,50 @@ int restore_send_source_boot_object_v4(restored_client_t restore, struct idevice } } if (!path) { - plist_t build_identity = restore_get_build_identity_from_request(client, msg); + plist_t build_identity = restore_get_build_identity_from_request(client, message); if (build_identity_get_component_path(build_identity, component, &path) < 0) { error("ERROR: Unable to find %s path from build identity\n", component); return -1; } } - - int ret = extract_component(client->ipsw, path, &data, &size); - free(path); - path = NULL; - if (ret < 0) { - error("ERROR: Unable to extract component %s\n", component); - return -1; - } } - // Make plist - info("Sending %s now...\n", component_name); - - int64_t i = size; - while (i > 0) { - int blob_size = i > 8192 ? 8192 : i; - - dict = plist_new_dict(); - blob = plist_new_data((char *) (data + size - i), blob_size); - plist_dict_set_item(dict, "FileData", blob); - - restore_error = restored_send(restore, dict); - if (restore_error != RESTORE_E_SUCCESS) { - error("ERROR: Unable to send component %s data\n", component_name); - return -1; - } + if (!path) { + error("ERROR: Failed to get path for component %s\n", component); + return -1; + } - plist_free(dict); + uint64_t fsize = 0; + ipsw_get_file_size(client->ipsw, path, &fsize); - i -= blob_size; + restore_service_client_t service = _restore_get_service_client_for_data_request(client, message); + if (!service) { + error("ERROR: %s: Unable to connect to service client\n", __func__); + return -1; } - debug("\n"); - // Send FileDataDone - dict = plist_new_dict(); - plist_dict_set_item(dict, "FileDataDone", plist_new_bool(1)); + info("Sending %s now (%" PRIu64 " bytes)\n", component, fsize); - restore_error = restored_send(restore, dict); - if (restore_error != RESTORE_E_SUCCESS) { - error("ERROR: Unable to send component %s data\n", component_name); + struct _restore_send_file_data_ctx rctx; + rctx.client = client; + rctx.service = service; + rctx.last_progress = 0; + + if (ipsw_extract_send(client->ipsw, path, 8192, (ipsw_send_cb)_restore_send_file_data, &rctx) < 0) { + free(path); + _restore_service_free(service); + error("ERROR: Failed to send component %s\n", component); return -1; } + free(path); - plist_free(dict); - free(data); + _restore_service_free(service); - info("Done sending %s\n", component_name); + info("Done sending %s\n", component); return 0; } -int restore_send_restore_local_policy(restored_client_t restore, struct idevicerestore_client_t* client, plist_t msg) +int restore_send_restore_local_policy(struct idevicerestore_client_t* client, plist_t message) { unsigned int size = 0; unsigned char* data = NULL; @@ -3585,13 +4434,13 @@ int restore_send_restore_local_policy(restored_client_t restore, struct idevicer // The Update mode does not have a specific build identity for the recovery os. plist_t build_identity = restore_get_build_identity(client, client->flags & FLAG_ERASE ? 1 : 0); - int ret = get_recovery_os_local_policy_tss_response(client, build_identity, &client->tss_localpolicy, plist_dict_get_item(msg, "Arguments")); + int ret = get_recovery_os_local_policy_tss_response(client, build_identity, &client->tss_localpolicy, plist_dict_get_item(message, "Arguments")); if (ret < 0) { error("ERROR: Unable to get recovery os local policy tss response\n"); return -1; } - ret = personalize_component(component, component_data, component_size, client->tss_localpolicy, &data, &size); + ret = personalize_component(client, component, component_data, component_size, client->tss_localpolicy, &data, &size); free(component_data); component_data = NULL; if (ret < 0) { @@ -3602,7 +4451,15 @@ int restore_send_restore_local_policy(restored_client_t restore, struct idevicer plist_t dict = plist_new_dict(); plist_dict_set_item(dict, "Ap,LocalPolicy", plist_new_data((char*)data, size)); - int restore_error = restored_send(restore, dict); + restore_service_client_t service = _restore_get_service_client_for_data_request(client, message); + if (!service) { + error("ERROR: %s: Unable to connect to service client\n", __func__); + return -1; + } + + int restore_error = 0; + restore_error = _restore_service_send(service, dict, 0); + _restore_service_free(service); if (restore_error != RESTORE_E_SUCCESS) { error("ERROR: Unable to send component %s data\n", component); return -1; @@ -3614,19 +4471,25 @@ int restore_send_restore_local_policy(restored_client_t restore, struct idevicer return 0; } -int restore_send_buildidentity(restored_client_t restore, struct idevicerestore_client_t* client, plist_t msg) +int restore_send_buildidentity(struct idevicerestore_client_t* client, plist_t message) { restored_error_t restore_error; plist_t dict; + restore_service_client_t service = _restore_get_service_client_for_data_request(client, message); + if (!service) { + error("ERROR: %s: Unable to connect to service client\n", __func__); + return -1; + } + info("About to send BuildIdentity Dict...\n"); - plist_t build_identity = restore_get_build_identity_from_request(client, msg); + plist_t build_identity = restore_get_build_identity_from_request(client, message); dict = plist_new_dict(); plist_dict_set_item(dict, "BuildIdentityDict", plist_copy(build_identity)); - plist_t node = plist_access_path(msg, 2, "Arguments", "Variant"); + plist_t node = plist_access_path(message, 2, "Arguments", "Variant"); if(node) { plist_dict_set_item(dict, "Variant", plist_copy(node)); } else { @@ -3634,7 +4497,8 @@ int restore_send_buildidentity(restored_client_t restore, struct idevicerestore_ } info("Sending BuildIdentityDict now...\n"); - restore_error = restored_send(restore, dict); + restore_error = _restore_service_send(service, dict, 0); + _restore_service_free(service); plist_free(dict); if (restore_error != RESTORE_E_SUCCESS) { error("ERROR: Unable to send BuildIdentityDict (%d)\n", restore_error); @@ -3645,7 +4509,233 @@ int restore_send_buildidentity(restored_client_t restore, struct idevicerestore_ return 0; } -int restore_handle_data_request_msg(struct idevicerestore_client_t* client, idevice_t device, restored_client_t restore, plist_t message, plist_t build_identity, const char* filesystem) +int restore_send_recovery_os_file_asset_image(struct idevicerestore_client_t* client, plist_t message) +{ + char *fw_override_key = NULL; + plist_t node = plist_access_path(message, 2, "Arguments", "FWOverrideKey"); + if (PLIST_IS_STRING(node)) { + plist_get_string_val(node, &fw_override_key); + } + if (!fw_override_key) { + error("ERROR: Failed to get FWOverrideKey from arguments. Trying to continue anyway.\n"); + return -1; + } + + plist_t dict = plist_new_dict(); + if (!client->recovery_variant) { + error("ERROR: no RecoveryOS variant in BuildManifest. Trying to continue anyway.\n"); + plist_dict_set_item(dict, "RecoveryOSNoAssetFound", plist_new_bool(1)); + restored_send(client->restore->client, dict); + plist_free(dict); + return 0; + } + + if (strncmp(fw_override_key, "RecoveryOS", 10) != 0) { + error("ERROR: FWOVerrideKey has unexpected prefix\n"); + plist_dict_set_item(dict, "RecoveryOSNoAssetFound", plist_new_bool(1)); + restored_send(client->restore->client, dict); + plist_free(dict); + return 0; + } + + const char* component = fw_override_key+10; + char* path = NULL; + if (build_identity_get_component_path(client->recovery_variant, component, &path) < 0) { + error("ERROR: Unable to find %s path from recovery build identity. Trying to continue anyway.\n", component); + plist_dict_set_item(dict, "RecoveryOSNoAssetFound", plist_new_bool(1)); + restored_send(client->restore->client, dict); + plist_free(dict); + return 0; + } + + unsigned char* component_data = NULL; + unsigned int component_size = 0; + int ret = extract_component(client->ipsw, path, &component_data, &component_size); + free(path); + path = NULL; + if (ret < 0) { + error("ERROR: Unable to extract component %s. Trying to continue anyway.\n", component); + plist_dict_set_item(dict, "RecoveryOSNoAssetFound", plist_new_bool(1)); + restored_send(client->restore->client, dict); + plist_free(dict); + return 0; + } + + unsigned char* data = NULL; + unsigned int size = 0; + ret = personalize_component(client, component, component_data, component_size, client->tss_recoveryos_root_ticket, &data, &size); + free(component_data); + component_data = NULL; + if (ret < 0) { + error("ERROR: Unable to get personalized component %s. Trying to continue anyway.\n", component); + plist_dict_set_item(dict, "RecoveryOSNoAssetFound", plist_new_bool(1)); + restored_send(client->restore->client, dict); + plist_free(dict); + return 0; + } + + info("Sending %s\n", fw_override_key); + + plist_dict_set_item(dict, "AdditionalBootImages", plist_new_data((char*)data, size)); + free(data); + restored_send(client->restore->client, dict); + plist_free(dict); + + return 0; +} + +int restore_send_recovery_os_iboot_fw_files_images(struct idevicerestore_client_t* client, plist_t message) +{ + plist_t build_id_manifest = plist_dict_get_item(client->recovery_variant, "Manifest"); + if (!build_id_manifest) { + error("ERROR: Missing Manifest dictionary in build identity?!\n"); + return -1; + } + + plist_t firmware_files = plist_new_dict(); + + plist_dict_iter iter = NULL; + plist_dict_new_iter(build_id_manifest, &iter); + if (iter) { + char *component = NULL; + plist_t manifest_entry; + do { + component = NULL; + manifest_entry = NULL; + plist_dict_next_item(build_id_manifest, iter, &component, &manifest_entry); + if (component && PLIST_IS_DICT(manifest_entry)) { + uint8_t loaded_by_iboot = 0; + uint8_t loaded_by_iboot_stage1 = 0; + plist_t fw_node; + + fw_node = plist_access_path(manifest_entry, 2, "Info", "IsLoadedByiBoot"); + if (fw_node && plist_get_node_type(fw_node) == PLIST_BOOLEAN) { + plist_get_bool_val(fw_node, &loaded_by_iboot); + } + fw_node = plist_access_path(manifest_entry, 2, "Info", "IsLoadedByiBootStage1"); + if (fw_node && plist_get_node_type(fw_node) == PLIST_BOOLEAN) { + plist_get_bool_val(fw_node, &loaded_by_iboot_stage1); + } + if (loaded_by_iboot || loaded_by_iboot_stage1) { + plist_t comp_path = plist_access_path(manifest_entry, 2, "Info", "Path"); + if (comp_path) { + const char* path = plist_get_string_ptr(comp_path, NULL); + unsigned char* component_data = NULL; + unsigned int component_size = 0; + int ret = extract_component(client->ipsw, path, &component_data, &component_size); + if (ret == 0) { + unsigned char* data = NULL; + unsigned int size = 0; + ret = personalize_component(client, component, component_data, component_size, client->tss_recoveryos_root_ticket, &data, &size); + free(component_data); + component_data = NULL; + if (ret == 0) { + plist_dict_set_item(firmware_files, component, plist_new_data((char*)data, size)); + free(data); + } + } + } + } + } + free(component); + } while (manifest_entry); + plist_mem_free(iter); + } + + plist_t dict = plist_new_dict(); + + if (plist_dict_get_size(firmware_files) == 0) { + plist_free(firmware_files); + info("NOTE: No iBoot firmware files. Continuing.\n"); + plist_dict_set_item(dict, "RecoveryOSNoAssetFound", plist_new_bool(1)); + restored_send(client->restore->client, dict); + plist_free(dict); + return 0; + + } + + info("Sending iBoot additional firmware files\n"); + + plist_dict_set_item(dict, "AdditionalBootImages", firmware_files); + restored_send(client->restore->client, dict); + plist_free(dict); + + return 0; +} + +int restore_send_recovery_os_image(struct idevicerestore_client_t* client, plist_t message) +{ + const char* component = "OS"; + char* path = NULL; + if (build_identity_get_component_path(client->recovery_variant, component, &path) < 0) { + error("ERROR: Unable to find %s path from build identity\n", component); + return -1; + } + if (!path) { + error("ERROR: Failed to get path for component %s\n", component); + return -1; + } + + uint64_t fsize = 0; + ipsw_get_file_size(client->ipsw, path, &fsize); + + restore_service_client_t service = _restore_get_service_client_for_data_request(client, message); + if (!service) { + error("ERROR: %s: Unable to connect to service client\n", __func__); + return -1; + } + + info("Sending %s now (%" PRIu64 " bytes)\n", component, fsize); + + struct _restore_send_file_data_ctx rctx; + rctx.client = client; + rctx.service = service; + rctx.last_progress = 0; + + if (ipsw_extract_send(client->ipsw, path, 8192, (ipsw_send_cb)_restore_send_file_data, &rctx) < 0) { + free(path); + _restore_service_free(service); + error("ERROR: Failed to send component %s\n", component); + return -1; + } + free(path); + + _restore_service_free(service); + + info("Done sending %s\n", component); + + return 0; +} + +int restore_send_recovery_os_version_data(struct idevicerestore_client_t* client, plist_t message) +{ + plist_t build_id_info = plist_dict_get_item(client->recovery_variant, "Info"); + if (!build_id_info) { + error("ERROR: Missing Info dictionary in build identity?!\n"); + return -1; + } + plist_t version_data = plist_new_dict(); + plist_dict_copy_item(version_data, build_id_info, "BuildNumber", NULL); + plist_dict_copy_item(version_data, build_id_info, "Variant", NULL); + plist_dict_copy_item(version_data, build_id_info, "BuildTrain", NULL); + plist_dict_copy_item(version_data, build_id_info, "ProductVersion", "ProductMarketingVersion"); + char *xml = NULL; + uint32_t xml_len = 0; + plist_to_xml(version_data, &xml, &xml_len); + plist_free(version_data); + + info("Sending RecoveryOS version data\n"); + + plist_t dict = plist_new_dict(); + plist_dict_set_item(dict, "RecoveryOSVersionData", plist_new_data(xml, xml_len)); + plist_mem_free(xml); + restored_send(client->restore->client, dict); + plist_free(dict); + + return 0; +} + +int restore_handle_data_request_msg(struct idevicerestore_client_t* client, plist_t message) { plist_t node = NULL; @@ -3654,38 +4744,38 @@ int restore_handle_data_request_msg(struct idevicerestore_client_t* client, idev node = plist_dict_get_item(message, "DataType"); if (node && PLIST_STRING == plist_get_node_type(node)) { const char *type = plist_get_string_ptr(node, NULL); - +debug("%s: type = %s\n", __func__, type); // this request is sent when restored is ready to receive the filesystem if (!strcmp(type, "SystemImageData")) { - if(restore_send_filesystem(client, device, filesystem) < 0) { + if (restore_send_filesystem(client, message) < 0) { error("ERROR: Unable to send filesystem\n"); return -2; } } else if (!strcmp(type, "BuildIdentityDict")) { - if (restore_send_buildidentity(restore, client, message) < 0) { + if (restore_send_buildidentity(client, message) < 0) { error("ERROR: Unable to send RootTicket\n"); return -1; } } else if (!strcmp(type, "PersonalizedBootObjectV3")) { - if (restore_send_personalized_boot_object_v3(restore, client, message, build_identity) < 0) { + if (restore_send_personalized_boot_object_v3(client, message) < 0) { error("ERROR: Unable to send PersonalizedBootObjectV3\n"); return -1; } } else if (!strcmp(type, "SourceBootObjectV4")) { - if (restore_send_source_boot_object_v4(restore, client, message, build_identity) < 0) { + if (restore_send_source_boot_object_v4(client, message) < 0) { error("ERROR: Unable to send SourceBootObjectV4\n"); return -1; } } else if (!strcmp(type, "RecoveryOSLocalPolicy")) { - if (restore_send_restore_local_policy(restore, client, message) < 0) { + if (restore_send_restore_local_policy(client, message) < 0) { error("ERROR: Unable to send RecoveryOSLocalPolicy\n"); return -1; } @@ -3693,7 +4783,7 @@ int restore_handle_data_request_msg(struct idevicerestore_client_t* client, idev // this request is sent when restored is ready to receive the filesystem else if (!strcmp(type, "RecoveryOSASRImage")) { - if(restore_send_filesystem(client, device, filesystem) < 0) { + if (restore_send_filesystem(client, message) < 0) { error("ERROR: Unable to send filesystem\n"); return -2; } @@ -3701,7 +4791,7 @@ int restore_handle_data_request_msg(struct idevicerestore_client_t* client, idev // Send RecoveryOS RTD else if(!strcmp(type, "RecoveryOSRootTicketData")) { - if (restore_send_recovery_os_root_ticket(restore, client) < 0) { + if (restore_send_recovery_os_root_ticket(client, message) < 0) { error("ERROR: Unable to send RootTicket\n"); return -1; } @@ -3709,35 +4799,35 @@ int restore_handle_data_request_msg(struct idevicerestore_client_t* client, idev // send RootTicket (== APTicket from the TSS request) else if (!strcmp(type, "RootTicket")) { - if (restore_send_root_ticket(restore, client) < 0) { + if (restore_send_root_ticket(client, message) < 0) { error("ERROR: Unable to send RootTicket\n"); return -1; } } // send KernelCache else if (!strcmp(type, "KernelCache")) { - if (restore_send_component(restore, client, build_identity, "KernelCache", NULL) < 0) { + if (restore_send_component(client, message, "KernelCache", NULL) < 0) { error("ERROR: Unable to send kernelcache\n"); return -1; } } else if (!strcmp(type, "DeviceTree")) { - if (restore_send_component(restore, client, build_identity, "DeviceTree", NULL) < 0) { + if (restore_send_component(client, message, "DeviceTree", NULL) < 0) { error("ERROR: Unable to send DeviceTree\n"); return -1; } } else if (!strcmp(type, "SystemImageRootHash")) { - if (restore_send_component(restore, client, build_identity, "SystemVolume", type) < 0) { + if (restore_send_component(client, message, "SystemVolume", type) < 0) { error("ERROR: Unable to send SystemImageRootHash data\n"); return -1; } } else if (!strcmp(type, "SystemImageCanonicalMetadata")) { - if (restore_send_component(restore, client, build_identity, "Ap,SystemVolumeCanonicalMetadata", type) < 0) { + if (restore_send_component(client, message, "Ap,SystemVolumeCanonicalMetadata", type) < 0) { error("ERROR: Unable to send SystemImageCanonicalMetadata data\n"); return -1; } @@ -3745,7 +4835,7 @@ int restore_handle_data_request_msg(struct idevicerestore_client_t* client, idev else if (!strcmp(type, "NORData")) { if((client->flags & FLAG_EXCLUDE) == 0) { - if(restore_send_nor(restore, client, build_identity, message) < 0) { + if(restore_send_nor(client, message) < 0) { error("ERROR: Unable to send NOR data\n"); return -1; } @@ -3756,75 +4846,117 @@ int restore_handle_data_request_msg(struct idevicerestore_client_t* client, idev } else if (!strcmp(type, "BasebandData")) { - if(restore_send_baseband_data(restore, client, build_identity, message) < 0) { + if(restore_send_baseband_data(client, message) < 0) { error("ERROR: Unable to send baseband data\n"); return -1; } } else if (!strcmp(type, "FDRTrustData")) { - if(restore_send_fdr_trust_data(restore, device) < 0) { + if(restore_send_fdr_trust_data(client, message) < 0) { error("ERROR: Unable to send FDR Trust data\n"); return -1; } } else if (!strcmp(type, "FUDData")) { - if(restore_send_image_data(restore, client, build_identity, message, "FUDImageList", "IsFUDFirmware", "FUDImageData") < 0) { + if(restore_send_image_data(client, message, "FUDImageList", "IsFUDFirmware", "FUDImageData") < 0) { error("ERROR: Unable to send FUD data\n"); return -1; } } else if (!strcmp(type, "FirmwareUpdaterPreflight")) { - if(restore_send_firmware_updater_preflight(restore, client, build_identity, message) < 0) { + if(restore_send_firmware_updater_preflight(client, message) < 0) { error("ERROR: Unable to send FirmwareUpdaterPreflight\n"); return -1; } } else if (!strcmp(type, "FirmwareUpdaterData")) { - if(restore_send_firmware_updater_data(restore, client, build_identity, message) < 0) { + if(restore_send_firmware_updater_data(client, message) < 0) { error("ERROR: Unable to send FirmwareUpdater data\n"); return -1; } } else if (!strcmp(type, "PersonalizedData")) { - if(restore_send_image_data(restore, client, build_identity, message, "ImageList", NULL, "ImageData") < 0) { + if(restore_send_image_data(client, message, "ImageList", NULL, "ImageData") < 0) { error("ERROR: Unable to send Personalized data\n"); return -1; } } else if (!strcmp(type, "EANData")) { - if(restore_send_image_data(restore, client, build_identity, message, "EANImageList", "IsEarlyAccessFirmware", "EANData") < 0) { + if(restore_send_image_data(client, message, "EANImageList", "IsEarlyAccessFirmware", "EANData") < 0) { error("ERROR: Unable to send Personalized data\n"); return -1; } } else if (!strcmp(type, "BootabilityBundle")) { - if (restore_send_bootability_bundle_data(restore, client, build_identity, message, device) < 0) { + if (restore_send_bootability_bundle_data(client, message) < 0) { error("ERROR: Unable to send BootabilityBundle data\n"); return -1; } } else if (!strcmp(type, "ReceiptManifest")) { - if (restore_send_receipt_manifest(restore, client, build_identity) < 0) { + if (restore_send_receipt_manifest(client, message) < 0) { error("ERROR: Unable to send ReceiptManifest data\n"); return -1; } } else if (!strcmp(type, "BasebandUpdaterOutputData")) { - if (restore_handle_baseband_updater_output_data(restore, client, device, message) < 0) { + if (restore_handle_baseband_updater_output_data(client, message) < 0) { error("ERROR: Unable to send BasebandUpdaterOutputData data\n"); return -1; } } + else if (!strcmp(type, "URLAsset")) { + if (restore_send_url_asset(client, message) < 0) { + error("ERROR: Unable to send URLAsset data\n"); + return -1; + } + } + + else if (!strcmp(type, "StreamedImageDecryptionKey")) { + if (restore_send_streamed_image_decryption_key(client, message) < 0) { + error("ERROR: Unable to send StreamedImageDecryptionKey data\n"); + return -1; + } + } + + else if (!strcmp(type, "RecoveryOSFileAssetImage")) { + if (restore_send_recovery_os_file_asset_image(client, message) < 0) { + error("ERROR: Unable to send RecoveryOSFileImageAssetImage data\n"); + return -1; + } + } + + else if (!strcmp(type, "RecoveryOSIBootFWFilesImages")) { + if (restore_send_recovery_os_iboot_fw_files_images(client, message) < 0) { + error("ERROR: Unable to send RecoveryOSIBootFWFilesImages data\n"); + return -1; + } + } + + else if (!strcmp(type, "RecoveryOSImage")) { + if (restore_send_recovery_os_image(client, message) < 0) { + error("ERROR: Unable to send RecoveryOSImage data\n"); + return -1; + } + } + + else if (!strcmp(type, "RecoveryOSVersionData")) { + if (restore_send_recovery_os_version_data(client, message) < 0) { + error("ERROR: Unable to send RecoveryOSVersionData data\n"); + return -1; + } + } + else { // Unknown DataType!! error("Unknown data request '%s' received\n", type); @@ -3835,10 +4967,134 @@ int restore_handle_data_request_msg(struct idevicerestore_client_t* client, idev return 0; } +struct _restore_async_args { + struct idevicerestore_client_t* client; + plist_t message; +}; + +static void* _restore_handle_async_data_request(void* args) +{ + struct _restore_async_args* async_args = (struct _restore_async_args*)args; + struct idevicerestore_client_t* client = async_args->client; + plist_t message = async_args->message; + free(async_args); + + int err = restore_handle_data_request_msg(client, message); + if (err < 0) { + client->async_err = err; + client->flags |= FLAG_QUIT; + } + + plist_free(message); + return NULL; +} + +static int restore_handle_restored_crash(struct idevicerestore_client_t* client, plist_t message) +{ + plist_t backtrace = plist_dict_get_item(message, "RestoredBacktrace"); + info("*** restored crashed, backtrace following ***"); + if (PLIST_IS_STRING(backtrace)) { + info("%s\n", plist_get_string_ptr(backtrace, NULL)); + } else if (PLIST_IS_ARRAY(backtrace)) { + uint32_t i = 0; + for (i = 0; i < plist_array_get_size(backtrace); i++) { + plist_t line = plist_array_get_item(backtrace, i); + info("\t%s\n", plist_get_string_ptr(line, NULL)); + } + } else { + debug_plist(message); + } + return 0; +} + +static int restore_handle_async_wait(struct idevicerestore_client_t* client, plist_t message) +{ + debug("AsyncWait\n"); + if (idevicerestore_debug) + debug_plist(message); + return 0; +} + +static int restore_handle_restore_attestation(struct idevicerestore_client_t* client, plist_t message) +{ + if (idevicerestore_debug) + debug_plist(message); + debug("Sending RestoreShouldAttest: false\n"); + plist_t dict = plist_new_dict(); + plist_dict_set_item(dict, "RestoreShouldAttest", plist_new_bool(0)); + restored_error_t restore_error = restored_send(client->restore->client, dict); + plist_free(dict); + if (restore_error != RESTORE_E_SUCCESS) { + error("ERROR: Unable to send RestoreShouldAttest (%d)\n", restore_error); + return -1; + } + return 0; +} + +static void _restore_calculate_recovery_os_partition_size(struct idevicerestore_client_t* client, uint64_t* min_size, uint64_t* max_size) +{ + const char* asset_list[] = { "OS", "KernelCache", "DeviceTree", "iBEC", "AppleLogo", "StaticTrustCache", "iBootData", "Diags", "Ap,SystemVolumeCanonicalMetadata", "SystemVolume", "BaseSystemVolume", "Ap,BaseSystemTrustCache", "AVISP1,RTKitOS", NULL }; + + if (min_size) *min_size = 356; + if (max_size) *max_size = 420; + + double total_size = 0; + plist_t firmware_items = plist_new_dict(); + plist_t build_id_manifest = plist_dict_get_item(client->recovery_variant, "Manifest"); + plist_dict_iter iter = NULL; + plist_dict_new_iter(build_id_manifest, &iter); + if (iter) { + char *component = NULL; + plist_t manifest_entry; + do { + component = NULL; + manifest_entry = NULL; + plist_dict_next_item(build_id_manifest, iter, &component, &manifest_entry); + if (component && PLIST_IS_DICT(manifest_entry) && plist_dict_get_item(firmware_items, component) == NULL) { + int add_image = 0; + int i = 0; + while (asset_list[i]) { + if (!strcmp(asset_list[i], component)) { + add_image = 1; + break; + } + i++; + } + plist_t fw_node; + fw_node = plist_access_path(manifest_entry, 2, "Info", "IsLoadedByiBoot"); + if (fw_node && plist_get_node_type(fw_node) == PLIST_BOOLEAN) { + uint8_t loaded_by_iboot = 0; + plist_get_bool_val(fw_node, &loaded_by_iboot); + if (loaded_by_iboot) { + add_image = 1; + } + } + if (add_image) { + plist_t p_path = plist_access_path(manifest_entry, 2, "Info", "Path"); + if (p_path) { + const char* path = plist_get_string_ptr(p_path, NULL); + uint64_t fsize = 0; + if (ipsw_get_file_size(client->ipsw, path, &fsize) == 0) { + debug("%s: Adding %s (%s, %llu bytes)\n", __func__, component, path, fsize); + total_size += (double)fsize / 0x100000; + } + } + } + } + free(component); + } while (manifest_entry); + plist_mem_free(iter); + } + total_size = ceil(total_size); + if (min_size) *min_size = total_size * 1.05 + 25; + if (max_size) *max_size = total_size * 1.25 + 25; +} + // Extracted from ac2 plist_t restore_supported_data_types() { plist_t dict = plist_new_dict(); + plist_dict_set_item(dict, "AuthInstallCACert", plist_new_bool(1)); plist_dict_set_item(dict, "BasebandBootData", plist_new_bool(0)); plist_dict_set_item(dict, "BasebandData", plist_new_bool(0)); plist_dict_set_item(dict, "BasebandStackData", plist_new_bool(0)); @@ -3856,20 +5112,26 @@ plist_t restore_supported_data_types() plist_dict_set_item(dict, "FileData", plist_new_bool(0)); plist_dict_set_item(dict, "FileDataDone", plist_new_bool(0)); plist_dict_set_item(dict, "FirmwareUpdaterData", plist_new_bool(0)); + plist_dict_set_item(dict, "FirmwareUpdaterDataV2", plist_new_bool(0)); + plist_dict_set_item(dict, "FirmwareUpdaterDataV3", plist_new_bool(1)); + plist_dict_set_item(dict, "FirmwareUpdaterPreflight", plist_new_bool(1)); plist_dict_set_item(dict, "GrapeFWData", plist_new_bool(0)); plist_dict_set_item(dict, "HPMFWData", plist_new_bool(0)); plist_dict_set_item(dict, "HostSystemTime", plist_new_bool(1)); plist_dict_set_item(dict, "KernelCache", plist_new_bool(0)); + plist_dict_set_item(dict, "MessageUseStreamedImageFile", plist_new_bool(1)); plist_dict_set_item(dict, "NORData", plist_new_bool(0)); plist_dict_set_item(dict, "NitrogenFWData", plist_new_bool(1)); plist_dict_set_item(dict, "OpalFWData", plist_new_bool(0)); plist_dict_set_item(dict, "OverlayRootDataCount", plist_new_bool(0)); plist_dict_set_item(dict, "OverlayRootDataForKey", plist_new_bool(1)); + plist_dict_set_item(dict, "OverlayRootDataForKeyIndex", plist_new_bool(1)); plist_dict_set_item(dict, "PeppyFWData", plist_new_bool(1)); plist_dict_set_item(dict, "PersonalizedBootObjectV3", plist_new_bool(0)); plist_dict_set_item(dict, "PersonalizedData", plist_new_bool(1)); plist_dict_set_item(dict, "ProvisioningData", plist_new_bool(0)); plist_dict_set_item(dict, "RamdiskFWData", plist_new_bool(1)); + plist_dict_set_item(dict, "ReceiptManifest", plist_new_bool(1)); plist_dict_set_item(dict, "RecoveryOSASRImage", plist_new_bool(1)); plist_dict_set_item(dict, "RecoveryOSAppleLogo", plist_new_bool(1)); plist_dict_set_item(dict, "RecoveryOSDeviceTree", plist_new_bool(1)); @@ -3883,6 +5145,7 @@ plist_t restore_supported_data_types() plist_dict_set_item(dict, "RecoveryOSRootTicketData", plist_new_bool(1)); plist_dict_set_item(dict, "RecoveryOSStaticTrustCache", plist_new_bool(1)); plist_dict_set_item(dict, "RecoveryOSVersionData", plist_new_bool(1)); + plist_dict_set_item(dict, "RestoreLocalPolicy", plist_new_bool(1)); plist_dict_set_item(dict, "RootData", plist_new_bool(0)); plist_dict_set_item(dict, "RootTicket", plist_new_bool(0)); plist_dict_set_item(dict, "S3EOverride", plist_new_bool(0)); @@ -3893,14 +5156,10 @@ plist_t restore_supported_data_types() plist_dict_set_item(dict, "SystemImageCanonicalMetadata", plist_new_bool(0)); plist_dict_set_item(dict, "SystemImageData", plist_new_bool(0)); plist_dict_set_item(dict, "SystemImageRootHash", plist_new_bool(0)); + plist_dict_set_item(dict, "URLAsset", plist_new_bool(1)); plist_dict_set_item(dict, "USBCFWData", plist_new_bool(0)); plist_dict_set_item(dict, "USBCOverride", plist_new_bool(0)); - plist_dict_set_item(dict, "FirmwareUpdaterPreflight", plist_new_bool(1)); - plist_dict_set_item(dict, "ReceiptManifest", plist_new_bool(1)); - plist_dict_set_item(dict, "FirmwareUpdaterDataV2", plist_new_bool(0)); - plist_dict_set_item(dict, "RestoreLocalPolicy", plist_new_bool(1)); - plist_dict_set_item(dict, "AuthInstallCACert", plist_new_bool(1)); - plist_dict_set_item(dict, "OverlayRootDataForKeyIndex", plist_new_bool(1)); + plist_dict_set_item(dict, "UpdateVolumeOverlayRootDataCount", plist_new_bool(1)); return dict; } @@ -3908,8 +5167,11 @@ plist_t restore_supported_data_types() plist_t restore_supported_message_types() { plist_t dict = plist_new_dict(); + plist_dict_set_item(dict, "AsyncDataRequestMsg", plist_new_bool(1)); + plist_dict_set_item(dict, "AsyncWait", plist_new_bool(1)); plist_dict_set_item(dict, "BBUpdateStatusMsg", plist_new_bool(0)); plist_dict_set_item(dict, "CheckpointMsg", plist_new_bool(1)); + plist_dict_set_item(dict, "CrashLog", plist_new_bool(1)); plist_dict_set_item(dict, "DataRequestMsg", plist_new_bool(0)); plist_dict_set_item(dict, "FDRSubmit", plist_new_bool(1)); plist_dict_set_item(dict, "MsgType", plist_new_bool(0)); @@ -3919,6 +5181,7 @@ plist_t restore_supported_message_types() plist_dict_set_item(dict, "ProvisioningInfo", plist_new_bool(0)); plist_dict_set_item(dict, "ProvisioningStatusMsg", plist_new_bool(0)); plist_dict_set_item(dict, "ReceivedFinalStatusMsg", plist_new_bool(0)); + plist_dict_set_item(dict, "RestoreAttestation", plist_new_bool(1)); plist_dict_set_item(dict, "RestoredCrash", plist_new_bool(1)); plist_dict_set_item(dict, "StatusMsg", plist_new_bool(0)); return dict; @@ -3936,7 +5199,7 @@ static void rp_status_cb(reverse_proxy_client_t client, reverse_proxy_status_t s } #endif -int restore_device(struct idevicerestore_client_t* client, plist_t build_identity, const char* filesystem) +int restore_device(struct idevicerestore_client_t* client, plist_t build_identity) { int err = 0; char* type = NULL; @@ -3960,6 +5223,7 @@ int restore_device(struct idevicerestore_client_t* client, plist_t build_identit } info("Device %s has successfully entered restore mode\n", client->udid); + client->restore->build_identity = build_identity; restore = client->restore->client; device = client->restore->device; @@ -4075,15 +5339,15 @@ int restore_device(struct idevicerestore_client_t* client, plist_t build_identit //plist_dict_set_item(opts, "AuthInstallRestoreBehavior", plist_new_string("Erase")); plist_dict_set_item(opts, "AutoBootDelay", plist_new_uint(0)); - if (client->preflight_info) { - plist_t bbus = plist_copy(client->preflight_info); + if (client->firmware_preflight_info) { + plist_t bbus = plist_copy(client->firmware_preflight_info); plist_dict_remove_item(bbus, "FusingStatus"); plist_dict_remove_item(bbus, "PkHash"); plist_dict_set_item(opts, "BBUpdaterState", bbus); - _plist_dict_copy_data(opts, client->preflight_info, "BasebandNonce", "Nonce"); + plist_dict_copy_data(opts, client->firmware_preflight_info, "BasebandNonce", "Nonce"); } plist_dict_set_item(opts, "SupportedDataTypes", restore_supported_data_types()); @@ -4129,7 +5393,7 @@ int restore_device(struct idevicerestore_client_t* client, plist_t build_identit } } else { // FIXME: new on iOS 5 ? - plist_dict_set_item(opts, "BootImageType", plist_new_string("UserOrInternal")); + plist_dict_set_item(opts, "BootImageType", plist_new_string("User")); // FIXME: required? //plist_dict_set_item(opts, "BootImageFile", plist_new_string("018-7923-347.dmg")); plist_dict_set_item(opts, "DFUFileType", plist_new_string("RELEASE")); @@ -4155,7 +5419,32 @@ int restore_device(struct idevicerestore_client_t* client, plist_t build_identit // FIXME: new on iOS 5 ? plist_dict_set_item(opts, "SystemImageType", plist_new_string("User")); // FIXME: does this have any effect actually? - plist_dict_set_item(opts, "UpdateBaseband", plist_new_bool(0)); + plist_dict_set_item(opts, "UpdateBaseband", plist_new_bool(1)); + plist_dict_set_item(opts, "InstallDiags", plist_new_bool(0)); + if (client->recovery_variant) { + plist_dict_set_item(opts, "InstallRecoveryOS", plist_new_bool(1)); + plist_dict_set_item(opts, "RecoveryOSBundlePath", plist_new_string("/tmp/Per2.tmp")); + plist_t recovery_variant = plist_access_path(client->recovery_variant, 2, "Info", "Variant"); + plist_dict_set_item(opts, "AuthInstallRecoveryOSVariant", plist_copy(recovery_variant)); + uint64_t max_size = 0; + uint64_t min_size = 0; + _restore_calculate_recovery_os_partition_size(client, &min_size, &max_size); + info("Calculated recoveryOSPartitionSize as %" PRIu64 " MB\n", min_size); + info("Calculated recoveryOSMaxPartitionSize as %" PRIu64 " MB\n", max_size); + plist_dict_set_item(opts, "recoveryOSMaxPartitionSize", plist_new_uint(max_size)); + plist_dict_set_item(opts, "recoveryOSPartitionSize", plist_new_uint(min_size)); + } + + if (plist_dict_get_bool(client->parameters, "RequiresNonceSlot")) { + info("Device will use nonce slots.\n"); + } else { + info("Device will not use nonce slots.\n"); + } + + // Added for iOS 18.0 beta 1 + plist_dict_set_item(opts, "HostHasFixFor99053849", plist_new_bool(1)); + plist_dict_set_item(opts, "SystemImageFormat", plist_new_string("AEAWrappedDiskImage")); + plist_dict_set_item(opts, "WaitForDeviceConnectionToFinishStateMachine", plist_new_bool(0)); plist_t sep = plist_access_path(build_identity, 3, "Manifest", "SEP", "Info"); if (sep) { @@ -4175,6 +5464,15 @@ int restore_device(struct idevicerestore_client_t* client, plist_t build_identit plist_dict_set_item(opts, "PersonalizedDuringPreflight", plist_new_bool(1)); } + // Added for iOS 18.0 and macOS 15.0 + plist_t async_data_types = plist_new_dict(); + plist_dict_set_item(async_data_types, "BasebandData", plist_new_bool(0)); + plist_dict_set_item(async_data_types, "RecoveryOSASRImage", plist_new_bool(0)); + plist_dict_set_item(async_data_types, "StreamedImageDecryptionKey", plist_new_bool(0)); + plist_dict_set_item(async_data_types, "SystemImageData", plist_new_bool(0)); + plist_dict_set_item(async_data_types, "URLAsset", plist_new_bool(1)); + plist_dict_set_item(opts, "SupportedAsyncDataTypes", async_data_types); + plist_dict_set_item(opts, "RootToInstall", plist_new_bool(0)); char* guid = generate_guid(); if (guid) { @@ -4191,10 +5489,14 @@ int restore_device(struct idevicerestore_client_t* client, plist_t build_identit spp = plist_copy(spp); } else { spp = plist_new_dict(); + plist_dict_set_item(spp, "1024", plist_new_uint(1280)); plist_dict_set_item(spp, "128", plist_new_uint(1280)); plist_dict_set_item(spp, "16", plist_new_uint(160)); + plist_dict_set_item(spp, "256", plist_new_uint(1280)); plist_dict_set_item(spp, "32", plist_new_uint(320)); + plist_dict_set_item(spp, "512", plist_new_uint(1280)); plist_dict_set_item(spp, "64", plist_new_uint(640)); + plist_dict_set_item(spp, "768", plist_new_uint(1280)); plist_dict_set_item(spp, "8", plist_new_uint(80)); } plist_dict_set_item(opts, "SystemPartitionPadding", spp); @@ -4259,7 +5561,25 @@ int restore_device(struct idevicerestore_client_t* client, plist_t build_identit // files sent to the server by the client. these data requests include // SystemImageData, RootTicket, KernelCache, NORData and BasebandData requests if (!strcmp(type, "DataRequestMsg")) { - err = restore_handle_data_request_msg(client, device, restore, message, build_identity, filesystem); + err = restore_handle_data_request_msg(client, message); + } + + // async data request message + else if (!strcmp(type, "AsyncDataRequestMsg")) { + THREAD_T t = THREAD_T_NULL; + struct _restore_async_args* args = (struct _restore_async_args*)malloc(sizeof(struct _restore_async_args)); + args->client = client; + args->message = plist_copy(message); + if (thread_new(&t, _restore_handle_async_data_request, args) < 0) { + free(args); + error("ERROR: Failed to start async data request handler thread!\n"); + err = -1; + if (client->flags & FLAG_IGNORE_ERRORS) { + client->flags &= ~FLAG_IGNORE_ERRORS; + } + } else { + thread_detach(t); + } } // restore logs are available if a previous restore failed @@ -4276,7 +5596,7 @@ int restore_device(struct idevicerestore_client_t* client, plist_t build_identit // status messages usually indicate the current state of the restored // process or often to signal an error has been encountered else if (!strcmp(type, "StatusMsg")) { - err = restore_handle_status_msg(restore, message); + err = restore_handle_status_msg(client, message); if (restore_finished) { plist_t dict = plist_new_dict(); plist_dict_set_item(dict, "MsgType", plist_new_string("ReceivedFinalStatusMsg")); @@ -4288,39 +5608,71 @@ int restore_device(struct idevicerestore_client_t* client, plist_t build_identit else if (!strcmp(type, "CheckpointMsg")) { uint64_t ckpt_id; - uint64_t ckpt_res; + int64_t ckpt_res; uint8_t ckpt_complete = 0; + const char* ckpt_name = NULL; // Get checkpoint id node = plist_dict_get_item(message, "CHECKPOINT_ID"); - if (!node || plist_get_node_type(node) != PLIST_UINT) { + if (!node || plist_get_node_type(node) != PLIST_INT) { debug("Failed to parse checkpoint id from checkpoint plist\n"); - return -1; + err = -1; + break; } plist_get_uint_val(node, &ckpt_id); + // Get checkpoint_name + node = plist_dict_get_item(message, "CHECKPOINT_NAME"); + ckpt_name = (node) ? plist_get_string_ptr(node, NULL) : "unknown"; // Get checkpoint result node = plist_dict_get_item(message, "CHECKPOINT_RESULT"); - if (!node || plist_get_node_type(node) != PLIST_UINT) { + if (!node || plist_get_node_type(node) != PLIST_INT) { debug("Failed to parse checkpoint result from checkpoint plist\n"); - return -1; + err = -1; + break; } - plist_get_uint_val(node, &ckpt_res); + plist_get_int_val(node, &ckpt_res); // Get checkpoint complete node = plist_dict_get_item(message, "CHECKPOINT_COMPLETE"); if (PLIST_IS_BOOLEAN(node)) { plist_get_bool_val(node, &ckpt_complete); } - if (ckpt_complete) - info("Checkpoint %" PRIu64 " complete with code %" PRIu64 "\n", ckpt_id, ckpt_res); + + if (ckpt_complete) { + info("Checkpoint completed id: 0x%" PRIX64 " (%s) result=%" PRIi64 "\n", ckpt_id, ckpt_name, ckpt_res); + } else { + info("Checkpoint started id: 0x%" PRIX64 " (%s)\n", ckpt_id, ckpt_name); + } + node = plist_dict_get_item(message, "CHECKPOINT_WARNING"); + if (node) { + info("Checkpoint WARNING id: 0x%" PRIX64 " result=%" PRIi64 ": %s\n", ckpt_id, ckpt_res, plist_get_string_ptr(node, NULL)); + } + node = plist_dict_get_item(message, "CHECKPOINT_ERROR"); + if (node) { + info("Checkpoint FAILURE id: 0x%" PRIX64 " result=%" PRIi64 ": %s\n", ckpt_id, ckpt_res, plist_get_string_ptr(node, NULL)); + } } // baseband update message else if (!strcmp(type, "BBUpdateStatusMsg")) { - err = restore_handle_bb_update_status_msg(restore, message); + err = restore_handle_bb_update_status_msg(client, message); } // baseband updater output data request else if (!strcmp(type, "BasebandUpdaterOutputData")) { - err = restore_handle_baseband_updater_output_data(restore, client, device, message); + err = restore_handle_baseband_updater_output_data(client, message); + } + + // handle restored crash, print backtrace + else if (!strcmp(type, "RestoredCrash")) { + err = restore_handle_restored_crash(client, message); + } + + // handle async wait + else if (!strcmp(type, "AsyncWait")) { + err = restore_handle_async_wait(client, message); + } + + else if (!strcmp(type, "RestoreAttestation")) { + err = restore_handle_restore_attestation(client, message); } // there might be some other message types i'm not aware of, but I think @@ -4335,6 +5687,9 @@ int restore_device(struct idevicerestore_client_t* client, plist_t build_identit plist_free(message); message = NULL; } + if (client->async_err != 0) { + err = client->async_err; + } #ifdef HAVE_REVERSE_PROXY reverse_proxy_client_free(rproxy); diff --git a/src/restore.h b/src/restore.h index ad3b7214..763331dd 100644 --- a/src/restore.h +++ b/src/restore.h @@ -38,9 +38,9 @@ struct restore_client_t { idevice_t device; char* udid; unsigned int operation; - const char* filesystem; uint64_t protocol_version; restored_client_t client; + plist_t build_identity; }; int restore_check_mode(struct idevicerestore_client_t* client); @@ -50,16 +50,16 @@ void restore_client_free(struct idevicerestore_client_t* client); int restore_is_image4_supported(struct idevicerestore_client_t* client); int restore_reboot(struct idevicerestore_client_t* client); const char* restore_progress_string(unsigned int operation); -int restore_handle_status_msg(restored_client_t client, plist_t msg); -int restore_handle_progress_msg(struct idevicerestore_client_t* client, plist_t msg); -int restore_handle_data_request_msg(struct idevicerestore_client_t* client, idevice_t device, restored_client_t restore, plist_t message, plist_t build_identity, const char* filesystem); -int restore_send_nor(restored_client_t restore, struct idevicerestore_client_t* client, plist_t build_identity, plist_t message); -int restore_send_root_ticket(restored_client_t restore, struct idevicerestore_client_t* client); -int restore_send_component(restored_client_t restore, struct idevicerestore_client_t* client, plist_t build_identity, const char* component, const char* component_name); -int restore_device(struct idevicerestore_client_t* client, plist_t build_identity, const char* filesystem); +int restore_handle_status_msg(struct idevicerestore_client_t* client, plist_t message); +int restore_handle_progress_msg(struct idevicerestore_client_t* client, plist_t message); +int restore_handle_data_request_msg(struct idevicerestore_client_t* client, plist_t message); +int restore_send_nor(struct idevicerestore_client_t* client, plist_t message); +int restore_send_root_ticket(struct idevicerestore_client_t* client, plist_t message); +int restore_send_component(struct idevicerestore_client_t* client, plist_t message, const char* component, const char* component_name); +int restore_device(struct idevicerestore_client_t* client, plist_t build_identity); int restore_open_with_timeout(struct idevicerestore_client_t* client); -int restore_send_filesystem(struct idevicerestore_client_t* client, idevice_t device, const char* filesystem); -int restore_send_fdr_trust_data(restored_client_t restore, idevice_t device); +int restore_send_filesystem(struct idevicerestore_client_t* client, plist_t message); +int restore_send_fdr_trust_data(struct idevicerestore_client_t* client, plist_t message); #ifdef __cplusplus } diff --git a/src/sha1.c b/src/sha1.c deleted file mode 100644 index 02557ffe..00000000 --- a/src/sha1.c +++ /dev/null @@ -1,294 +0,0 @@ -/* -SHA-1 in C -By Steve Reid -100% Public Domain -Test Vectors (from FIPS PUB 180-1) -"abc" - A9993E36 4706816A BA3E2571 7850C26C 9CD0D89D -"abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq" - 84983E44 1C3BD26E BAAE4AA1 F95129E5 E54670F1 -A million repetitions of "a" - 34AA973C D4C4DAA4 F61EEB2B DBAD2731 6534016F -*/ - -/* #define LITTLE_ENDIAN * This should be #define'd already, if true. */ -/* #define SHA1HANDSOFF * Copies data before messing with it. */ - -#define SHA1HANDSOFF - -#include -#include - -/* for uint32_t */ -#include - -#include "sha1.h" - - -#define rol(value, bits) (((value) << (bits)) | ((value) >> (32 - (bits)))) - -/* blk0() and blk() perform the initial expand. */ -/* I got the idea of expanding during the round function from SSLeay */ -#if BYTE_ORDER == LITTLE_ENDIAN -#define blk0(i) (block->l[i] = (rol(block->l[i],24)&0xFF00FF00) \ - |(rol(block->l[i],8)&0x00FF00FF)) -#elif BYTE_ORDER == BIG_ENDIAN -#define blk0(i) block->l[i] -#else -#error "Endianness not defined!" -#endif -#define blk(i) (block->l[i&15] = rol(block->l[(i+13)&15]^block->l[(i+8)&15] \ - ^block->l[(i+2)&15]^block->l[i&15],1)) - -/* (R0+R1), R2, R3, R4 are the different operations used in SHA1 */ -#define R0(v,w,x,y,z,i) z+=((w&(x^y))^y)+blk0(i)+0x5A827999+rol(v,5);w=rol(w,30); -#define R1(v,w,x,y,z,i) z+=((w&(x^y))^y)+blk(i)+0x5A827999+rol(v,5);w=rol(w,30); -#define R2(v,w,x,y,z,i) z+=(w^x^y)+blk(i)+0x6ED9EBA1+rol(v,5);w=rol(w,30); -#define R3(v,w,x,y,z,i) z+=(((w|x)&y)|(w&x))+blk(i)+0x8F1BBCDC+rol(v,5);w=rol(w,30); -#define R4(v,w,x,y,z,i) z+=(w^x^y)+blk(i)+0xCA62C1D6+rol(v,5);w=rol(w,30); - - -/* Hash a single 512-bit block. This is the core of the algorithm. */ - -void SHA1Transform( - uint32_t state[5], - const unsigned char buffer[64] -) -{ - uint32_t a, b, c, d, e; - - typedef union - { - unsigned char c[64]; - uint32_t l[16]; - } CHAR64LONG16; - -#ifdef SHA1HANDSOFF - CHAR64LONG16 block[1]; /* use array to appear as a pointer */ - - memcpy(block, buffer, 64); -#else - /* The following had better never be used because it causes the - * pointer-to-const buffer to be cast into a pointer to non-const. - * And the result is written through. I threw a "const" in, hoping - * this will cause a diagnostic. - */ - CHAR64LONG16 *block = (const CHAR64LONG16 *) buffer; -#endif - /* Copy context->state[] to working vars */ - a = state[0]; - b = state[1]; - c = state[2]; - d = state[3]; - e = state[4]; - /* 4 rounds of 20 operations each. Loop unrolled. */ - R0(a, b, c, d, e, 0); - R0(e, a, b, c, d, 1); - R0(d, e, a, b, c, 2); - R0(c, d, e, a, b, 3); - R0(b, c, d, e, a, 4); - R0(a, b, c, d, e, 5); - R0(e, a, b, c, d, 6); - R0(d, e, a, b, c, 7); - R0(c, d, e, a, b, 8); - R0(b, c, d, e, a, 9); - R0(a, b, c, d, e, 10); - R0(e, a, b, c, d, 11); - R0(d, e, a, b, c, 12); - R0(c, d, e, a, b, 13); - R0(b, c, d, e, a, 14); - R0(a, b, c, d, e, 15); - R1(e, a, b, c, d, 16); - R1(d, e, a, b, c, 17); - R1(c, d, e, a, b, 18); - R1(b, c, d, e, a, 19); - R2(a, b, c, d, e, 20); - R2(e, a, b, c, d, 21); - R2(d, e, a, b, c, 22); - R2(c, d, e, a, b, 23); - R2(b, c, d, e, a, 24); - R2(a, b, c, d, e, 25); - R2(e, a, b, c, d, 26); - R2(d, e, a, b, c, 27); - R2(c, d, e, a, b, 28); - R2(b, c, d, e, a, 29); - R2(a, b, c, d, e, 30); - R2(e, a, b, c, d, 31); - R2(d, e, a, b, c, 32); - R2(c, d, e, a, b, 33); - R2(b, c, d, e, a, 34); - R2(a, b, c, d, e, 35); - R2(e, a, b, c, d, 36); - R2(d, e, a, b, c, 37); - R2(c, d, e, a, b, 38); - R2(b, c, d, e, a, 39); - R3(a, b, c, d, e, 40); - R3(e, a, b, c, d, 41); - R3(d, e, a, b, c, 42); - R3(c, d, e, a, b, 43); - R3(b, c, d, e, a, 44); - R3(a, b, c, d, e, 45); - R3(e, a, b, c, d, 46); - R3(d, e, a, b, c, 47); - R3(c, d, e, a, b, 48); - R3(b, c, d, e, a, 49); - R3(a, b, c, d, e, 50); - R3(e, a, b, c, d, 51); - R3(d, e, a, b, c, 52); - R3(c, d, e, a, b, 53); - R3(b, c, d, e, a, 54); - R3(a, b, c, d, e, 55); - R3(e, a, b, c, d, 56); - R3(d, e, a, b, c, 57); - R3(c, d, e, a, b, 58); - R3(b, c, d, e, a, 59); - R4(a, b, c, d, e, 60); - R4(e, a, b, c, d, 61); - R4(d, e, a, b, c, 62); - R4(c, d, e, a, b, 63); - R4(b, c, d, e, a, 64); - R4(a, b, c, d, e, 65); - R4(e, a, b, c, d, 66); - R4(d, e, a, b, c, 67); - R4(c, d, e, a, b, 68); - R4(b, c, d, e, a, 69); - R4(a, b, c, d, e, 70); - R4(e, a, b, c, d, 71); - R4(d, e, a, b, c, 72); - R4(c, d, e, a, b, 73); - R4(b, c, d, e, a, 74); - R4(a, b, c, d, e, 75); - R4(e, a, b, c, d, 76); - R4(d, e, a, b, c, 77); - R4(c, d, e, a, b, 78); - R4(b, c, d, e, a, 79); - /* Add the working vars back into context.state[] */ - state[0] += a; - state[1] += b; - state[2] += c; - state[3] += d; - state[4] += e; - /* Wipe variables */ - a = b = c = d = e = 0; -#ifdef SHA1HANDSOFF - memset(block, '\0', sizeof(block)); -#endif -} - - -/* SHA1Init - Initialize new context */ - -void SHA1Init( - SHA1_CTX * context -) -{ - /* SHA1 initialization constants */ - context->state[0] = 0x67452301; - context->state[1] = 0xEFCDAB89; - context->state[2] = 0x98BADCFE; - context->state[3] = 0x10325476; - context->state[4] = 0xC3D2E1F0; - context->count[0] = context->count[1] = 0; -} - - -/* Run your data through this. */ - -void SHA1Update( - SHA1_CTX * context, - const unsigned char *data, - size_t len -) -{ - size_t i; - - size_t j; - - j = context->count[0]; - if ((context->count[0] += len << 3) < j) - context->count[1]++; - context->count[1] += (len >> 29); - j = (j >> 3) & 63; - if ((j + len) > 63) - { - memcpy(&context->buffer[j], data, (i = 64 - j)); - SHA1Transform(context->state, context->buffer); - for (; i + 63 < len; i += 64) - { - SHA1Transform(context->state, &data[i]); - } - j = 0; - } - else - i = 0; - memcpy(&context->buffer[j], &data[i], len - i); -} - - -/* Add padding and return the message digest. */ - -void SHA1Final( - unsigned char digest[20], - SHA1_CTX * context -) -{ - unsigned i; - - unsigned char finalcount[8]; - - unsigned char c; - -#if 0 /* untested "improvement" by DHR */ - /* Convert context->count to a sequence of bytes - * in finalcount. Second element first, but - * big-endian order within element. - * But we do it all backwards. - */ - unsigned char *fcp = &finalcount[8]; - - for (i = 0; i < 2; i++) - { - uint32_t t = context->count[i]; - - int j; - - for (j = 0; j < 4; t >>= 8, j++) - *--fcp = (unsigned char) t} -#else - for (i = 0; i < 8; i++) - { - finalcount[i] = (unsigned char) ((context->count[(i >= 4 ? 0 : 1)] >> ((3 - (i & 3)) * 8)) & 255); /* Endian independent */ - } -#endif - c = 0200; - SHA1Update(context, &c, 1); - while ((context->count[0] & 504) != 448) - { - c = 0000; - SHA1Update(context, &c, 1); - } - SHA1Update(context, finalcount, 8); /* Should cause a SHA1Transform() */ - for (i = 0; i < 20; i++) - { - digest[i] = (unsigned char) - ((context->state[i >> 2] >> ((3 - (i & 3)) * 8)) & 255); - } - /* Wipe variables */ - memset(context, '\0', sizeof(*context)); - memset(&finalcount, '\0', sizeof(finalcount)); -} - -void SHA1( - const unsigned char *str, - size_t len, - unsigned char *hash_out -) -{ - SHA1_CTX ctx; - size_t ii; - - SHA1Init(&ctx); - for (ii=0; ii - 100% Public Domain - */ - -#include "stdint.h" - -typedef struct -{ - uint32_t state[5]; - uint32_t count[2]; - unsigned char buffer[64]; -} SHA1_CTX; - -void SHA1Transform( - uint32_t state[5], - const unsigned char buffer[64] - ); - -void SHA1Init( - SHA1_CTX * context - ); - -void SHA1Update( - SHA1_CTX * context, - const unsigned char *data, - size_t len - ); - -void SHA1Final( - unsigned char digest[20], - SHA1_CTX * context - ); - -void SHA1( - const unsigned char *str, - size_t len, - unsigned char *hash_out); - -#endif /* SHA1_H */ diff --git a/src/sha512.c b/src/sha512.c deleted file mode 100644 index 8f7c59d1..00000000 --- a/src/sha512.c +++ /dev/null @@ -1,314 +0,0 @@ -/* LibTomCrypt, modular cryptographic library -- Tom St Denis - * - * LibTomCrypt is a library that provides various cryptographic - * algorithms in a highly modular and flexible manner. - * - * The library is free for all purposes without any express - * guarantee it works. - * - * Tom St Denis, tomstdenis@gmail.com, http://libtom.org - */ - -#include "fixedint.h" -#include "sha512.h" - -/* the K array */ -static const uint64_t K[80] = { - UINT64_C(0x428a2f98d728ae22), UINT64_C(0x7137449123ef65cd), - UINT64_C(0xb5c0fbcfec4d3b2f), UINT64_C(0xe9b5dba58189dbbc), - UINT64_C(0x3956c25bf348b538), UINT64_C(0x59f111f1b605d019), - UINT64_C(0x923f82a4af194f9b), UINT64_C(0xab1c5ed5da6d8118), - UINT64_C(0xd807aa98a3030242), UINT64_C(0x12835b0145706fbe), - UINT64_C(0x243185be4ee4b28c), UINT64_C(0x550c7dc3d5ffb4e2), - UINT64_C(0x72be5d74f27b896f), UINT64_C(0x80deb1fe3b1696b1), - UINT64_C(0x9bdc06a725c71235), UINT64_C(0xc19bf174cf692694), - UINT64_C(0xe49b69c19ef14ad2), UINT64_C(0xefbe4786384f25e3), - UINT64_C(0x0fc19dc68b8cd5b5), UINT64_C(0x240ca1cc77ac9c65), - UINT64_C(0x2de92c6f592b0275), UINT64_C(0x4a7484aa6ea6e483), - UINT64_C(0x5cb0a9dcbd41fbd4), UINT64_C(0x76f988da831153b5), - UINT64_C(0x983e5152ee66dfab), UINT64_C(0xa831c66d2db43210), - UINT64_C(0xb00327c898fb213f), UINT64_C(0xbf597fc7beef0ee4), - UINT64_C(0xc6e00bf33da88fc2), UINT64_C(0xd5a79147930aa725), - UINT64_C(0x06ca6351e003826f), UINT64_C(0x142929670a0e6e70), - UINT64_C(0x27b70a8546d22ffc), UINT64_C(0x2e1b21385c26c926), - UINT64_C(0x4d2c6dfc5ac42aed), UINT64_C(0x53380d139d95b3df), - UINT64_C(0x650a73548baf63de), UINT64_C(0x766a0abb3c77b2a8), - UINT64_C(0x81c2c92e47edaee6), UINT64_C(0x92722c851482353b), - UINT64_C(0xa2bfe8a14cf10364), UINT64_C(0xa81a664bbc423001), - UINT64_C(0xc24b8b70d0f89791), UINT64_C(0xc76c51a30654be30), - UINT64_C(0xd192e819d6ef5218), UINT64_C(0xd69906245565a910), - UINT64_C(0xf40e35855771202a), UINT64_C(0x106aa07032bbd1b8), - UINT64_C(0x19a4c116b8d2d0c8), UINT64_C(0x1e376c085141ab53), - UINT64_C(0x2748774cdf8eeb99), UINT64_C(0x34b0bcb5e19b48a8), - UINT64_C(0x391c0cb3c5c95a63), UINT64_C(0x4ed8aa4ae3418acb), - UINT64_C(0x5b9cca4f7763e373), UINT64_C(0x682e6ff3d6b2b8a3), - UINT64_C(0x748f82ee5defb2fc), UINT64_C(0x78a5636f43172f60), - UINT64_C(0x84c87814a1f0ab72), UINT64_C(0x8cc702081a6439ec), - UINT64_C(0x90befffa23631e28), UINT64_C(0xa4506cebde82bde9), - UINT64_C(0xbef9a3f7b2c67915), UINT64_C(0xc67178f2e372532b), - UINT64_C(0xca273eceea26619c), UINT64_C(0xd186b8c721c0c207), - UINT64_C(0xeada7dd6cde0eb1e), UINT64_C(0xf57d4f7fee6ed178), - UINT64_C(0x06f067aa72176fba), UINT64_C(0x0a637dc5a2c898a6), - UINT64_C(0x113f9804bef90dae), UINT64_C(0x1b710b35131c471b), - UINT64_C(0x28db77f523047d84), UINT64_C(0x32caab7b40c72493), - UINT64_C(0x3c9ebe0a15c9bebc), UINT64_C(0x431d67c49c100d4c), - UINT64_C(0x4cc5d4becb3e42b6), UINT64_C(0x597f299cfc657e2a), - UINT64_C(0x5fcb6fab3ad6faec), UINT64_C(0x6c44198c4a475817) -}; - -/* Various logical functions */ - -#define ROR64c(x, y) \ - ( ((((x)&UINT64_C(0xFFFFFFFFFFFFFFFF))>>((uint64_t)(y)&UINT64_C(63))) | \ - ((x)<<((uint64_t)(64-((y)&UINT64_C(63)))))) & UINT64_C(0xFFFFFFFFFFFFFFFF)) - -#define STORE64H(x, y) \ - { (y)[0] = (unsigned char)(((x)>>56)&255); (y)[1] = (unsigned char)(((x)>>48)&255); \ - (y)[2] = (unsigned char)(((x)>>40)&255); (y)[3] = (unsigned char)(((x)>>32)&255); \ - (y)[4] = (unsigned char)(((x)>>24)&255); (y)[5] = (unsigned char)(((x)>>16)&255); \ - (y)[6] = (unsigned char)(((x)>>8)&255); (y)[7] = (unsigned char)((x)&255); } - -#define LOAD64H(x, y) \ - { x = (((uint64_t)((y)[0] & 255))<<56)|(((uint64_t)((y)[1] & 255))<<48) | \ - (((uint64_t)((y)[2] & 255))<<40)|(((uint64_t)((y)[3] & 255))<<32) | \ - (((uint64_t)((y)[4] & 255))<<24)|(((uint64_t)((y)[5] & 255))<<16) | \ - (((uint64_t)((y)[6] & 255))<<8)|(((uint64_t)((y)[7] & 255))); } - - -#define Ch(x,y,z) (z ^ (x & (y ^ z))) -#define Maj(x,y,z) (((x | y) & z) | (x & y)) -#define S(x, n) ROR64c(x, n) -#define R(x, n) (((x) &UINT64_C(0xFFFFFFFFFFFFFFFF))>>((uint64_t)n)) -#define Sigma0(x) (S(x, 28) ^ S(x, 34) ^ S(x, 39)) -#define Sigma1(x) (S(x, 14) ^ S(x, 18) ^ S(x, 41)) -#define Gamma0(x) (S(x, 1) ^ S(x, 8) ^ R(x, 7)) -#define Gamma1(x) (S(x, 19) ^ S(x, 61) ^ R(x, 6)) -#ifndef MIN - #define MIN(x, y) ( ((x)<(y))?(x):(y) ) -#endif - -/* compress 1024-bits */ -static int sha512_compress(sha512_context *md, unsigned char *buf) -{ - uint64_t S[8], W[80], t0, t1; - int i; - - /* copy state into S */ - for (i = 0; i < 8; i++) { - S[i] = md->state[i]; - } - - /* copy the state into 1024-bits into W[0..15] */ - for (i = 0; i < 16; i++) { - LOAD64H(W[i], buf + (8*i)); - } - - /* fill W[16..79] */ - for (i = 16; i < 80; i++) { - W[i] = Gamma1(W[i - 2]) + W[i - 7] + Gamma0(W[i - 15]) + W[i - 16]; - } - -/* Compress */ - #define RND(a,b,c,d,e,f,g,h,i) \ - t0 = h + Sigma1(e) + Ch(e, f, g) + K[i] + W[i]; \ - t1 = Sigma0(a) + Maj(a, b, c);\ - d += t0; \ - h = t0 + t1; - - for (i = 0; i < 80; i += 8) { - RND(S[0],S[1],S[2],S[3],S[4],S[5],S[6],S[7],i+0); - RND(S[7],S[0],S[1],S[2],S[3],S[4],S[5],S[6],i+1); - RND(S[6],S[7],S[0],S[1],S[2],S[3],S[4],S[5],i+2); - RND(S[5],S[6],S[7],S[0],S[1],S[2],S[3],S[4],i+3); - RND(S[4],S[5],S[6],S[7],S[0],S[1],S[2],S[3],i+4); - RND(S[3],S[4],S[5],S[6],S[7],S[0],S[1],S[2],i+5); - RND(S[2],S[3],S[4],S[5],S[6],S[7],S[0],S[1],i+6); - RND(S[1],S[2],S[3],S[4],S[5],S[6],S[7],S[0],i+7); - } - - #undef RND - - - - /* feedback */ - for (i = 0; i < 8; i++) { - md->state[i] = md->state[i] + S[i]; - } - - return 0; -} - - -/** - Initialize the hash state - @param md The hash state you wish to initialize - @return 0 if successful -*/ -int sha512_init(sha512_context * md) { - if (md == NULL) return 1; - - md->curlen = 0; - md->length = 0; - md->state[0] = UINT64_C(0x6a09e667f3bcc908); - md->state[1] = UINT64_C(0xbb67ae8584caa73b); - md->state[2] = UINT64_C(0x3c6ef372fe94f82b); - md->state[3] = UINT64_C(0xa54ff53a5f1d36f1); - md->state[4] = UINT64_C(0x510e527fade682d1); - md->state[5] = UINT64_C(0x9b05688c2b3e6c1f); - md->state[6] = UINT64_C(0x1f83d9abfb41bd6b); - md->state[7] = UINT64_C(0x5be0cd19137e2179); - md->num_qwords = 8; - - return 0; -} - -/** - Process a block of memory though the hash - @param md The hash state - @param in The data to hash - @param inlen The length of the data (octets) - @return 0 if successful -*/ -int sha512_update (sha512_context * md, const unsigned char *in, size_t inlen) -{ - size_t n; - size_t i; - int err; - if (md == NULL) return 1; - if (in == NULL) return 1; - if (md->curlen > sizeof(md->buf)) { - return 1; - } - while (inlen > 0) { - if (md->curlen == 0 && inlen >= 128) { - if ((err = sha512_compress (md, (unsigned char *)in)) != 0) { - return err; - } - md->length += 128 * 8; - in += 128; - inlen -= 128; - } else { - n = MIN(inlen, (128 - md->curlen)); - - for (i = 0; i < n; i++) { - md->buf[i + md->curlen] = in[i]; - } - - - md->curlen += n; - in += n; - inlen -= n; - if (md->curlen == 128) { - if ((err = sha512_compress (md, md->buf)) != 0) { - return err; - } - md->length += 8*128; - md->curlen = 0; - } - } - } - return 0; -} - -/** - Terminate the hash to get the digest - @param md The hash state - @param out [out] The destination of the hash (64 bytes) - @return 0 if successful -*/ -int sha512_final(sha512_context * md, unsigned char *out) -{ - int i; - - if (md == NULL) return 1; - if (out == NULL) return 1; - - if (md->curlen >= sizeof(md->buf)) { - return 1; - } - - /* increase the length of the message */ - md->length += md->curlen * UINT64_C(8); - - /* append the '1' bit */ - md->buf[md->curlen++] = (unsigned char)0x80; - - /* if the length is currently above 112 bytes we append zeros - * then compress. Then we can fall back to padding zeros and length - * encoding like normal. - */ - if (md->curlen > 112) { - while (md->curlen < 128) { - md->buf[md->curlen++] = (unsigned char)0; - } - sha512_compress(md, md->buf); - md->curlen = 0; - } - - /* pad upto 120 bytes of zeroes - * note: that from 112 to 120 is the 64 MSB of the length. We assume that you won't hash - * > 2^64 bits of data... :-) - */ - while (md->curlen < 120) { - md->buf[md->curlen++] = (unsigned char)0; - } - - /* store length */ - STORE64H(md->length, md->buf+120); - sha512_compress(md, md->buf); - - /* copy output */ - for (i = 0; i < md->num_qwords; i++) { - STORE64H(md->state[i], out+(8*i)); - } - - return 0; -} - -int sha512(const unsigned char *message, size_t message_len, unsigned char *out) -{ - sha512_context ctx; - int ret; - if ((ret = sha512_init(&ctx))) return ret; - if ((ret = sha512_update(&ctx, message, message_len))) return ret; - if ((ret = sha512_final(&ctx, out))) return ret; - return 0; -} - -int sha384_init(sha384_context * md) { - if (md == NULL) return 1; - - md->curlen = 0; - md->length = 0; - md->state[0] = UINT64_C(0xcbbb9d5dc1059ed8); - md->state[1] = UINT64_C(0x629a292a367cd507); - md->state[2] = UINT64_C(0x9159015a3070dd17); - md->state[3] = UINT64_C(0x152fecd8f70e5939); - md->state[4] = UINT64_C(0x67332667ffc00b31); - md->state[5] = UINT64_C(0x8eb44a8768581511); - md->state[6] = UINT64_C(0xdb0c2e0d64f98fa7); - md->state[7] = UINT64_C(0x47b5481dbefa4fa4); - md->num_qwords = 6; - - return 0; -} - -int sha384_final(sha384_context * md, unsigned char* out) -{ - return sha512_final(md, out); -} - -int sha384_update(sha384_context * md, const unsigned char *in, size_t inlen) -{ - return sha512_update(md, in, inlen); -} - -int sha384(const unsigned char *message, size_t message_len, unsigned char *out) -{ - sha384_context ctx; - int ret; - if ((ret = sha384_init(&ctx))) return ret; - if ((ret = sha384_update(&ctx, message, message_len))) return ret; - if ((ret = sha384_final(&ctx, out))) return ret; - return 0; -} diff --git a/src/sha512.h b/src/sha512.h deleted file mode 100644 index 72db47b5..00000000 --- a/src/sha512.h +++ /dev/null @@ -1,32 +0,0 @@ -#ifndef SHA512_H -#define SHA512_H - -#include - -#include "fixedint.h" - -/* state */ -typedef struct sha512_context_ { - uint64_t length, state[8]; - size_t curlen; - unsigned char buf[128]; - int num_qwords; -} sha512_context; - -#define SHA512_DIGEST_LENGTH 64 - -int sha512_init(sha512_context * md); -int sha512_final(sha512_context * md, unsigned char *out); -int sha512_update(sha512_context * md, const unsigned char *in, size_t inlen); -int sha512(const unsigned char *message, size_t message_len, unsigned char *out); - -typedef sha512_context sha384_context; - -#define SHA384_DIGEST_LENGTH 48 - -int sha384_init(sha384_context * md); -int sha384_final(sha384_context * md, unsigned char *out); -int sha384_update(sha384_context * md, const unsigned char *in, size_t inlen); -int sha384(const unsigned char *message, size_t message_len, unsigned char *out); - -#endif diff --git a/src/tss.c b/src/tss.c deleted file mode 100644 index b6980a73..00000000 --- a/src/tss.c +++ /dev/null @@ -1,1691 +0,0 @@ -/* - * tss.c - * Functions for communicating with Apple's TSS server - * - * Copyright (c) 2010-2013 Martin Szulecki. All Rights Reserved. - * Copyright (c) 2012 Nikias Bassen. All Rights Reserved. - * Copyright (c) 2010 Joshua Hill. All Rights Reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - */ - -#include -#include -#include -#include -#include -#include - -#include "tss.h" -#include "img3.h" -#include "common.h" -#include "idevicerestore.h" - -#include "endianness.h" - -#define AUTH_VERSION "850.0.2" - -#ifdef WIN32 -#define TSS_CLIENT_VERSION_STRING "libauthinstall_Win-"AUTH_VERSION"" -#else -#define TSS_CLIENT_VERSION_STRING "libauthinstall-"AUTH_VERSION"" -#endif -#define ECID_STRSIZE 0x20 - -typedef struct { - int length; - char* content; -} tss_response; - -char* ecid_to_string(uint64_t ecid) -{ - char* ecid_string = malloc(ECID_STRSIZE); - memset(ecid_string, '\0', ECID_STRSIZE); - if (ecid == 0) { - error("ERROR: Invalid ECID passed.\n"); - return NULL; - } - snprintf(ecid_string, ECID_STRSIZE, "%"PRIu64, ecid); - return ecid_string; -} - -plist_t tss_request_new(plist_t overrides) -{ - plist_t request = plist_new_dict(); - - plist_dict_set_item(request, "@BBTicket", plist_new_bool(1)); - plist_dict_set_item(request, "@HostPlatformInfo", -#ifdef WIN32 - plist_new_string("windows") -#else - plist_new_string("mac") -#endif - ); - - plist_dict_set_item(request, "@VersionInfo", plist_new_string(TSS_CLIENT_VERSION_STRING)); - char* guid = generate_guid(); - if (guid) { - plist_dict_set_item(request, "@UUID", plist_new_string(guid)); - free(guid); - } - - /* apply overrides */ - if (overrides) { - plist_dict_merge(&request, overrides); - } - - return request; -} - -int tss_request_add_local_policy_tags(plist_t request, plist_t parameters) -{ - plist_dict_set_item(request, "@ApImg4Ticket", plist_new_bool(1)); - - if (_plist_dict_copy_bool(request, parameters, "Ap,LocalBoot", NULL) < 0) { - error("ERROR: Unable to find required Ap,LocalBoot in parameters\n"); - return -1; - } - - if (_plist_dict_copy_item(request, parameters, "Ap,LocalPolicy", NULL) < 0) { - error("ERROR: Unable to find required Ap,LocalPolicy in parameters\n"); - return -1; - } - - if (_plist_dict_copy_data(request, parameters, "Ap,NextStageIM4MHash", NULL) < 0) { - error("ERROR: Unable to find required Ap,NextStageIM4MHash in parameters\n"); - return -1; - } - - _plist_dict_copy_data(request, parameters, "Ap,RecoveryOSPolicyNonceHash", NULL); - _plist_dict_copy_data(request, parameters, "Ap,VolumeUUID", NULL); - _plist_dict_copy_uint(request, parameters, "ApECID", NULL); - _plist_dict_copy_uint(request, parameters, "ApChipID", NULL); - _plist_dict_copy_uint(request, parameters, "ApBoardID", NULL); - _plist_dict_copy_uint(request, parameters, "ApSecurityDomain", NULL); - _plist_dict_copy_data(request, parameters, "ApNonce", NULL); - - if (!plist_dict_get_item(request, "ApSecurityMode")) { - /* copy from parameters if available */ - if (_plist_dict_copy_bool(request, parameters, "ApSecurityMode", NULL) < 0) { - error("ERROR: Unable to find required ApSecurityMode in parameters\n"); - return -1; - } - } - if (!plist_dict_get_item(request, "ApProductionMode")) { - /* copy from parameters if available */ - if (_plist_dict_copy_bool(request, parameters, "ApProductionMode", NULL) < 0) { - error("ERROR: Unable to find required ApProductionMode in parameters\n"); - return -1; - } - } - - return 0; -} - -int tss_parameters_add_from_manifest(plist_t parameters, plist_t build_identity, bool include_manifest) -{ - plist_t node = NULL; - - if (_plist_dict_copy_data(parameters, build_identity, "UniqueBuildID", NULL) < 0) { - error("ERROR: Unable to find UniqueBuildID node\n"); - return -1; - } - - _plist_dict_copy_string(parameters, build_identity, "Ap,OSLongVersion", NULL); - - if (_plist_dict_copy_uint(parameters, build_identity, "ApChipID", NULL) < 0) {; - error("ERROR: Unable to find ApChipID node\n"); - return -1; - } - - if (_plist_dict_copy_uint(parameters, build_identity, "ApBoardID", NULL) < 0) { - error("ERROR: Unable to find ApBoardID node\n"); - return -1; - } - - _plist_dict_copy_uint(parameters, build_identity, "ApSecurityDomain", NULL); - _plist_dict_copy_uint(parameters, build_identity, "BMU,BoardID", NULL); - _plist_dict_copy_uint(parameters, build_identity, "BMU,ChipID", NULL); - - if (_plist_dict_copy_uint(parameters, build_identity, "BbChipID", NULL) < 0) { - debug("NOTE: Unable to find BbChipID node\n"); - } - - if (_plist_dict_copy_data(parameters, build_identity, "BbProvisioningManifestKeyHash", NULL) < 0) { - debug("NOTE: Unable to find BbProvisioningManifestKeyHash node\n"); - } - - if (_plist_dict_copy_data(parameters, build_identity, "BbActivationManifestKeyHash", NULL) < 0) { - debug("NOTE: Unable to find BbActivationManifestKeyHash node\n"); - } - - if (_plist_dict_copy_data(parameters, build_identity, "BbCalibrationManifestKeyHash", NULL) < 0) { - debug("NOTE: Unable to find BbCalibrationManifestKeyHash node\n"); - } - - if (_plist_dict_copy_data(parameters, build_identity, "BbFactoryActivationManifestKeyHash", NULL) < 0) { - debug("NOTE: Unable to find BbFactoryActivationManifestKeyHash node\n"); - } - - if (_plist_dict_copy_data(parameters, build_identity, "BbFDRSecurityKeyHash", NULL) < 0) { - debug("NOTE: Unable to find BbFDRSecurityKeyHash node\n"); - } - - /* BbSkeyId - Used by XMM 6180/GSM */ - if (_plist_dict_copy_data(parameters, build_identity, "BbSkeyId", NULL) < 0) { - debug("NOTE: Unable to find BbSkeyId node\n"); - } - - /* SE,ChipID - Used for SE firmware request */ - _plist_dict_copy_uint(parameters, build_identity, "SE,ChipID", NULL); - - /* Savage,ChipID - Used for Savage firmware request */ - _plist_dict_copy_uint(parameters, build_identity, "Savage,ChipID", NULL); - - /* add Savage,PatchEpoch - Used for Savage firmware request */ - _plist_dict_copy_uint(parameters, build_identity, "Savage,PatchEpoch", NULL); - - /* Yonkers,BoardID - Used for Yonkers firmware request */ - _plist_dict_copy_uint(parameters, build_identity, "Yonkers,BoardID", NULL); - - /* Yonkers,ChipID - Used for Yonkers firmware request */ - _plist_dict_copy_uint(parameters, build_identity, "Yonkers,ChipID", NULL); - - /* add Yonkers,PatchEpoch - Used for Yonkers firmware request */ - _plist_dict_copy_uint(parameters, build_identity, "Yonkers,PatchEpoch", NULL); - - _plist_dict_copy_uint(parameters, build_identity, "Rap,BoardID", NULL); - _plist_dict_copy_uint(parameters, build_identity, "Rap,ChipID", NULL); - _plist_dict_copy_uint(parameters, build_identity, "Rap,SecurityDomain", NULL); - - _plist_dict_copy_uint(parameters, build_identity, "Baobab,BoardID", NULL); - _plist_dict_copy_uint(parameters, build_identity, "Baobab,ChipID", NULL); - _plist_dict_copy_uint(parameters, build_identity, "Baobab,ManifestEpoch", NULL); - _plist_dict_copy_uint(parameters, build_identity, "Baobab,SecurityDomain", NULL); - - _plist_dict_copy_uint(parameters, build_identity, "eUICC,ChipID", NULL); - - _plist_dict_copy_data(parameters, build_identity, "PearlCertificationRootPub", NULL); - - _plist_dict_copy_uint(parameters, build_identity, "Timer,BoardID,1", NULL); - _plist_dict_copy_uint(parameters, build_identity, "Timer,BoardID,2", NULL); - _plist_dict_copy_uint(parameters, build_identity, "Timer,ChipID,1", NULL); - _plist_dict_copy_uint(parameters, build_identity, "Timer,ChipID,2", NULL); - _plist_dict_copy_uint(parameters, build_identity, "Timer,SecurityDomain,1", NULL); - _plist_dict_copy_uint(parameters, build_identity, "Timer,SecurityDomain,2", NULL); - - _plist_dict_copy_item(parameters, build_identity, "Cryptex1,ChipID", NULL); - _plist_dict_copy_item(parameters, build_identity, "Cryptex1,Type", NULL); - _plist_dict_copy_item(parameters, build_identity, "Cryptex1,SubType", NULL); - _plist_dict_copy_item(parameters, build_identity, "Cryptex1,ProductClass", NULL); - _plist_dict_copy_item(parameters, build_identity, "Cryptex1,UseProductClass", NULL); - _plist_dict_copy_item(parameters, build_identity, "Cryptex1,NonceDomain", NULL); - _plist_dict_copy_item(parameters, build_identity, "Cryptex1,Version", NULL); - _plist_dict_copy_item(parameters, build_identity, "Cryptex1,PreauthorizationVersion", NULL); - _plist_dict_copy_item(parameters, build_identity, "Cryptex1,FakeRoot", NULL); - _plist_dict_copy_item(parameters, build_identity, "Cryptex1,SystemOS", NULL); - _plist_dict_copy_item(parameters, build_identity, "Cryptex1,SystemVolume", NULL); - _plist_dict_copy_item(parameters, build_identity, "Cryptex1,SystemTrustCache", NULL); - _plist_dict_copy_item(parameters, build_identity, "Cryptex1,AppOS", NULL); - _plist_dict_copy_item(parameters, build_identity, "Cryptex1,AppVolume", NULL); - _plist_dict_copy_item(parameters, build_identity, "Cryptex1,AppTrustCache", NULL); - _plist_dict_copy_item(parameters, build_identity, "Cryptex1,MobileAssetBrainOS", NULL); - _plist_dict_copy_item(parameters, build_identity, "Cryptex1,MobileAssetBrainVolume", NULL); - _plist_dict_copy_item(parameters, build_identity, "Cryptex1,MobileAssetBrainTrustCache", NULL); - - if (include_manifest) { - /* add build identity manifest dictionary */ - node = plist_dict_get_item(build_identity, "Manifest"); - if (!node || plist_get_node_type(node) != PLIST_DICT) { - error("ERROR: Unable to find Manifest node\n"); - return -1; - } - plist_dict_set_item(parameters, "Manifest", plist_copy(node)); - } - - return 0; -} - -int tss_request_add_ap_img4_tags(plist_t request, plist_t parameters) -{ - if (!parameters) { - error("ERROR: Missing required AP parameters\n"); - return -1; - } - - _plist_dict_copy_string(request, parameters, "Ap,OSLongVersion", NULL); - - if (_plist_dict_copy_data(request, parameters, "ApNonce", NULL) < 0) { - error("ERROR: Unable to find required ApNonce in parameters\n"); - return -1; - } - - plist_dict_set_item(request, "@ApImg4Ticket", plist_new_bool(1)); - - if (!plist_dict_get_item(request, "ApSecurityMode")) { - /* copy from parameters if available */ - if (_plist_dict_copy_bool(request, parameters, "ApSecurityMode", NULL) < 0) { - error("ERROR: Unable to find required ApSecurityMode in parameters\n"); - return -1; - } - } - if (!plist_dict_get_item(request, "ApProductionMode")) { - /* ApProductionMode */ - if (_plist_dict_copy_bool(request, parameters, "ApProductionMode", NULL) < 0) { - error("ERROR: Unable to find required ApProductionMode in parameters\n"); - return -1; - } - } - - _plist_dict_copy_data(request, parameters, "SepNonce", "ApSepNonce"); - _plist_dict_copy_data(request, parameters, "PearlCertificationRootPub", NULL); - - return 0; -} - -int tss_request_add_ap_img3_tags(plist_t request, plist_t parameters) -{ - if (!parameters) { - error("ERROR: Missing required AP parameters\n"); - return -1; - } - - if (_plist_dict_copy_data(request, parameters, "ApNonce", NULL) < 0) { - error("ERROR: Unable to find required ApNonce in parameters\n"); - return -1; - } - - plist_dict_set_item(request, "@APTicket", plist_new_bool(1)); - - if (_plist_dict_copy_uint(request, parameters, "ApBoardID", NULL) < 0) { - error("ERROR: Unable to find required ApBoardID in request\n"); - return -1; - } - - if (_plist_dict_copy_uint(request, parameters, "ApChipID", NULL) < 0) { - error("ERROR: Unable to find required ApChipID in request\n"); - return -1; - } - - if (_plist_dict_copy_uint(request, parameters, "ApSecurityDomain", NULL) < 0) { - error("ERROR: Unable to find required ApSecurityDomain in request\n"); - return -1; - } - - if (_plist_dict_copy_bool(request, parameters, "ApProductionMode", NULL) < 0) { - error("ERROR: Unable to find required ApProductionMode in parameters\n"); - return -1; - } - - return 0; -} - -int tss_request_add_common_tags(plist_t request, plist_t parameters, plist_t overrides) -{ - _plist_dict_copy_uint(request, parameters, "ApECID", NULL); - _plist_dict_copy_data(request, parameters, "UniqueBuildID", NULL); - _plist_dict_copy_uint(request, parameters, "ApChipID", NULL); - _plist_dict_copy_uint(request, parameters, "ApBoardID", NULL); - _plist_dict_copy_uint(request, parameters, "ApSecurityDomain", NULL); - - /* apply overrides */ - if (overrides) { - plist_dict_merge(&request, overrides); - } - - return 0; -} - -static void tss_entry_apply_restore_request_rules(plist_t tss_entry, plist_t parameters, plist_t rules) -{ - if (!tss_entry || !rules) { - return; - } - if (plist_get_node_type(tss_entry) != PLIST_DICT) { - return; - } - if (plist_get_node_type(rules) != PLIST_ARRAY) { - return; - } - - uint32_t i; - for (i = 0; i < plist_array_get_size(rules); i++) { - plist_t rule = plist_array_get_item(rules, i); - plist_t conditions = plist_dict_get_item(rule, "Conditions"); - plist_dict_iter iter = NULL; - plist_dict_new_iter(conditions, &iter); - char* key = NULL; - plist_t value = NULL; - plist_t value2 = NULL; - int conditions_fulfilled = 1; - while (conditions_fulfilled) { - plist_dict_next_item(conditions, iter, &key, &value); - if (key == NULL) - break; - if (!strcmp(key, "ApRawProductionMode")) { - value2 = plist_dict_get_item(parameters, "ApProductionMode"); - } else if (!strcmp(key, "ApCurrentProductionMode")) { - value2 = plist_dict_get_item(parameters, "ApProductionMode"); - } else if (!strcmp(key, "ApRawSecurityMode")) { - value2 = plist_dict_get_item(parameters, "ApSecurityMode"); - } else if (!strcmp(key, "ApRequiresImage4")) { - value2 = plist_dict_get_item(parameters, "ApSupportsImg4"); - } else if (!strcmp(key, "ApDemotionPolicyOverride")) { - value2 = plist_dict_get_item(parameters, "DemotionPolicy"); - } else if (!strcmp(key, "ApInRomDFU")) { - value2 = plist_dict_get_item(parameters, "ApInRomDFU"); - } else { - error("WARNING: Unhandled condition '%s' while parsing RestoreRequestRules\n", key); - value2 = NULL; - } - if (value2) { - conditions_fulfilled = plist_compare_node_value(value, value2); - } else { - conditions_fulfilled = 0; - } - free(key); - } - free(iter); - iter = NULL; - - if (!conditions_fulfilled) { - continue; - } - - plist_t actions = plist_dict_get_item(rule, "Actions"); - plist_dict_new_iter(actions, &iter); - while (1) { - plist_dict_next_item(actions, iter, &key, &value); - if (key == NULL) - break; - uint8_t bv = 255; - plist_get_bool_val(value, &bv); - if (bv != 255) { - value2 = plist_dict_get_item(tss_entry, key); - if (value2) { - plist_dict_remove_item(tss_entry, key); - } - debug("DEBUG: Adding %s=%s to TSS entry\n", key, (bv) ? "true" : "false"); - plist_dict_set_item(tss_entry, key, plist_new_bool(bv)); - } - free(key); - } - } -} - -int tss_request_add_ap_recovery_tags(plist_t request, plist_t parameters, plist_t overrides) -{ - /* loop over components from build manifest */ - plist_t manifest_node = plist_dict_get_item(parameters, "Manifest"); - if (!manifest_node || plist_get_node_type(manifest_node) != PLIST_DICT) { - error("ERROR: Unable to find restore manifest\n"); - return -1; - } - - /* add components to request */ - char* key = NULL; - plist_t manifest_entry = NULL; - plist_dict_iter iter = NULL; - plist_dict_new_iter(manifest_node, &iter); - while (1) { - free(key); - key = NULL; - plist_dict_next_item(manifest_node, iter, &key, &manifest_entry); - if (key == NULL) - break; - if (!manifest_entry || plist_get_node_type(manifest_entry) != PLIST_DICT) { - error("ERROR: Unable to fetch BuildManifest entry\n"); - free(key); - return -1; - } - - /* do not populate BaseBandFirmware, only in basebaseband request */ - if ((strcmp(key, "BasebandFirmware") == 0)) { - continue; - } - - // Compared to ac2, not needed for RecoveryOSRootTicket - if ((strcmp(key, "SE,UpdatePayload") == 0)) { - continue; - } - if ((strcmp(key, "BaseSystem") == 0)) { - continue; - } - if ((strcmp(key, "ANS") == 0)) { - continue; - } - if ((strcmp(key, "Ap,AudioBootChime") == 0)) { - continue; - } - if ((strcmp(key, "Ap,CIO") == 0)) { - continue; - } - if ((strcmp(key, "Ap,RestoreCIO") == 0)) { - continue; - } - if ((strcmp(key, "Ap,RestoreTMU") == 0)) { - continue; - } - if ((strcmp(key, "Ap,TMU") == 0)) { - continue; - } - if ((strcmp(key, "Ap,rOSLogo1") == 0)) { - continue; - } - if ((strcmp(key, "Ap,rOSLogo2") == 0)) { - continue; - } - if ((strcmp(key, "AppleLogo") == 0)) { - continue; - } - if ((strcmp(key, "DCP") == 0)) { - continue; - } - if ((strcmp(key, "LLB") == 0)) { - continue; - } - if ((strcmp(key, "RecoveryMode") == 0)) { - continue; - } - if ((strcmp(key, "RestoreANS") == 0)) { - continue; - } - if ((strcmp(key, "RestoreDCP") == 0)) { - continue; - } - if ((strcmp(key, "RestoreDeviceTree") == 0)) { - continue; - } - if ((strcmp(key, "RestoreKernelCache") == 0)) { - continue; - } - if ((strcmp(key, "RestoreLogo") == 0)) { - continue; - } - if ((strcmp(key, "RestoreRamDisk") == 0)) { - continue; - } - if ((strcmp(key, "RestoreSEP") == 0)) { - continue; - } - if ((strcmp(key, "SEP") == 0)) { - continue; - } - if ((strcmp(key, "ftap") == 0)) { - continue; - } - if ((strcmp(key, "ftsp") == 0)) { - continue; - } - if ((strcmp(key, "iBEC") == 0)) { - continue; - } - if ((strcmp(key, "iBSS") == 0)) { - continue; - } - if ((strcmp(key, "rfta") == 0)) { - continue; - } - if ((strcmp(key, "rfts") == 0)) { - continue; - } - - /* FIXME: only used with diagnostics firmware */ - if (strcmp(key, "Diags") == 0) { - continue; - } - - plist_t info_dict = plist_dict_get_item(manifest_entry, "Info"); - if (!info_dict) { - continue; - } - - if (_plist_dict_get_bool(parameters, "_OnlyFWComponents")) { - if (!_plist_dict_get_bool(manifest_entry, "Trusted")) { - debug("DEBUG: %s: Skipping '%s' as it is not trusted\n", __func__, key); - continue; - } - - if (!_plist_dict_get_bool(info_dict, "IsFirmwarePayload") && !_plist_dict_get_bool(info_dict, "IsSecondaryFirmwarePayload") && !_plist_dict_get_bool(info_dict, "IsFUDFirmware")) { - debug("DEBUG: %s: Skipping '%s' as it is neither firmware nor secondary nor FUD firmware payload\n", __func__, key); - continue; - } - } - - /* copy this entry */ - plist_t tss_entry = plist_copy(manifest_entry); - - /* remove obsolete Info node */ - plist_dict_remove_item(tss_entry, "Info"); - - /* handle RestoreRequestRules */ - plist_t rules = plist_access_path(manifest_entry, 2, "Info", "RestoreRequestRules"); - if (rules) { - debug("DEBUG: Applying restore request rules for entry %s\n", key); - tss_entry_apply_restore_request_rules(tss_entry, parameters, rules); - } - - /* Make sure we have a Digest key for Trusted items even if empty */ - if (_plist_dict_get_bool(manifest_entry, "Trusted") && !plist_dict_get_item(manifest_entry, "Digest")) { - debug("DEBUG: No Digest data, using empty value for entry %s\n", key); - plist_dict_set_item(tss_entry, "Digest", plist_new_data(NULL, 0)); - } - - /* finally add entry to request */ - plist_dict_set_item(request, key, tss_entry); - } - free(key); - free(iter); - - /* apply overrides */ - if (overrides) { - plist_dict_merge(&request, overrides); - } - - return 0; -} - -int tss_request_add_ap_tags(plist_t request, plist_t parameters, plist_t overrides) -{ - /* loop over components from build manifest */ - plist_t manifest_node = plist_dict_get_item(parameters, "Manifest"); - if (!manifest_node || plist_get_node_type(manifest_node) != PLIST_DICT) { - error("ERROR: Unable to find restore manifest\n"); - return -1; - } - - /* add components to request */ - char* key = NULL; - plist_t manifest_entry = NULL; - plist_dict_iter iter = NULL; - plist_dict_new_iter(manifest_node, &iter); - while (1) { - free(key); - key = NULL; - plist_dict_next_item(manifest_node, iter, &key, &manifest_entry); - if (key == NULL) - break; - if (!manifest_entry || plist_get_node_type(manifest_entry) != PLIST_DICT) { - error("ERROR: Unable to fetch BuildManifest entry\n"); - free(key); - return -1; - } - - /* do not populate BaseBandFirmware, only in basebaseband request */ - if ((strcmp(key, "BasebandFirmware") == 0)) { - continue; - } - - // Compared to ac2, not needed - if ((strcmp(key, "SE,UpdatePayload") == 0)) { - continue; - } - - // Compared to ac2, not needed - if ((strcmp(key, "BaseSystem") == 0)) { - continue; - } - - /* FIXME: only used with diagnostics firmware */ - if (strcmp(key, "Diags") == 0) { - continue; - } - - plist_t info_dict = plist_dict_get_item(manifest_entry, "Info"); - if (!info_dict) { - continue; - } - - if (_plist_dict_get_bool(parameters, "ApSupportsImg4")) { - if (!plist_dict_get_item(info_dict, "RestoreRequestRules")) { - debug("DEBUG: %s: Skipping '%s' as it doesn't have RestoreRequestRules\n", __func__, key); - continue; - } - } - - if (_plist_dict_get_bool(parameters, "_OnlyFWComponents")) { - if (!_plist_dict_get_bool(manifest_entry, "Trusted")) { - debug("DEBUG: %s: Skipping '%s' as it is not trusted\n", __func__, key); - continue; - } - - if (!_plist_dict_get_bool(info_dict, "IsFirmwarePayload") && !_plist_dict_get_bool(info_dict, "IsSecondaryFirmwarePayload") && !_plist_dict_get_bool(info_dict, "IsFUDFirmware")) { - debug("DEBUG: %s: Skipping '%s' as it is neither firmware nor secondary nor FUD firmware payload\n", __func__, key); - continue; - } - } - - /* skip components with IsFTAB:true */ - if (_plist_dict_get_bool(info_dict, "IsFTAB")) { - debug("DEBUG: %s: Skipping FTAB component '%s'\n", __func__, key); - continue; - } - - /* copy this entry */ - plist_t tss_entry = plist_copy(manifest_entry); - - /* remove obsolete Info node */ - plist_dict_remove_item(tss_entry, "Info"); - - /* handle RestoreRequestRules */ - plist_t rules = plist_access_path(manifest_entry, 2, "Info", "RestoreRequestRules"); - if (rules) { - debug("DEBUG: Applying restore request rules for entry %s\n", key); - tss_entry_apply_restore_request_rules(tss_entry, parameters, rules); - } - - /* Make sure we have a Digest key for Trusted items even if empty */ - if (_plist_dict_get_bool(manifest_entry, "Trusted") && !plist_dict_get_item(manifest_entry, "Digest")) { - debug("DEBUG: No Digest data, using empty value for entry %s\n", key); - plist_dict_set_item(tss_entry, "Digest", plist_new_data(NULL, 0)); - } - - /* finally add entry to request */ - plist_dict_set_item(request, key, tss_entry); - } - free(key); - free(iter); - - /* apply overrides */ - if (overrides) { - plist_dict_merge(&request, overrides); - } - - return 0; -} - -int tss_request_add_baseband_tags(plist_t request, plist_t parameters, plist_t overrides) -{ - plist_t node = NULL; - - plist_dict_set_item(request, "@BBTicket", plist_new_bool(1)); - - _plist_dict_copy_uint(request, parameters, "BbChipID", NULL); - _plist_dict_copy_data(request, parameters, "BbProvisioningManifestKeyHash", NULL); - /* BbActivationManifestKeyHash - Used by Qualcomm MDM6610 */ - _plist_dict_copy_data(request, parameters, "BbActivationManifestKeyHash", NULL); - _plist_dict_copy_data(request, parameters, "BbCalibrationManifestKeyHash", NULL); - _plist_dict_copy_data(request, parameters, "BbFactoryActivationManifestKeyHash", NULL); - _plist_dict_copy_data(request, parameters, "BbFDRSecurityKeyHash", NULL); - /* BbSkeyId - Used by XMM 6180/GSM */ - _plist_dict_copy_data(request, parameters, "BbSkeyId", NULL); - _plist_dict_copy_data(request, parameters, "BbNonce", NULL); - _plist_dict_copy_uint(request, parameters, "BbGoldCertId", NULL); - - uint64_t bb_chip_id = _plist_dict_get_uint(request, "BbChipID"); - int32_t bb_cert_id = (int32_t)_plist_dict_get_uint(request, "BbGoldCertId"); - - if (_plist_dict_copy_data(request, parameters, "BbSNUM", NULL) < 0) { - error("ERROR: Unable to find required BbSNUM in parameters\n"); - return -1; - } - - /* BasebandFirmware */ - node = plist_access_path(parameters, 2, "Manifest", "BasebandFirmware"); - if (!node || plist_get_node_type(node) != PLIST_DICT) { - error("ERROR: Unable to get BasebandFirmware node\n"); - return -1; - } - plist_t bbfwdict = plist_copy(node); - node = NULL; - if (plist_dict_get_item(bbfwdict, "Info")) { - plist_dict_remove_item(bbfwdict, "Info"); - } - - if (bb_chip_id == 0x68) { - /* depending on the BasebandCertId remove certain nodes */ - if (bb_cert_id == 0x26F3FACC || bb_cert_id == 0x5CF2EC4E || bb_cert_id == 0x8399785A) { - plist_dict_remove_item(bbfwdict, "PSI2-PartialDigest"); - plist_dict_remove_item(bbfwdict, "RestorePSI2-PartialDigest"); - } else { - plist_dict_remove_item(bbfwdict, "PSI-PartialDigest"); - plist_dict_remove_item(bbfwdict, "RestorePSI-PartialDigest"); - } - } - - plist_dict_set_item(request, "BasebandFirmware", bbfwdict); - - /* apply overrides */ - if (overrides) { - plist_dict_merge(&request, overrides); - } - - return 0; -} - -int tss_request_add_se_tags(plist_t request, plist_t parameters, plist_t overrides) -{ - plist_t manifest_node = plist_dict_get_item(parameters, "Manifest"); - if (!manifest_node || plist_get_node_type(manifest_node) != PLIST_DICT) { - error("ERROR: %s: Unable to get restore manifest from parameters\n", __func__); - return -1; - } - - /* add tags indicating we want to get the SE,Ticket */ - plist_dict_set_item(request, "@BBTicket", plist_new_bool(1)); - plist_dict_set_item(request, "@SE,Ticket", plist_new_bool(1)); - - if (_plist_dict_copy_uint(request, parameters, "SE,ChipID", NULL) < 0) { - error("ERROR: %s: Unable to find required SE,ChipID in parameters\n", __func__); - return -1; - } - - if (_plist_dict_copy_data(request, parameters, "SE,ID", NULL) < 0) { - error("ERROR: %s: Unable to find required SE,ID in parameters\n", __func__); - return -1; - } - - if (_plist_dict_copy_data(request, parameters, "SE,Nonce", NULL) < 0) { - error("ERROR: %s: Unable to find required SE,Nonce in parameters\n", __func__); - return -1; - } - - if (_plist_dict_copy_data(request, parameters, "SE,RootKeyIdentifier", NULL) < 0) { - error("ERROR: %s: Unable to find required SE,RootKeyIdentifier in parameters\n", __func__); - return -1; - } - - /* 'IsDev' determines whether we have Production or Development */ - uint8_t is_dev = _plist_dict_get_bool(parameters, "SE,IsDev"); - - /* add SE,* components from build manifest to request */ - char* key = NULL; - plist_t manifest_entry = NULL; - plist_dict_iter iter = NULL; - plist_dict_new_iter(manifest_node, &iter); - while (1) { - free(key); - key = NULL; - plist_dict_next_item(manifest_node, iter, &key, &manifest_entry); - if (key == NULL) - break; - if (!manifest_entry || plist_get_node_type(manifest_entry) != PLIST_DICT) { - error("ERROR: Unable to fetch BuildManifest entry\n"); - free(key); - return -1; - } - - if (strncmp(key, "SE,", 3)) { - continue; - } - - /* copy this entry */ - plist_t tss_entry = plist_copy(manifest_entry); - - /* remove Info node */ - plist_dict_remove_item(tss_entry, "Info"); - - /* remove Development or Production key/hash node */ - if (is_dev) { - if (plist_dict_get_item(tss_entry, "ProductionCMAC")) - plist_dict_remove_item(tss_entry, "ProductionCMAC"); - if (plist_dict_get_item(tss_entry, "ProductionUpdatePayloadHash")) - plist_dict_remove_item(tss_entry, "ProductionUpdatePayloadHash"); - } else { - if (plist_dict_get_item(tss_entry, "DevelopmentCMAC")) - plist_dict_remove_item(tss_entry, "DevelopmentCMAC"); - if (plist_dict_get_item(tss_entry, "DevelopmentUpdatePayloadHash")) - plist_dict_remove_item(tss_entry, "DevelopmentUpdatePayloadHash"); - } - - /* add entry to request */ - plist_dict_set_item(request, key, tss_entry); - } - free(key); - free(iter); - - /* apply overrides */ - if (overrides) { - plist_dict_merge(&request, overrides); - } - - return 0; -} - -int tss_request_add_savage_tags(plist_t request, plist_t parameters, plist_t overrides, char **component_name) -{ - plist_t node = NULL; - - plist_t manifest_node = plist_dict_get_item(parameters, "Manifest"); - if (!manifest_node || plist_get_node_type(manifest_node) != PLIST_DICT) { - error("ERROR: %s: Unable to get restore manifest from parameters\n", __func__); - return -1; - } - - /* add tags indicating we want to get the Savage,Ticket */ - plist_dict_set_item(request, "@BBTicket", plist_new_bool(1)); - plist_dict_set_item(request, "@Savage,Ticket", plist_new_bool(1)); - - if (_plist_dict_copy_data(request, parameters, "Savage,UID", NULL) < 0) { - error("ERROR: %s: Unable to find required Savage,UID in parameters\n", __func__); - return -1; - } - - /* add SEP */ - node = plist_access_path(manifest_node, 2, "SEP", "Digest"); - if (!node) { - error("ERROR: Unable to get SEP digest from manifest\n"); - return -1; - } - plist_t dict = plist_new_dict(); - plist_dict_set_item(dict, "Digest", plist_copy(node)); - plist_dict_set_item(request, "SEP", dict); - - if (_plist_dict_copy_uint(request, parameters, "Savage,PatchEpoch", NULL) < 0) { - error("ERROR: %s: Unable to find required Savage,PatchEpoch in parameters\n", __func__); - return -1; - } - - if (_plist_dict_copy_uint(request, parameters, "Savage,ChipID", NULL) < 0) { - error("ERROR: %s: Unable to find required Savage,ChipID in parameters\n", __func__); - return -1; - } - - if (_plist_dict_copy_bool(request, parameters, "Savage,AllowOfflineBoot", NULL) < 0) { - error("ERROR: %s: Unable to find required Savage,AllowOfflineBoot in parameters\n", __func__); - return -1; - } - - if (_plist_dict_copy_bool(request, parameters, "Savage,ReadFWKey", NULL) < 0) { - error("ERROR: %s: Unable to find required Savage,ReadFWKey in parameters\n", __func__); - return -1; - } - - if (_plist_dict_copy_bool(request, parameters, "Savage,ProductionMode", NULL) < 0) { - error("ERROR: %s: Unable to find required Savage,ProductionMode in parameters\n", __func__); - return -1; - } - - const char *comp_name = NULL; - uint8_t isprod = _plist_dict_get_bool(request, "Savage,ProductionMode"); - - /* get the right component name */ - comp_name = (isprod) ? "Savage,B0-Prod-Patch" : "Savage,B0-Dev-Patch"; - node = plist_dict_get_item(parameters, "Savage,Revision"); - if (node && (plist_get_node_type(node) == PLIST_DATA)) { - unsigned char *savage_rev = NULL; - uint64_t savage_rev_len = 0; - plist_get_data_val(node, (char**)&savage_rev, &savage_rev_len); - if (savage_rev_len > 0) { - if (((savage_rev[0] | 0x10) & 0xF0) == 0x30) { - comp_name = (isprod) ? "Savage,B2-Prod-Patch" : "Savage,B2-Dev-Patch"; - } else if ((savage_rev[0] & 0xF0) == 0xA0) { - comp_name = (isprod) ? "Savage,BA-Prod-Patch" : "Savage,BA-Dev-Patch"; - } - } - free(savage_rev); - } - - /* add Savage,B?-*-Patch */ - node = plist_dict_get_item(manifest_node, comp_name); - if (!node) { - error("ERROR: Unable to get %s entry from manifest\n", comp_name); - return -1; - } - dict = plist_copy(node); - plist_dict_remove_item(dict, "Info"); - plist_dict_set_item(request, comp_name, dict); - - if (component_name) { - *component_name = strdup(comp_name); - } - - if (_plist_dict_copy_data(request, parameters, "Savage,Nonce", NULL) < 0) { - error("ERROR: %s: Unable to find required Savage,Nonce in parameters\n", __func__); - return -1; - } - - if (_plist_dict_copy_bool(request, parameters, "Savage,ReadECKey", NULL) < 0) { - error("ERROR: %s: Unable to find required Savage,ReadECKey in parameters\n", __func__); - return -1; - } - - /* apply overrides */ - if (overrides) { - plist_dict_merge(&request, overrides); - } - - return 0; -} - -int tss_request_add_yonkers_tags(plist_t request, plist_t parameters, plist_t overrides, char **component_name) -{ - plist_t node = NULL; - - plist_t manifest_node = plist_dict_get_item(parameters, "Manifest"); - if (!manifest_node || plist_get_node_type(manifest_node) != PLIST_DICT) { - error("ERROR: %s: Unable to get restore manifest from parameters\n", __func__); - return -1; - } - - /* add tags indicating we want to get the Savage,Ticket */ - plist_dict_set_item(request, "@BBTicket", plist_new_bool(1)); - plist_dict_set_item(request, "@Yonkers,Ticket", plist_new_bool(1)); - - /* add SEP */ - node = plist_access_path(manifest_node, 2, "SEP", "Digest"); - if (!node) { - error("ERROR: Unable to get SEP digest from manifest\n"); - return -1; - } - plist_t dict = plist_new_dict(); - plist_dict_set_item(dict, "Digest", plist_copy(node)); - plist_dict_set_item(request, "SEP", dict); - - { - static const char *keys[] = {"Yonkers,AllowOfflineBoot", "Yonkers,BoardID", "Yonkers,ChipID", "Yonkers,ECID", "Yonkers,Nonce", "Yonkers,PatchEpoch", "Yonkers,ProductionMode", "Yonkers,ReadECKey", "Yonkers,ReadFWKey", }; - int i; - for (i = 0; i < (int)(sizeof(keys) / sizeof(keys[0])); ++i) { - node = plist_dict_get_item(parameters, keys[i]); - if (!node) { - error("ERROR: %s: Unable to find required %s in parameters\n", __func__, keys[i]); - } - plist_dict_set_item(request, keys[i], plist_copy(node)); - node = NULL; - } - } - - char *comp_name = NULL; - plist_t comp_node = NULL; - uint8_t isprod = _plist_dict_get_bool(parameters, "Yonkers,ProductionMode"); - uint64_t fabrevision = _plist_dict_get_uint(parameters, "Yonkers,FabRevision"); - - plist_dict_iter iter = NULL; - plist_dict_new_iter(manifest_node, &iter); - while (iter) { - node = NULL; - comp_name = NULL; - plist_dict_next_item(manifest_node, iter, &comp_name, &node); - if (comp_name == NULL) { - node = NULL; - break; - } - if (strncmp(comp_name, "Yonkers,", 8) == 0) { - int target_node = 1; - plist_t sub_node; - if ((sub_node = plist_dict_get_item(node, "EPRO")) != NULL && plist_get_node_type(sub_node) == PLIST_BOOLEAN) { - uint8_t b = 0; - plist_get_bool_val(sub_node, &b); - target_node &= ((isprod) ? b : !b); - } - if ((sub_node = plist_dict_get_item(node, "FabRevision")) != NULL && plist_get_node_type(sub_node) == PLIST_UINT) { - uint64_t v = 0; - plist_get_uint_val(sub_node, &v); - target_node &= (v == fabrevision); - } - if (target_node) { - comp_node = node; - break; - } - } - free(comp_name); - } - free(iter); - - if (comp_name == NULL) { - error("ERROR: No Yonkers node for %s/%lu\n", (isprod) ? "Production" : "Development", (unsigned long)fabrevision); - return -1; - } - - /* add Yonkers,SysTopPatch* */ - if (comp_node != NULL) { - plist_t comp_dict = plist_copy(comp_node); - plist_dict_remove_item(comp_dict, "Info"); - plist_dict_set_item(request, comp_name, comp_dict); - } - - if (component_name) { - *component_name = comp_name; - } else { - free(comp_name); - } - - /* apply overrides */ - if (overrides) { - plist_dict_merge(&request, overrides); - } - - return 0; -} - -int tss_request_add_vinyl_tags(plist_t request, plist_t parameters, plist_t overrides) -{ - plist_t node = NULL; - - plist_t manifest_node = plist_dict_get_item(parameters, "Manifest"); - if (!manifest_node || plist_get_node_type(manifest_node) != PLIST_DICT) { - error("ERROR: %s: Unable to get restore manifest from parameters\n", __func__); - return -1; - } - - /* add tags indicating we want to get the eUICC,Ticket */ - plist_dict_set_item(request, "@BBTicket", plist_new_bool(1)); - plist_dict_set_item(request, "@eUICC,Ticket", plist_new_bool(1)); - - _plist_dict_copy_uint(request, parameters, "eUICC,ChipID", NULL); - _plist_dict_copy_data(request, parameters, "eUICC,EID", NULL); - _plist_dict_copy_data(request, parameters, "eUICC,RootKeyIdentifier", NULL); - - /* set Nonce for eUICC,Gold component */ - node = plist_dict_get_item(parameters, "EUICCGoldNonce"); - if (node) { - plist_t n = plist_dict_get_item(request, "eUICC,Gold"); - if (n) { - plist_dict_set_item(n, "Nonce", plist_copy(node)); - } - } - - /* set Nonce for eUICC,Main component */ - node = plist_dict_get_item(parameters, "EUICCMainNonce"); - if (node) { - plist_t n = plist_dict_get_item(request, "eUICC,Main"); - if (n) { - plist_dict_set_item(n, "Nonce", plist_copy(node)); - } - } - - /* apply overrides */ - if (overrides) { - plist_dict_merge(&request, overrides); - } - - return 0; -} - -int tss_request_add_rose_tags(plist_t request, plist_t parameters, plist_t overrides) -{ - plist_t node = NULL; - - plist_t manifest_node = plist_dict_get_item(parameters, "Manifest"); - if (!manifest_node || plist_get_node_type(manifest_node) != PLIST_DICT) { - error("ERROR: %s: Unable to get restore manifest from parameters\n", __func__); - return -1; - } - - /* add tags indicating we want to get the Rap,Ticket */ - plist_dict_set_item(request, "@BBTicket", plist_new_bool(1)); - plist_dict_set_item(request, "@Rap,Ticket", plist_new_bool(1)); - - _plist_dict_copy_uint(request, parameters, "Rap,BoardID", NULL); - _plist_dict_copy_uint(request, parameters, "Rap,ChipID", NULL); - _plist_dict_copy_uint(request, parameters, "Rap,ECID", NULL); - _plist_dict_copy_data(request, parameters, "Rap,Nonce", NULL); - _plist_dict_copy_bool(request, parameters, "Rap,ProductionMode", NULL); - _plist_dict_copy_uint(request, parameters, "Rap,SecurityDomain", NULL); - _plist_dict_copy_bool(request, parameters, "Rap,SecurityMode", NULL); - - char *comp_name = NULL; - plist_dict_iter iter = NULL; - plist_dict_new_iter(manifest_node, &iter); - while (iter) { - node = NULL; - comp_name = NULL; - plist_dict_next_item(manifest_node, iter, &comp_name, &node); - if (comp_name == NULL) { - node = NULL; - break; - } - if (strncmp(comp_name, "Rap,", 4) == 0) { - plist_t manifest_entry = plist_copy(node); - - /* handle RestoreRequestRules */ - plist_t rules = plist_access_path(manifest_entry, 2, "Info", "RestoreRequestRules"); - if (rules) { - debug("DEBUG: Applying restore request rules for entry %s\n", comp_name); - tss_entry_apply_restore_request_rules(manifest_entry, parameters, rules); - } - - /* Make sure we have a Digest key for Trusted items even if empty */ - if (_plist_dict_get_bool(manifest_entry, "Trusted") && !plist_dict_get_item(manifest_entry, "Digest")) { - debug("DEBUG: No Digest data, using empty value for entry %s\n", comp_name); - plist_dict_set_item(manifest_entry, "Digest", plist_new_data(NULL, 0)); - } - - plist_dict_remove_item(manifest_entry, "Info"); - - /* finally add entry to request */ - plist_dict_set_item(request, comp_name, manifest_entry); - } - free(comp_name); - } - free(iter); - - /* apply overrides */ - if (overrides) { - plist_dict_merge(&request, overrides); - } - - return 0; -} - -int tss_request_add_veridian_tags(plist_t request, plist_t parameters, plist_t overrides) -{ - plist_t node = NULL; - - plist_t manifest_node = plist_dict_get_item(parameters, "Manifest"); - if (!manifest_node || plist_get_node_type(manifest_node) != PLIST_DICT) { - error("ERROR: %s: Unable to get restore manifest from parameters\n", __func__); - return -1; - } - - /* add tags indicating we want to get the BMU,Ticket */ - plist_dict_set_item(request, "@BBTicket", plist_new_bool(1)); - plist_dict_set_item(request, "@BMU,Ticket", plist_new_bool(1)); - - _plist_dict_copy_uint(request, parameters, "BMU,BoardID", NULL); - _plist_dict_copy_uint(request, parameters, "BMU,ChipID", "ChipID"); - _plist_dict_copy_data(request, parameters, "BMU,Nonce", "Nonce"); - _plist_dict_copy_bool(request, parameters, "BMU,ProductionMode", "ProductionMode"); - _plist_dict_copy_uint(request, parameters, "BMU,UniqueID", "UniqueID"); - - char *comp_name = NULL; - plist_dict_iter iter = NULL; - plist_dict_new_iter(manifest_node, &iter); - while (iter) { - node = NULL; - comp_name = NULL; - plist_dict_next_item(manifest_node, iter, &comp_name, &node); - if (comp_name == NULL) { - node = NULL; - break; - } - if (strncmp(comp_name, "BMU,", 4) == 0) { - plist_t manifest_entry = plist_copy(node); - - /* handle RestoreRequestRules */ - plist_t rules = plist_access_path(manifest_entry, 2, "Info", "RestoreRequestRules"); - if (rules) { - debug("DEBUG: Applying restore request rules for entry %s\n", comp_name); - tss_entry_apply_restore_request_rules(manifest_entry, parameters, rules); - } - - /* Make sure we have a Digest key for Trusted items even if empty */ - if (_plist_dict_get_bool(manifest_entry, "Trusted") && !plist_dict_get_item(manifest_entry, "Digest")) { - debug("DEBUG: No Digest data, using empty value for entry %s\n", comp_name); - plist_dict_set_item(manifest_entry, "Digest", plist_new_data(NULL, 0)); - } - - plist_dict_remove_item(manifest_entry, "Info"); - - /* finally add entry to request */ - plist_dict_set_item(request, comp_name, manifest_entry); - } - free(comp_name); - } - free(iter); - - /* apply overrides */ - if (overrides) { - plist_dict_merge(&request, overrides); - } - - return 0; -} - -int tss_request_add_tcon_tags(plist_t request, plist_t parameters, plist_t overrides) -{ - plist_t node = NULL; - - plist_t manifest_node = plist_dict_get_item(parameters, "Manifest"); - if (!manifest_node || plist_get_node_type(manifest_node) != PLIST_DICT) { - error("ERROR: %s: Unable to get restore manifest from parameters\n", __func__); - return -1; - } - - /* add tags indicating we want to get the Baobab,Ticket */ - plist_dict_set_item(request, "@BBTicket", plist_new_bool(1)); - plist_dict_set_item(request, "@Baobab,Ticket", plist_new_bool(1)); - - _plist_dict_copy_uint(request, parameters, "Baobab,BoardID", NULL); - _plist_dict_copy_uint(request, parameters, "Baobab,ChipID", NULL); - _plist_dict_copy_data(request, parameters, "Baobab,ECID", NULL); - _plist_dict_copy_uint(request, parameters, "Baobab,Life", NULL); - _plist_dict_copy_uint(request, parameters, "Baobab,ManifestEpoch", NULL); - _plist_dict_copy_bool(request, parameters, "Baobab,ProductionMode", NULL); - _plist_dict_copy_uint(request, parameters, "Baobab,SecurityDomain", NULL); - _plist_dict_copy_data(request, parameters, "Baobab,UpdateNonce", NULL); - - uint8_t isprod = _plist_dict_get_bool(parameters, "Baobab,ProductionMode"); - - char *comp_name = NULL; - plist_dict_iter iter = NULL; - plist_dict_new_iter(manifest_node, &iter); - while (iter) { - node = NULL; - comp_name = NULL; - plist_dict_next_item(manifest_node, iter, &comp_name, &node); - if (comp_name == NULL) { - node = NULL; - break; - } - if (strncmp(comp_name, "Baobab,", 7) == 0) { - plist_t manifest_entry = plist_copy(node); - - plist_dict_remove_item(manifest_entry, "Info"); - plist_dict_set_item(manifest_entry, "EPRO", plist_new_bool(isprod)); - - /* finally add entry to request */ - plist_dict_set_item(request, comp_name, manifest_entry); - } - free(comp_name); - } - free(iter); - - /* apply overrides */ - if (overrides) { - plist_dict_merge(&request, overrides); - } - return 0; -} - -int tss_request_add_timer_tags(plist_t request, plist_t parameters, plist_t overrides) -{ - plist_t node = NULL; - uint32_t tag = 0; - - plist_t manifest_node = plist_dict_get_item(parameters, "Manifest"); - if (!manifest_node || plist_get_node_type(manifest_node) != PLIST_DICT) { - error("ERROR: %s: Unable to get restore manifest from parameters\n", __func__); - return -1; - } - - /* add tags indicating we want to get the Timer ticket */ - plist_dict_set_item(request, "@BBTicket", plist_new_bool(1)); - - node = plist_dict_get_item(parameters, "TicketName"); - if (!node) { - error("ERROR: %s: Missing TicketName\n", __func__); - return -1; - } - char key[64]; - sprintf(key, "@%s", plist_get_string_ptr(node, NULL)); - - plist_dict_set_item(request, key, plist_new_bool(1)); - - tag = (uint32_t)_plist_dict_get_uint(parameters, "TagNumber"); - - sprintf(key, "Timer,BoardID,%u", tag); - _plist_dict_copy_uint(request, parameters, key, NULL); - - sprintf(key, "Timer,ChipID,%u", tag); - _plist_dict_copy_uint(request, parameters, key, NULL); - - sprintf(key, "Timer,SecurityDomain,%u", tag); - _plist_dict_copy_uint(request, parameters, key, NULL); - - sprintf(key, "Timer,SecurityMode,%u", tag); - _plist_dict_copy_bool(request, parameters, key, NULL); - - sprintf(key, "Timer,ProductionMode,%u", tag); - _plist_dict_copy_bool(request, parameters, key, NULL); - - sprintf(key, "Timer,ECID,%u", tag); - _plist_dict_copy_uint(request, parameters, key, NULL); - - sprintf(key, "Timer,Nonce,%u", tag); - _plist_dict_copy_data(request, parameters, key, NULL); - - char *comp_name = NULL; - plist_dict_iter iter = NULL; - plist_dict_new_iter(manifest_node, &iter); - while (iter) { - node = NULL; - comp_name = NULL; - plist_dict_next_item(manifest_node, iter, &comp_name, &node); - if (comp_name == NULL) { - node = NULL; - break; - } - if (!strncmp(comp_name, "Timer,", 6)) { - plist_t manifest_entry = plist_copy(node); - - /* handle RestoreRequestRules */ - plist_t rules = plist_access_path(manifest_entry, 2, "Info", "RestoreRequestRules"); - if (rules) { - debug("DEBUG: Applying restore request rules for entry %s\n", comp_name); - tss_entry_apply_restore_request_rules(manifest_entry, parameters, rules); - } - - /* Make sure we have a Digest key for Trusted items even if empty */ - if (_plist_dict_get_bool(manifest_entry, "Trusted") && !plist_dict_get_item(manifest_entry, "Digest")) { - debug("DEBUG: No Digest data, using empty value for entry %s\n", comp_name); - plist_dict_set_item(manifest_entry, "Digest", plist_new_data(NULL, 0)); - } - - plist_dict_remove_item(manifest_entry, "Info"); - - /* finally add entry to request */ - plist_dict_set_item(request, comp_name, manifest_entry); - } - free(comp_name); - } - free(iter); - - /* apply overrides */ - if (overrides) { - plist_dict_merge(&request, overrides); - } - - return 0; -} - -static size_t tss_write_callback(char* data, size_t size, size_t nmemb, tss_response* response) -{ - size_t total = size * nmemb; - if (total != 0) { - response->content = realloc(response->content, response->length + total + 1); - memcpy(response->content + response->length, data, total); - response->content[response->length + total] = '\0'; - response->length += total; - } - - return total; -} - -plist_t tss_request_send(plist_t tss_request, const char* server_url_string) -{ - if (idevicerestore_debug) { - debug_plist(tss_request); - } - - char* request = NULL; - int status_code = -1; - int retry = 0; - int max_retries = 15; - unsigned int size = 0; - char curl_error_message[CURL_ERROR_SIZE]; - - const char* urls[6] = { - "https://gs.apple.com/TSS/controller?action=2", - "https://17.171.36.30/TSS/controller?action=2", - "https://17.151.36.30/TSS/controller?action=2", - "http://gs.apple.com/TSS/controller?action=2", - "http://17.171.36.30/TSS/controller?action=2", - "http://17.151.36.30/TSS/controller?action=2" - }; - - plist_to_xml(tss_request, &request, &size); - - tss_response* response = NULL; - memset(curl_error_message, '\0', CURL_ERROR_SIZE); - - while (retry++ < max_retries) { - response = NULL; - CURL* handle = curl_easy_init(); - if (handle == NULL) { - break; - } - struct curl_slist* header = NULL; - header = curl_slist_append(header, "Cache-Control: no-cache"); - header = curl_slist_append(header, "Content-type: text/xml; charset=\"utf-8\""); - header = curl_slist_append(header, "Expect:"); - - response = malloc(sizeof(tss_response)); - if (response == NULL) { - fprintf(stderr, "Unable to allocate sufficient memory\n"); - return NULL; - } - - response->length = 0; - response->content = malloc(1); - response->content[0] = '\0'; - - /* disable SSL verification to allow download from untrusted https locations */ - curl_easy_setopt(handle, CURLOPT_SSL_VERIFYPEER, 0); - - curl_easy_setopt(handle, CURLOPT_ERRORBUFFER, curl_error_message); - curl_easy_setopt(handle, CURLOPT_WRITEFUNCTION, (curl_write_callback)&tss_write_callback); - curl_easy_setopt(handle, CURLOPT_WRITEDATA, response); - curl_easy_setopt(handle, CURLOPT_HTTPHEADER, header); - curl_easy_setopt(handle, CURLOPT_POSTFIELDS, request); - curl_easy_setopt(handle, CURLOPT_USERAGENT, USER_AGENT_STRING); - curl_easy_setopt(handle, CURLOPT_POSTFIELDSIZE, strlen(request)); - if (server_url_string) { - curl_easy_setopt(handle, CURLOPT_URL, server_url_string); - } else { - int url_index = (retry - 1) % 6; - curl_easy_setopt(handle, CURLOPT_URL, urls[url_index]); - info("Request URL set to %s\n", urls[url_index]); - } - - info("Sending TSS request attempt %d... ", retry); - - curl_easy_perform(handle); - curl_slist_free_all(header); - curl_easy_cleanup(handle); - - if (strstr(response->content, "MESSAGE=SUCCESS")) { - status_code = 0; - info("response successfully received\n"); - break; - } - - if (response->length > 0) { - error("TSS server returned: %s\n", response->content); - } - - char* status = strstr(response->content, "STATUS="); - if (status) { - sscanf(status+7, "%d&%*s", &status_code); - } - if (status_code == -1) { - error("%s\n", curl_error_message); - // no status code in response. retry - free(response->content); - free(response); - response = NULL; - sleep(2); - continue; - } else if (status_code == 8) { - // server error (invalid bb request?) - break; - } else if (status_code == 49) { - // server error (invalid bb data, e.g. BbSNUM?) - break; - } else if (status_code == 69 || status_code == 94) { - // This device isn't eligible for the requested build. - break; - } else if (status_code == 100) { - // server error, most likely the request was malformed - break; - } else if (status_code == 126) { - // An internal error occured, most likely the request was malformed - break; - } else { - error("ERROR: tss_send_request: Unhandled status code %d\n", status_code); - } - } - - if (status_code != 0) { - if (response && strstr(response->content, "MESSAGE=") != NULL) { - char* message = strstr(response->content, "MESSAGE=") + strlen("MESSAGE="); - error("ERROR: TSS request failed (status=%d, message=%s)\n", status_code, message); - } else { - error("ERROR: TSS request failed: %s (status=%d)\n", curl_error_message, status_code); - } - free(request); - if (response) free(response->content); - if (response) free(response); - return NULL; - } - - char* tss_data = strstr(response->content, "content); - free(response); - return NULL; - } - - uint32_t tss_size = 0; - plist_t tss_response = NULL; - tss_size = response->length - (tss_data - response->content); - plist_from_xml(tss_data, tss_size, &tss_response); - free(response->content); - free(response); - - if (idevicerestore_debug) { - debug_plist(tss_response); - } - - free(request); - - return tss_response; -} - -static int tss_response_get_data_by_key(plist_t response, const char* name, unsigned char** buffer, unsigned int* length) -{ - plist_t node = plist_dict_get_item(response, name); - if (!node || plist_get_node_type(node) != PLIST_DATA) { - debug("DEBUG: %s: No entry '%s' in TSS response\n", __func__, name); - return -1; - } - - char *data = NULL; - uint64_t len = 0; - plist_get_data_val(node, &data, &len); - if (data) { - *length = (unsigned int)len; - *buffer = (unsigned char*)data; - return 0; - } else { - error("ERROR: Unable to get %s data from TSS response\n", name); - return -1; - } -} - -int tss_response_get_ap_img4_ticket(plist_t response, unsigned char** ticket, unsigned int* length) -{ - return tss_response_get_data_by_key(response, "ApImg4Ticket", ticket, length); -} - -int tss_response_get_ap_ticket(plist_t response, unsigned char** ticket, unsigned int* length) -{ - return tss_response_get_data_by_key(response, "APTicket", ticket, length); -} - -int tss_response_get_baseband_ticket(plist_t response, unsigned char** ticket, unsigned int* length) -{ - return tss_response_get_data_by_key(response, "BBTicket", ticket, length); -} - -int tss_response_get_path_by_entry(plist_t response, const char* entry, char** path) -{ - char* path_string = NULL; - plist_t path_node = NULL; - plist_t entry_node = NULL; - - *path = NULL; - - entry_node = plist_dict_get_item(response, entry); - if (!entry_node || plist_get_node_type(entry_node) != PLIST_DICT) { - debug("DEBUG: %s: No entry '%s' in TSS response\n", __func__, entry); - return -1; - } - - path_node = plist_dict_get_item(entry_node, "Path"); - if (!path_node || plist_get_node_type(path_node) != PLIST_STRING) { - debug("NOTE: Unable to find %s path in TSS entry\n", entry); - return -1; - } - plist_get_string_val(path_node, &path_string); - - *path = path_string; - return 0; -} - -int tss_response_get_blob_by_path(plist_t tss, const char* path, unsigned char** blob) -{ - uint32_t i = 0; - uint32_t tss_size = 0; - uint64_t blob_size = 0; - char* entry_key = NULL; - char* blob_data = NULL; - char* entry_path = NULL; - plist_t tss_entry = NULL; - plist_t blob_node = NULL; - plist_t path_node = NULL; - plist_dict_iter iter = NULL; - - *blob = NULL; - - plist_dict_new_iter(tss, &iter); - tss_size = plist_dict_get_size(tss); - for (i = 0; i < tss_size; i++) { - plist_dict_next_item(tss, iter, &entry_key, &tss_entry); - if (entry_key == NULL) - break; - - if (!tss_entry || plist_get_node_type(tss_entry) != PLIST_DICT) { - continue; - } - - path_node = plist_dict_get_item(tss_entry, "Path"); - if (!path_node || plist_get_node_type(path_node) != PLIST_STRING) { - error("ERROR: Unable to find TSS path node in entry %s\n", entry_key); - free(iter); - return -1; - } - - plist_get_string_val(path_node, &entry_path); - if (strcmp(path, entry_path) == 0) { - blob_node = plist_dict_get_item(tss_entry, "Blob"); - if (!blob_node || plist_get_node_type(blob_node) != PLIST_DATA) { - error("ERROR: Unable to find TSS blob node in entry %s\n", entry_key); - free(iter); - return -1; - } - plist_get_data_val(blob_node, &blob_data, &blob_size); - break; - } - - free(entry_key); - } - free(iter); - - if (blob_data == NULL || blob_size <= 0) { - return -1; - } - - *blob = (unsigned char*)blob_data; - return 0; -} - -int tss_response_get_blob_by_entry(plist_t response, const char* entry, unsigned char** blob) -{ - uint64_t blob_size = 0; - char* blob_data = NULL; - plist_t blob_node = NULL; - plist_t tss_entry = NULL; - - *blob = NULL; - - tss_entry = plist_dict_get_item(response, entry); - if (!tss_entry || plist_get_node_type(tss_entry) != PLIST_DICT) { - debug("DEBUG: %s: No entry '%s' in TSS response\n", __func__, entry); - return -1; - } - - blob_node = plist_dict_get_item(tss_entry, "Blob"); - if (!blob_node || plist_get_node_type(blob_node) != PLIST_DATA) { - error("ERROR: Unable to find blob in %s entry\n", entry); - return -1; - } - plist_get_data_val(blob_node, &blob_data, &blob_size); - - *blob = (unsigned char*)blob_data; - return 0; -} diff --git a/src/tss.h b/src/tss.h deleted file mode 100644 index 719be818..00000000 --- a/src/tss.h +++ /dev/null @@ -1,75 +0,0 @@ -/* - * tss.h - * Definitions for communicating with Apple's TSS server. - * - * Copyright (c) 2013 Martin Szulecki. All Rights Reserved. - * Copyright (c) 2012 Nikias Bassen. All Rights Reserved. - * Copyright (c) 2010 Joshua Hill. All Rights Reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - */ - -#ifndef IDEVICERESTORE_TSS_H -#define IDEVICERESTORE_TSS_H - -#ifdef __cplusplus -extern "C" { -#endif - -#include -#include - -/* parameters */ -int tss_parameters_add_from_manifest(plist_t parameters, plist_t build_identity, bool include_manifest); - -/* request */ -plist_t tss_request_new(plist_t overrides); - -int tss_request_add_local_policy_tags(plist_t request, plist_t parameters); -int tss_request_add_common_tags(plist_t request, plist_t parameters, plist_t overrides); -int tss_request_add_ap_tags(plist_t request, plist_t parameters, plist_t overrides); -int tss_request_add_ap_recovery_tags(plist_t request, plist_t parameters, plist_t overrides); -int tss_request_add_baseband_tags(plist_t request, plist_t parameters, plist_t overrides); -int tss_request_add_se_tags(plist_t request, plist_t parameters, plist_t overrides); -int tss_request_add_savage_tags(plist_t request, plist_t parameters, plist_t overrides, char **component_name); -int tss_request_add_yonkers_tags(plist_t request, plist_t parameters, plist_t overrides, char **component_name); -int tss_request_add_vinyl_tags(plist_t request, plist_t parameters, plist_t overrides); -int tss_request_add_rose_tags(plist_t request, plist_t parameters, plist_t overrides); -int tss_request_add_veridian_tags(plist_t request, plist_t parameters, plist_t overrides); -int tss_request_add_tcon_tags(plist_t request, plist_t parameters, plist_t overrides); -int tss_request_add_timer_tags(plist_t request, plist_t parameters, plist_t overrides); - -int tss_request_add_ap_img4_tags(plist_t request, plist_t parameters); -int tss_request_add_ap_img3_tags(plist_t request, plist_t parameters); - -/* i/o */ -plist_t tss_request_send(plist_t request, const char* server_url_string); - -/* response */ -int tss_response_get_ap_img4_ticket(plist_t response, unsigned char** ticket, unsigned int* length); -int tss_response_get_ap_ticket(plist_t response, unsigned char** ticket, unsigned int* length); -int tss_response_get_baseband_ticket(plist_t response, unsigned char** ticket, unsigned int* length); -int tss_response_get_path_by_entry(plist_t response, const char* entry, char** path); -int tss_response_get_blob_by_path(plist_t response, const char* path, unsigned char** blob); -int tss_response_get_blob_by_entry(plist_t response, const char* entry, unsigned char** blob); - -/* helpers */ -char* ecid_to_string(uint64_t ecid); - -#ifdef __cplusplus -} -#endif - -#endif