HackPluto's Blog

Stunnel环境配置与源码分析

字数统计: 1.2k阅读时长: 6 min
2020/08/24 Share

Stunnel 环境搭建

Stunnel是一款可以加密网络数据的TCP连接,并可工作在Unix和Windows平台上,它采用Client/Server模式,将Client端的网络数据采用SSL(Secure Sockets Layer)加密后,安全的传输到指定的Server端再进行解密还原,然后再发送到访问的服务器。

基于Ubuntu16.04搭建

在官网下载5.56版本源码

编译安装

1
2
./configure;
make && make install;

AvvYko

生成证书

1
2
3
openssl genrsa -out key.pem 2048
openssl req -new -x509 -key key.pem -out cert.pem -days 1095
cat key.pem cert.pem >> /etc/stunnel/stunnel.pem

基本上,以上命令用于创建私钥,使用该密钥创建证书并将它们中的两个合并到一个名为“ stunnel.pem”的文件中以与Stunnel一起使用。

配置文件

这里展示通过stunnel加密HTTP流量

client端:

1
2
3
4
5
cert = /usr/local/etc/stunnel/stunnel.pem
client = yes
[apache]
accept = 127.0.0.1:8080
connect = 172.20.10.5:80

server端:

1
2
3
4
5
6
client = no

[apache]
accept = 127.0.0.1:80
connect = 172.20.10.2:8080
cert = /usr/local/etc/stunnel/stunnel.pem

Stunnel源码分析

使用软件:sourcetaril

使用源码分析软件导入 \src 文件夹

重要数据结构

tls_data_struct

1
2
3
4
5
6
7
struct tls_data_struct {
ALLOC_LIST *alloc_head;
size_t alloc_bytes, alloc_blocks;
CLI *c;
SERVICE_OPTIONS *opt;
char *id;
};

ZsBjwL

tls_data_struct 包含了一个alloc_list结构体,还有alloc的字节数和块数

alloc_list_struct

1
2
3
4
5
6
7
8
9
10
11
12
struct alloc_list_struct {
ALLOC_LIST *prev, *next; #前向指针,后向指针
TLS_DATA *tls;
size_t size;
const char *alloc_file, *free_file;
int alloc_line, free_line;
#ifdef DEBUG_PADDING
char debug[DEBUG_PADDING];
#endif
uint64_t valid_canary, magic;
#ifdef __GNUC__
}

hylHh7

alloc_list_struct 是一个双向链表,包含10个元素

service_options_struct

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
typedef struct service_options_struct {
struct service_options_struct *next; /* next node in the services list */
SSL_CTX *ctx; /* TLS context */
char *servname; /* service name for logging & permission checking */
int ref; /* reference counter for delayed deallocation */

/* service-specific data for stunnel.c */
#ifndef USE_WIN32
uid_t uid;
gid_t gid;
#endif
int bound_ports; /* number of ports bound to this service */

/* service-specific data for log.c */
int log_level; /* debug level for logging */
LOG_ID log_id; /* logging session id type */

/* service-specific data for sthreads.c */
#ifndef USE_FORK
size_t stack_size; /* stack size for this thread */
#endif

/* some global data for network.c */
SOCK_OPT *sock_opts;

/* service-specific data for verify.c */
char *ca_dir; /* directory for hashed certs */
char *ca_file; /* file containing bunches of certs */
char *crl_dir; /* directory for hashed CRLs */
char *crl_file; /* file containing bunches of CRLs */
#ifndef OPENSSL_NO_OCSP
char *ocsp_url;
unsigned long ocsp_flags;
#endif /* !defined(OPENSSL_NO_OCSP) */
#if OPENSSL_VERSION_NUMBER>=0x10002000L
NAME_LIST *check_host, *check_email, *check_ip; /* cert subject checks */
NAME_LIST *config; /* OpenSSL CONF options */
#endif /* OPENSSL_VERSION_NUMBER>=0x10002000L */

/* service-specific data for ctx.c */
char *cipher_list;
#ifndef OPENSSL_NO_TLS1_3
char *ciphersuites;
#endif /* TLS 1.3 */
char *cert; /* cert filename */
char *key; /* pem (priv key/cert) filename */
long session_size, session_timeout;
long unsigned ssl_options_set;
#if OPENSSL_VERSION_NUMBER>=0x009080dfL
long unsigned ssl_options_clear;
#endif /* OpenSSL 0.9.8m or later */
#if OPENSSL_VERSION_NUMBER>=0x10100000L
int min_proto_version, max_proto_version;
#else /* OPENSSL_VERSION_NUMBER<0x10100000L */
SSL_METHOD *client_method, *server_method;
#endif /* OPENSSL_VERSION_NUMBER<0x10100000L */
SOCKADDR_UNION sessiond_addr;
#ifndef OPENSSL_NO_TLSEXT
char *sni;
SERVERNAME_LIST *servername_list_head, *servername_list_tail;
#endif /* !defined(OPENSSL_NO_TLSEXT) */
#ifndef OPENSSL_NO_PSK
char *psk_identity;
PSK_KEYS *psk_keys, *psk_selected;
PSK_TABLE psk_sorted;
#endif /* !defined(OPENSSL_NO_PSK) */
#ifndef OPENSSL_NO_ECDH
char *curves;
#endif /* !defined(OPENSSL_NO_ECDH) */
#ifndef OPENSSL_NO_ENGINE
ENGINE *engine; /* engine to read the private key */
#endif /* !defined(OPENSSL_NO_ENGINE) */
#if OPENSSL_VERSION_NUMBER>=0x10000000L
TICKET_KEY *ticket_key; /* key for handling session tickets */
TICKET_KEY *ticket_mac; /* key for protecting session tickets */
#endif /* OpenSSL 1.0.0 or later */

/* service-specific data for client.c */
char *exec_name; /* program name for local mode */
#ifdef USE_WIN32
char *exec_args; /* program arguments for local mode */
#else
char **exec_args; /* program arguments for local mode */
#endif
SOCKADDR_UNION source_addr;
SOCKADDR_LIST local_addr, connect_addr, redirect_addr;
SOCKET *local_fd; /* array of accepting file descriptors */
SSL_SESSION **connect_session; /* per-destination client session cache */
SSL_SESSION *session; /* previous client session for delayed resolver */
int timeout_busy; /* maximum waiting for data time */
int timeout_close; /* maximum close_notify time */
int timeout_connect; /* maximum connect() time */
int timeout_idle; /* maximum idle connection time */
enum {FAILOVER_RR, FAILOVER_PRIO} failover; /* failover strategy */
unsigned rr; /* per-service sequential number for round-robin failover */
char *username;

/* service-specific data for protocol.c */
char * protocol;
char *protocol_host;
char *protocol_domain;
char *protocol_username;
char *protocol_password;
char *protocol_authentication;

/* service-specific data for ui_*.c */
#ifdef USE_WIN32
LPTSTR file, help;
#endif
unsigned section_number;
char *chain;

/* on/off switches */
struct {
unsigned request_cert:1; /* request a peer certificate */
unsigned require_cert:1; /* require a client certificate */
unsigned verify_chain:1; /* verify certificate chain */
unsigned verify_peer:1; /* verify peer certificate */
unsigned accept:1; /* endpoint: accept */
unsigned client:1;
unsigned delayed_lookup:1;
#ifdef USE_LIBWRAP
unsigned libwrap:1;
#endif
unsigned local:1; /* outgoing interface specified */
unsigned retry:1; /* loop remote+program */
unsigned sessiond:1;
#ifndef USE_WIN32
unsigned pty:1;
unsigned transparent_src:1;
#endif
unsigned transparent_dst:1; /* endpoint: transparent destination */
unsigned protocol_endpoint:1; /* dynamic target from the protocol */
unsigned reset:1; /* reset sockets on error */
unsigned renegotiation:1;
unsigned connect_before_ssl:1;
#ifndef OPENSSL_NO_OCSP
unsigned aia:1; /* Authority Information Access */
unsigned nonce:1; /* send and verify OCSP nonce */
#endif /* !defined(OPENSSL_NO_OCSP) */
#ifndef OPENSSL_NO_DH
unsigned dh_temp_params:1;
#endif /* OPENSSL_NO_DH */
#ifndef USE_WIN32
unsigned log_stderr:1; /* a copy of the global switch */
#endif /* USE_WIN32 */
} option;
} SERVICE_OPTIONS;

函数流程


先分析Linux下的执行流程

首先查找int main定位到ui_unix.c

int main

1
2
3
4
5
6
7
8
9
10
11
int main(int argc, char* argv[]) { /* execution begins here 8-) */
int retval;

#ifdef M_MMAP_THRESHOLD
mallopt(M_MMAP_THRESHOLD, 4096); #设置MMAP分配阈值为4KB
#endif
tls_init(); /* initialize thread-local storage */
retval=main_unix(argc, argv);
main_cleanup();
return retval;
}

8HURKW

main函数是整个程序的启动函数,设置完MMAP后第一个调用的函数就是TLS初始化函数

tls_init

1
2
3
4
5
6
7
8
9
10
11
12
13
/* this has to be the first function called from ui_*.c */
void tls_init() {
tls_platform_init();
tls_initialized=1;
ui_tls=tls_alloc(NULL, NULL, "ui");
#if OPENSSL_VERSION_NUMBER>=0x10100000L
CRYPTO_set_mem_functions(str_alloc_detached_debug,
str_realloc_detached_debug, str_free_debug);
#else
CRYPTO_set_mem_ex_functions(str_alloc_detached_debug,
str_realloc_detached_debug, free_function);
#endif
}

从代码注释可以看到不管是Linux还是Windows main函数调用的第一个函数都是这个初始化函数

xcNEo2

在tls_init中调用的第一个是tls_platform_init(),这个函数的作用是

然后将tls_initialized设置为1,然后调用tls_alloc

tls_alloc

首先新建一个TLS_DATA类型的指针tls_data,然后为tls_data在堆上分配空间。在str_init中做了两件事,1是将tls_data的alloc_head设置为NULL 2是

CATALOG
  1. 1. Stunnel 环境搭建
    1. 1.1. 基于Ubuntu16.04搭建
      1. 1.1.1. 编译安装
      2. 1.1.2. 生成证书
      3. 1.1.3. 配置文件
  2. 2. Stunnel源码分析
    1. 2.1. 重要数据结构
      1. 2.1.1. tls_data_struct
      2. 2.1.2. alloc_list_struct
      3. 2.1.3. service_options_struct
    2. 2.2. 函数流程
      1. 2.2.1. int main
      2. 2.2.2. tls_init
        1. 2.2.2.1. tls_alloc