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