openconnect源碼分析


架構

1.  支持哪些特性?

1.1 支持 anyconnect 和 nc 兩種VPN協議,從 vpn_proto 中,可以看出整個程序的大致功能。

anyconnect (cisco): 包括 cstp 和 udp

nc (juniper network): 包括 oncp 和 esp

1.2 支持 win 和 unix-like 兩種客戶端。

1.3 支持 openssl 和 gnutls 兩種協議。

目前使用的是GNUTLS

2.  代碼架構

如本章1.1所示,VPN支持 anyconnect 和 juniper 兩種VPN協議。篇幅所限,下文中只介紹了 anyconnect 協議的實現細節。

整個代碼層級可以分為四層:

第一層(總的對外接口): library

第二層(主要過程,即認證和心跳): mainloop auth auth-common

第三層(細分的邏輯模塊,包括應用層的通信協議和虛擬網卡管理): tun/tun-windows dtls cstp http http auth

第四層: 其他基礎功能模塊

library  主要的對外接口
auth 實現認證功能
auth-common 處理表單時,生成token的其他途徑
mainloop 實現心跳功能
http 實現認證時,所涉及的HTTP通信功能
http-auth 實現認證時,處理HTTP--401狀態碼的幾種手段
cstp

認證成功后,建立TCP連接,發送CONNECT報文,建立HTTP隧道

基於TCP的心跳

重連

dtls

 認證成功后,建立UDP連接,建立DTLS連接

基於UDP的心跳

重連

gnutls 為TCP協議提供TLS相關接口
gnutls-dtls 為UDP協議提供TLS相關接口
gnutls-pkcs12 提供證書校驗相關接口
ssl

提供TCP、UDP連接接口

提供cmd_fd的監控接口

tun/tun-windows 提供隧道的建立和心跳接口
script 支持隧道建立時的腳本執行
lzs 提供lzs壓縮算法的接口
名稱
功能

 

 

 

 

 

認證

1.  認證概覽

認證階段涉及至少三次(如果密碼輸入錯誤,則不只三次)的HTTP請求。參見本章第三節

整個認證過程分為四個階段:

階段1: 第一次HTTP請求返回相應的form表單,包括分組選擇和賬號密碼填寫。參見3.1

階段2:程序提示用戶選擇分組,之后進入第二次HTTP請求,本次請求中新增了分組選擇。參見3.2

階段3: 分組發送后,服務端繼續返回表單,客戶端提示用戶輸入賬戶密碼,之后發送給服務端。參見3.3

階段4:(可選)根據最后一次HTTP通信返回的profile-uri和profile-hash,對服務端的數據進行驗證。參見2.3和3.3

2.  認證過程中的關鍵步驟

2.1 do_http_request

do_http_request 包括 HTTP-request 和 HTTP-response 兩個過程。

HTTP-request

報文准備  
openconnect_open_https 負責HTTP連接的建立,在cstp_connect中也會被調用
ssl_write 發送報文
名稱
功能

HTTP-response

process_http_response

處理報文的頭部,並按照transfor-encoding讀取body內容,通常為chunk。

connection正常為keep-alive,如果為close,則直接不再處理本次通信。

location用於保存重定向url,如果循環重定向或者超過三次重定向,則認證失敗。

第三個函數參數 http_auth_hdrs 用於匹配401相關的報文。

gen_authoriztion_hdr 處理401 unauthorized,實現了四種http認證方式
handle_redirect 更新url並進入下一次http請求
名稱
功能

2.2 openconnect_open_https

openconnect_open_https,負責HTTP連接的建立,在cstp_connect中也會被調用。

connect_https_socket

建立TCP連接,如果之前保存過對端的ip則直接連接。

verify_peer 被注冊的回調,調用gnutls庫實現客戶端的證書驗證
gnutls初始化 涉及gnutls會話的初始化,以及綁定到之前建立的tcp套接字
cstp_handshake 一個標准的select非阻塞連接場景
名稱
功能


2.3 parse_xml_response

auth

三次http通信中,前兩次都是“main”,最后一次是“success”

用於handle_auth_form中,檢查到“success”則不再處理

session-token 即重連中使用的cookie
config

主要讀取其中的profile-uri和profile-hash,配合obtain_cookie中的fetch_config進行二次校驗

error 認證出錯,則包含相應的錯誤信息
名稱
功能


2.4 handle_auth_form

該函數會被調用兩次:

第一次:處理選擇分組,返回newgroup。對應三次HTTP請求的第一次和第二次。參見3.1和3.2

第二次:處理賬戶密碼,返回OK。對應三次HTTP請求的第二次和第三次。(可以重復進入)參見3.2和3.3

注:opeconnect還支持四種token模式,具體沒有深究。

3. 認證中涉及的三次HTTP請求報文 

 報文涉及到敏感數據,有興趣的話可以自己使用openconnect參數自行打印HTTP報文。

 

 

 

 

連接

1.  建立CSTP連接

1.1  連接過程

cstp連接負責發送CONNECT報文,從而建立客戶端到服務器之間的隧道代理。

PS:為何需要CONNECT報文?

