tls认证--ssl处理server端编写
tls session
typedef struct _tls_session_t { SSL_CTX *ctx; SSL *ssl; #if OPENSSL_VERSION_NUMBER >= 0x10001000L SSL_SESSION *session; #endif tls_info_t info; BIO *into_ssl; BIO *from_ssl; record_t clean_in; //!< Data that needs to be sent but only after it is soiled. record_t clean_out; //!< Data that is cleaned after receiving. record_t dirty_in; //!< Data server receives. record_t dirty_out; //!< Data server sends. void (*record_init)(record_t *buf); void (*record_close)(record_t *buf); unsigned int (*record_plus)(record_t *buf, void const *ptr, unsigned int size); unsigned int (*record_minus)(record_t *buf, void *ptr, unsigned int size); bool invalid_hb_used; //!< Whether heartbleed attack was detected. bool connected; //!< whether the outgoing socket is connected bool is_init_finished; //!< whether or not init is finished bool client_cert_ok; //!< whether or not we validated the client certificate bool authentication_success; //!< whether or not the user was authenticated (cert or PW) /* * Framed-MTU attribute in RADIUS, if present, can also be used to set this */ size_t mtu; //!< Current fragment size transmitted. size_t tls_msg_len; //!< Actual/Total TLS message length. bool fragment; //!< Flag, In fragment mode or not. bool length_flag; //!< A flag to include length in every TLS Data/Alert packet. //!< If set to no then only the first fragment contains length. int peap_flag; size_t tls_record_in_total_len; //!< How long the peer indicated the complete tls record //!< would be. size_t tls_record_in_recvd_len; //!< How much of the record we've received so far. char const *label; bool allow_session_resumption; //!< Whether session resumption is allowed. bool session_not_resumed; //!< Whether our session was not resumed. fr_tls_server_conf_t const *conf; //! for better complaints } tls_session_t;
Server
1、初始化
static void session_close(tls_session_t *ssn) { if (ssn->ssl) { SSL_set_quiet_shutdown(ssn->ssl, 1); SSL_shutdown(ssn->ssl); SSL_free(ssn->ssl); ssn->ssl = NULL; } record_close(&ssn->clean_in); record_close(&ssn->clean_out); record_close(&ssn->dirty_in); record_close(&ssn->dirty_out); session_init(ssn); } static void record_init(record_t *rec) { rec->used = 0; } static void record_close(record_t *rec) { rec->used = 0; } /* * Copy data to the intermediate buffer, before we send * it somewhere. */ static unsigned int record_plus(record_t *rec, void const *ptr, unsigned int size) { unsigned int added = MAX_RECORD_SIZE - rec->used; if(added > size) added = size; if(added == 0) return 0; memcpy(rec->data + rec->used, ptr, added); rec->used += added; return added; }
static void session_init(tls_session_t *ssn) { ssn->ssl = NULL; ssn->into_ssl = ssn->from_ssl = NULL; record_init(&ssn->clean_in); record_init(&ssn->clean_out); record_init(&ssn->dirty_in); record_init(&ssn->dirty_out); memset(&ssn->info, 0, sizeof(ssn->info)); ssn->mtu = 0; ssn->fragment = false; ssn->tls_msg_len = 0; ssn->length_flag = false; ssn->opaque = NULL; ssn->free_opaque = NULL; }
new_tls = SSL_new(conf->ctx); /* We use the SSL's "app_data" to indicate a call-back */ SSL_set_app_data(new_tls, NULL); state = alloc_zero(tls_session_t) session_init(state); state->ctx = conf->ctx; state->ssl = new_tls; state->conf = conf; state->record_init = record_init; state->record_close = record_close; state->record_plus = record_plus; state->record_minus = record_minus; /* * Create & hook the BIOs to handle the dirty side of the * SSL. This is *very important* as we want to handle * the transmission part. Now the only IO interface * that SSL is aware of, is our defined BIO buffers. * * This means that all SSL IO is done to/from memory, * and we can update those BIOs from the packets we've * received. */// SSL_set_bio(SSL *ssl, BIO *rbio, BIO *wbio); state->into_ssl = BIO_new(BIO_s_mem()); state->from_ssl = BIO_new(BIO_s_mem()); SSL_set_bio(state->ssl, state->into_ssl, state->from_ssl); /* * Add the message callback to identify what type of * message/handshake is passed */ SSL_set_msg_callback(new_tls, cbtls_msg); SSL_set_msg_callback_arg(new_tls, state); SSL_set_info_callback(new_tls, cbtls_info); /* * In Server mode we only accept. */ /* * Verify the peer certificate, if asked. */ if (client_cert) { verify_mode = SSL_VERIFY_PEER; verify_mode |= SSL_VERIFY_FAIL_IF_NO_PEER_CERT; verify_mode |= SSL_VERIFY_CLIENT_ONCE; } SSL_set_verify(state->ssl, verify_mode, cbtls_verify); SSL_set_ex_data(state->ssl, FR_TLS_EX_INDEX_CONF, (void *)conf); SSL_set_ex_data(state->ssl, FR_TLS_EX_INDEX_SSN, (void *)state); SSL_set_accept_state(state->ssl); /* i.e. it should REALLY be called MTU, and the code here * should figure out what that means for TLS fragment size. * asking the administrator to know the internal details * of EAP-TLS in order to calculate fragment sizes is * just too much. */ state->mtu = conf->fragment_size;
主要是:创建ssl 上下文,设置ssl关联bio,设置
BIO_mem 内存操作回调读写函数,设置ssl握手时 msg 回调
2、server read数据client-hello并handshake
(tls_session->record_plus)(&tls_session->dirty_in, tlspacket->data, tlspacket->dlen)) if (ssn->dirty_in.used > 0) { err = BIO_write(ssn->into_ssl, ssn->dirty_in.data, ssn->dirty_in.used); if (err != (int) ssn->dirty_in.used) { DEBUG("(TLS) Failed writing %zd bytes to SSL BIO: %d", ssn->dirty_in.used, err); record_init(&ssn->dirty_in); return 0; } record_init(&ssn->dirty_in); } err = SSL_read(ssn->ssl, ssn->clean_out.data + ssn->clean_out.used, sizeof(ssn->clean_out.data) - ssn->clean_out.used); if (err > 0) { ssn->clean_out.used += err; return 1; } if (!tls_error_io_log(ssn, err, "Failed reading from OpenSSL")) return 0; /* Some Extra STATE information for easy debugging */ if (!ssn->is_init_finished && SSL_is_init_finished(ssn->ssl)) { VALUE_PAIR *vp; char const *str_version; RDEBUG2("(TLS) Connection Established"); ssn->is_init_finished = true; switch (SSL_version(ssn->ssl)) { case SSL2_VERSION: str_version = "SSL 2.0"; break; case SSL3_VERSION: str_version = "SSL 3.0"; break; case TLS1_VERSION: str_version = "TLS 1.0"; break; #ifdef TLS1_1_VERSION case TLS1_1_VERSION: str_version = "TLS 1.1"; break; #endif #ifdef TLS1_2_VERSION case TLS1_2_VERSION: str_version = "TLS 1.2"; break; #endif #ifdef TLS1_3_VERSION case TLS1_3_VERSION: str_version = "TLS 1.3"; break; #endif default: str_version = "UNKNOWN"; break; } } else if (SSL_in_init(ssn->ssl)) { RDEBUG2("(TLS) In Handshake Phase"); } else if (SSL_in_before(ssn->ssl)) { RDEBUG2("(TLS) Before Handshake Phase"); } else if (SSL_in_accept_init(ssn->ssl)) { RDEBUG2("(TLS) In Accept mode"); } else if (SSL_in_connect_init(ssn->ssl)) { RDEBUG2("(TLS) In Connect mode"); } err = BIO_ctrl_pending(ssn->from_ssl); if (err > 0) { err = BIO_read(ssn->from_ssl, ssn->dirty_out.data, sizeof(ssn->dirty_out.data)); if (err > 0) { DEBUG("(TLS) got %d bytes of data", err); ssn->dirty_out.used = err; } else if (BIO_should_retry(ssn->from_ssl)) { record_init(&ssn->dirty_in); DEBUG2("(TLS) Asking for more data in tunnel."); return 1; } else { tls_error_log(NULL, "Error reading from OpenSSL"); record_init(&ssn->dirty_in); return 0; } } else { DEBUG2("(TLS) Application data."); /* Its clean application data, leave whatever is in the buffer */ } /* We are done with dirty_in, reinitialize it */ record_init(&ssn->dirty_in);
上面的handshake数据 server-hello是怎样产生的呢?
结论是:
err = SSL_read(ssn->ssl, ssn->clean_out.data + ssn->clean_out.used,sizeof(ssn->clean_out.data) - ssn->clean_out.used);
如果读到handshake 数据也会调用
static int ssl3_read_internal(SSL *s, void *buf, size_t len, int peek, size_t *readbytes) { int ret; clear_sys_error();// 清除错误信息 if (s->s3->renegotiate)// 检查协商是否成功 ssl3_renegotiate_check(s, 0); s->s3->in_read_app_data = 1;// 标志现在在读应用层数据 // ssl3读取数据, 类型是SSL3_RT_APPLICATION_DATA,应用数据 ret = s->method->ssl_read_bytes(s, SSL3_RT_APPLICATION_DATA, NULL, buf, len, peek, readbytes); // s->s3->in_read_app_data == 2表示要读协商数据 if ((ret == -1) && (s->s3->in_read_app_data == 2)) { /* * ssl3_read_bytes decided to call s->handshake_func, which called * ssl3_read_bytes to read handshake data. However, ssl3_read_bytes * actually found application data and thinks that application data * makes sense here; so disable handshake processing and try to read * application data again. */ ossl_statem_set_in_handshake(s, 1); // 重新读数据 ret = s->method->ssl_read_bytes(s, SSL3_RT_APPLICATION_DATA, NULL, buf, len, peek, readbytes); ossl_statem_set_in_handshake(s, 0); } else s->s3->in_read_app_data = 0; //读取数据的主要函数实际为ssl3_read_bytes(): return ret; }
ssl3_read_bytes----->调用 /* * Unexpected handshake message (ClientHello, NewSessionTicket (TLS1.3) or * protocol violation) 该报文类型为 SSL3_RT_HANDSHAKE 而不是 SSL3_RT_APPLICATION_DATA 报文, 说明我们收到了一个握手报文。服务端会尝试收取到足够长度的握手报文, 并在此进入握手函数 s->handshake_func 恢复握手过程 */ if ((s->rlayer.handshake_fragment_len >= 4) && !ossl_statem_get_in_handshake(s)) { int ined = (s->early_data_state == SSL_EARLY_DATA_READING); /* We found handshake data, so we're going back into init */ ossl_statem_set_in_init(s, 1); i = s->handshake_func(s);// 重新握手协商 /* SSLfatal() already called if appropriate */ if (i < 0) return i; if (i == 0) { return -1; }
检测tls握手阶段是否正常
err = BIO_ctrl_pending(ssn->from_ssl); if (err > 0) { err = BIO_read(ssn->from_ssl, ssn->dirty_out.data, sizeof(ssn->dirty_out.data)); if (err > 0) { RDEBUG3("(TLS) got %d bytes of data", err); ssn->dirty_out.used = err; } else if (BIO_should_retry(ssn->from_ssl)) { record_init(&ssn->dirty_in); RDEBUG2("(TLS) Asking for more data in tunnel."); return 1; } else { tls_error_log(NULL, "Error reading from OpenSSL"); record_init(&ssn->dirty_in); return 0; } } else { RDEBUG2("(TLS) Application data."); /* Its clean application data, leave whatever is in the buffer */ #if 0 record_init(&ssn->clean_out); #endif } /* We are done with dirty_in, reinitialize it */ record_init(&ssn->dirty_in);
分析:server读取client-hello raw数据写入到ssl-into,进行ssl 握手, 并且出现ssl-into解码后的数据, 此时会产生server-hello, 从from-ssl中读取server-hello等数据,后续发送出去。
3、server read数据client的 Certificate 、Client Key Exchange等数据
此时server设置为Done initial handshake 状态
err = BIO_write(ssn->into_ssl, ssn->dirty_in.data, ssn->dirty_in.used); err = SSL_read(ssn->ssl, ssn->clean_out.data + ssn->clean_out.used, sizeof(ssn->clean_out.data) - ssn->clean_out.used); if (err > 0) { ssn->clean_out.used += err; return 1; } ------ //再次走一遍上一阶段流程,判断是否建立连接
响应:err = BIO_ctrl_pending(ssn->from_ssl);
err = BIO_read(ssn->from_ssl, ssn->dirty_out.data,sizeof(ssn->dirty_out.data));
也就是响应 发送Change Cipher Spec Encrypted Handshake Message 报文
http代理服务器(3-4-7层代理)-网络事件库公共组件、内核kernel驱动 摄像头驱动 tcpip网络协议栈、netfilter、bridge 好像看过!!!!
但行好事 莫问前程
--身高体重180的胖子
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· SQL Server 2025 AI相关能力初探
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
2022-07-15 记录根文件系统镜像img制作
2019-07-15 linux 进程间通信 共享内存 shmat
2019-07-15 linux 进程间通信 共享内存 mmap