Browse code

tls: server side SNI support

- adapted from the patch of Klaus Darilion for Kamailio v1.5, which was
lost with the ser integration
- this is a partial patch of the original one, adding support for SNI
when accepting TLS connections

Daniel-Constantin Mierla authored on 16/02/2015 23:22:19
Showing 6 changed files
... ...
@@ -160,6 +160,7 @@ static cfg_option_t options[] = {
160 160
 	{"cipher_list",         .f = cfg_parse_str_opt, .flags = CFG_STR_SHMMEM},
161 161
 	{"ca_list",             .f = cfg_parse_str_opt, .flags = CFG_STR_SHMMEM},
162 162
 	{"crl",                 .f = cfg_parse_str_opt, .flags = CFG_STR_SHMMEM},
163
+	{"server_name",         .f = cfg_parse_str_opt, .flags = CFG_STR_SHMMEM},
163 164
 	{0}
164 165
 };
165 166
 
... ...
@@ -183,6 +184,7 @@ static void update_opt_variables(void)
183 184
 	options[12].param = &domain->cipher_list;
184 185
 	options[13].param = &domain->ca_file;
185 186
 	options[14].param = &domain->crl_file;
187
+	options[15].param = &domain->server_name;
186 188
 }
187 189
 
188 190
 
... ...
@@ -29,6 +29,14 @@
29 29
 #include "../../str.h"
30 30
 #include "tls_domain.h"
31 31
 
32
+#include <openssl/ssl.h>
33
+
34
+#ifndef OPENSSL_NO_TLSEXT
35
+#ifndef SSL_CTRL_SET_TLSEXT_HOSTNAME
36
+#define OPENSSL_NO_TLSEXT
37
+#endif
38
+#endif
39
+
32 40
 tls_domains_cfg_t* tls_load_config(str* filename);
33 41
 
