/* * Copyright (C) 2021 Huawei Device Co., Ltd. * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "daemon.h" #ifndef TEST_HASH #include "hdc_hash_gen.h" #endif #include "serial_struct.h" #include #include #include #include #include #include #include #include namespace Hdc { #ifdef USE_CONFIG_UV_THREADS HdcDaemon::HdcDaemon(bool serverOrDaemonIn, size_t uvThreadSize) : HdcSessionBase(serverOrDaemonIn, uvThreadSize) #else HdcDaemon::HdcDaemon(bool serverOrDaemonIn) : HdcSessionBase(serverOrDaemonIn, -1) #endif { clsTCPServ = nullptr; clsUSBServ = nullptr; #ifdef HDC_EMULATOR clsBridgeServ = nullptr; #endif #ifdef HDC_SUPPORT_UART clsUARTServ = nullptr; #endif clsJdwp = nullptr; authEnable = false; } HdcDaemon::~HdcDaemon() { WRITE_LOG(LOG_DEBUG, "~HdcDaemon"); } void HdcDaemon::ClearInstanceResource() { TryStopInstance(); Base::TryCloseLoop(&loopMain, "HdcDaemon::~HdcDaemon"); if (clsTCPServ) { delete (HdcDaemonTCP *)clsTCPServ; clsTCPServ = nullptr; } if (clsUSBServ) { delete (HdcDaemonUSB *)clsUSBServ; clsUSBServ = nullptr; } #ifdef HDC_EMULATOR if (clsBridgeServ) { delete (HdcDaemonBridge *)clsBridgeServ; } #endif #ifdef HDC_SUPPORT_UART if (clsUARTServ) { delete (HdcDaemonUART *)clsUARTServ; } clsUARTServ = nullptr; #endif if (clsJdwp) { delete (HdcJdwp *)clsJdwp; clsJdwp = nullptr; } WRITE_LOG(LOG_DEBUG, "~HdcDaemon finish"); } void HdcDaemon::TryStopInstance() { ClearSessions(); if (clsTCPServ) { WRITE_LOG(LOG_DEBUG, "Stop TCP"); ((HdcDaemonTCP *)clsTCPServ)->Stop(); } if (clsUSBServ) { WRITE_LOG(LOG_DEBUG, "Stop USB"); ((HdcDaemonUSB *)clsUSBServ)->Stop(); } #ifdef HDC_EMULATOR if (clsBridgeServ) { WRITE_LOG(LOG_DEBUG, "Stop Bridge"); ((HdcDaemonBridge *)clsBridgeServ)->Stop(); } #endif #ifdef HDC_SUPPORT_UART if (clsUARTServ) { WRITE_LOG(LOG_DEBUG, "Stop UART"); ((HdcDaemonUART *)clsUARTServ)->Stop(); } #endif ((HdcJdwp *)clsJdwp)->Stop(); // workaround temply remove MainLoop instance clear ReMainLoopForInstanceClear(); WRITE_LOG(LOG_DEBUG, "Stop loopmain"); } bool HdcDaemon::GetAuthByPassValue() { // enable security string secure; #ifdef CONFIG_HDC_OSPM_AUTH_DISABLE WRITE_LOG(LOG_INFO, "Get param secure failed caused by hdc ospm auth disable"); return false; #endif if (!SystemDepend::GetDevItem("const.hdc.secure", secure)) { WRITE_LOG(LOG_INFO, "Get param secure failed"); return true; } if (Base::Trim(secure) != "1") { WRITE_LOG(LOG_INFO, "Authentication is not required in root mode"); return false; } string oemmode; if (!SystemDepend::GetDevItem("const.boot.oemmode", oemmode)) { WRITE_LOG(LOG_INFO, "Get param oemmode failed"); return true; } if (Base::Trim(oemmode) != "rd") { WRITE_LOG(LOG_INFO, "The device is locked, Authentication is required"); return true; } string authbypass; if (!SystemDepend::GetDevItem("persist.hdc.auth_bypass", authbypass)) { WRITE_LOG(LOG_INFO, "Get param auth_bypass failed"); return true; } if (Base::Trim(authbypass) == "1") { WRITE_LOG(LOG_INFO, "Auth bypass, allow access"); return false; } return true; } #ifdef HDC_SUPPORT_UART void HdcDaemon::InitMod(bool bEnableTCP, bool bEnableUSB, [[maybe_unused]] bool bEnableUART) #else void HdcDaemon::InitMod(bool bEnableTCP, bool bEnableUSB) #endif { WRITE_LOG(LOG_DEBUG, "HdcDaemon InitMod"); #ifdef HDC_SUPPORT_UART WRITE_LOG(LOG_DEBUG, "bEnableTCP:%d,bEnableUSB:%d", bEnableTCP, bEnableUSB); #endif if (bEnableTCP) { // tcp clsTCPServ = new(std::nothrow) HdcDaemonTCP(false, this); if (clsTCPServ == nullptr) { WRITE_LOG(LOG_FATAL, "InitMod new clsTCPServ failed"); return; } ((HdcDaemonTCP *)clsTCPServ)->Initial(); } if (bEnableUSB) { // usb clsUSBServ = new(std::nothrow) HdcDaemonUSB(false, this); if (clsUSBServ == nullptr) { WRITE_LOG(LOG_FATAL, "InitMod new clsUSBServ failed"); return; } ((HdcDaemonUSB *)clsUSBServ)->Initial(); } #ifdef HDC_SUPPORT_UART WRITE_LOG(LOG_DEBUG, "bEnableUART:%d", bEnableUART); if (bEnableUART) { // UART clsUARTServ = new(std::nothrow) HdcDaemonUART(*this); if (clsUARTServ == nullptr) { WRITE_LOG(LOG_FATAL, "InitMod new clsUARTServ failed"); return; } ((HdcDaemonUART *)clsUARTServ)->Initial(); } #endif clsJdwp = new(std::nothrow) HdcJdwp(&loopMain, &loopMainStatus); if (clsJdwp == nullptr) { WRITE_LOG(LOG_FATAL, "InitMod new clsJdwp failed"); return; } ((HdcJdwp *)clsJdwp)->Initial(); #ifndef HDC_EMULATOR authEnable = GetAuthByPassValue(); #endif } #ifdef HDC_EMULATOR #ifdef HDC_SUPPORT_UART void HdcDaemon::InitMod(bool bEnableTCP, bool bEnableUSB, bool bEnableBridge, [[maybe_unused]] bool bEnableUART) { InitMod(bEnableTCP, bEnableUSB, bEnableUART); #else void HdcDaemon::InitMod(bool bEnableTCP, bool bEnableUSB, bool bEnableBridge) { InitMod(bEnableTCP, bEnableUSB); #endif if (bEnableBridge) { clsBridgeServ = new(std::nothrow) HdcDaemonBridge(false, this); if (clsBridgeServ == nullptr) { WRITE_LOG(LOG_FATAL, "InitMod new clsBridgeServ failed"); return; } ((HdcDaemonBridge *)clsBridgeServ)->Initial(); } } #endif // clang-format off bool HdcDaemon::RedirectToTask(HTaskInfo hTaskInfo, HSession hSession, const uint32_t channelId, const uint16_t command, uint8_t *payload, const int payloadSize) { StartTraceScope("HdcDaemon::RedirectToTask"); bool ret = true; hTaskInfo->ownerSessionClass = this; switch (command) { case CMD_UNITY_EXECUTE: case CMD_UNITY_REMOUNT: case CMD_UNITY_REBOOT: case CMD_UNITY_RUNMODE: case CMD_UNITY_HILOG: case CMD_UNITY_ROOTRUN: case CMD_UNITY_BUGREPORT_INIT: case CMD_JDWP_LIST: case CMD_JDWP_TRACK: case CMD_UNITY_EXECUTE_EX: ret = TaskCommandDispatch(hTaskInfo, TYPE_UNITY, command, payload, payloadSize); break; case CMD_SHELL_INIT: case CMD_SHELL_DATA: ret = TaskCommandDispatch(hTaskInfo, TYPE_SHELL, command, payload, payloadSize); break; case CMD_FILE_CHECK: case CMD_FILE_DATA: case CMD_FILE_FINISH: case CMD_FILE_INIT: case CMD_FILE_BEGIN: case CMD_FILE_MODE: case CMD_DIR_MODE: ret = TaskCommandDispatch(hTaskInfo, TASK_FILE, command, payload, payloadSize); break; // One-way function, so fewer options case CMD_APP_CHECK: case CMD_APP_DATA: case CMD_APP_UNINSTALL: ret = TaskCommandDispatch(hTaskInfo, TASK_APP, command, payload, payloadSize); break; case CMD_FORWARD_INIT: case CMD_FORWARD_CHECK: case CMD_FORWARD_ACTIVE_MASTER: case CMD_FORWARD_ACTIVE_SLAVE: case CMD_FORWARD_DATA: case CMD_FORWARD_FREE_CONTEXT: case CMD_FORWARD_CHECK_RESULT: ret = TaskCommandDispatch(hTaskInfo, TASK_FORWARD, command, payload, payloadSize); break; default: // ignore unknown command break; } return ret; } // clang-format on bool HdcDaemon::ShowPermitDialog() { pid_t pid; int fds[2]; pipe(fds); if ((pid = fork()) == -1) { WRITE_LOG(LOG_FATAL, "fork failed %s", strerror(errno)); return false; } if (pid == 0) { Base::DeInitProcess(); // close the child read channel close(fds[0]); // redirect the child write channel dup2(fds[1], STDOUT_FILENO); dup2(fds[1], STDERR_FILENO); setsid(); setpgid(pid, pid); int ret = execl("/system/bin/hdcd_user_permit", "hdcd_user_permit", NULL); if (ret < 0) { // if execl failed need return false WRITE_LOG(LOG_FATAL, "start user_permit failed %d: %s", ret, strerror(errno)); return false; } } else { Base::CloseFd(fds[1]); waitpid(pid, nullptr, 0); char buf[1024] = { 0 }; int nbytes = read(fds[0], buf, sizeof(buf) - 1); WRITE_LOG(LOG_FATAL, "user_permit put %d bytes: %s", nbytes, buf); close(fds[0]); } return true; } UserPermit HdcDaemon::PostUIConfirm(string hostname, string pubkey) { // clear result first if (!SystemDepend::SetDevItem("persist.hdc.daemon.auth_result", "auth_result_none")) { WRITE_LOG(LOG_FATAL, "debug auth result failed, so refuse this connect"); return REFUSE; } // then write para for setting if (!SystemDepend::SetDevItem("persist.hdc.client.hostname", hostname.c_str())) { WRITE_LOG(LOG_FATAL, "set param(%s) failed", hostname.c_str()); return REFUSE; } uint8_t sha256Result[SHA256_DIGEST_LENGTH] = { 0 }; if (SHA256(reinterpret_cast(pubkey.c_str()), pubkey.length(), sha256Result) == nullptr) { WRITE_LOG(LOG_FATAL, "sha256 pubkey failed"); return REFUSE; } string hex = Base::Convert2HexStr(sha256Result, SHA256_DIGEST_LENGTH); if (!SystemDepend::SetDevItem("persist.hdc.client.pubkey_sha256", hex.c_str())) { WRITE_LOG(LOG_DEBUG, "Failed to set pubkey prop."); return REFUSE; } if (!ShowPermitDialog()) { WRITE_LOG(LOG_FATAL, "show dialog failed, so refuse this connect."); return REFUSE; } string authResult; if (!SystemDepend::GetDevItem("persist.hdc.daemon.auth_result", authResult)) { WRITE_LOG(LOG_FATAL, "user refuse [%s] this developer [%s]", authResult.c_str(), hostname.c_str()); return REFUSE; } WRITE_LOG(LOG_FATAL, "user permit_result [%s] for this developer [%s]", authResult.c_str(), hostname.c_str()); string prifix = "auth_result:"; string result = authResult.substr(prifix.length()); if (result == "1") { return ALLOWONCE; } if (result == "2") { return ALLOWFORVER; } return REFUSE; } bool HdcDaemon::GetHostPubkeyInfo(const string& buf, string& hostname, string& pubkey) { // "\f" asicc is 0x0C char separator = '\x0C'; hostname = buf.substr(0, buf.find(separator)); pubkey = buf.substr(buf.find(separator) + 1); WRITE_LOG(LOG_INFO, "hostname is [%s], pubkey is [%s]", hostname.c_str(), pubkey.substr(0, pubkey.size() / 2).c_str()); return (!hostname.empty() && !pubkey.empty()); } void HdcDaemon::ClearKnownHosts() { char const *keyfile = "/data/service/el1/public/hdc/hdc_keys"; if (!authEnable || HandDaemonAuthBypass()) { WRITE_LOG(LOG_INFO, "not enable secure, noneed clear keyfile"); return; } string authcancel; if (!SystemDepend::GetDevItem("persist.hdc.daemon.auth_cancel", authcancel)) { WRITE_LOG(LOG_FATAL, "get param auth_cancel failed"); return; } if (authcancel != "true") { WRITE_LOG(LOG_FATAL, "param auth_cancel is not true: %s", authcancel.c_str()); return; } if (!SystemDepend::SetDevItem("persist.hdc.daemon.auth_cancel", "false")) { WRITE_LOG(LOG_FATAL, "set param auth_cancel failed"); } std::ofstream keyofs(keyfile, std::ios::out | std::ios::trunc); if (!keyofs.is_open()) { WRITE_LOG(LOG_FATAL, "open keyfile %s error", keyfile); return; } keyofs.flush(); keyofs.close(); WRITE_LOG(LOG_FATAL, "clear keyfile %s over", keyfile); return; } void HdcDaemon::UpdateKnownHosts(const string& key) { char const *keyfile = "/data/service/el1/public/hdc/hdc_keys"; std::ofstream keyofs(keyfile, std::ios::app); if (!keyofs.is_open()) { WRITE_LOG(LOG_FATAL, "open keyfile %s error", keyfile); return; } string keytmp = key + "\n"; keyofs.write(keytmp.c_str(), keytmp.length()); keyofs.flush(); keyofs.close(); WRITE_LOG(LOG_FATAL, "save new key [%s] into keyfile %s over", key.substr(0, key.size() / 2).c_str(), keyfile); return; } bool HdcDaemon::AlreadyInKnownHosts(const string& key) { char const *keyfile = "/data/service/el1/public/hdc/hdc_keys"; std::ifstream keyifs(keyfile); if (!keyifs.is_open()) { WRITE_LOG(LOG_FATAL, "open keyfile %s error", keyfile); return false; } std::string keys((std::istreambuf_iterator(keyifs)), std::istreambuf_iterator()); // the length of pubkey is 625 const int keyLength = 625; if (key.size() == keyLength && keys.find(key) != string::npos) { keyifs.close(); return true; } WRITE_LOG(LOG_FATAL, "key [%s] not in keyfile %s", key.substr(0, key.size() / 2).c_str(), keyfile); keyifs.close(); return false; } bool HdcDaemon::HandDaemonAuthInit(HSession hSession, const uint32_t channelId, SessionHandShake &handshake) { hSession->tokenRSA = Base::GetSecureRandomString(SHA_DIGEST_LENGTH); handshake.authType = AUTH_PUBLICKEY; /* * If we know client support RSA_3072_SHA512 in AUTH_NONE phase * Then told client that the server also support RSA_3072_SHA512 auth * Notice, before here is "handshake.buf = hSession->tokenRSA", but the server not use it */ if (hSession->verifyType == AuthVerifyType::RSA_3072_SHA512) { handshake.buf.clear(); Base::TlvAppend(handshake.buf, TAG_AUTH_TYPE, std::to_string(AuthVerifyType::RSA_3072_SHA512)); WRITE_LOG(LOG_INFO, "client support RSA_3072_SHA512 auth for %u session", hSession->sessionId); } string bufString = SerialStruct::SerializeToString(handshake); Send(hSession->sessionId, channelId, CMD_KERNEL_HANDSHAKE, reinterpret_cast(const_cast(bufString.c_str())), bufString.size()); InitSessionAuthInfo(hSession->sessionId, hSession->tokenRSA); return true; } bool HdcDaemon::HandDaemonAuthPubkey(HSession hSession, const uint32_t channelId, SessionHandShake &handshake) { bool ret = false; string hostname, pubkey; do { if (!GetHostPubkeyInfo(handshake.buf, hostname, pubkey)) { WRITE_LOG(LOG_FATAL, "get pubkey failed for %u", hSession->sessionId); break; } if (AlreadyInKnownHosts(pubkey)) { ret = true; break; } string confirmmsg = "[E000002]:The device unauthorized.\r\n"\ "This server's public key is not set.\r\n"\ "Please check for a confirmation dialog on your device.\r\n"\ "Otherwise try 'hdc kill' if that seems wrong."; std::thread notifymsg([this, &handshake, channelId, sessionId = hSession->sessionId, &confirmmsg]() { this->EchoHandshakeMsg(handshake, channelId, sessionId, confirmmsg); }); notifymsg.detach(); UserPermit permit = PostUIConfirm(hostname, pubkey); if (permit == ALLOWONCE) { WRITE_LOG(LOG_FATAL, "user allow onece for %u", hSession->sessionId); ret = true; } else if (permit == ALLOWFORVER) { WRITE_LOG(LOG_FATAL, "user allow forever for %u", hSession->sessionId); UpdateKnownHosts(pubkey); ret = true; } else { WRITE_LOG(LOG_FATAL, "user refuse for %u", hSession->sessionId); ret = false; } } while (0); if (ret) { SendAuthMsg(handshake, channelId, hSession, pubkey); } else { string notifymsg = "[E000003]:The device unauthorized.\r\n"\ "The user denied the access for the device.\r\n"\ "Please execute 'hdc kill' and redo your command,\r\n"\ "then check for a confirmation dialog on your device."; EchoHandshakeMsg(handshake, channelId, hSession->sessionId, notifymsg); } return true; } /* * tokenSignBase64 is Base64 encode of the signing from server * token is the source data same of server for signing */ bool HdcDaemon::RsaSignVerify(HSession hSession, EVP_PKEY_CTX *ctx, const string &tokenSignBase64, const string &token) { unsigned char tokenSha512[SHA512_DIGEST_LENGTH]; try { std::unique_ptr tokenRsaSign = std::make_unique(tokenSignBase64.length()); // Get the real token sign int tokenRsaSignLen = EVP_DecodeBlock(tokenRsaSign.get(), reinterpret_cast(tokenSignBase64.c_str()), tokenSignBase64.length()); if (tokenRsaSignLen <= 0) { WRITE_LOG(LOG_FATAL, "base64 decode token sign failed for session %u", hSession->sessionId); return false; } SHA512(reinterpret_cast(token.c_str()), token.size(), tokenSha512); if (EVP_PKEY_verify(ctx, tokenRsaSign.get(), tokenRsaSignLen, tokenSha512, sizeof(tokenSha512)) < 0) { WRITE_LOG(LOG_FATAL, "verify failed for session %u", hSession->sessionId); return false; } } catch (std::exception &e) { WRITE_LOG(LOG_FATAL, "sign verify failed for session %u with exception %s", hSession->sessionId, e.what()); return false; } WRITE_LOG(LOG_FATAL, "sign verify success for session %u", hSession->sessionId); return true; } bool HdcDaemon::AuthVerifyRsaSign(HSession hSession, const string &tokenSign, const string &token, RSA *rsa) { EVP_PKEY *signKey = nullptr; EVP_PKEY_CTX *ctx = nullptr; bool signRet = false; signKey = EVP_PKEY_new(); if (signKey == nullptr) { WRITE_LOG(LOG_FATAL, "EVP_PKEY_new failed"); return false; } do { if (EVP_PKEY_set1_RSA(signKey, rsa) <= 0) { WRITE_LOG(LOG_FATAL, "EVP_PKEY_new failed"); break; } // the length of vaild sign result for BASE64 can't bigger than EVP_PKEY_size(signKey) * 2 if (tokenSign.size() > ((size_t)EVP_PKEY_size(signKey) * (size_t)2)) { WRITE_LOG(LOG_FATAL, "invalid base64 sign size %zd for session %u", tokenSign.size(), hSession->sessionId); break; } ctx = EVP_PKEY_CTX_new(signKey, nullptr); if (ctx == nullptr) { WRITE_LOG(LOG_FATAL, "EVP_PKEY_CTX_new failed"); break; } if (EVP_PKEY_verify_init(ctx) <= 0) { WRITE_LOG(LOG_FATAL, "EVP_PKEY_CTX_new failed"); break; } if (EVP_PKEY_CTX_set_rsa_padding(ctx, RSA_PKCS1_PSS_PADDING) <= 0 || EVP_PKEY_CTX_set_rsa_pss_saltlen(ctx, RSA_PSS_SALTLEN_AUTO) <= 0) { WRITE_LOG(LOG_FATAL, "set saltlen or padding failed"); break; } if (EVP_PKEY_CTX_set_signature_md(ctx, EVP_sha512()) <= 0) { WRITE_LOG(LOG_FATAL, "EVP_PKEY_CTX_set_signature_md failed"); break; } signRet = RsaSignVerify(hSession, ctx, tokenSign, token); } while (0); if (ctx != nullptr) { EVP_PKEY_CTX_free(ctx); } if (signKey != nullptr) { EVP_PKEY_free(signKey); } return signRet; } bool HdcDaemon::AuthVerify(HSession hSession, const string &encryptToken, const string &token, const string &pubkey) { BIO *bio = nullptr; RSA *rsa = nullptr; const unsigned char *pubkeyp = reinterpret_cast(pubkey.c_str()); bool verifyResult = false; do { bio = BIO_new(BIO_s_mem()); if (bio == nullptr) { WRITE_LOG(LOG_FATAL, "bio failed for session %u", hSession->sessionId); break; } int wbytes = BIO_write(bio, pubkeyp, pubkey.length()); if (wbytes <= 0) { WRITE_LOG(LOG_FATAL, "bio write failed %d for session %u", wbytes, hSession->sessionId); break; } rsa = PEM_read_bio_RSA_PUBKEY(bio, nullptr, nullptr, nullptr); if (rsa == nullptr) { WRITE_LOG(LOG_FATAL, "rsa failed for session %u", hSession->sessionId); break; } if (hSession->verifyType == AuthVerifyType::RSA_3072_SHA512) { verifyResult = AuthVerifyRsaSign(hSession, encryptToken, token, rsa); } else { verifyResult = AuthVerifyRsa(hSession, encryptToken, token, rsa); } } while (0); if (bio) { BIO_free(bio); } if (rsa) { RSA_free(rsa); } return verifyResult; } bool HdcDaemon::AuthVerifyRsa(HSession hSession, const string &encryptToken, const string &token, RSA *rsa) { const unsigned char *tokenp = reinterpret_cast(encryptToken.c_str()); unsigned char tokenDecode[BUF_SIZE_DEFAULT] = { 0 }; unsigned char decryptToken[BUF_SIZE_DEFAULT] = { 0 }; // for rsa encrypt, the length of encryptToken can't bigger than BUF_SIZE_DEFAULT // Base64 divides every 3 bytes(8 bits) of the original data into 4 segments(6 bits), // each of which is then mapped to a new character. const int scaleOfEncrypt = 4; const int scaleOfDecode = 3; if (encryptToken.length() > BUF_SIZE_DEFAULT / scaleOfDecode * scaleOfEncrypt) { WRITE_LOG(LOG_FATAL, "invalid encryptToken, length is %zd", encryptToken.length()); return false; } int tbytes = EVP_DecodeBlock(tokenDecode, tokenp, encryptToken.length()); if (tbytes <= 0) { WRITE_LOG(LOG_FATAL, "base64 decode pubkey failed"); return false; } int bytes = RSA_public_decrypt(tbytes, tokenDecode, decryptToken, rsa, RSA_PKCS1_PADDING); if (bytes < 0) { WRITE_LOG(LOG_FATAL, "decrypt failed(%lu) for session %u", ERR_get_error(), hSession->sessionId); return false; } string sdecryptToken(reinterpret_cast(decryptToken), bytes); if (sdecryptToken != token) { WRITE_LOG(LOG_FATAL, "auth failed for session %u)", hSession->sessionId); return false; } return true; } bool HdcDaemon::HandDaemonAuthSignature(HSession hSession, const uint32_t channelId, SessionHandShake &handshake) { // When Host is first connected to the device, the signature authentication is inevitable, and the // certificate verification must be triggered. // // When the certificate is verified, the client sends a public key to the device, triggered the system UI // jump out dialog, and click the system, the system will store the Host public key certificate in the // device locally, and the signature authentication will be correct when the subsequent connection is // connected. string token = GetSessionAuthToken(hSession->sessionId); string pubkey = GetSessionAuthPubkey(hSession->sessionId); if (!AuthVerify(hSession, handshake.buf, token, pubkey)) { WRITE_LOG(LOG_FATAL, "auth failed for session %u", hSession->sessionId); // Next auth EchoHandshakeMsg(handshake, channelId, hSession->sessionId, "[E000010]:Auth failed, cannt login the device."); return true; } WRITE_LOG(LOG_FATAL, "auth success for session %u", hSession->sessionId); UpdateSessionAuthOk(hSession->sessionId); SendAuthOkMsg(handshake, channelId, hSession->sessionId); return true; } bool HdcDaemon::HandDaemonAuthBypass(void) { // persist.hdc.auth_bypass 1 is bypass orelse(0 or not set) not bypass string authbypass; SystemDepend::GetDevItem("persist.hdc.auth_bypass", authbypass); return Base::Trim(authbypass) == "1"; } bool HdcDaemon::HandDaemonAuth(HSession hSession, const uint32_t channelId, SessionHandShake &handshake) { if (!authEnable) { WRITE_LOG(LOG_INFO, "not enable secure, allow access for %u", hSession->sessionId); UpdateSessionAuthOk(hSession->sessionId); SendAuthOkMsg(handshake, channelId, hSession->sessionId); return true; } else if (handshake.version < "Ver: 3.0.0b") { WRITE_LOG(LOG_INFO, "session %u client version %s is too low %u authType %d", hSession->sessionId, handshake.version.c_str(), handshake.authType); AuthRejectLowClient(handshake, channelId, hSession->sessionId); return true; } else if (GetSessionAuthStatus(hSession->sessionId) == AUTH_OK) { WRITE_LOG(LOG_INFO, "session %u already auth ok", hSession->sessionId); return true; } if (handshake.authType == AUTH_NONE) { return HandDaemonAuthInit(hSession, channelId, handshake); } else if (GetSessionAuthStatus(hSession->sessionId) == AUTH_NONE && GetSessionAuthToken(hSession->sessionId).size() > 0 && handshake.authType == AUTH_PUBLICKEY) { return HandDaemonAuthPubkey(hSession, channelId, handshake); } else if (GetSessionAuthStatus(hSession->sessionId) == AUTH_PUBLICKEY && handshake.authType == AUTH_SIGNATURE) { return HandDaemonAuthSignature(hSession, channelId, handshake); #ifdef HDC_SUPPORT_ENCRYPT_TCP } else if (GetSessionAuthStatus(hSession->sessionId) == AUTH_PUBLICKEY && handshake.authType == AUTH_SSL_TLS_PSK) { return DaemonSSLHandshake(hSession, channelId, handshake); #endif } else { WRITE_LOG(LOG_FATAL, "invalid auth state %d for session %u", handshake.authType, hSession->sessionId); return false; } } /* * For daemon, if server add new capability, we can parse it here */ void HdcDaemon::GetServerCapability(HSession &hSession, SessionHandShake &handshake) { /* * Check if server support RSA_3072_SHA512 for auth * if the value not contain RSA_3072_SHA512, We treat it not support */ std::map tlvMap; hSession->verifyType = AuthVerifyType::RSA_ENCRYPT; if (!Base::TlvToStringMap(handshake.buf, tlvMap)) { WRITE_LOG(LOG_INFO, "maybe old version client for %u session", hSession->sessionId); return; } if (tlvMap.find(TAG_AUTH_TYPE) != tlvMap.end() && tlvMap[TAG_AUTH_TYPE] == std::to_string(AuthVerifyType::RSA_3072_SHA512)) { hSession->verifyType = AuthVerifyType::RSA_3072_SHA512; } WRITE_LOG(LOG_INFO, "client auth type is %u for %u session", hSession->verifyType, hSession->sessionId); //Get server support features ParsePeerSupportFeatures(hSession, tlvMap); } void HdcDaemon::DaemonSessionHandshakeInit(HSession &hSession, SessionHandShake &handshake) { // daemon handshake 1st packet uint32_t unOld = hSession->sessionId; hSession->sessionId = handshake.sessionId; hSession->connectKey = handshake.connectKey; hSession->handshakeOK = false; AdminSession(OP_UPDATE, unOld, hSession); #ifdef HDC_SUPPORT_UART if (hSession->connType == CONN_SERIAL and clsUARTServ!= nullptr) { WRITE_LOG(LOG_DEBUG, " HdcDaemon::DaemonSessionHandshake %s", handshake.ToDebugString().c_str()); if (clsUARTServ != nullptr) { (static_cast(clsUARTServ))->OnNewHandshakeOK(hSession->sessionId); } } else #endif // HDC_SUPPORT_UART if (clsUSBServ != nullptr && hSession->connType == CONN_USB) { (reinterpret_cast(clsUSBServ))->OnNewHandshakeOK(hSession->sessionId); } handshake.sessionId = 0; handshake.connectKey = ""; // Get server capability GetServerCapability(hSession, handshake); } // host( ) ---(TLS handshake client hello )--> hdcd( ) step 1 // host( ) <--(TLS handshake server hello )--- hdcd( ) step 2 // host(ok) ---(TLS handshake change cipher)--> hdcd( ) step 3 // host(ok) <--(TLS handshake change cipher)--- hdcd(ok) step 4 #ifdef HDC_SUPPORT_ENCRYPT_TCP bool HdcDaemon::DaemonSSLHandshake(HSession hSession, const uint32_t channelId, SessionHandShake &handshake) { uint8_t *payload = reinterpret_cast(handshake.buf.data()); int payloadSize = static_cast(handshake.buf.size()); if (!hSession->classSSL) { WRITE_LOG(LOG_WARN, "DaemonSSLHandshake classSSL is nullptr"); return false; } HdcSSLBase *hssl = static_cast(hSession->classSSL); if (!hssl) { WRITE_LOG(LOG_WARN, "hssl is null"); return false; } if (payloadSize > 0) { int retw = hssl->DoBIOWrite(payload, payloadSize); if (retw != payloadSize) { WRITE_LOG(LOG_WARN, "BIO_write failed, payloadsize is %d", payloadSize); return false; } } vector buf; int ret = hssl->PerformHandshake(buf); if (ret == RET_SUCCESS) { // SSL handshake step 2 and step 4 if (buf.size() == 0) { // no handshake data WRITE_LOG(LOG_WARN, "SSL PerformHandshake failed, buffer data size is 0"); return false; } handshake.buf.assign(buf.begin(), buf.end()); string bufString = SerialStruct::SerializeToString(handshake); Send(hSession->sessionId, channelId, CMD_KERNEL_HANDSHAKE, reinterpret_cast(const_cast(bufString.c_str())), bufString.size()); } if (hssl->SetHandshakeLabel(hSession)) { std::this_thread::sleep_for(std::chrono::milliseconds(SSL_HANDSHAKE_FINISHED_WAIT_TIME)); UpdateSessionAuthOk(hSession->sessionId); SendAuthOkMsg(handshake, channelId, hSession->sessionId); if (!hssl->ClearPsk()) { WRITE_LOG(LOG_WARN, "clear Pre Shared Key failed"); ret = ERR_GENERIC; } } fill(buf.begin(), buf.end(), 0); return ret == RET_SUCCESS; } #endif bool HdcDaemon::DaemonSessionHandshake(HSession hSession, const uint32_t channelId, uint8_t *payload, int payloadSize) { StartTraceScope("HdcDaemon::DaemonSessionHandshake"); // session handshake step2 string s = string(reinterpret_cast(payload), payloadSize); SessionHandShake handshake; string err; SerialStruct::ParseFromString(handshake, s); #ifdef HDC_DEBUG WRITE_LOG(LOG_DEBUG, "session %s try to handshake", hSession->ToDebugString().c_str()); #endif // banner to check is parse ok... if (handshake.banner != HANDSHAKE_MESSAGE) { hSession->availTailIndex = 0; WRITE_LOG(LOG_FATAL, "Recv server-hello failed"); return false; } if (handshake.authType == AUTH_NONE) { if (GetSessionAuthStatus(handshake.sessionId) != AUTH_NONE) { WRITE_LOG(LOG_FATAL, "session %u is already, But now session is %u, refused!", handshake.sessionId, hSession->sessionId); return false; } DaemonSessionHandshakeInit(hSession, handshake); } if (!HandDaemonAuth(hSession, channelId, handshake)) { WRITE_LOG(LOG_FATAL, "auth failed"); return false; } string version = Base::GetVersion() + HDC_MSG_HASH; WRITE_LOG(LOG_DEBUG, "receive hs version = %s", handshake.version.c_str()); if (!handshake.version.empty() && handshake.version != version) { WRITE_LOG(LOG_FATAL, "DaemonSessionHandshake failed! version not match [%s] vs [%s]", handshake.version.c_str(), version.c_str()); #ifdef HDC_CHECK_CHECK hSession->availTailIndex = 0; handshake.banner = HANDSHAKE_FAILED; string failedString = SerialStruct::SerializeToString(handshake); Send(hSession->sessionId, channelId, CMD_KERNEL_HANDSHAKE, (uint8_t *)failedString.c_str(), failedString.size()); return false; #endif } if (handshake.version.empty()) { handshake.version = Base::GetVersion(); WRITE_LOG(LOG_FATAL, "set version if check mode = %s", handshake.version.c_str()); } // handshake auth OK.Can append the sending device information to HOST #ifdef HDC_DEBUG WRITE_LOG(LOG_INFO, "session %u handshakeOK send back CMD_KERNEL_HANDSHAKE", hSession->sessionId); #endif hSession->handshakeOK = true; return true; } bool HdcDaemon::IsExpectedParam(const string& param, const string& expect) { string out; SystemDepend::GetDevItem(param.c_str(), out); return (out.empty() || out == expect); // default empty } bool HdcDaemon::CheckControl(const uint16_t command) { bool ret = false; // default no debug switch (command) { // this switch is match RedirectToTask function case CMD_UNITY_EXECUTE: case CMD_UNITY_EXECUTE_EX: case CMD_UNITY_REMOUNT: case CMD_UNITY_REBOOT: case CMD_UNITY_RUNMODE: case CMD_UNITY_HILOG: case CMD_UNITY_ROOTRUN: case CMD_UNITY_BUGREPORT_INIT: case CMD_JDWP_LIST: case CMD_JDWP_TRACK: case CMD_SHELL_INIT: case CMD_SHELL_DATA: { ret = IsExpectedParam("persist.hdc.control.shell", "true"); break; } case CMD_FILE_CHECK: case CMD_FILE_DATA: case CMD_FILE_FINISH: case CMD_FILE_INIT: case CMD_FILE_BEGIN: case CMD_FILE_MODE: case CMD_DIR_MODE: case CMD_APP_CHECK: case CMD_APP_DATA: case CMD_APP_UNINSTALL: { ret = IsExpectedParam("persist.hdc.control.file", "true"); break; } case CMD_FORWARD_INIT: case CMD_FORWARD_CHECK: case CMD_FORWARD_ACTIVE_MASTER: case CMD_FORWARD_ACTIVE_SLAVE: case CMD_FORWARD_DATA: case CMD_FORWARD_FREE_CONTEXT: case CMD_FORWARD_CHECK_RESULT: { ret = IsExpectedParam("persist.hdc.control.fport", "true"); break; } default: ret = true; // other ECHO_RAW and so on } return ret; } bool HdcDaemon::CheckAuthStatus(HSession hSession, const uint32_t channelId, const uint16_t command) { if (authEnable && (GetSessionAuthStatus(hSession->sessionId) != AUTH_OK) && command != CMD_KERNEL_HANDSHAKE && command != CMD_KERNEL_CHANNEL_CLOSE && command != CMD_SSL_HANDSHAKE) { string authmsg = GetSessionAuthmsg(hSession->sessionId); WRITE_LOG(LOG_WARN, "session %u auth failed: %s for command %u", hSession->sessionId, authmsg.c_str(), command); if (!authmsg.empty()) { LogMsg(hSession->sessionId, channelId, MSG_FAIL, authmsg.c_str()); } uint8_t count = 1; Send(hSession->sessionId, channelId, CMD_KERNEL_CHANNEL_CLOSE, &count, 1); return false; } return true; } bool HdcDaemon::FetchCommand(HSession hSession, const uint32_t channelId, const uint16_t command, uint8_t *payload, const int payloadSize) { StartTraceScope("HdcDaemon::FetchCommand"); bool ret = true; if (!CheckAuthStatus(hSession, channelId, command)) { return true; } if (command != CMD_UNITY_BUGREPORT_DATA && command != CMD_SHELL_DATA && command != CMD_FORWARD_DATA && command != CMD_FILE_DATA && command != CMD_APP_DATA) { WRITE_LOG(LOG_DEBUG, "FetchCommand channelId:%u command:%u", channelId, command); } switch (command) { case CMD_KERNEL_HANDSHAKE: { // session handshake step2 ret = DaemonSessionHandshake(hSession, channelId, payload, payloadSize); break; } case CMD_KERNEL_CHANNEL_CLOSE: { // Daemon is only cleaning up the Channel task ClearOwnTasks(hSession, channelId); if (payloadSize >= 1 && *payload != 0) { --(*payload); Send(hSession->sessionId, channelId, CMD_KERNEL_CHANNEL_CLOSE, payload, 1); } ret = true; break; } case CMD_HEARTBEAT_MSG: { // heartbeat msg std::string str = hSession->heartbeat.HandleRecvHeartbeatMsg(payload, payloadSize); WRITE_LOG(LOG_INFO, "recv %s for session %u", str.c_str(), hSession->sessionId); break; } default: ret = true; if (CheckControl(command)) { ret = DispatchTaskData(hSession, channelId, command, payload, payloadSize); } else { LogMsg(hSession->sessionId, channelId, MSG_FAIL, "debugging is not allowed"); uint8_t count = 1; Send(hSession->sessionId, channelId, CMD_KERNEL_CHANNEL_CLOSE, &count, 1); } break; } return ret; } bool HdcDaemon::RemoveInstanceTask(const uint8_t op, HTaskInfo hTask) { bool ret = true; if (!hTask->taskClass) { WRITE_LOG(LOG_FATAL, "RemoveInstanceTask taskClass is null channelId:%u", hTask->channelId); return ret; } switch (hTask->taskType) { case TYPE_UNITY: ret = DoTaskRemove(hTask, op); break; case TYPE_SHELL: ret = DoTaskRemove(hTask, op); break; case TASK_FILE: ret = DoTaskRemove(hTask, op); break; case TASK_FORWARD: ret = DoTaskRemove(hTask, op); break; case TASK_APP: ret = DoTaskRemove(hTask, op); break; default: ret = false; break; } return ret; } bool HdcDaemon::ServerCommand(const uint32_t sessionId, const uint32_t channelId, const uint16_t command, uint8_t *bufPtr, const int size) { return Send(sessionId, channelId, command, reinterpret_cast(bufPtr), size) > 0; } void HdcDaemon::JdwpNewFileDescriptor(const uint8_t *buf, const int bytesIO) { uint8_t spcmd = *const_cast(buf); if (spcmd == SP_JDWP_NEWFD) { int cnt = sizeof(uint8_t) + sizeof(uint32_t) * 2; if (bytesIO < cnt) { WRITE_LOG(LOG_FATAL, "jdwp newfd data insufficient bytesIO:%d", bytesIO); return; } uint32_t pid = *reinterpret_cast(const_cast(buf + 1)); uint32_t fd = *reinterpret_cast(const_cast(buf + 5)); // 5 : fd offset ((HdcJdwp *)clsJdwp)->SendJdwpNewFD(pid, fd); } else if (spcmd == SP_ARK_NEWFD) { // SP_ARK_NEWFD | fd[1] | ark:pid@tid@Debugger int cnt = sizeof(uint8_t) + sizeof(uint32_t); if (bytesIO < cnt) { WRITE_LOG(LOG_FATAL, "ark newfd data insufficient bytesIO:%d", bytesIO); return; } int32_t fd = *reinterpret_cast(const_cast(buf + 1)); std::string arkstr = std::string( reinterpret_cast(const_cast(buf + 5)), bytesIO - 5); // 5 : fd offset WRITE_LOG(LOG_DEBUG, "JdwpNewFileDescriptor arkstr:%s fd:%d", arkstr.c_str(), fd); ((HdcJdwp *)clsJdwp)->SendArkNewFD(arkstr, fd); } } void HdcDaemon::NotifyInstanceSessionFree(HSession hSession, bool freeOrClear) { if (!freeOrClear) { WRITE_LOG(LOG_WARN, "NotifyInstanceSessionFree freeOrClear false"); return; // ignore step 1 } if (clsUSBServ != nullptr) { auto clsUsbModule = reinterpret_cast(clsUSBServ); clsUsbModule->OnSessionFreeFinally(hSession); } } void HdcDaemon::InitSessionAuthInfo(uint32_t sessionid, string token) { HdcDaemonAuthInfo info = { AUTH_NONE, token }; mapAuthStatusMutex.lock(); mapAuthStatus[sessionid] = info; mapAuthStatusMutex.unlock(); } void HdcDaemon::UpdateSessionAuthOk(uint32_t sessionid) { HdcDaemonAuthInfo info; mapAuthStatusMutex.lock(); info = mapAuthStatus[sessionid]; info.authtype = AUTH_OK; info.token = ""; info.pubkey = ""; mapAuthStatus[sessionid] = info; mapAuthStatusMutex.unlock(); } void HdcDaemon::UpdateSessionAuthPubkey(uint32_t sessionid, string pubkey) { HdcDaemonAuthInfo info; mapAuthStatusMutex.lock(); info = mapAuthStatus[sessionid]; info.authtype = AUTH_PUBLICKEY; info.pubkey = pubkey; mapAuthStatus[sessionid] = info; mapAuthStatusMutex.unlock(); } void HdcDaemon::UpdateSessionAuthmsg(uint32_t sessionid, string authmsg) { HdcDaemonAuthInfo info; mapAuthStatusMutex.lock(); info = mapAuthStatus[sessionid]; info.authtype = AUTH_FAIL; info.authmsg = authmsg; mapAuthStatus[sessionid] = info; mapAuthStatusMutex.unlock(); } void HdcDaemon::DeleteSessionAuthStatus(uint32_t sessionid) { mapAuthStatusMutex.lock(); mapAuthStatus.erase(sessionid); mapAuthStatusMutex.unlock(); } HdcSessionBase::AuthType HdcDaemon::GetSessionAuthStatus(uint32_t sessionid) { HdcDaemonAuthInfo info; info.authtype = AUTH_NONE; mapAuthStatusMutex.lock(); if (mapAuthStatus.count(sessionid) > 0) { info = mapAuthStatus[sessionid]; } mapAuthStatusMutex.unlock(); return info.authtype; } string HdcDaemon::GetSessionAuthToken(uint32_t sessionid) { HdcDaemonAuthInfo info; mapAuthStatusMutex.lock(); if (mapAuthStatus.count(sessionid) > 0) { info = mapAuthStatus[sessionid]; } mapAuthStatusMutex.unlock(); return info.token; } string HdcDaemon::GetSessionAuthPubkey(uint32_t sessionid) { HdcDaemonAuthInfo info; mapAuthStatusMutex.lock(); if (mapAuthStatus.count(sessionid) > 0) { info = mapAuthStatus[sessionid]; } mapAuthStatusMutex.unlock(); return info.pubkey; } string HdcDaemon::GetSessionAuthmsg(uint32_t sessionid) { HdcDaemonAuthInfo info; mapAuthStatusMutex.lock(); if (mapAuthStatus.count(sessionid) > 0) { info = mapAuthStatus[sessionid]; } mapAuthStatusMutex.unlock(); return info.authmsg; } void HdcDaemon::SendAuthOkMsg(SessionHandShake &handshake, uint32_t channelid, uint32_t sessionid, string msg, string daemonAuthResult) { char hostname[BUF_SIZE_MEDIUM] = { 0 }; if (gethostname(hostname, BUF_SIZE_MEDIUM) != 0) { WRITE_LOG(LOG_FATAL, "get hostname failed %s", strerror(errno)); } if (handshake.version < "Ver: 3.0.0b") { if (msg.empty()) { msg = hostname; } handshake.buf = msg; } else { string emgmsg; Base::TlvAppend(emgmsg, TAG_EMGMSG, msg); Base::TlvAppend(emgmsg, TAG_DEVNAME, hostname); Base::TlvAppend(emgmsg, TAG_DAEOMN_AUTHSTATUS, daemonAuthResult); AddFeatureTagToEmgmsg(emgmsg); handshake.buf = emgmsg; } handshake.authType = AUTH_OK; string bufString = SerialStruct::SerializeToString(handshake); Send(sessionid, channelid, CMD_KERNEL_HANDSHAKE, reinterpret_cast(const_cast(bufString.c_str())), bufString.size()); uint8_t count = 1; Send(sessionid, channelid, CMD_KERNEL_CHANNEL_CLOSE, &count, 1); } void HdcDaemon::SendAuthSignMsg(SessionHandShake &handshake, uint32_t channelId, uint32_t sessionid, string pubkey, string token) { UpdateSessionAuthPubkey(sessionid, pubkey); handshake.authType = AUTH_SIGNATURE; handshake.buf = token; string bufString = SerialStruct::SerializeToString(handshake); Send(sessionid, channelId, CMD_KERNEL_HANDSHAKE, reinterpret_cast(const_cast(bufString.c_str())), bufString.size()); } void HdcDaemon::SendAuthMsg(SessionHandShake &handshake, const uint32_t channelId, HSession &hSession, string pubkey) { #ifdef HDC_SUPPORT_ENCRYPT_TCP if (hSession->connType == CONN_TCP && hSession->supportEncrypt) { SendAuthEncryptPsk(handshake, channelId, hSession, pubkey); return; } #endif SendAuthSignMsg(handshake, channelId, hSession->sessionId, pubkey, hSession->tokenRSA); } #ifdef HDC_SUPPORT_ENCRYPT_TCP void HdcDaemon::SendAuthEncryptPsk(SessionHandShake &handshake, const uint32_t channelId, HSession &hSession, string pubkey) { UpdateSessionAuthPubkey(hSession->sessionId, pubkey); handshake.authType = AUTH_SSL_TLS_PSK; if (!hSession->classSSL) { SSLInfoPtr hSSLInfo = new (std::nothrow) HdcSSLInfo(); if (!hSSLInfo) { WRITE_LOG(LOG_FATAL, "SendAuthEncryptPsk new HdcSSLInfo failed"); return; } HdcSSLBase::SetSSLInfo(hSSLInfo, hSession); hSession->classSSL = new (std::nothrow) HdcDaemonSSL(hSSLInfo); // long lifetime with session. delete hSSLInfo; if (!hSession->classSSL) { WRITE_LOG(LOG_FATAL, "SendAuthEncryptPsk new HdcDaemonSSL failed"); return; } } HdcSSLBase *hssl = static_cast(hSession->classSSL); if (!hssl) { WRITE_LOG(LOG_WARN, "hssl is null"); return; } if (!hssl->GenPsk()) { WRITE_LOG(LOG_WARN, "gen psk failed"); return; } std::unique_ptr payload(std::make_unique(BUF_SIZE_DEFAULT2)); int payloadSize = hssl->GetPskEncrypt(payload.get(), BUF_SIZE_DEFAULT2, pubkey); if (payloadSize <= 0) { WRITE_LOG(LOG_WARN, "RsaPubkeyEncrpt failed"); return; } handshake.buf = string(reinterpret_cast(payload.get()), payloadSize); string bufString = SerialStruct::SerializeToString(handshake); Send(hSession->sessionId, channelId, CMD_KERNEL_HANDSHAKE, reinterpret_cast(const_cast(bufString.c_str())), bufString.size()); hssl->InitSSL(); } #endif void HdcDaemon::EchoHandshakeMsg(SessionHandShake &handshake, uint32_t channelid, uint32_t sessionid, string msg) { SendAuthOkMsg(handshake, channelid, sessionid, msg, DAEOMN_UNAUTHORIZED); LogMsg(sessionid, channelid, MSG_FAIL, msg.c_str()); UpdateSessionAuthmsg(sessionid, msg); } void HdcDaemon::AuthRejectLowClient(SessionHandShake &handshake, uint32_t channelid, uint32_t sessionid) { string msg = "[E000001]:The sdk hdc.exe version is too low, please upgrade to the latest version."; EchoHandshakeMsg(handshake, channelid, sessionid, msg); } void HdcDaemon::AddFeatureTagToEmgmsg(string &emgmsg) { Base::TlvAppend(emgmsg, TAG_FEATURE_SHELL_OPT, "enable"); //told server, we support features Base::TlvAppend(emgmsg, TAG_SUPPORT_FEATURE, Base::FeatureToString(Base::GetSupportFeature())); } } // namespace Hdc