• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright 2023 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 //! Handle parsing of APK manifest files.
18 //! The manifest file is written as XML text, but is stored in the APK
19 //! as Android binary compressed XML. This library is a wrapper around
20 //! a thin C++ wrapper around libandroidfw, which contains the same
21 //! parsing code as used by package manager and aapt2 (amongst other
22 //! things).
23 
24 use anyhow::{bail, Context, Result};
25 use apkmanifest_bindgen::{
26     extractManifestInfo, freeManifestInfo, getPackageName, getRollbackIndex, getVersionCode,
27     hasRelaxedRollbackProtectionPermission,
28 };
29 use std::ffi::CStr;
30 use std::fs::File;
31 use std::path::Path;
32 
33 /// Information extracted from the Android manifest inside an APK.
34 #[derive(Debug, Default, Eq, PartialEq)]
35 pub struct ApkManifestInfo {
36     /// The package name of the app.
37     pub package: String,
38     /// The version code of the app.
39     pub version_code: u64,
40     /// Rollback index of the app used in the sealing dice policy.
41     /// This is only set if the apk manifest has USE_RELAXED_MICRODROID_ROLLBACK_PROTECTION
42     /// permission.
43     pub rollback_index: Option<u32>,
44     /// Whether manifest has USE_RELAXED_MICRODROID_ROLLBACK_PROTECTION permission.
45     pub has_relaxed_rollback_protection_permission: bool,
46 }
47 
48 const ANDROID_MANIFEST: &str = "AndroidManifest.xml";
49 
50 /// Find the manifest inside the given APK and return information from it.
get_manifest_info<P: AsRef<Path>>(apk_path: P) -> Result<ApkManifestInfo>51 pub fn get_manifest_info<P: AsRef<Path>>(apk_path: P) -> Result<ApkManifestInfo> {
52     let apk = File::open(apk_path.as_ref())?;
53     let manifest = apkzip::read_file(apk, ANDROID_MANIFEST)?;
54 
55     // Safety: The function only reads the memory range we specify and does not hold
56     // any reference to it.
57     let native_info = unsafe { extractManifestInfo(manifest.as_ptr() as _, manifest.len()) };
58     if native_info.is_null() {
59         bail!("Failed to parse manifest")
60     };
61 
62     scopeguard::defer! {
63         // Safety: The value we pass is the result of calling extractManifestInfo as required.
64         // We must call this exactly once, after we have finished using it, which the scopeguard
65         // ensures.
66         unsafe { freeManifestInfo(native_info); }
67     }
68 
69     // Safety: It is always safe to call this with a valid native_info, which we have,
70     // and it always returns a valid nul-terminated C string with the same lifetime as native_info.
71     // We immediately make a copy.
72     let package = unsafe { CStr::from_ptr(getPackageName(native_info)) };
73     let package = package.to_str().context("Invalid package name")?.to_string();
74 
75     // Safety: It is always safe to call this with a valid native_info, which we have.
76     let version_code = unsafe { getVersionCode(native_info) };
77 
78     // Safety: It is always safe to call this with a valid native_info, which we have.
79     let rollback_index = unsafe {
80         let rollback_index = getRollbackIndex(native_info);
81         if rollback_index.is_null() {
82             None
83         } else {
84             Some(*rollback_index)
85         }
86     };
87 
88     // Safety: It is always safe to call this with a valid native_info, which we have.
89     let has_relaxed_rollback_protection_permission =
90         unsafe { hasRelaxedRollbackProtectionPermission(native_info) };
91 
92     Ok(ApkManifestInfo {
93         package,
94         version_code,
95         rollback_index,
96         has_relaxed_rollback_protection_permission,
97     })
98 }
99