34 42
 /*
... ...
@@ -35,6 +35,7 @@
35 35
 #include "../../pt.h"
36 36
 #include "../../cfg/cfg.h"
37 37
 #include "../../dprint.h"
38
+#include "tls_config.h"
38 39
 #include "tls_server.h"
39 40
 #include "tls_util.h"
40 41
 #include "tls_mod.h"
... ...
@@ -878,6 +879,71 @@ static int tls_ssl_ctx_set_read_ahead(SSL_CTX* ctx, long val, void* unused)
878 879
 	return 0;
879 880
 }
880 881
 
882
+
883
+#ifndef OPENSSL_NO_TLSEXT
884
+
885
+/**
886
+ * @brief SNI callback function
887
+ *
888
+ * callback on server_name -> trigger context switch if a TLS domain
889
+ * for the server_name is found (checks socket too) */
890
+static int tls_server_name_cb(SSL *ssl, int *ad, void *private)
891
+{
892
+    tls_domain_t *orig_domain, *new_domain;
893
+	str server_name;
894
+
895
+	orig_domain = (tls_domain_t*)private;
896
+	server_name.s = (char*)SSL_get_servername(ssl, TLSEXT_NAMETYPE_host_name);
897
+	if (server_name.s)  {
898
+		LM_DBG("received server_name (TLS extension): '%s'\n", server_name.s);
899
+	} else {
900
+		LM_DBG("SSL_get_servername returned NULL: return SSL_TLSEXT_ERR_NOACK\n");
901
+		return SSL_TLSEXT_ERR_NOACK;
902
+	}
903
+
904
+	server_name.len = strlen(server_name.s);
905
+
906
+	new_domain = tls_lookup_cfg(*tls_domains_cfg, TLS_DOMAIN_SRV,
907
+			&orig_domain->ip, orig_domain->port, &server_name);
908
+	if (new_domain==NULL) {
909
+		LM_DBG("TLS domain for socket [%s:%d] and server_name='%s' "
910
+			"not found\n", ip_addr2a(&orig_domain->ip),
911
+			orig_domain->port, server_name.s);
912
+		/* we do not perform SSL_CTX switching, thus the default server domain
913
+		   for this socket (or the default server domain) will be used. */
914
+		return SSL_TLSEXT_ERR_ALERT_WARNING;
915
+	}
916
+
917
+	LM_DBG("TLS cfg domain selected for received server name [%s]:"
918
+		" socket [%s:%d] server name='%s' -"
919
+		" switching SSL CTX to %p dom %p%s\n",
920
+		server_name.s, ip_addr2a(&new_domain->ip),
921
+		new_domain->port, ZSW(new_domain->server_name.s),
922
+		new_domain->ctx[process_no], new_domain,
923
+		(new_domain->type & TLS_DOMAIN_DEF)?" (default)":"");
924
+	SSL_set_SSL_CTX(ssl, new_domain->ctx[process_no]);
925
+	/* SSL_set_SSL_CTX only sets the correct certificate parameters, but does
926
+	   set the proper verify options. Thus this will be done manually! */
927
+
928
+	SSL_set_options(ssl, SSL_CTX_get_options(ssl->ctx));
929
+	if ((SSL_get_verify_mode(ssl) == SSL_VERIFY_NONE) ||
930
+				(SSL_num_renegotiations(ssl) == 0)) {
931
+		/*
932
+		 * Only initialize the verification settings from the ctx
933
+		 * if they are not yet set, or if we're called when a new
934
+		 * SSL connection is set up (num_renegotiations == 0).
935
+		 * Otherwise, we would possibly reset a per-directory
936
+		 * configuration which was put into effect by ssl_hook_access.
937
+		 */
938
+		SSL_set_verify(ssl, SSL_CTX_get_verify_mode(ssl->ctx),
939
+			SSL_CTX_get_verify_callback(ssl->ctx));
940
+	}
941
+
942
+	return SSL_TLSEXT_ERR_OK;
943
+}
944
+#endif
945
+
946
+
881 947
 /**
882 948
  * @brief Initialize all domain attributes from default domains if necessary
883 949
  * @param d initialized TLS domain
... ...
@@ -915,8 +981,36 @@ static int fix_domain(tls_domain_t* d, tls_domain_t* def)
915 981
 		if(d->method>TLS_USE_TLSvRANGE) {
916 982
 			SSL_CTX_set_options(d->ctx[i], (long)ssl_methods[d->method - 1]);
917 983
 		}
984
+#ifndef OPENSSL_NO_TLSEXT
985
+		/*
986
+		* check server domains for server_name extension and register
987
+		* callback function
988
+		*/
989
+		if ((d->type & TLS_DOMAIN_SRV) && d->server_name.len>0) {
990
+			if (!SSL_CTX_set_tlsext_servername_callback(d->ctx[i], tls_server_name_cb)) {
991
+				LM_ERR("register server_name callback handler for socket "
992
+					"[%s:%d], server_name='%s' failed for proc %d\n",
993
+					ip_addr2a(&d->ip), d->port, d->server_name.s, i);
994
+				return -1;
995
+			}
996
+			if (!SSL_CTX_set_tlsext_servername_arg(d->ctx[i], d)) {
997
+				LM_ERR("register server_name callback handler data for socket "
998
+					"[%s:%d], server_name='%s' failed for proc %d\n",
999
+					ip_addr2a(&d->ip), d->port, d->server_name.s, i);
1000
+				return -1;
1001
+			}
1002
+		}
1003
+#endif
918 1004
 	}
919
-	
1005
+
1006
+#ifndef OPENSSL_NO_TLSEXT
1007
+	if ((d->type & TLS_DOMAIN_SRV) && d->server_name.len>0) {
1008
+		LM_NOTICE("registered server_name callback handler for socket "
1009
+			"[%s:%d], server_name='%s' ...\n", ip_addr2a(&d->ip), d->port,
1010
+			d->server_name.s);
1011
+	}
1012
+#endif
1013
+
920 1014
 	if (load_cert(d) < 0) return -1;
921 1015
 	if (load_ca_list(d) < 0) return -1;
922 1016
 	if (load_crl(d) < 0) return -1;
... ...
@@ -1195,7 +1289,7 @@ tls_domains_cfg_t* tls_new_cfg(void)
1195 1289
  * @return found configuration or default, if not found
1196 1290
  */
1197 1291
 tls_domain_t* tls_lookup_cfg(tls_domains_cfg_t* cfg, int type,
1198
-								struct ip_addr* ip, unsigned short port)
1292
+		struct ip_addr* ip, unsigned short port, str *sname)
1199 1293
 {
1200 1294
 	tls_domain_t *p;
1201 1295
 
... ...
@@ -1208,8 +1302,17 @@ tls_domain_t* tls_lookup_cfg(tls_domains_cfg_t* cfg, int type,
1208 1302
 	}
1209 1303
 
1210 1304
 	while (p) {
1211
-		if ((p->port == port) && ip_addr_cmp(&p->ip, ip))
1212
-			return p;
1305
+		if ((p->port == port) && ip_addr_cmp(&p->ip, ip)) {
1306
+			if(sname && sname->len>0) {
1307
+				if(p->server_name.len==sname->len
1308
+					&& strncasecmp(p->server_name.s, sname->s, sname->len)==0) {
1309
+					LM_DBG("socket+server_name based TLS server domain found\n");
1310
+					return p;
1311
+				}
1312
+			} else {
1313
+				return p;
1314
+			}
1315
+		}
1213 1316
 		p = p->next;
1214 1317
 	}
1215 1318
 
... ...
@@ -1238,8 +1341,13 @@ static int domain_exists(tls_domains_cfg_t* cfg, tls_domain_t* d)
1238 1341
 	}
1239 1342
 
1240 1343
 	while (p) {
1241
-		if ((p->port == d->port) && ip_addr_cmp(&p->ip, &d->ip))
1242
-			return 1;
1344
+		if ((p->port == d->port) && ip_addr_cmp(&p->ip, &d->ip)) {
1345
+			if(p->server_name.len==0) {
1346
+				LM_WARN("another tls domain with same address was defined"
1347
+						" and no server name provided\n");
1348
+				return 1;
1349
+			}
1350
+		}
1243 1351
 		p = p->next;
1244 1352
 	}
1245 1353
 
... ...
@@ -105,6 +105,7 @@ typedef struct tls_domain {
105 105
 	str cipher_list;
106 106
 	enum tls_method method;
107 107
 	str crl_file;
108
+	str server_name;
108 109
 	struct tls_domain* next;
109 110
 } tls_domain_t;
110 111
 
... ...
@@ -189,10 +190,11 @@ int tls_fix_domains_cfg(tls_domains_cfg_t* cfg, tls_domain_t* srv_defaults,
189 190
  * @param type type of configuration
190 191
  * @param ip IP for configuration
191 192
  * @param port port for configuration
193
+ * @param sname server name
192 194
  * @return found configuration or default, if not found
193 195
  */
194 196
 tls_domain_t* tls_lookup_cfg(tls_domains_cfg_t* cfg, int type,
195
-								struct ip_addr* ip, unsigned short port);
197
+				struct ip_addr* ip, unsigned short port, str *sname);
196 198
 
197 199
 
198 200
 /**
... ...
@@ -138,6 +138,8 @@ const SSL_METHOD* ssl_methods[TLS_METHOD_MAX];
138 138
 #define NULL_GRACE_PERIOD 10U
139 139
 */
140 140
 
141
+
142
+
141 143
 inline static char* buf_append(char* buf, char* end, char* str, int str_len)
142 144
 {
143 145
 	if ( (buf+str_len)<end){
... ...
@@ -151,6 +151,8 @@ static int tls_complete_init(struct tcp_connection* c)
151 151
 	      * count immediately.
152 152
 	      */
153 153
 
154
+	LM_DBG("completing tls connection initialization\n");
155
+
154 156
 	lock_get(tls_domains_cfg_lock);
155 157
 	cfg = *tls_domains_cfg;
156 158
 
... ...
@@ -164,17 +166,19 @@ static int tls_complete_init(struct tcp_connection* c)
164 166
 	if (c->flags & F_CONN_PASSIVE) {
165 167
 		state=S_TLS_ACCEPTING;
166 168
 		dom = tls_lookup_cfg(cfg, TLS_DOMAIN_SRV,
167
-								&c->rcv.dst_ip, c->rcv.dst_port);
169
+								&c->rcv.dst_ip, c->rcv.dst_port, 0);
168 170
 	} else {
169 171
 		state=S_TLS_CONNECTING;
170 172
 		dom = tls_lookup_cfg(cfg, TLS_DOMAIN_CLI,
171
-								&c->rcv.dst_ip, c->rcv.dst_port);
173
+								&c->rcv.dst_ip, c->rcv.dst_port, 0);
172 174
 	}
173 175
 	if (unlikely(c->state<0)) {
174 176
 		BUG("Invalid connection (state %d)\n", c->state);
175 177
 		goto error;
176 178
 	}
177
-	DBG("Using TLS domain %s\n", tls_domain_str(dom));
179
+	DBG("Using initial TLS domain %s (dom %p ctx %p sn [%s])\n",
180
+			tls_domain_str(dom), dom, dom->ctx[process_no],
181
+			ZSW(dom->server_name.s));
178 182
 
179 183
 	data = (struct tls_extra_data*)shm_malloc(sizeof(struct tls_extra_data));
180 184
 	if (!data) {