1 /*
2  * Copyright (C) 2013-2018 Nikos Mavrogiannopoulos
3  *
4  * This program is free software; you can redistribute it and/or modify
5  * it under the terms of the GNU General Public License as published by
6  * the Free Software Foundation; either version 2 of the License, or
7  * (at your option) any later version.
8  *
9  * This program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  * GNU General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License
15  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
16  */
17 
18 #include <config.h>
19 
20 #include <stdio.h>
21 #include <stdlib.h>
22 #include <string.h>
23 #include <unistd.h>
24 #include <sys/types.h>
25 #include <sys/select.h>
26 #include <sys/wait.h>
27 #include <fcntl.h>
28 #include <sys/socket.h>
29 #include <netdb.h>
30 #include <errno.h>
31 #include <sys/ioctl.h>
32 #include <gnutls/gnutls.h>
33 #include <gnutls/crypto.h>
34 #include <ccan/hash/hash.h>
35 
36 #include <main.h>
37 #include <sec-mod-resume.h>
38 #include <common.h>
39 #include <ip-util.h>
40 #include <tlslib.h>
41 
handle_resume_delete_req(sec_mod_st * sec,const SessionResumeFetchMsg * req)42 int handle_resume_delete_req(sec_mod_st *sec,
43 			     const SessionResumeFetchMsg *req)
44 {
45 	tls_cache_st *cache;
46 	struct htable_iter iter;
47 	size_t key;
48 
49 	key = hash_any(req->session_id.data, req->session_id.len, 0);
50 
51 	cache = htable_firstval(sec->tls_db.ht, &iter, key);
52 	while (cache != NULL) {
53 		if (req->session_id.len == cache->session_id_size &&
54 		    memcmp(req->session_id.data, cache->session_id,
55 			   req->session_id.len) == 0) {
56 
57 			cache->session_data_size = 0;
58 			cache->session_id_size = 0;
59 
60 			htable_delval(sec->tls_db.ht, &iter);
61 			talloc_free(cache);
62 			sec->tls_db.entries--;
63 			return 0;
64 		}
65 
66 		cache = htable_nextval(sec->tls_db.ht, &iter, key);
67 	}
68 
69 	return 0;
70 }
71 
handle_resume_fetch_req(sec_mod_st * sec,const SessionResumeFetchMsg * req,SessionResumeReplyMsg * rep)72 int handle_resume_fetch_req(sec_mod_st *sec,
73 			    const SessionResumeFetchMsg *req,
74 			    SessionResumeReplyMsg *rep)
75 {
76 	tls_cache_st *cache;
77 	struct htable_iter iter;
78 	size_t key;
79 
80 	rep->reply = SESSION_RESUME_REPLY_MSG__RESUME__REP__FAILED;
81 
82 	key = hash_any(req->session_id.data, req->session_id.len, 0);
83 
84 	cache = htable_firstval(sec->tls_db.ht, &iter, key);
85 	while (cache != NULL) {
86 		if (req->session_id.len == cache->session_id_size &&
87 		    memcmp(req->session_id.data, cache->session_id,
88 			   req->session_id.len) == 0) {
89 
90 			if (req->vhost && cache->vhostname && c_strcasecmp(req->vhost, cache->vhostname) != 0)
91 				return 0;
92 			else if (req->vhost != cache->vhostname)
93 				return 0;
94 
95 			if (req->cli_addr.len == cache->remote_addr_len &&
96 			    ip_cmp((struct sockaddr_storage *)req->cli_addr.data, &cache->remote_addr) == 0) {
97 
98 				rep->reply =
99 				    SESSION_RESUME_REPLY_MSG__RESUME__REP__OK;
100 
101 				rep->has_session_data = 1;
102 
103 				rep->session_data.data =
104 				    (void *)cache->session_data;
105 				rep->session_data.len =
106 				    cache->session_data_size;
107 
108 				seclog_hex(sec, LOG_DEBUG, "TLS session DB resuming",
109 					  req->session_id.data,
110 					  req->session_id.len, 0);
111 
112 				return 0;
113 			}
114 		}
115 
116 		cache = htable_nextval(sec->tls_db.ht, &iter, key);
117 	}
118 
119 	return 0;
120 
121 }
122 
handle_resume_store_req(sec_mod_st * sec,const SessionResumeStoreReqMsg * req)123 int handle_resume_store_req(sec_mod_st *sec,
124 			    const SessionResumeStoreReqMsg *req)
125 {
126 	tls_cache_st *cache;
127 	size_t key;
128 	unsigned int max;
129 
130 	if (req->session_id.len > GNUTLS_MAX_SESSION_ID)
131 		return -1;
132 	if (req->session_data.len > MAX_SESSION_DATA_SIZE)
133 		return -1;
134 
135 	max = MAX(2 * GETCONFIG(sec)->max_clients, DEFAULT_MAX_CACHED_TLS_SESSIONS);
136 	if (sec->tls_db.entries >= max) {
137 		seclog(sec, LOG_INFO,
138 		      "maximum number of stored TLS sessions reached (%u)",
139 		      max);
140 		return -1;
141 	}
142 
143 	if (req->cli_addr.len == 0) {
144 		seclog(sec, LOG_INFO,
145 		      "invalid address length");
146 		return -1;
147 	}
148 
149 	key = hash_any(req->session_id.data, req->session_id.len, 0);
150 
151 	cache = talloc(sec->tls_db.ht, tls_cache_st);
152 	if (cache == NULL)
153 		return -1;
154 
155 	cache->session_id_size = req->session_id.len;
156 	cache->session_data_size = req->session_data.len;
157 	cache->remote_addr_len = req->cli_addr.len;
158 	if (req->vhost)
159 		cache->vhostname = talloc_strdup(cache, req->vhost);
160 	else
161 		cache->vhostname = NULL;
162 
163 	memcpy(cache->session_id, req->session_id.data, req->session_id.len);
164 	memcpy(cache->session_data, req->session_data.data,
165 	       req->session_data.len);
166 	memcpy(&cache->remote_addr, req->cli_addr.data, req->cli_addr.len);
167 
168 	if (htable_add(sec->tls_db.ht, key, cache) == 0) {
169 		seclog(sec, LOG_INFO,
170 		      "could not add TLS session to hash table");
171 		talloc_free(cache);
172 	} else {
173 		sec->tls_db.entries++;
174 
175 		seclog_hex(sec, LOG_DEBUG, "TLS session DB storing",
176 					req->session_id.data,
177 					req->session_id.len, 0);
178 	}
179 
180 	return 0;
181 }
182 
expire_tls_sessions(sec_mod_st * sec)183 void expire_tls_sessions(sec_mod_st *sec)
184 {
185 	tls_cache_st *cache;
186 	struct htable_iter iter;
187 	time_t now, exp;
188 
189 	now = time(0);
190 
191 	cache = htable_first(sec->tls_db.ht, &iter);
192 	while (cache != NULL) {
193 		gnutls_datum_t d;
194 
195 		d.data = (void *)cache->session_data;
196 		d.size = cache->session_data_size;
197 
198 		exp = gnutls_db_check_entry_time(&d);
199 
200 		if (now - exp > TLS_SESSION_EXPIRATION_TIME(GETCONFIG(sec))) {
201 			cache->session_id_size = 0;
202 
203 			htable_delval(sec->tls_db.ht, &iter);
204 
205 			safe_memset(cache->session_data, 0, cache->session_data_size);
206 			talloc_free(cache);
207 			sec->tls_db.entries--;
208 		}
209 		cache = htable_next(sec->tls_db.ht, &iter);
210 	}
211 
212 	return;
213 }
214