1 /*
2  * Copyright (C) 2021 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 //! compsvc is a service to run compilation tasks in a PVM upon request. It is able to set up
18 //! file descriptors backed by authfs (via authfs_service) and pass the file descriptors to the
19 //! actual compiler.
20 
21 use anyhow::{bail, Context, Result};
22 use log::{error, info};
23 use rustutils::system_properties;
24 use std::default::Default;
25 use std::fs::read_dir;
26 use std::iter::zip;
27 use std::path::{Path, PathBuf};
28 use std::sync::RwLock;
29 
30 use crate::artifact_signer::ArtifactSigner;
31 use crate::compilation::odrefresh;
32 use crate::compos_key;
33 use authfs_aidl_interface::aidl::com::android::virt::fs::IAuthFsService::{
34     IAuthFsService, AUTHFS_SERVICE_SOCKET_NAME,
35 };
36 use binder::{
37     BinderFeatures, ExceptionCode, Interface, IntoBinderResult, Result as BinderResult, Strong,
38 };
39 use compos_aidl_interface::aidl::com::android::compos::ICompOsService::{
40     BnCompOsService, ICompOsService, OdrefreshArgs::OdrefreshArgs,
41 };
42 use compos_common::binder::to_binder_result;
43 use compos_common::odrefresh::{is_system_property_interesting, ODREFRESH_PATH};
44 use rpcbinder::RpcSession;
45 
46 /// Constructs a binder object that implements ICompOsService.
new_binder() -> Result<Strong<dyn ICompOsService>>47 pub fn new_binder() -> Result<Strong<dyn ICompOsService>> {
48     let service = CompOsService {
49         odrefresh_path: PathBuf::from(ODREFRESH_PATH),
50         initialized: RwLock::new(None),
51     };
52     Ok(BnCompOsService::new_binder(service, BinderFeatures::default()))
53 }
54 
55 struct CompOsService {
56     odrefresh_path: PathBuf,
57 
58     /// A locked protected tri-state.
59     ///  * None: uninitialized
60     ///  * Some(true): initialized successfully
61     ///  * Some(false): failed to initialize
62     initialized: RwLock<Option<bool>>,
63 }
64 
65 impl Interface for CompOsService {}
66 
67 impl ICompOsService for CompOsService {
initializeSystemProperties(&self, names: &[String], values: &[String]) -> BinderResult<()>68     fn initializeSystemProperties(&self, names: &[String], values: &[String]) -> BinderResult<()> {
69         let mut initialized = self.initialized.write().unwrap();
70         if initialized.is_some() {
71             return Err(format!("Already initialized: {initialized:?}"))
72                 .or_binder_exception(ExceptionCode::ILLEGAL_STATE);
73         }
74         *initialized = Some(false);
75 
76         if names.len() != values.len() {
77             return Err(format!(
78                 "Received inconsistent number of keys ({}) and values ({})",
79                 names.len(),
80                 values.len()
81             ))
82             .or_binder_exception(ExceptionCode::ILLEGAL_ARGUMENT);
83         }
84         for (name, value) in zip(names, values) {
85             if !is_system_property_interesting(name) {
86                 return Err(format!("Received invalid system property {name}"))
87                     .or_binder_exception(ExceptionCode::ILLEGAL_ARGUMENT);
88             }
89             let result = system_properties::write(name, value);
90             if result.is_err() {
91                 error!("Failed to setprop {}", &name);
92                 return to_binder_result(result);
93             }
94         }
95         *initialized = Some(true);
96         Ok(())
97     }
98 
odrefresh(&self, args: &OdrefreshArgs) -> BinderResult<i8>99     fn odrefresh(&self, args: &OdrefreshArgs) -> BinderResult<i8> {
100         let initialized = *self.initialized.read().unwrap();
101         if !initialized.unwrap_or(false) {
102             return Err("Service has not been initialized")
103                 .or_binder_exception(ExceptionCode::ILLEGAL_STATE);
104         }
105 
106         to_binder_result(self.do_odrefresh(args))
107     }
108 
getPublicKey(&self) -> BinderResult<Vec<u8>>109     fn getPublicKey(&self) -> BinderResult<Vec<u8>> {
110         to_binder_result(compos_key::get_public_key())
111     }
112 
getAttestationChain(&self) -> BinderResult<Vec<u8>>113     fn getAttestationChain(&self) -> BinderResult<Vec<u8>> {
114         to_binder_result(compos_key::get_attestation_chain())
115     }
116 
quit(&self) -> BinderResult<()>117     fn quit(&self) -> BinderResult<()> {
118         // When our process exits, Microdroid will shut down the VM.
119         info!("Received quit request, exiting");
120         std::process::exit(0);
121     }
122 }
123 
124 impl CompOsService {
do_odrefresh(&self, args: &OdrefreshArgs) -> Result<i8>125     fn do_odrefresh(&self, args: &OdrefreshArgs) -> Result<i8> {
126         log::debug!("Prepare to connect to {}", AUTHFS_SERVICE_SOCKET_NAME);
127         let authfs_service: Strong<dyn IAuthFsService> = RpcSession::new()
128             .setup_unix_domain_client(AUTHFS_SERVICE_SOCKET_NAME)
129             .with_context(|| format!("Failed to connect to {}", AUTHFS_SERVICE_SOCKET_NAME))?;
130         let exit_code = odrefresh(&self.odrefresh_path, args, authfs_service, |output_dir| {
131             // authfs only shows us the files we created, so it's ok to just sign everything
132             // under the output directory.
133             let mut artifact_signer = ArtifactSigner::new(&output_dir);
134             add_artifacts(&output_dir, &mut artifact_signer)?;
135 
136             artifact_signer.write_info_and_signature(&output_dir.join("compos.info"))
137         })
138         .context("odrefresh failed")?;
139         Ok(exit_code as i8)
140     }
141 }
142 
add_artifacts(target_dir: &Path, artifact_signer: &mut ArtifactSigner) -> Result<()>143 fn add_artifacts(target_dir: &Path, artifact_signer: &mut ArtifactSigner) -> Result<()> {
144     for entry in
145         read_dir(target_dir).with_context(|| format!("Traversing {}", target_dir.display()))?
146     {
147         let entry = entry?;
148         let file_type = entry.file_type()?;
149         if file_type.is_dir() {
150             add_artifacts(&entry.path(), artifact_signer)?;
151         } else if file_type.is_file() {
152             artifact_signer.add_artifact(&entry.path())?;
153         } else {
154             // authfs shouldn't create anything else, but just in case
155             bail!("Unexpected file type in artifacts: {:?}", entry);
156         }
157     }
158     Ok(())
159 }
160