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