因為SSL隧道通信過程中,報文全部為加密的(包括請求頭和狀態行),代理無法解析響應內容,所以這個時候代理只能透明轉發。

 

請求發送成功后,服務器會返回cstp和dtls兩種報文配置,用於之后的本地網絡配置(虛擬網卡的ip、dns、netmask等)、通信配置(加密算法、壓縮算法、dtls的通信端口、mtu等)、心跳配置(心跳間隔)等。

openconnect_open_https

參見2.2

start_cstp_connection

dtsl-sessionid 用於cstp重連過程中,與重連報文檢查類似,當發現數據變化則直接放棄重連

名稱
功能

 2.  建立DTLS連接

dtls_attempt_period 根據dtls_setup最后一個參數配置
DTLS配置

根據CONNECT返回的報文配置

udp_sockaddr

初始化UDP套接字相關屬性

udp_connect 創建socket連接

start_dtls_handshake

一系列的DTLS初始化,並綁定套接字
dtls_try_handshake

建立DTLS連接

使用二分法探測MTU

名稱
功能

  

 

 

 

心跳

1.  心跳概覽

reconnect_timeout和reconnect_interval

根據mainloop函數參數配置

cmd_fd

win下為socket,mac下為pipe

用於消息線程與心跳線程之間通信

心跳循環

包含三種心跳

可通過cmd_fd終止

也會因為重連失敗、捕獲異常等終止

cstp_bye

發送服務端斷開VPN連接
os_shutdown_tun

清理本地的網卡配置

名稱
功能

2.  隧道

2.1  建立隧道

prepare_script_env

根據CSTP建立連接時返回的報文配置,將本地網絡環境加入環境變量

socketpair

本地的UDP讀寫端口,用於父子進程間的通信

子進程

應用環境變量並執行vpnc腳本

openconnect_setup_tun_fd

即父進程保存的sockectpair文件描述符

設置為非阻塞,並加入監控

用於tun_mainloop中

名稱
功能

2.2  tun_mainloop

隧道建立是win和mac區別點最大的地方。隧道建立成功后,會產生tun_mainloop。

tun_mainloop實現了客戶端和網卡設備之間的通信,在TUN建立時會產生相應的網卡文件描述符。

WIN和MAC在文件描述符方面有着顯著的區別:

win的tun_fd被賦值為tap網卡的文件描述符;

mac的tun_fd被賦值為socketpair,與子進程vpnc腳本之間互相通信。

3.  DTLS心跳

3.1  dtls的狀態

DTLS_SECRET和DTLS_NOSECRET目前沒有使用。

DTLS_DISABLED:如果被設置為這個狀態,則不會有DTLS相關的任何操作。

DTLS_SLEEPING:當接收到上層傳遞的PAUSED命令(cmd_fd)時,處於這個狀態。

DTLS_CONNECTING:start_dtls_handshake后處於這個狀態。(詳見第二章第二節)

DTLS_CONNECTTED:dtls_try_handshake后處於這個狀態。(詳見第二章第二節)

3.2  dtls心跳

dtls心跳分為四個步驟:

步驟1: 檢查dtls狀態,並執行相應操作;

步驟2: 檢查是否有輸入,如果有,則更新dtls_times.last_rx,檢查包的內容並執行相應操作;

 

AC_PKT_DATA

DTLS接收到的數據包,在tun_mainloop中傳遞給tun_fd

AC_PKT_DPD_OUT

服務器要求客戶端發送DPD包request

AC_PKT_DPD_RESP

服務器返回的DPD包response

AC_PKT_KEEPALIVE

 

服務器發送的keep-alive包

AC_PKT_COMPRESSED 接收到需要解壓的UDP包
AC_PKT_DISCONN 服務端發起斷開本次VPN連接 (只有CSTP處理)
AC_PKT_TERM_SERVER 同AC_PKT_DISCONN (只有CSTP處理)
名稱
功能

步驟3: 調用keepalive_action檢查是否需要執行探活操作;

KA_DPD

需要發送DPD包

KA_DPD_DEAD

檢測到DPD超時,發起重連

KA_KEEPALIVE

發送keep-alive包

KA_REKEY

 

重連

名稱
功能

步驟4: 轉發TUN設備發送的數據至VPN服務器;

3.3  dtls重連

調用connect_dtls_socket。(詳見上一章第二節)

4.  CSTP心跳

4.1  cstp心跳

CSTP心跳分為四個步驟:

步驟1: 同DTLS步驟1;

步驟2: 檢查是否有需要被寫入的包,如果有,則嘗試寫入;如果寫入失敗,則迅速判斷是否需要重連;

步驟3: 同DTLS步驟3;

步驟4: 如果DTLS沒有連接,執行DTLS的第四步操作;

4.2  cstp重連

CSTP心跳分為兩個步驟:

步驟1: 調用cstp_connect建立連接:(詳細過程見上一章1.1節)

步驟2: 調用調用vpnc腳本,傳遞“reconnect”參數。

 

 

 

 

refs:

  anyconnect vpn 協議分析    https://nikmav.blogspot.com/2013/11/inside-ssl-vpn-protocol.html


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM