• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2023 Huawei Device Co., Ltd.
3  * Licensed under the Apache License, Version 2.0 (the "License");
4  * you may not use this file except in compliance with the License.
5  * You may obtain a copy of the License at
6  *
7  *     http://www.apache.org/licenses/LICENSE-2.0
8  *
9  * Unless required by applicable law or agreed to in writing, software
10  * distributed under the License is distributed on an "AS IS" BASIS,
11  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12  * See the License for the specific language governing permissions and
13  * limitations under the License.
14  */
15 //! auth
16 #![allow(missing_docs)]
17 
18 use hdc::config::{self, *};
19 use hdc::serializer::native_struct;
20 use hdc::serializer::serialize::Serialization;
21 use hdc::transfer;
22 
23 use openssl::base64;
24 use openssl::rsa::{Padding, Rsa};
25 use ylong_runtime::sync::RwLock;
26 
27 use super::sys_para::*;
28 use crate::utils::hdc_log::*;
29 use std::collections::HashMap;
30 use std::fs::File;
31 use std::io::{self, prelude::*, Error, ErrorKind, Write};
32 use std::path::Path;
33 use std::process::Command;
34 use std::string::ToString;
35 use std::sync::Arc;
36 
37 #[derive(Clone, PartialEq, Eq)]
38 pub enum AuthStatus {
39     Init(String),           // with plain
40     Pubk((String, String)), // with (plain, pk)
41     Ok,
42     Fail,
43 }
44 
45 pub enum UserPermit {
46     Refuse = 0,
47     AllowOnce = 1,
48     AllowForever = 2,
49 }
50 
51 type AuthStatusMap_ = Arc<RwLock<HashMap<u32, AuthStatus>>>;
52 
53 pub struct AuthStatusMap {}
54 impl AuthStatusMap {
get_instance() -> AuthStatusMap_55     fn get_instance() -> AuthStatusMap_ {
56         static mut AUTH_STATUS_MAP: Option<AuthStatusMap_> = None;
57         unsafe {
58             AUTH_STATUS_MAP
59                 .get_or_insert_with(|| Arc::new(RwLock::new(HashMap::new())))
60                 .clone()
61         }
62     }
63 
get(session_id: u32) -> AuthStatus64     pub async fn get(session_id: u32) -> AuthStatus {
65         let instance = Self::get_instance();
66         let map = instance.read().await;
67         map.get(&session_id).unwrap().clone()
68     }
69 
put(session_id: u32, auth_status: AuthStatus)70     async fn put(session_id: u32, auth_status: AuthStatus) {
71         let instance = Self::get_instance();
72         let mut map = instance.write().await;
73         map.insert(session_id, auth_status);
74     }
75 }
76 
handshake_init(task_message: TaskMessage) -> io::Result<(u32, TaskMessage)>77 pub async fn handshake_init(task_message: TaskMessage) -> io::Result<(u32, TaskMessage)> {
78     if task_message.command != HdcCommand::KernelHandshake {
79         return Err(Error::new(ErrorKind::Other, "unknown command flag"));
80     }
81 
82     let mut recv = native_struct::SessionHandShake::default();
83     recv.parse(task_message.payload)?;
84 
85     hdc::info!("recv handshake: {:#?}", recv);
86     if recv.banner != HANDSHAKE_MESSAGE {
87         return Err(Error::new(ErrorKind::Other, "Recv server-hello failed"));
88     }
89 
90     hdc::info!(
91         "client version({}) for session:{}",
92         recv.version.as_str(),
93         recv.session_id
94     );
95     if recv.version.as_str() < "Ver: 2.0.0" {
96         hdc::info!(
97             "client version({}) is too low, return OK for session:{}",
98             recv.version.as_str(),
99             recv.session_id
100         );
101         return Ok((
102             recv.session_id,
103             make_ok_message(recv.session_id, task_message.channel_id).await,
104         ));
105     }
106     if !is_auth_enable().await {
107         hdc::info!(
108             "auth enable is false, return OK for session:{}",
109             recv.session_id
110         );
111         return Ok((
112             recv.session_id,
113             make_ok_message(recv.session_id, task_message.channel_id).await,
114         ));
115     }
116 
117     // auth is required
118     let buf = generate_token_wait().await;
119 
120     AuthStatusMap::put(recv.session_id, AuthStatus::Init(buf.clone())).await;
121 
122     let send = native_struct::SessionHandShake {
123         banner: HANDSHAKE_MESSAGE.to_string(),
124         session_id: recv.session_id,
125         connect_key: "".to_string(),
126         buf,
127         auth_type: AuthType::Publickey as u8,
128         version: get_version(),
129     };
130 
131     hdc::info!("send handshake: {:#?}", send);
132     let message = TaskMessage {
133         channel_id: task_message.channel_id,
134         command: HdcCommand::KernelHandshake,
135         payload: send.serialize(),
136     };
137     Ok((recv.session_id, message))
138 }
139 
make_sign_message(session_id: u32, token: String, channel_id: u32) -> TaskMessage140 async fn make_sign_message(session_id: u32, token: String, channel_id: u32) -> TaskMessage {
141     let send = native_struct::SessionHandShake {
142         banner: HANDSHAKE_MESSAGE.to_string(),
143         session_id,
144         connect_key: "".to_string(),
145         buf: token,
146         auth_type: AuthType::Signature as u8,
147         version: get_version(),
148     };
149     TaskMessage {
150         channel_id,
151         command: HdcCommand::KernelHandshake,
152         payload: send.serialize(),
153     }
154 }
155 
make_ok_message(session_id: u32, channel_id: u32) -> TaskMessage156 async fn make_ok_message(session_id: u32, channel_id: u32) -> TaskMessage {
157     AuthStatusMap::put(session_id, AuthStatus::Ok).await;
158 
159     let send = native_struct::SessionHandShake {
160         banner: HANDSHAKE_MESSAGE.to_string(),
161         session_id,
162         connect_key: "".to_string(),
163         auth_type: AuthType::OK as u8,
164         version: get_version(),
165         buf: match nix::unistd::gethostname() {
166             Ok(hostname) => hostname.into_string().unwrap(),
167             Err(_) => String::from("unknown"),
168         },
169     };
170     TaskMessage {
171         channel_id,
172         command: HdcCommand::KernelHandshake,
173         payload: send.serialize(),
174     }
175 }
176 
get_host_pubkey_info(buf: &str) -> (String, String)177 pub fn get_host_pubkey_info(buf: &str) -> (String, String) {
178     if let Some((hostname, pubkey)) = buf.split_once(HDC_HOST_DAEMON_BUF_SEPARATOR) {
179         (hostname.to_string(), pubkey.to_string())
180     } else {
181         ("".to_string(), "".to_string())
182     }
183 }
184 
get_new_session_id(task_message: &TaskMessage) -> io::Result<u32>185 pub async fn get_new_session_id(task_message: &TaskMessage) -> io::Result<u32> {
186     let mut recv = native_struct::SessionHandShake::default();
187     recv.parse(task_message.payload.clone())?;
188     Ok(recv.session_id)
189 }
190 
handshake_task(task_message: TaskMessage, session_id: u32) -> io::Result<()>191 pub async fn handshake_task(task_message: TaskMessage, session_id: u32) -> io::Result<()> {
192     if let AuthStatus::Ok = AuthStatusMap::get(session_id).await {
193         hdc::info!("session {} already auth ok", session_id);
194         return Ok(());
195     }
196 
197     let channel_id = task_message.channel_id;
198 
199     if !is_auth_enable().await {
200         hdc::info!("auth enable is false, return OK for session:{}", session_id);
201         transfer::put(session_id, make_ok_message(session_id, channel_id).await).await;
202         return Ok(());
203     }
204 
205     let mut recv = native_struct::SessionHandShake::default();
206     recv.parse(task_message.payload)?;
207     hdc::info!("recv handshake: {:#?}", recv);
208 
209     if recv.auth_type == AuthType::Publickey as u8 {
210         let plain = if let AuthStatus::Init(buf) = AuthStatusMap::get(session_id).await {
211             hdc::info!("get plain success for session {}", session_id);
212             buf
213         } else {
214             hdc::error!("get plain failed for session {}", session_id);
215             handshake_fail(session_id, channel_id, "auth failed".to_string()).await;
216             return Ok(());
217         };
218         let token = plain.clone();
219 
220         let (hostname, pubkey) = get_host_pubkey_info(recv.buf.trim());
221         if pubkey.is_empty() {
222             hdc::error!("get public key from host failed");
223             handshake_fail(
224                 session_id,
225                 channel_id,
226                 "no public key, you may need update your hdc client".to_string(),
227             )
228             .await;
229             return Ok(());
230         }
231         if hostname.is_empty() {
232             hdc::error!("get hostname from host failed");
233             handshake_fail(
234                 session_id,
235                 channel_id,
236                 "no hostname, you may need update your hdc client".to_string(),
237             )
238             .await;
239             return Ok(());
240         }
241 
242         let known_hosts = read_known_hosts_pubkey();
243         if known_hosts.contains(&pubkey) {
244             hdc::info!("pubkey matches known host({})", hostname);
245             AuthStatusMap::put(session_id, AuthStatus::Pubk((plain, pubkey))).await;
246             transfer::put(
247                 session_id,
248                 make_sign_message(session_id, token, channel_id).await,
249             )
250             .await;
251             return Ok(());
252         }
253         match require_user_permittion(&hostname).await {
254             UserPermit::AllowForever => {
255                 hdc::info!("allow forever");
256                 if write_known_hosts_pubkey(&pubkey).is_err() {
257                     handshake_fail(
258                         session_id,
259                         channel_id,
260                         "write public key failed".to_string(),
261                     )
262                     .await;
263 
264                     hdc::error!("write public key failed");
265                     return Ok(());
266                 }
267                 AuthStatusMap::put(session_id, AuthStatus::Pubk((plain, pubkey))).await;
268                 transfer::put(
269                     session_id,
270                     make_sign_message(session_id, token, channel_id).await,
271                 )
272                 .await;
273             }
274             UserPermit::AllowOnce => {
275                 hdc::info!("allow once");
276                 AuthStatusMap::put(session_id, AuthStatus::Pubk((plain, pubkey))).await;
277                 transfer::put(
278                     session_id,
279                     make_sign_message(session_id, token, channel_id).await,
280                 )
281                 .await;
282             }
283             _ => {
284                 hdc::info!("user refuse");
285                 handshake_fail(
286                     session_id,
287                     channel_id,
288                     "public key refused by device".to_string(),
289                 )
290                 .await;
291                 return Ok(());
292             }
293         }
294     } else if recv.auth_type == AuthType::Signature as u8 {
295         match validate_signature(recv.buf, session_id).await {
296             Ok(()) => {
297                 transfer::put(session_id, make_ok_message(session_id, channel_id).await).await;
298                 transfer::put(
299                     session_id,
300                     TaskMessage {
301                         channel_id,
302                         command: HdcCommand::KernelChannelClose,
303                         payload: vec![0],
304                     },
305                 )
306                 .await;
307             }
308             Err(e) => {
309                 let errlog = e.to_string();
310                 hdc::error!("validate signature failed: {}", &errlog);
311                 handshake_fail(session_id, channel_id, errlog).await;
312             }
313         }
314     } else {
315         hdc::error!(
316             "invalid auth_type: {} for session {}",
317             recv.auth_type,
318             session_id
319         );
320         // handshake_fail session_id, channel_id auth failed await
321         transfer::put(session_id, make_ok_message(session_id, channel_id).await).await;
322     }
323     Ok(())
324 }
325 
validate_signature(signature: String, session_id: u32) -> io::Result<()>326 async fn validate_signature(signature: String, session_id: u32) -> io::Result<()> {
327     let (plain, pubkey) =
328         if let AuthStatus::Pubk((plain, pubkey)) = AuthStatusMap::get(session_id).await {
329             (plain, pubkey)
330         } else {
331             return Err(Error::new(ErrorKind::Other, "auth failed"));
332         };
333 
334     let signature_bytes = if let Ok(bytes) = base64::decode_block(&signature) {
335         bytes
336     } else {
337         return Err(Error::new(ErrorKind::Other, "signature decode failed"));
338     };
339 
340     let rsa = if let Ok(cipher) = Rsa::public_key_from_pem(pubkey.as_bytes()) {
341         cipher
342     } else {
343         return Err(Error::new(ErrorKind::Other, "pubkey convert failed"));
344     };
345 
346     let mut buf = vec![0_u8; config::RSA_BIT_NUM];
347     let dec_size = rsa
348         .public_decrypt(&signature_bytes, &mut buf, Padding::PKCS1)
349         .unwrap_or(0);
350 
351     if plain.as_bytes() == &buf[..dec_size] {
352         Ok(())
353     } else {
354         Err(Error::new(ErrorKind::Other, "signature not match"))
355     }
356 }
357 
read_known_hosts_pubkey() -> Vec<String>358 fn read_known_hosts_pubkey() -> Vec<String> {
359     let file_name = Path::new(config::RSA_PUBKEY_PATH).join(config::RSA_PUBKEY_NAME);
360     if let Ok(keys) = std::fs::read_to_string(&file_name) {
361         let mut key_vec = vec![];
362         let mut tmp_vec = vec![];
363 
364         for line in keys.split('\n') {
365             if line.contains("BEGIN PUBLIC KEY") {
366                 tmp_vec.clear();
367             }
368             tmp_vec.push(line);
369             if line.contains("END PUBLIC KEY") {
370                 key_vec.push(tmp_vec.join("\n"));
371             }
372         }
373 
374         hdc::debug!("read {} known hosts from file", key_vec.len());
375         key_vec
376     } else {
377         hdc::info!("pubkey file {:#?} not exists", file_name);
378         vec![]
379     }
380 }
381 
write_known_hosts_pubkey(pubkey: &String) -> io::Result<()>382 fn write_known_hosts_pubkey(pubkey: &String) -> io::Result<()> {
383     let file_name = Path::new(config::RSA_PUBKEY_PATH).join(config::RSA_PUBKEY_NAME);
384     if !file_name.exists() {
385         hdc::info!("create pubkeys file at {:#?}", file_name);
386         let _ = std::fs::create_dir_all(config::RSA_PUBKEY_PATH);
387         let _ = std::fs::File::create(&file_name).unwrap();
388     }
389 
390     let _ = match std::fs::File::options().append(true).open(file_name) {
391         Ok(mut f) => writeln!(&mut f, "{}", pubkey),
392         Err(e) => {
393             hdc::error!("write pubkey err: {e}");
394             return Err(e);
395         }
396     };
397     Ok(())
398 }
399 
show_permit_dialog() -> bool400 fn show_permit_dialog() -> bool {
401     let cmd = "/system/bin/hdcd_user_permit";
402     let result = Command::new(config::SHELL_PROG).args(["-c", cmd]).output();
403     match result {
404         Ok(output) => {
405             let msg = [output.stdout, output.stderr].concat();
406             let mut str = String::from_utf8(msg).unwrap();
407             str = str.replace('\n', " ");
408             hdc::error!("show dialog over, {}.", str);
409             true
410         }
411         Err(e) => {
412             hdc::error!("show dialog failed, {}.", e.to_string());
413             false
414         }
415     }
416 }
417 
is_auth_enable() -> bool418 pub async fn is_auth_enable() -> bool {
419     let (_, debug_auth_enable) = get_dev_item("rw.hdc.daemon.debug_auth_enable", "_");
420     hdc::error!("debug_auth_enable is {}.", debug_auth_enable);
421     match debug_auth_enable.trim() {
422         "force" => true,
423         "true" => {
424             let (_, auth_enable) = get_dev_item("const.hdc.secure", "_");
425             hdc::error!("const.hdc.secure is {}.", auth_enable);
426             auth_enable.trim().to_lowercase() == "1"
427         }
428         _ => false,
429     }
430 }
431 
require_user_permittion(hostname: &str) -> UserPermit432 async fn require_user_permittion(hostname: &str) -> UserPermit {
433     // todo debug
434     let default_permit = "auth_result_none";
435     // clear result first
436     if !set_dev_item("persist.hdc.daemon.auth_result", default_permit) {
437         hdc::error!("debug auth result failed, so refuse this connect.");
438         return UserPermit::Refuse;
439     }
440 
441     // then write para for setting
442     if !set_dev_item("persist.hdc.client.hostname", hostname) {
443         hdc::error!("set param({}) failed.", hostname);
444         return UserPermit::Refuse;
445     }
446     if !show_permit_dialog() {
447         hdc::error!("show dialog failed, so refuse this connect.");
448         return UserPermit::Refuse;
449     }
450     let permit_result = match get_dev_item("persist.hdc.daemon.auth_result", "_") {
451         (false, _) => {
452             hdc::error!("get_dev_item auth_result failed");
453             UserPermit::Refuse
454         }
455         (true, auth_result) => {
456             hdc::error!("user permit result is:({})", auth_result);
457             match auth_result.strip_prefix("auth_result:").unwrap().trim() {
458                 "1" => UserPermit::AllowOnce,
459                 "2" => UserPermit::AllowForever,
460                 _ => UserPermit::Refuse,
461             }
462         }
463     };
464     permit_result
465 }
466 
handshake_fail(session_id: u32, channel_id: u32, msg: String)467 async fn handshake_fail(session_id: u32, channel_id: u32, msg: String) {
468     AuthStatusMap::put(session_id, AuthStatus::Fail).await;
469     let send = native_struct::SessionHandShake {
470         banner: HANDSHAKE_MESSAGE.to_string(),
471         session_id,
472         auth_type: AuthType::Fail as u8,
473         buf: msg,
474         ..Default::default()
475     };
476     transfer::put(
477         session_id,
478         TaskMessage {
479             channel_id,
480             command: config::HdcCommand::KernelHandshake,
481             payload: send.serialize(),
482         },
483     )
484     .await;
485 }
486 
generate_token() -> io::Result<String>487 async fn generate_token() -> io::Result<String> {
488     let mut random_file = File::open("/dev/random")?;
489     let mut buffer = [0; HDC_HANDSHAKE_TOKEN_LEN];
490     random_file.read_exact(&mut buffer)?;
491     let random_vec: Vec<_> = buffer.iter().map(|h| format!("{:02X}", h)).collect();
492     let token = random_vec.join("");
493     Ok(token)
494 }
generate_token_wait() -> String495 async fn generate_token_wait() -> String {
496     loop {
497         match generate_token().await {
498             Ok(token) => {
499                 break token;
500             }
501             Err(e) => {
502                 let errlog = e.to_string();
503                 hdc::error!("generate token failed: {}", &errlog);
504             }
505         }
506     }
507 }
508