Add TODO list.
[tinc] / tnl / tnl.c
1 /*
2     tnl.c -- tunnels
3
4     Copyright (C) 2003-2004 Guus Sliepen <guus@tinc-vpn.org>,
5
6     This program is free software; you can redistribute it and/or modify
7     it under the terms of the GNU General Public License as published by
8     the Free Software Foundation; either version 2 of the License, or
9     (at your option) any later version.
10
11     This program is distributed in the hope that it will be useful,
12     but WITHOUT ANY WARRANTY; without even the implied warranty of
13     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14     GNU General Public License for more details.
15
16     You should have received a copy of the GNU General Public License
17     along with this program; if not, write to the Free Software
18     Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
19
20     $Id$
21 */
22
23 #include "system.h"
24
25 #include <gnutls/gnutls.h>
26
27 #include "support/avl.h"
28 #include "support/sockaddr.h"
29 #include "support/xalloc.h"
30 #include "tnl/tnl.h"
31
32 static bool tnl_send(tnl_t *tnl, const void *buf, int len) {
33         int result;
34
35         while(len) {
36                 result = gnutls_record_send(tnl->session, buf, len);
37                 if(result <= 0) {
38                         if(result == GNUTLS_E_INTERRUPTED || result == GNUTLS_E_AGAIN)
39                                 continue;
40
41                         if(result)
42                                 logger(LOG_ERR, _("tnl: error while sending: %s"), gnutls_strerror(result));
43                         else
44                                 logger(LOG_INFO, _("tnl: connection closed by peer"));
45
46                         tnl->error(tnl, result);
47                         tnl->close(tnl);
48                         return !result;
49                 }
50
51                 buf += result;
52                 len -= result;
53         }
54
55         return true;
56 }
57
58 static bool tnl_recv(tnl_t *tnl) {
59         int result;
60         tnl_record_t *record = (tnl_record_t *)tnl->buf;
61
62         result = gnutls_record_recv(tnl->session, tnl->buf + tnl->bufread, sizeof tnl->buf - tnl->bufread);
63         if(result <= 0) {
64                 if(result == GNUTLS_E_INTERRUPTED || result == GNUTLS_E_AGAIN)
65                         return true;
66
67                 if(result)
68                         logger(LOG_ERR, _("tnl: error while receiving: %s"), gnutls_strerror(result));
69                 else
70                         logger(LOG_INFO, _("tnl: connection closed by peer"));
71
72                 tnl->error(tnl, result);
73                 tnl->close(tnl);
74                 return !result;
75         }
76
77         tnl->bufread += result;
78
79         while(tnl->bufread >= sizeof *record && tnl->bufread - sizeof *record >= record->len) {
80                 switch(record->type) {
81                         case TNL_RECORD_META:
82                                 tnl->recv_meta(tnl, record->data, record->len);
83                                 break;
84
85                         case TNL_RECORD_PACKET:
86                                 tnl->recv_packet(tnl, record->data, record->len);
87                                 break;
88                                 
89                         default:
90                                 logger(LOG_ERR, _("tnl: error while receiving: %s"), _("unknown record type"));
91                                 tnl->error(tnl, EINVAL);
92                                 tnl->close(tnl);
93                                 return false;
94                 }
95
96                 tnl->bufread -= sizeof *record + record->len;
97                 memmove(tnl->buf, record->data + record->len, tnl->bufread);
98         }
99 }
100
101 static bool tnl_recv_handler(fd_t *fd) {
102         tnl_t *tnl = fd->data;
103         int result;
104
105         result = gnutls_record_recv(tnl->session, tnl->buf + tnl->bufread, sizeof(tnl->buf) - tnl->bufread);
106         if(result < 0) {
107                 if(gnutls_error_is_fatal(result)) {
108                         logger(LOG_DEBUG, _("tnl: reception failed: %s\n"), gnutls_strerror(result));
109                         tnl->error(tnl, result);
110                         tnl->close(tnl);
111                         return false;
112                 }
113
114                 return true;
115         }
116
117         tnl->bufread += result;
118         return tnl_recv(tnl);
119 }
120
121 static bool tnl_authenticate(tnl_t *tnl) {
122         gnutls_x509_crt cert;
123         const gnutls_datum *certs;
124         int ncerts = 0, result;
125         char buf[1024], *name, *p;
126         int len;
127
128         certs = gnutls_certificate_get_peers(tnl->session, &ncerts);
129
130         if (!certs || !ncerts) {
131                 logger(LOG_ERR, _("tnl: no certificates from %s"), tnl->remote.hostname);
132                 return false;
133         }
134
135         len = sizeof buf;
136         gnutls_x509_crt_init(&cert);
137         result = gnutls_x509_crt_import(cert, certs, GNUTLS_X509_FMT_DER) ?: gnutls_x509_crt_get_dn(cert, buf, &len);
138
139         if(result) {
140                 logger(LOG_ERR, _("tnl: error importing certificate from %s: %s"), tnl->remote.hostname, gnutls_strerror(errno));
141                 gnutls_x509_crt_deinit(cert);
142                 return false;
143         }
144
145         name = strstr(buf, "CN=");
146         if(!name) {
147                 logger(LOG_ERR, _("tnl: no name in certificate from %s"), tnl->remote.hostname);
148                 gnutls_x509_crt_deinit(cert);
149                 return false;
150         }
151
152         name += 3;
153         for(p = name; *p && *p != ','; p++);
154         *p = '\0';
155
156         if(tnl->remote.id && strcmp(tnl->remote.id, name)) {
157                 logger(LOG_ERR, _("tnl: peer %s is %s instead of %s"), tnl->remote.hostname, name, tnl->remote.id);
158                 return false;
159         }
160
161         replace(tnl->remote.id, name);
162
163         result = gnutls_certificate_verify_peers(tnl->session);
164
165         if(result < 0) {
166                 logger(LOG_ERR, "tnl: error verifying certificate from %s (%s): %s\n", tnl->remote.id, tnl->remote.hostname, gnutls_strerror(result));
167                 return false;
168         }
169
170         if(result) {
171                 logger(LOG_ERR, "tnl: certificate from %s (%s) not good, verification result %x", tnl->remote.id, tnl->remote.hostname, result);
172                 return false;
173         }
174 }
175
176         
177
178 static bool tnl_handshake_handler(fd_t *fd) {
179         char id[1024];
180         tnl_t *tnl = fd->data;
181         int result;
182
183         result = gnutls_handshake(tnl->session);
184         if(result < 0) {
185                 if(gnutls_error_is_fatal(result)) {
186                         logger(LOG_ERR, "tnl: handshake error: %s\n", gnutls_strerror(result));
187                         tnl->close(tnl);
188                         return false;
189                 }
190
191                 /* check other stuff? */
192                 return true;
193         }
194         
195         logger(LOG_DEBUG, _("tnl: handshake finished"));
196
197         if(!tnl_authenticate(tnl))
198                 return false;
199
200         tnl->status == TNL_STATUS_UP;
201         tnl->fd.read = tnl_recv_handler;
202         tnl->accept(tnl);
203         return true;
204 }
205
206 static bool tnl_send_meta(tnl_t *tnl, const void *buf, int len) {
207         tnl_record_t record = {
208                 .type = TNL_RECORD_META,
209                 .len = len,
210         };
211
212         return tnl_send(tnl, &record, sizeof record) && tnl_send(tnl, buf, len);
213 }
214
215 static bool tnl_send_packet(tnl_t *tnl, const void *buf, int len) {
216         tnl_record_t record = {
217                 .type = TNL_RECORD_PACKET,
218                 .len = len,
219         };
220
221         return tnl_send(tnl, &record, sizeof record) && tnl_send(tnl, buf, len);
222 }
223
224 static bool tnl_close(tnl_t *tnl) {
225         if(tnl->session) {
226                 gnutls_bye(tnl->session, GNUTLS_SHUT_RDWR);
227                 gnutls_deinit(tnl->session);
228         }
229                 
230         fd_del(&tnl->fd);
231         close(tnl->fd.fd);
232         
233         return true;
234 }
235
236 static bool tnl_accept_error(tnl_t *tnl, int errnum) {
237         logger(LOG_ERR, _("tnl: error %d on accepted tunnel"));
238         return true;
239 }
240
241 static bool tnl_accept_handler(fd_t *fd) {
242         tnl_listen_t *listener = fd->data;
243         tnl_t *tnl;
244         struct sockaddr_storage ss;
245         socklen_t len = sizeof ss;
246         int sock;       
247         
248         sock = accept(fd->fd, sa(&ss), &len);
249
250         if(sock == -1) {
251                 logger(LOG_ERR, _("tnl: could not accept incoming connection: %s"), strerror(errno));
252                 return false;
253         }
254
255         logger(LOG_DEBUG, _("tnl: accepted incoming connection"));
256
257         sa_unmap(&ss);
258
259         clear(new(tnl));
260         tnl->local = listener->local;
261         tnl->remote.address = ss;
262         len = sizeof tnl->local.address;
263         getsockname(sock, sa(&tnl->local.address), &len);
264         sa_unmap(&tnl->local.address);
265         tnl->type = listener->type;
266         tnl->protocol = listener->protocol;
267         tnl->status = TNL_STATUS_CONNECTING;
268         tnl->error = tnl_accept_error;
269         tnl->close = tnl_close;
270
271         tnl->fd.fd = sock;
272         tnl->fd.read = tnl_handshake_handler;
273         tnl->fd.data = tnl;
274
275         fcntl(sock, F_SETFL, fcntl(sock, F_GETFL) | O_NONBLOCK);
276
277         gnutls_init(&tnl->session, GNUTLS_SERVER);
278         //gnutls_handshake_set_private_extensions(tnl->session, 1);
279         gnutls_set_default_priority(tnl->session);
280         gnutls_credentials_set(tnl->session, GNUTLS_CRD_CERTIFICATE, tnl->local.cred);
281         gnutls_certificate_server_set_request(tnl->session, GNUTLS_CERT_REQUEST);
282         gnutls_transport_set_ptr(tnl->session, (gnutls_transport_ptr)sock);
283         gnutls_handshake(tnl->session);
284
285         tnl->accept = listener->accept;
286         
287         fd_add(&tnl->fd);
288         
289         return true;
290 }       
291
292 static bool tnl_connect_handler(fd_t *fd) {
293         tnl_t *tnl = fd->data;
294         int result;
295         socklen_t len;
296
297         len = sizeof result;
298         getsockopt(fd->fd, SOL_SOCKET, SO_ERROR, &result, &len);
299         if(result) {
300                 logger(LOG_ERR, "tnl: error while connecting: %s", strerror(result));
301                 tnl->error(tnl, result);
302                 tnl->close(tnl);
303                 return false;
304         }
305         
306         fcntl(tnl->fd.fd, F_SETFL, fcntl(tnl->fd.fd, F_GETFL) | O_NONBLOCK);
307
308         tnl->status = TNL_STATUS_HANDSHAKE;
309         gnutls_init(&tnl->session, GNUTLS_CLIENT);
310         //gnutls_handshake_set_private_extensions(tnl->session, 1);
311         gnutls_set_default_priority(tnl->session);
312         gnutls_credentials_set(tnl->session, GNUTLS_CRD_CERTIFICATE, tnl->local.cred);
313         gnutls_certificate_server_set_request(tnl->session, GNUTLS_CERT_REQUEST);
314         gnutls_transport_set_ptr(tnl->session, (gnutls_transport_ptr)fd->fd);
315         gnutls_handshake(tnl->session);
316
317         tnl->fd.write = NULL;
318         tnl->fd.read = tnl_handshake_handler;
319         fd_mod(&tnl->fd);
320
321         logger(LOG_DEBUG, _("tnl: connected"));
322         
323         return true;
324 }
325
326 bool tnl_connect(tnl_t *tnl) {
327         int sock;
328
329         sock = socket(sa_family(&tnl->remote.address), tnl->type, tnl->protocol);
330
331         if(sock == -1) {
332                 logger(LOG_ERR, _("tnl: could not create socket: %s"), strerror(errno));
333                 return false;
334         }
335         
336 #if 0
337         if(sa_nonzero(&tnl->local.address) && bind(sock, sa(&tnl->local.address), sa_len(&tnl->local.address)) == -1) {
338                 logger(LOG_ERR, _("tnl: could not bind socket: %s"), strerror(errno));
339                 close(sock);
340                 return false;
341         }
342 #endif
343
344         if(connect(sock, sa(&tnl->remote.address), sa_len(&tnl->remote.address)) == -1) {
345                 logger(LOG_ERR, _("tnl: could not connect: %s"), strerror(errno));
346                 close(sock);
347                 return false;
348         }
349
350         tnl->status = TNL_STATUS_CONNECTING;
351
352         tnl->fd.fd = sock;
353         tnl->fd.write = tnl_connect_handler;
354         tnl->fd.data = tnl;
355
356         tnl->send_packet = tnl_send_packet;
357         tnl->send_meta = tnl_send_meta;
358         tnl->close = tnl_close;
359         
360         fd_add(&tnl->fd);
361
362         return true;
363 }
364
365 static bool tnl_listen_close(tnl_listen_t *listener) {
366         fd_del(&listener->fd);
367         close(listener->fd.fd);
368         return true;
369 }
370
371 bool tnl_listen(tnl_listen_t *listener) {
372         int sock;
373
374         sock = socket(sa_family(&listener->local.address), listener->type, listener->protocol);
375
376         if(sock == -1) {
377                 logger(LOG_ERR, _("tnl: could not create listener socket: %s"), strerror(errno));
378                 return false;
379         }
380         
381         if(bind(sock, sa(&listener->local.address), sa_len(&listener->local.address)) == -1) {
382                 logger(LOG_ERR, _("tnl: could not bind listener socket: %s"), strerror(errno));
383                 return false;
384         }
385         
386         if(listen(sock, 10) == -1) {
387                 logger(LOG_ERR, _("tnl: could not listen on listener socket: %s"), strerror(errno));
388                 return false;
389         }
390
391         listener->fd.fd = sock;
392         listener->fd.read = tnl_accept_handler;
393         listener->fd.data = listener;
394         listener->close = tnl_listen_close;
395
396         fd_add(&listener->fd);
397
398         return true;
399 }