blob: 20a1b89ca7e6bb00eac898300e89a229665f4c72 [file] [log] [blame]
// Copyright 2022, 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.
//! Implementation of the AIDL interface `IVmPayloadService`.
use android_system_virtualization_payload::aidl::android::system::virtualization::payload::IVmPayloadService::{
BnVmPayloadService, IVmPayloadService, VM_PAYLOAD_SERVICE_SOCKET_NAME, AttestationResult::AttestationResult,
STATUS_FAILED_TO_PREPARE_CSR_AND_KEY
};
use android_system_virtualmachineservice::aidl::android::system::virtualmachineservice::IVirtualMachineService::IVirtualMachineService;
use anyhow::{anyhow, Context, Result};
use avflog::LogResult;
use binder::{Interface, BinderFeatures, ExceptionCode, Strong, IntoBinderResult, Status};
use client_vm_csr::{generate_attestation_key_and_csr, ClientVmAttestationData};
use log::info;
use rpcbinder::RpcServer;
use crate::vm_secret::VmSecret;
use std::os::unix::io::OwnedFd;
/// Implementation of `IVmPayloadService`.
struct VmPayloadService {
allow_restricted_apis: bool,
virtual_machine_service: Strong<dyn IVirtualMachineService>,
secret: VmSecret,
}
impl IVmPayloadService for VmPayloadService {
fn notifyPayloadReady(&self) -> binder::Result<()> {
self.virtual_machine_service.notifyPayloadReady()
}
fn getVmInstanceSecret(&self, identifier: &[u8], size: i32) -> binder::Result<Vec<u8>> {
if !(0..=32).contains(&size) {
return Err(anyhow!("size {size} not in range (0..=32)"))
.or_binder_exception(ExceptionCode::ILLEGAL_ARGUMENT);
}
let mut instance_secret = vec![0; size.try_into().unwrap()];
self.secret
.derive_payload_sealing_key(identifier, &mut instance_secret)
.context("Failed to derive VM instance secret")
.with_log()
.or_service_specific_exception(-1)?;
Ok(instance_secret)
}
fn getDiceAttestationChain(&self) -> binder::Result<Vec<u8>> {
self.check_restricted_apis_allowed()?;
if let Some(bcc) = self.secret.dice_artifacts().bcc() {
Ok(bcc.to_vec())
} else {
Err(anyhow!("bcc is none")).or_binder_exception(ExceptionCode::ILLEGAL_STATE)
}
}
fn getDiceAttestationCdi(&self) -> binder::Result<Vec<u8>> {
self.check_restricted_apis_allowed()?;
Ok(self.secret.dice_artifacts().cdi_attest().to_vec())
}
fn requestAttestation(&self, challenge: &[u8]) -> binder::Result<AttestationResult> {
self.check_restricted_apis_allowed()?;
let ClientVmAttestationData { private_key, csr } =
generate_attestation_key_and_csr(challenge, self.secret.dice_artifacts())
.map_err(|e| {
Status::new_service_specific_error_str(
STATUS_FAILED_TO_PREPARE_CSR_AND_KEY,
Some(format!("Failed to prepare the CSR and key pair: {e:?}")),
)
})
.with_log()?;
let csr = csr
.into_cbor_vec()
.map_err(|e| {
Status::new_service_specific_error_str(
STATUS_FAILED_TO_PREPARE_CSR_AND_KEY,
Some(format!("Failed to serialize CSR into CBOR: {e:?}")),
)
})
.with_log()?;
let cert_chain = self.virtual_machine_service.requestAttestation(&csr)?;
Ok(AttestationResult {
privateKey: private_key.as_slice().to_vec(),
certificateChain: cert_chain,
})
}
}
impl Interface for VmPayloadService {}
impl VmPayloadService {
/// Creates a new `VmPayloadService` instance from the `IVirtualMachineService` reference.
fn new(
allow_restricted_apis: bool,
vm_service: Strong<dyn IVirtualMachineService>,
secret: VmSecret,
) -> VmPayloadService {
Self { allow_restricted_apis, virtual_machine_service: vm_service, secret }
}
fn check_restricted_apis_allowed(&self) -> binder::Result<()> {
if self.allow_restricted_apis {
Ok(())
} else {
Err(anyhow!("Use of restricted APIs is not allowed"))
.with_log()
.or_binder_exception(ExceptionCode::SECURITY)
}
}
}
/// Registers the `IVmPayloadService` service.
pub(crate) fn register_vm_payload_service(
allow_restricted_apis: bool,
vm_service: Strong<dyn IVirtualMachineService>,
secret: VmSecret,
vm_payload_service_fd: OwnedFd,
) -> Result<()> {
let vm_payload_binder = BnVmPayloadService::new_binder(
VmPayloadService::new(allow_restricted_apis, vm_service, secret),
BinderFeatures::default(),
);
let server = RpcServer::new_bound_socket(vm_payload_binder.as_binder(), vm_payload_service_fd)?;
info!("The RPC server '{}' is running.", VM_PAYLOAD_SERVICE_SOCKET_NAME);
// Move server reference into a background thread and run it forever.
std::thread::spawn(move || {
server.join();
});
Ok(())
}