blob: 0a158764f7164c2c2eaeb14a8e79eb2bb117b764 [file] [log] [blame]
/*
* Copyright (C) 2021 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
//! compsvc is a service to run compilation tasks in a PVM upon request. It is able to set up
//! file descriptors backed by authfs (via authfs_service) and pass the file descriptors to the
//! actual compiler.
use anyhow::Result;
use binder_common::new_binder_exception;
use log::warn;
use std::default::Default;
use std::env;
use std::path::PathBuf;
use std::sync::{Arc, RwLock};
use crate::compilation::{compile_cmd, odrefresh, CompilerOutput};
use crate::compos_key_service::CompOsKeyService;
use crate::fsverity;
use authfs_aidl_interface::aidl::com::android::virt::fs::IAuthFsService::IAuthFsService;
use compos_aidl_interface::aidl::com::android::compos::{
CompOsKeyData::CompOsKeyData,
CompilationResult::CompilationResult,
FdAnnotation::FdAnnotation,
ICompOsService::{BnCompOsService, ICompOsService},
};
use compos_aidl_interface::binder::{
BinderFeatures, ExceptionCode, Interface, Result as BinderResult, Strong,
};
const AUTHFS_SERVICE_NAME: &str = "authfs_service";
const DEX2OAT_PATH: &str = "/apex/com.android.art/bin/dex2oat64";
const ODREFRESH_PATH: &str = "/apex/com.android.art/bin/odrefresh";
/// Constructs a binder object that implements ICompOsService.
pub fn new_binder() -> Result<Strong<dyn ICompOsService>> {
let service = CompOsService {
dex2oat_path: PathBuf::from(DEX2OAT_PATH),
odrefresh_path: PathBuf::from(ODREFRESH_PATH),
key_service: CompOsKeyService::new()?,
key_blob: Arc::new(RwLock::new(Vec::new())),
};
Ok(BnCompOsService::new_binder(service, BinderFeatures::default()))
}
struct CompOsService {
dex2oat_path: PathBuf,
odrefresh_path: PathBuf,
key_service: CompOsKeyService,
key_blob: Arc<RwLock<Vec<u8>>>,
}
impl CompOsService {
fn generate_raw_fsverity_signature(
&self,
key_blob: &[u8],
fsverity_digest: &fsverity::Sha256Digest,
) -> Vec<u8> {
let formatted_digest = fsverity::to_formatted_digest(fsverity_digest);
self.key_service.do_sign(key_blob, &formatted_digest[..]).unwrap_or_else(|e| {
warn!("Failed to sign the fsverity digest, returning empty signature. Error: {}", e);
Vec::new()
})
}
}
impl Interface for CompOsService {}
impl ICompOsService for CompOsService {
fn initializeSigningKey(&self, key_blob: &[u8]) -> BinderResult<()> {
let mut w = self.key_blob.write().unwrap();
if w.is_empty() {
*w = Vec::from(key_blob);
Ok(())
} else {
Err(new_binder_exception(ExceptionCode::ILLEGAL_STATE, "Cannot re-initialize the key"))
}
}
fn initializeClasspaths(
&self,
boot_classpath: &str,
dex2oat_boot_classpath: &str,
system_server_classpath: &str,
) -> BinderResult<()> {
// TODO(198211396): Implement correctly.
env::set_var("BOOTCLASSPATH", boot_classpath);
env::set_var("DEX2OATBOOTCLASSPATH", dex2oat_boot_classpath);
env::set_var("SYSTEMSERVERCLASSPATH", system_server_classpath);
Ok(())
}
fn odrefresh(
&self,
system_dir_fd: i32,
output_dir_fd: i32,
zygote_arch: &str,
) -> BinderResult<CompilationResult> {
if system_dir_fd < 0 || output_dir_fd < 0 {
return Err(new_binder_exception(
ExceptionCode::ILLEGAL_ARGUMENT,
"The remote FDs are expected to be non-negative",
));
}
if zygote_arch != "zygote64" && zygote_arch != "zygote64_32" {
return Err(new_binder_exception(
ExceptionCode::ILLEGAL_ARGUMENT,
"Invalid zygote arch",
));
}
let authfs_service = get_authfs_service()?;
let output = odrefresh(
&self.odrefresh_path,
system_dir_fd,
output_dir_fd,
zygote_arch,
authfs_service,
)
.map_err(|e| {
warn!("odrefresh failed: {}", e);
new_binder_exception(
ExceptionCode::SERVICE_SPECIFIC,
format!("odrefresh failed: {}", e),
)
})?;
match output {
CompilerOutput::ExitCode(exit_code) => {
Ok(CompilationResult { exitCode: exit_code, ..Default::default() })
}
_ => Err(new_binder_exception(ExceptionCode::SERVICE_SPECIFIC, "odrefresh failed")),
}
}
fn compile_cmd(
&self,
args: &[String],
fd_annotation: &FdAnnotation,
) -> BinderResult<CompilationResult> {
let authfs_service = get_authfs_service()?;
let output =
compile_cmd(&self.dex2oat_path, args, authfs_service, fd_annotation).map_err(|e| {
new_binder_exception(
ExceptionCode::SERVICE_SPECIFIC,
format!("Compilation failed: {}", e),
)
})?;
match output {
CompilerOutput::Digests { oat, vdex, image } => {
let key = &*self.key_blob.read().unwrap();
if key.is_empty() {
Err(new_binder_exception(
ExceptionCode::ILLEGAL_STATE,
"Key is not initialized",
))
} else {
let oat_signature = self.generate_raw_fsverity_signature(key, &oat);
let vdex_signature = self.generate_raw_fsverity_signature(key, &vdex);
let image_signature = self.generate_raw_fsverity_signature(key, &image);
Ok(CompilationResult {
exitCode: 0,
oatSignature: oat_signature,
vdexSignature: vdex_signature,
imageSignature: image_signature,
})
}
}
CompilerOutput::ExitCode(exit_code) => {
Ok(CompilationResult { exitCode: exit_code, ..Default::default() })
}
}
}
fn compile(&self, _marshaled: &[u8], _fd_annotation: &FdAnnotation) -> BinderResult<i8> {
Err(new_binder_exception(ExceptionCode::UNSUPPORTED_OPERATION, "Not yet implemented"))
}
fn generateSigningKey(&self) -> BinderResult<CompOsKeyData> {
self.key_service
.do_generate()
.map_err(|e| new_binder_exception(ExceptionCode::ILLEGAL_STATE, e.to_string()))
}
fn verifySigningKey(&self, key_blob: &[u8], public_key: &[u8]) -> BinderResult<bool> {
Ok(if let Err(e) = self.key_service.do_verify(key_blob, public_key) {
warn!("Signing key verification failed: {}", e.to_string());
false
} else {
true
})
}
fn sign(&self, data: &[u8]) -> BinderResult<Vec<u8>> {
let key = &*self.key_blob.read().unwrap();
if key.is_empty() {
Err(new_binder_exception(ExceptionCode::ILLEGAL_STATE, "Key is not initialized"))
} else {
self.key_service
.do_sign(key, data)
.map_err(|e| new_binder_exception(ExceptionCode::ILLEGAL_STATE, e.to_string()))
}
}
}
fn get_authfs_service() -> BinderResult<Strong<dyn IAuthFsService>> {
Ok(authfs_aidl_interface::binder::get_interface(AUTHFS_SERVICE_NAME)?)
}