• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright (C) 2023 Huawei Device Co., Ltd.
2 // Licensed under the Apache License, Version 2.0 (the "License");
3 // you may not use this file except in compliance with the License.
4 // You may obtain a copy of the License at
5 //
6 //     http://www.apache.org/licenses/LICENSE-2.0
7 //
8 // Unless required by applicable law or agreed to in writing, software
9 // distributed under the License is distributed on an "AS IS" BASIS,
10 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11 // See the License for the specific language governing permissions and
12 // limitations under the License.
13 
14 use std::error::Error;
15 
16 use ylong_http_client::async_impl::{Client, Request};
17 use ylong_http_client::{
18     Certificate, HttpClientError, Interceptor, Proxy, PubKeyPins, Redirect, Timeout, TlsVersion,
19 };
20 
21 cfg_oh! {
22     use crate::manage::SystemConfig;
23     use crate::utils::url_policy::check_url_domain;
24 }
25 
26 use super::files::BundleCache;
27 use crate::task::config::{Action, TaskConfig};
28 use crate::task::files::convert_path;
29 
30 const CONNECT_TIMEOUT: u64 = 60;
31 const SECONDS_IN_ONE_WEEK: u64 = 7 * 24 * 60 * 60;
32 
build_client( config: &TaskConfig, #[cfg(feature = "oh")] mut system: SystemConfig, ) -> Result<Client, Box<dyn Error + Send + Sync>>33 pub(crate) fn build_client(
34     config: &TaskConfig,
35     #[cfg(feature = "oh")] mut system: SystemConfig,
36 ) -> Result<Client, Box<dyn Error + Send + Sync>> {
37     let mut client = Client::builder()
38         .connect_timeout(Timeout::from_secs(CONNECT_TIMEOUT))
39         .request_timeout(Timeout::from_secs(SECONDS_IN_ONE_WEEK))
40         .min_tls_version(TlsVersion::TLS_1_2);
41 
42     client = client.sockets_owner(config.common_data.uid as u32, config.common_data.uid as u32);
43     // Set redirect strategy.
44     if config.common_data.redirect {
45         client = client.redirect(Redirect::limited(usize::MAX));
46     } else {
47         client = client.redirect(Redirect::none());
48     }
49 
50     // Set HTTP proxy.
51     #[cfg(feature = "oh")]
52     if let Some(proxy) = build_task_proxy(config)? {
53         client = client.proxy(proxy);
54     } else if let Some(proxy) = build_system_proxy(&system)? {
55         client = client.proxy(proxy);
56     }
57 
58     // HTTP url that contains redirects also require a certificate when
59     // redirected to HTTPS.
60 
61     // Set system certs.
62     #[cfg(feature = "oh")]
63     if let Some(certs) = system.certs.take() {
64         for cert in certs.into_iter() {
65             client = client.add_root_certificate(cert)
66         }
67     }
68 
69     // Set task certs.
70     let certificates = build_task_certs(config)?;
71     for cert in certificates.into_iter() {
72         client = client.add_root_certificate(cert)
73     }
74 
75     // Set task certificate pinned_key.
76     if let Some(pinned_key) = build_task_certificate_pins(config)? {
77         client = client.add_public_key_pins(pinned_key);
78     }
79 
80     const ATOMIC_SERVICE: u32 = 1;
81     if config.bundle_type == ATOMIC_SERVICE {
82         let domain_type = action_to_domain_type(config.common_data.action);
83         info!(
84             "ApiPolicy Domain check, tid {}, bundle {}, domain_type {}, url {}",
85             config.common_data.task_id, &config.bundle, &domain_type, &config.url
86         );
87         #[cfg(feature = "oh")]
88         if let Some(is_accessed) = check_url_domain(&config.bundle, &domain_type, &config.url) {
89             if !is_accessed {
90                 error!(
91                     "Intercept request by domain check, tid {}, bundle {}, domain_type {}, url {}",
92                     config.common_data.task_id, &config.bundle, &domain_type, &config.url
93                 );
94                 sys_event!(
95                     ExecFault,
96                     DfxCode::URL_POLICY_FAULT_00,
97                     &format!(
98                     "Intercept request by domain check, tid {}, bundle {}, domain_type {}, url {}",
99                 config.common_data.task_id, &config.bundle, &domain_type, &config.url)
100                 );
101 
102                 return Err(Box::new(HttpClientError::other(
103                     "Intercept request by domain check",
104                 )));
105             }
106         } else {
107             info!(
108                 "Intercept request by domain check, tid {}, domain_type {}, url {}",
109                 config.common_data.task_id, &domain_type, &config.url
110             );
111         }
112 
113         #[cfg(feature = "oh")]
114         {
115             let interceptors = DomainInterceptor::new(config.bundle.clone(), domain_type);
116             client = client.interceptor(interceptors);
117         }
118 
119         info!(
120             "add interceptor domain check, tid {}",
121             config.common_data.task_id
122         );
123     }
124 
125     // Build client.
126     Ok(cvt_res_error!(
127         client.build().map_err(Box::new),
128         "Build client failed",
129     ))
130 }
131 
build_task_proxy(config: &TaskConfig) -> Result<Option<Proxy>, Box<dyn Error + Send + Sync>>132 fn build_task_proxy(config: &TaskConfig) -> Result<Option<Proxy>, Box<dyn Error + Send + Sync>> {
133     if config.proxy.is_empty() {
134         return Ok(None);
135     }
136 
137     Ok(Some(cvt_res_error!(
138         Proxy::all(&config.proxy).build().map_err(Box::new),
139         "Create task proxy failed",
140     )))
141 }
142 
build_task_certificate_pins( config: &TaskConfig, ) -> Result<Option<PubKeyPins>, Box<dyn Error + Send + Sync>>143 fn build_task_certificate_pins(
144     config: &TaskConfig,
145 ) -> Result<Option<PubKeyPins>, Box<dyn Error + Send + Sync>> {
146     if config.certificate_pins.is_empty() {
147         return Ok(None);
148     }
149 
150     Ok(Some(cvt_res_error!(
151         PubKeyPins::builder()
152             .add(&config.url, &config.certificate_pins)
153             .build()
154             .map_err(Box::new),
155         "Create task certificate pinned_key failed",
156     )))
157 }
158 
159 #[cfg(feature = "oh")]
build_system_proxy( system: &SystemConfig, ) -> Result<Option<Proxy>, Box<dyn Error + Send + Sync>>160 fn build_system_proxy(
161     system: &SystemConfig,
162 ) -> Result<Option<Proxy>, Box<dyn Error + Send + Sync>> {
163     let proxy_host = &system.proxy_host;
164 
165     if proxy_host.is_empty() {
166         return Ok(None);
167     }
168 
169     let proxy_port = &system.proxy_port;
170     let proxy_url = match proxy_port.is_empty() {
171         true => proxy_host.clone(),
172         false => format!("{}:{}", proxy_host, proxy_port),
173     };
174     let no_proxy = &system.proxy_exlist;
175     Ok(Some(cvt_res_error!(
176         Proxy::all(&proxy_url)
177             .no_proxy(no_proxy)
178             .build()
179             .map_err(Box::new),
180         "Create system proxy failed",
181     )))
182 }
183 
build_task_certs(config: &TaskConfig) -> Result<Vec<Certificate>, Box<dyn Error + Send + Sync>>184 fn build_task_certs(config: &TaskConfig) -> Result<Vec<Certificate>, Box<dyn Error + Send + Sync>> {
185     let uid = config.common_data.uid;
186     let paths = config.certs_path.as_slice();
187     let mut bundle_cache = BundleCache::new(config);
188 
189     let mut certs = Vec::new();
190     for (idx, path) in paths.iter().enumerate() {
191         let bundle_name = bundle_cache.get_value()?;
192         let path = convert_path(uid, &bundle_name, path);
193         let cert = cvt_res_error!(
194             Certificate::from_path(&path).map_err(Box::new),
195             "Parse task cert failed - idx: {}",
196             idx,
197         );
198         certs.push(cert);
199     }
200     Ok(certs)
201 }
202 
action_to_domain_type(action: Action) -> String203 fn action_to_domain_type(action: Action) -> String {
204     match action {
205         Action::Download => "download".to_string(),
206         Action::Upload => "upload".to_string(),
207         Action::Any => "".to_string(),
208         _ => unreachable!(),
209     }
210 }
211 
212 struct DomainInterceptor {
213     app_id: String,
214     domain_type: String,
215 }
216 
217 impl DomainInterceptor {
new(app_id: String, domain_type: String) -> Self218     fn new(app_id: String, domain_type: String) -> Self {
219         DomainInterceptor {
220             app_id,
221             domain_type,
222         }
223     }
224 }
225 
226 #[cfg(feature = "oh")]
227 impl Interceptor for DomainInterceptor {
228     /// Intercepts the redirect request.
intercept_redirect_request(&self, request: &Request) -> Result<(), HttpClientError>229     fn intercept_redirect_request(&self, request: &Request) -> Result<(), HttpClientError> {
230         let url = &request.uri().to_string();
231         info!(
232             "ApiPolicy Domain check redirect, bundle {}, domain_type {}, url {}",
233             &self.app_id, &self.domain_type, &url
234         );
235         match check_url_domain(&self.app_id, &self.domain_type, url).unwrap_or(true) {
236             true => Ok(()),
237             false => Err(HttpClientError::other(
238                 "Intercept redirect request by domain check",
239             )),
240         }
241     }
242 }
243