本文分析SPICE的网络传输层实现,包括SSL/TLS加密、SASL认证和WebSocket支持。
背景与安全需求
SPICE传输敏感的桌面数据,安全性至关重要:
- 数据加密:防止网络窃听
- 身份认证:防止未授权访问
- 传输灵活性:支持多种网络环境
SPICE通过分层设计支持多种安全机制的组合。
整体架构

RedStream 网络流
核心结构
RedStream封装了底层socket,提供统一的读写接口,支持多种传输层。
cpp
// red-stream.h
struct RedStream {
int socket; // 底层socket文件描述符
SpiceWatch *watch; // 事件监视器
RedStreamPrivate *priv; // 私有数据
};
// red-stream.cpp
struct RedStreamPrivate {
// ===== SSL支持 =====
SSL *ssl; // OpenSSL连接对象
// ===== SASL支持 =====
#if HAVE_SASL
RedSASL sasl; // SASL认证状态
#endif
// ===== WebSocket支持 =====
RedsWebSocket *ws; // WebSocket状态
// ===== 异步读取 =====
AsyncRead async_read; // 异步读取状态机
// ===== 连接信息 =====
SpiceChannelEventInfo* info; // 连接事件信息
// ===== Cork优化 =====
bool use_cork; // 是否使用TCP_CORK
bool corked; // 当前cork状态
// ===== 函数指针(传输层切换) =====
ssize_t (*read)(RedStream *s, void *buf, size_t nbyte);
ssize_t (*write)(RedStream *s, const void *buf, size_t nbyte);
ssize_t (*writev)(RedStream *s, const struct iovec *iov, int iovcnt);
// ===== 关联 =====
RedsState *reds;
SpiceCoreInterfaceInternal *core;
};
传输层切换机制:

通过函数指针动态切换传输层:
| 函数指针 | 传输层 | 说明 |
|---|---|---|
stream_socket_read/write |
普通TCP | 默认传输 |
stream_ssl_read/write |
SSL加密 | TLS加密传输 |
stream_sasl_read/write |
SASL层 | SASL安全层 |
stream_websocket_read/write |
WebSocket | 浏览器支持 |
SSL/TLS 加密
SSL 初始化
cpp
// reds.cpp
static int reds_init_ssl(RedsState *reds)
{
// ===== 安全选项配置 =====
long ssl_options =
SSL_OP_NO_SSLv2 | // 禁用SSLv2(已废弃)
SSL_OP_NO_SSLv3 | // 禁用SSLv3(POODLE漏洞)
SSL_OP_NO_TLSv1 | // 禁用TLSv1.0(弱)
SSL_OP_NO_COMPRESSION; // 禁用压缩(CRIME攻击)
#ifdef SSL_OP_NO_RENEGOTIATION
// 禁用重协商(防止DoS和降级攻击)
ssl_options |= SSL_OP_NO_RENEGOTIATION;
#endif
// ===== 全局初始化 =====
openssl_global_init();
// ===== 创建SSL上下文 =====
const SSL_METHOD *ssl_method = TLS_method();
reds->ctx = SSL_CTX_new(ssl_method);
// 应用安全选项
SSL_CTX_set_options(reds->ctx, ssl_options);
// ===== ECDH自动选择 =====
#if HAVE_DECL_SSL_CTX_SET_ECDH_AUTO
SSL_CTX_set_ecdh_auto(reds->ctx, 1);
#endif
// ===== 加载证书 =====
// 证书链文件
SSL_CTX_use_certificate_chain_file(
reds->ctx,
reds->config->ssl_parameters.certs_file);
// ===== 加载私钥 =====
SSL_CTX_set_default_passwd_cb(reds->ctx, ssl_password_cb);
SSL_CTX_use_PrivateKey_file(
reds->ctx,
reds->config->ssl_parameters.private_key_file,
SSL_FILETYPE_PEM);
// ===== 加载CA证书(用于验证客户端) =====
SSL_CTX_load_verify_locations(
reds->ctx,
reds->config->ssl_parameters.ca_certificate_file,
nullptr);
// ===== 加载DH参数(密钥交换) =====
if (strlen(reds->config->ssl_parameters.dh_key_file) > 0) {
load_dh_params(reds->ctx,
reds->config->ssl_parameters.dh_key_file);
}
// ===== 设置会话ID上下文 =====
SSL_CTX_set_session_id_context(
reds->ctx,
(const unsigned char *)"SPICE", 5);
// ===== 自定义密码套件(可选) =====
if (strlen(reds->config->ssl_parameters.ciphersuite) > 0) {
SSL_CTX_set_cipher_list(
reds->ctx,
reds->config->ssl_parameters.ciphersuite);
}
return 0;
}
安全配置说明:
| 配置 | 目的 |
|---|---|
| 禁用SSLv2/v3 | 已知存在安全漏洞 |
| 禁用TLSv1.0 | 不够安全,推荐TLS1.2+ |
| 禁用压缩 | 防止CRIME攻击 |
| 禁用重协商 | 防止DoS和降级攻击 |
| ECDH自动 | 自动选择最佳椭圆曲线 |
SSL 握手
cpp
// red-stream.cpp
// 启用SSL加密
RedStreamSslStatus red_stream_enable_ssl(RedStream *stream, SSL_CTX *ctx)
{
BIO *sbio;
// ===== 创建BIO(I/O抽象) =====
sbio = BIO_new_socket(stream->socket, BIO_NOCLOSE);
if (!sbio) {
return RED_STREAM_SSL_STATUS_ERROR;
}
// ===== 创建SSL对象 =====
stream->priv->ssl = SSL_new(ctx);
if (!stream->priv->ssl) {
BIO_free(sbio);
return RED_STREAM_SSL_STATUS_ERROR;
}
// ===== 绑定BIO =====
SSL_set_bio(stream->priv->ssl, sbio, sbio);
// ===== 切换读写函数 =====
stream->priv->write = stream_ssl_write_cb;
stream->priv->read = stream_ssl_read_cb;
red_stream_disable_writev(stream); // SSL不支持writev
// ===== 开始握手 =====
return red_stream_ssl_accept(stream);
}
// SSL握手(服务器端)
RedStreamSslStatus red_stream_ssl_accept(RedStream *stream)
{
int return_code = SSL_accept(stream->priv->ssl);
if (return_code == 1) {
// 握手成功
return RED_STREAM_SSL_STATUS_OK;
}
// ===== 处理非阻塞情况 =====
int ssl_error = SSL_get_error(stream->priv->ssl, return_code);
if (return_code == -1 &&
(ssl_error == SSL_ERROR_WANT_READ ||
ssl_error == SSL_ERROR_WANT_WRITE)) {
// 需要更多数据,稍后重试
return ssl_error == SSL_ERROR_WANT_READ
? RED_STREAM_SSL_STATUS_WAIT_FOR_READ
: RED_STREAM_SSL_STATUS_WAIT_FOR_WRITE;
}
// 握手失败
SSL_free(stream->priv->ssl);
stream->priv->ssl = nullptr;
return RED_STREAM_SSL_STATUS_ERROR;
}
SSL握手时序:

SASL 认证
SASL 结构
cpp
// red-stream.cpp
struct RedSASL {
sasl_conn_t *conn; // SASL连接
// ===== SSF协商 =====
int wantSSF :1; // 是否需要安全层
int runSSF :1; // 是否正在运行安全层
// ===== 编码缓冲 =====
const uint8_t *encoded; // 编码后的数据
unsigned int encodedLength;
unsigned int encodedOffset;
// ===== 接收缓冲 =====
SpiceBuffer inbuffer;
};
SASL 认证流程
cpp
// red-stream.cpp
// 启动SASL认证
bool red_sasl_start_auth(RedStream *stream,
RedSaslResult result_cb,
void *result_opaque)
{
RedSASL *sasl = &stream->priv->sasl;
// ===== 获取地址信息 =====
char *localAddr = red_stream_get_local_address(stream);
char *remoteAddr = red_stream_get_remote_address(stream);
// ===== 创建SASL服务器 =====
int err = sasl_server_new(
"spice", // 服务名
NULL, // FQDN(自动检测)
NULL, // 用户realm
localAddr, // 本地地址
remoteAddr, // 远程地址
NULL, // 回调(不需要)
SASL_SUCCESS_DATA, // 标志
&sasl->conn); // 输出连接
// ===== 配置外部SSF(如果有TLS) =====
if (stream->priv->ssl) {
sasl_ssf_t ssf = SSL_get_cipher_bits(stream->priv->ssl, NULL);
sasl_setprop(sasl->conn, SASL_SSF_EXTERNAL, &ssf);
} else {
// 无TLS时需要SASL提供安全层
sasl->wantSSF = 1;
}
// ===== 设置安全属性 =====
sasl_security_properties_t secprops;
memset(&secprops, 0, sizeof(secprops));
if (stream->priv->ssl) {
// 有TLS,不需要SASL SSF
secprops.min_ssf = 0;
secprops.max_ssf = 0;
secprops.maxbufsize = 8192;
} else {
// 无TLS,要求SASL提供加密
secprops.min_ssf = 56; // 最小56位(Kerberos级别)
secprops.max_ssf = 100000; // 最大无限制
secprops.maxbufsize = 8192;
// 禁止匿名和明文
secprops.security_flags =
SASL_SEC_NOANONYMOUS | SASL_SEC_NOPLAINTEXT;
}
sasl_setprop(sasl->conn, SASL_SEC_PROPS, &secprops);
// ===== 获取可用机制 =====
const char *mechlist;
sasl_listmech(sasl->conn, NULL, ",", ",", ",",
&mechlist, NULL, NULL);
// ===== 发送机制列表给客户端 =====
red_stream_write_u32_le(stream, strlen(mechlist));
red_stream_write_all(stream, mechlist, strlen(mechlist));
// ===== 等待客户端选择机制 =====
// ... 异步读取处理
return true;
}
SSF 检查
cpp
// red-stream.cpp
// 检查协商的安全强度因子
static int auth_sasl_check_ssf(RedSASL *sasl, int *runSSF)
{
*runSSF = 0;
if (!sasl->wantSSF) {
// 不需要SSF(已有TLS)
return 1;
}
// ===== 获取协商的SSF =====
const void *val;
sasl_getprop(sasl->conn, SASL_SSF, &val);
int ssf = *(const int *)val;
spice_debug("negotiated an SSF of %d", ssf);
// ===== 检查强度 =====
if (ssf < 56) {
// SSF不够(56位是Kerberos最低要求)
return 0;
}
*runSSF = 1; // 启用SASL安全层
return 1;
}
SASL认证时序:

WebSocket 支持
WebSocket 检测
cpp
// red-stream.cpp
// 检测是否是WebSocket连接
bool red_stream_is_websocket(RedStream *stream,
const void *buf,
size_t len)
{
if (stream->priv->ws) {
return false; // 已经是WebSocket
}
// ===== 尝试创建WebSocket =====
stream->priv->ws = websocket_new(
buf, len, // 初始数据
stream, // 关联的stream
(websocket_read_cb_t)stream->priv->read, // 原read
(websocket_write_cb_t)stream->priv->write, // 原write
(websocket_writev_cb_t)stream->priv->writev // 原writev
);
if (stream->priv->ws) {
// ===== 切换到WebSocket读写 =====
stream->priv->read = stream_websocket_read;
stream->priv->write = stream_websocket_write;
if (stream->priv->writev) {
stream->priv->writev = stream_websocket_writev;
}
return true;
}
return false;
}
WebSocket 握手
cpp
// websocket.c
// 创建WebSocket连接
RedsWebSocket *websocket_new(const void *buf, size_t len,
void *stream,
websocket_read_cb_t read_cb,
websocket_write_cb_t write_cb,
websocket_writev_cb_t writev_cb)
{
char rbuf[4096];
// ===== 读取HTTP请求 =====
memcpy(rbuf, buf, len);
int rc = read_cb(stream, rbuf + len, sizeof(rbuf) - len - 1);
len += rc;
rbuf[len] = 0;
// ===== 验证是否是WebSocket升级请求 =====
bool has_protocol;
if (!websocket_is_start(rbuf, &has_protocol)) {
return NULL; // 不是WebSocket
}
// ===== 生成响应 =====
char outbuf[1024];
websocket_create_reply(rbuf, outbuf, has_protocol);
// ===== 发送握手响应 =====
rc = write_cb(stream, outbuf, strlen(outbuf));
if (rc != strlen(outbuf)) {
return NULL;
}
// ===== 创建WebSocket状态 =====
RedsWebSocket *ws = g_new0(RedsWebSocket, 1);
ws->raw_stream = stream;
ws->raw_read = read_cb;
ws->raw_write = write_cb;
ws->raw_writev = writev_cb;
pong_init(&ws->pong);
pong_init(&ws->pending_pong);
return ws;
}
WebSocket 密钥生成
cpp
// websocket.c
// 生成WebSocket接受密钥
static char *generate_reply_key(char *buf)
{
// ===== 提取客户端密钥 =====
const char *key = find_str(buf, "\nSec-WebSocket-Key:");
if (key) {
const char *p = strchr(key, '\r');
char *k = g_strndup(key, p - key);
k = g_strstrip(k);
// ===== 计算SHA1哈希 =====
GChecksum *checksum = g_checksum_new(G_CHECKSUM_SHA1);
// 客户端密钥 + 固定GUID
g_checksum_update(checksum, (uint8_t *)k, strlen(k));
g_checksum_update(checksum,
(uint8_t *)WEBSOCKET_GUID,
strlen(WEBSOCKET_GUID));
// WEBSOCKET_GUID = "258EAFA5-E914-47DA-95CA-C5AB0DC85B11"
// ===== 获取摘要 =====
size_t sha1_size = g_checksum_type_get_length(G_CHECKSUM_SHA1);
uint8_t *sha1 = g_malloc(sha1_size);
g_checksum_get_digest(checksum, sha1, &sha1_size);
// ===== Base64编码 =====
char *b64 = g_base64_encode(sha1, sha1_size);
g_checksum_free(checksum);
g_free(sha1);
g_free(k);
return b64;
}
return NULL;
}
安全连接流程
完整流程
cpp
// reds.cpp
// 初始化SSL连接
static RedLinkInfo *reds_init_client_ssl_connection(
RedsState *reds,
int socket)
{
// 1. 创建基础连接
RedLinkInfo *link = reds_init_client_connection(reds, socket);
// 2. 启用SSL
RedStreamSslStatus ssl_status =
red_stream_enable_ssl(link->stream, reds->ctx);
switch (ssl_status) {
case RED_STREAM_SSL_STATUS_OK:
// 握手完成,继续协议处理
reds_handle_new_link(link);
return link;
case RED_STREAM_SSL_STATUS_WAIT_FOR_READ:
// 需要更多数据
link->stream->watch = reds_core_watch_add(
reds, link->stream->socket,
SPICE_WATCH_EVENT_READ,
reds_handle_ssl_accept, link);
break;
case RED_STREAM_SSL_STATUS_WAIT_FOR_WRITE:
// 需要发送数据
link->stream->watch = reds_core_watch_add(
reds, link->stream->socket,
SPICE_WATCH_EVENT_WRITE,
reds_handle_ssl_accept, link);
break;
case RED_STREAM_SSL_STATUS_ERROR:
reds_link_free(link);
return nullptr;
}
return link;
}
认证机制选择
cpp
// reds.cpp
static void reds_handle_auth_mechanism(void *opaque)
{
RedLinkInfo *link = (RedLinkInfo *)opaque;
RedsState *reds = link->reds;
switch (link->auth_mechanism.auth_mechanism) {
case SPICE_COMMON_CAP_AUTH_SPICE:
// ===== SPICE原生认证 =====
if (!reds->config->sasl_enabled) {
// 使用简单密码认证
reds_get_spice_ticket(link);
}
break;
#if HAVE_SASL
case SPICE_COMMON_CAP_AUTH_SASL:
// ===== SASL认证 =====
spice_debug("Starting SASL");
reds_start_auth_sasl(link);
break;
#endif
default:
// 未知机制
spice_warning("Unknown auth method");
reds_send_link_error(link, SPICE_LINK_ERR_INVALID_DATA);
reds_link_free(link);
}
}
安全检查
cpp
// reds.cpp
// 检查通道是否满足安全要求
static bool reds_security_check(RedLinkInfo *link)
{
// 获取通道的安全要求
ChannelSecurityOptions *security =
find_channel_security(link->link_mess->channel_type);
if (security) {
if (red_stream_is_ssl(link->stream)) {
// 当前是加密连接
if (security->options & SPICE_CHANNEL_SECURITY_SSL) {
return true; // 需要加密,满足
}
// 此通道不应加密
return false;
} else {
// 当前是非加密连接
if (security->options & SPICE_CHANNEL_SECURITY_NONE) {
return true; // 允许非加密
}
// 此通道需要加密
return false;
}
}
// 无特殊要求,默认允许
return true;
}
传输层组合
SPICE支持多种传输层的组合:
| 组合 | 协议栈 | 使用场景 |
|---|---|---|
| 组合1 | Socket → SPICE协议 | 内网信任环境 |
| 组合2 | Socket → SSL → SPICE协议 | 标准加密连接 |
| 组合3 | Socket → SSL → SASL → SPICE协议 | 企业级认证 |
| 组合4 | Socket → WebSocket → SPICE协议 | 浏览器客户端 |
| 组合5 | Socket → SSL → WebSocket → SPICE协议 | 安全浏览器客户端 |
| 组合6 | Socket → SSL → WebSocket → SASL → SPICE协议 | 完整安全堆栈 |
函数指针切换:
cpp
// 初始状态(普通socket)
stream->priv->read = stream_socket_read;
stream->priv->write = stream_socket_write;
// 启用SSL后
stream->priv->read = stream_ssl_read_cb;
stream->priv->write = stream_ssl_write_cb;
// 再启用WebSocket
stream->priv->read = stream_websocket_read;
stream->priv->write = stream_websocket_write;
// WebSocket内部调用之前的SSL函数
总结
| 机制 | 功能 | 配置 |
|---|---|---|
| SSL/TLS | 传输加密 | 证书、私钥、CA |
| SASL | 身份认证 | 机制列表、SSF |
| WebSocket | 浏览器支持 | 自动检测 |
安全建议:
- 始终启用TLS:使用TLS 1.2或更高版本
- 配置强密码套件:禁用弱密码
- 客户端证书:高安全环境使用双向认证
- 定期更新:保持OpenSSL库更新