• 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 crate::config::*;
19 
20 use hdc::config;
21 use hdc::config::TaskMessage;
22 use hdc::serializer::native_struct::SessionHandShake;
23 use hdc::serializer::serialize::Serialization;
24 use hdc::transfer;
25 
26 use std::io::{self, Error, ErrorKind};
27 use std::path::Path;
28 
29 use openssl::base64;
30 use openssl::rsa::{Padding, Rsa};
31 #[cfg(feature = "host")]
32 extern crate ylong_runtime_static as ylong_runtime;
33 use crate::task::{ConnectMap, ConnectStatus, DaemonInfo};
34 use hdc::common::base::Base;
35 
start_handshake_with_daemon( connect_key: String, session_id: u32, channel_id: u32, conn_type: ConnectType, )36 pub async fn start_handshake_with_daemon(
37     connect_key: String, session_id: u32, channel_id: u32, conn_type: ConnectType,
38 ) {
39     let handshake = SessionHandShake {
40         banner: HANDSHAKE_MESSAGE.to_string(),
41         session_id,
42         connect_key: connect_key.clone(),
43         version: config::get_version(),
44         auth_type: config::AuthType::None as u8,
45         ..Default::default()
46     };
47 
48     send_handshake_to_daemon(&handshake, channel_id).await;
49 
50     ConnectMap::put(
51         connect_key.clone(),
52         DaemonInfo {
53             session_id,
54             conn_type,
55             version: "unknown".to_string(),
56             conn_status: ConnectStatus::Unknown,
57             dev_name: "unknown".to_string(),
58             emg_msg: "".to_string(),
59             daemon_auth_status: DAEOMN_UNAUTHORIZED.to_string(),
60         },
61     )
62     .await;
63 }
64 
handshake_deal_daemon_auth_result(daemon: SessionHandShake, connect_key: String) -> io::Result<()>65 async fn handshake_deal_daemon_auth_result(daemon: SessionHandShake, connect_key: String) -> io::Result<()> {
66     let mut devname = "".to_string();
67     let mut auth_result = "".to_string();
68     let mut emg_msg = "".to_string();
69 
70     if daemon.version.as_str() < "Ver: 3.0.0b" {
71         if !daemon.buf.is_empty() {
72             devname = daemon.buf;
73         }
74     } else {
75         let auth_info = match Base::tlv_to_stringmap(daemon.buf.as_str()) {
76             Some(tlv_map) => tlv_map,
77             _ => {
78                 return Err(Error::new(ErrorKind::Other, "parse tlv failed"));
79             }
80         };
81         devname = match auth_info.get(TAG_DEVNAME) {
82             Some(devname) => devname.to_string(),
83             _ => "".to_string(),
84         };
85         auth_result = match auth_info.get(TAG_DAEOMN_AUTHSTATUS) {
86             Some(auth_result) => auth_result.to_string(),
87             _ => "".to_string(),
88         };
89         emg_msg = match auth_info.get(TAG_EMGMSG) {
90             Some(emg_msg) => emg_msg.to_string(),
91             _ => "".to_string(),
92         };
93     }
94 
95     hdc::info!(
96         "daemon auth result[{}] key[{}] ver[{}] devname[{}] emgmsg[{}]",
97         auth_result.clone(),
98         connect_key.clone(),
99         daemon.version.clone(),
100         devname.clone(),
101         emg_msg.clone()
102     );
103 
104     if ConnectMap::update(
105         connect_key.clone(),
106         ConnectStatus::Connected,
107         daemon.version.to_string(),
108         devname.to_string(),
109         emg_msg.to_string(),
110         auth_result.to_string(),
111     )
112     .await
113     {
114         Ok(())
115     } else {
116         hdc::error!("update connect status for {} failed", connect_key);
117         Err(Error::new(ErrorKind::Other, "not exist connect key"))
118     }
119 }
120 
handshake_task(msg: TaskMessage, session_id: u32, connect_key: String) -> io::Result<()>121 pub async fn handshake_task(msg: TaskMessage, session_id: u32, connect_key: String) -> io::Result<()> {
122     let rsa = load_or_create_prikey()?;
123     let mut recv = SessionHandShake::default();
124     let channel_id = msg.channel_id;
125     recv.parse(msg.payload)?;
126     hdc::info!("recv handshake: {:#?}", recv);
127 
128     if recv.banner != config::HANDSHAKE_MESSAGE {
129         hdc::info!("invalid banner {}", recv.banner);
130         return Err(Error::new(ErrorKind::Other, "Recv server-hello failed"));
131     }
132 
133     if recv.auth_type == config::AuthType::OK as u8 {
134         handshake_deal_daemon_auth_result(recv.clone(), connect_key.clone()).await
135     } else if recv.auth_type == config::AuthType::Publickey as u8 {
136         // send public key
137         let pubkey_pem = get_pubkey_pem(&rsa)?;
138         let mut buf = get_hostname()?;
139         buf.push(char::from_u32(12).unwrap());
140         buf.push_str(pubkey_pem.as_str());
141         let handshake = SessionHandShake {
142             banner: HANDSHAKE_MESSAGE.to_string(),
143             session_id,
144             connect_key: connect_key.clone(),
145             version: config::get_version(),
146             auth_type: config::AuthType::Publickey as u8,
147             buf,
148         };
149         send_handshake_to_daemon(&handshake, channel_id).await;
150         return Ok(());
151     } else if recv.auth_type == config::AuthType::Signature as u8 {
152         // send signature
153         let buf = get_signature_b64(&rsa, recv.buf)?;
154         let handshake = SessionHandShake {
155             banner: HANDSHAKE_MESSAGE.to_string(),
156             session_id,
157             connect_key: connect_key.clone(),
158             version: config::get_version(),
159             auth_type: config::AuthType::Signature as u8,
160             buf,
161         };
162         send_handshake_to_daemon(&handshake, channel_id).await;
163         return Ok(());
164     } else if recv.auth_type == config::AuthType::Fail as u8 {
165         hdc::info!("daemon auth failed");
166         return Err(Error::new(ErrorKind::Other, recv.buf.as_str()));
167     } else {
168         hdc::info!("invalid auth type {}", recv.auth_type);
169         return Err(Error::new(ErrorKind::Other, "unknown auth type"));
170     }
171 }
172 
load_or_create_prikey() -> io::Result<Rsa<openssl::pkey::Private>>173 fn load_or_create_prikey() -> io::Result<Rsa<openssl::pkey::Private>> {
174     let file = Path::new(&get_home_dir()).join(config::RSA_PRIKEY_PATH).join(config::RSA_PRIKEY_NAME);
175 
176     if let Ok(pem) = std::fs::read(&file) {
177         if let Ok(prikey) = Rsa::private_key_from_pem(&pem) {
178             hdc::info!("found existed private key");
179             return Ok(prikey);
180         } else {
181             hdc::error!("found broken private key, regenerating...");
182         }
183     }
184 
185     hdc::info!("create private key at {:#?}", file);
186     create_prikey()
187 }
188 
create_prikey() -> io::Result<Rsa<openssl::pkey::Private>>189 pub fn create_prikey() -> io::Result<Rsa<openssl::pkey::Private>> {
190     let prikey = Rsa::generate(config::RSA_BIT_NUM as u32).unwrap();
191     let pem = prikey.private_key_to_pem().unwrap();
192     let path = Path::new(&get_home_dir()).join(config::RSA_PRIKEY_PATH);
193     let file = path.join(config::RSA_PRIKEY_NAME);
194 
195     let _ = std::fs::create_dir_all(&path);
196 
197     if std::fs::write(file, pem).is_err() {
198         hdc::error!("write private key failed");
199         Err(Error::new(ErrorKind::Other, "write private key failed"))
200     } else {
201         Ok(prikey)
202     }
203 }
204 
get_pubkey_pem(rsa: &Rsa<openssl::pkey::Private>) -> io::Result<String>205 fn get_pubkey_pem(rsa: &Rsa<openssl::pkey::Private>) -> io::Result<String> {
206     if let Ok(pubkey) = rsa.public_key_to_pem() {
207         if let Ok(buf) = String::from_utf8(pubkey) {
208             Ok(buf)
209         } else {
210             Err(Error::new(ErrorKind::Other, "convert public key to pem string failed"))
211         }
212     } else {
213         Err(Error::new(ErrorKind::Other, "convert public key to pem string failed"))
214     }
215 }
216 
get_signature_b64(rsa: &Rsa<openssl::pkey::Private>, plain: String) -> io::Result<String>217 fn get_signature_b64(rsa: &Rsa<openssl::pkey::Private>, plain: String) -> io::Result<String> {
218     let mut enc = vec![0_u8; config::RSA_BIT_NUM];
219     match rsa.private_encrypt(plain.as_bytes(), &mut enc, Padding::PKCS1) {
220         Ok(size) => Ok(base64::encode_block(&enc[..size])),
221         Err(_) => Err(Error::new(ErrorKind::Other, "rsa private encrypt failed")),
222     }
223 }
224 
send_handshake_to_daemon(handshake: &SessionHandShake, channel_id: u32)225 async fn send_handshake_to_daemon(handshake: &SessionHandShake, channel_id: u32) {
226     hdc::info!("send handshake: {:#?}", handshake.clone());
227     transfer::put(
228         handshake.session_id,
229         TaskMessage { channel_id, command: config::HdcCommand::KernelHandshake, payload: handshake.serialize() },
230     )
231     .await;
232 }
233 
get_home_dir() -> String234 fn get_home_dir() -> String {
235     use std::process::Command;
236 
237     let output = if cfg!(target_os = "windows") {
238         Command::new("cmd").args(["/c", "echo %USERPROFILE%"]).output()
239     } else {
240         Command::new("sh").args(["-c", "echo ~"]).output()
241     };
242 
243     if let Ok(result) = output {
244         String::from_utf8(result.stdout).unwrap().trim().to_string()
245     } else {
246         hdc::warn!("get home dir failed, use current dir instead");
247         ".".to_string()
248     }
249 }
250 
get_hostname() -> io::Result<String>251 fn get_hostname() -> io::Result<String> {
252     use std::process::Command;
253 
254     let output = if cfg!(target_os = "windows") {
255         Command::new("cmd").args(["/c", "hostname"]).output()
256     } else {
257         Command::new("cmd").args(["-c", "hostname"]).output()
258     };
259 
260     if let Ok(result) = output {
261         Ok(String::from_utf8(result.stdout).unwrap())
262     } else {
263         Err(Error::new(ErrorKind::Other, "get hostname failed"))
264     }
265 }
266