Files
SimpleRemoter/client/TlsChannel.h
T
yuanyuanxianganddeepseek-v4-pro ca0d7626d2 Feat: One-way TLS transport for the device port
Add optional one-way TLS on the device port (6543), enabled per client via a
pinned trust anchor, so new clients encrypt their transport while legacy
plaintext clients keep working unchanged.

Server (server/go): sniff each accepted connection and wrap TLS peers
(ClientHello 0x16 0x03 + handshake type 0x01) in crypto/tls with MinVersion
TLS 1.2; everything else stays on the plaintext path. TLS is enabled only when
both YAMA_TLS_CERT and YAMA_TLS_KEY are set, and a cert/key load failure fails
startup loudly rather than silently serving plaintext.

Client (client/, Windows): Schannel one-way TLS. When the trust anchor is
present — registry value Software\<exehash>\settings\tls_cert (REG_SZ holding
the PEM), with fallback to settings.ini [settings] tls_cert or yama-ca.pem
beside the exe — every outbound connection (main + sub-connections) completes
a TLS 1.2 handshake before the app protocol and is strictly pinned to that
anchor via a chain engine with hExclusiveRoot; any handshake/cert failure is
fail-closed (no plaintext downgrade). Send uses EncryptMessage; Recv uses
DecryptMessage and reads plaintext from the stream-mode DATA segment (bufs[1])
rather than the record header.

Co-Authored-By: deepseek-v4-pro
2026-10-14 21:10:21 +02:00

89 lines
4.0 KiB
C++
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
// TlsChannel.h — Windows 客户端单向 TLS(Schannel)会话封装
//
// 用途:为 YAMA 客户端(被控端)提供「按需开启」的传输层加密。当信任锚证书存在时
// (注册表 Software\<exehash>\settings\tls_cert,或 yama-ca.pem 文件,见 TlsChannel.cpp
// 的 InitTls),所有出站 TCP 连接在应用协议首包前先完成 TLS 握手;握手失败一律断开
// (fail-closed,不回退明文)。
//
// 选型:Schannel(Windows 原生 SSPI),零第三方依赖;secur32.lib / crypt32.lib 为
// 系统库。单向 TLS:客户端只校验服务端证书链到固定的信任锚,不提供客户端证书,
// 客户端身份仍由隧道内登录(V1 PwdHmac / V2 ECDSA)承担。
//
// 线程模型:Handshake 在连接建立线程上、m_bConnected 置位前调用;Send/Recv 分别在
// 发送线程 / 接收线程上调用。上下文生命周期(Handshake/CloseHandles 释放 m_ctx)与
// Send/Recv 对 m_ctx 的使用(EncryptMessage/DecryptMessage)由 m_ctxMtx 互斥,避免
// 重连线程释放上下文时与使用线程产生 use-after-free。本对象作为 IOCPClient 的值成员
// 存在,析构时释放上下文。
//
// 注意:本文件仅在 _WIN32 下有效,非 Windows 构建(Linux/macOS/Android)不含任何内容,
// 客户端维持明文(TLS 接入按设计文档延后)。
#pragma once
#ifdef _WIN32
#include <WinSock2.h>
#ifndef SECURITY_WIN32
#define SECURITY_WIN32 1
#endif
#include <security.h>
#include <schannel.h>
#include <mutex>
#include <vector>
// 进程级 TLS 开关:首次调用时加载信任锚(注册表或文件),返回是否启用。线程安全、幂等。
bool TlsEnabled();
// 信任锚已配置但加载失败(损坏/格式错误)时为 true:表示管理员已「颁发」证书,
// 客户端必须拒绝连接(fail-closed),不得静默回退明文。线程安全、幂等。
bool TlsLoadError();
class TlsChannel
{
public:
TlsChannel() {}
~TlsChannel() { CloseHandles(); }
// 持 Schannel 句柄,禁止拷贝/赋值(避免句柄被二次释放)。
TlsChannel(const TlsChannel&) = delete;
TlsChannel& operator=(const TlsChannel&) = delete;
// 在已连通的 socket 上完成 TLS 客户端握手并校验服务端证书链。失败返回 false,
// 调用方必须关闭 socket(fail-closed)。可重复调用(重连时复用同一对象)。
bool Handshake(SOCKET sock);
// 解密读取:返回 >0 的明文字节数;0 表示对端正常关闭;SOCKET_ERROR(-1) 出错。
int Recv(SOCKET sock, char* buf, int len);
// 加密发送 len 字节:成功返回已完整发送的明文字节数(== len);失败返回已发送的
// 前缀字节数(>0)或 SOCKET_ERROR(一个字节都没发出去时)。
int Send(SOCKET sock, const char* buf, int len);
// 是否有已解密但尚未交付给应用层的明文(供接收线程在 select 前优先消费)。
bool HasPending() const { return !m_pending.empty(); }
private:
void CloseHandles();
bool SendAll(SOCKET sock, const void* data, int len);
bool VerifyServerCert();
CredHandle m_cred = {};
CtxtHandle m_ctx = {};
bool m_credValid = false; // m_cred 已分配,需 FreeCredentialsHandle
bool m_ctxValid = false; // m_ctx 已分配,需 DeleteSecurityContext
bool m_ready = false; // 握手完成,Send/Recv 可用
SecPkgContext_StreamSizes m_sizes = {};
// 保护上下文生命周期:重连线程的 Handshake/CloseHandles(释放 m_ctx)与工作线程的
// Recv(DecryptMessage)、发送线程的 Send(EncryptMessage)互斥,避免 use-after-free。
// recursive 是因为 Handshake 内部会再调 CloseHandles。
std::recursive_mutex m_ctxMtx;
std::vector<BYTE> m_raw; // 尚未解密 / 未消费完的 TLS 记录原始字节
std::vector<BYTE> m_pending; // 已解密、未交付给应用层的明文
};
#endif // _WIN32