4 Copyright (C) 2003-2004 Guus Sliepen <guus@tinc-vpn.org>,
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.
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.
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.
25 #include <gnutls/gnutls.h>
27 #include "support/avl.h"
28 #include "support/sockaddr.h"
29 #include "support/xalloc.h"
32 static bool tnl_send(tnl_t *tnl, const void *buf, int len) {
36 result = gnutls_record_send(tnl->session, buf, len);
38 if(result == GNUTLS_E_INTERRUPTED || result == GNUTLS_E_AGAIN)
42 logger(LOG_ERR, _("tnl: error while sending: %s"), gnutls_strerror(result));
44 logger(LOG_INFO, _("tnl: connection closed by peer"));
46 tnl->error(tnl, result);
58 static bool tnl_recv(tnl_t *tnl) {
60 tnl_record_t *record = (tnl_record_t *)tnl->buf;
62 result = gnutls_record_recv(tnl->session, tnl->buf + tnl->bufread, sizeof tnl->buf - tnl->bufread);
64 if(result == GNUTLS_E_INTERRUPTED || result == GNUTLS_E_AGAIN)
68 logger(LOG_ERR, _("tnl: error while receiving: %s"), gnutls_strerror(result));
70 logger(LOG_INFO, _("tnl: connection closed by peer"));
72 tnl->error(tnl, result);
77 tnl->bufread += result;
79 while(tnl->bufread >= sizeof *record && tnl->bufread - sizeof *record >= record->len) {
80 switch(record->type) {
82 tnl->recv_meta(tnl, record->data, record->len);
85 case TNL_RECORD_PACKET:
86 tnl->recv_packet(tnl, record->data, record->len);
90 logger(LOG_ERR, _("tnl: error while receiving: %s"), _("unknown record type"));
91 tnl->error(tnl, EINVAL);
96 tnl->bufread -= sizeof *record + record->len;
97 memmove(tnl->buf, record->data + record->len, tnl->bufread);
101 static bool tnl_recv_handler(fd_t *fd) {
102 tnl_t *tnl = fd->data;
105 result = gnutls_record_recv(tnl->session, tnl->buf + tnl->bufread, sizeof(tnl->buf) - tnl->bufread);
107 if(gnutls_error_is_fatal(result)) {
108 logger(LOG_DEBUG, _("tnl: reception failed: %s\n"), gnutls_strerror(result));
109 tnl->error(tnl, result);
117 tnl->bufread += result;
118 return tnl_recv(tnl);
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;
128 certs = gnutls_certificate_get_peers(tnl->session, &ncerts);
130 if (!certs || !ncerts) {
131 logger(LOG_ERR, _("tnl: no certificates from %s"), tnl->remote.hostname);
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);
140 logger(LOG_ERR, _("tnl: error importing certificate from %s: %s"), tnl->remote.hostname, gnutls_strerror(errno));
141 gnutls_x509_crt_deinit(cert);
145 name = strstr(buf, "CN=");
147 logger(LOG_ERR, _("tnl: no name in certificate from %s"), tnl->remote.hostname);
148 gnutls_x509_crt_deinit(cert);
153 for(p = name; *p && *p != ','; p++);
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);
161 replace(tnl->remote.id, name);
163 result = gnutls_certificate_verify_peers(tnl->session);
166 logger(LOG_ERR, "tnl: error verifying certificate from %s (%s): %s\n", tnl->remote.id, tnl->remote.hostname, gnutls_strerror(result));
171 logger(LOG_ERR, "tnl: certificate from %s (%s) not good, verification result %x", tnl->remote.id, tnl->remote.hostname, result);
178 static bool tnl_handshake_handler(fd_t *fd) {
180 tnl_t *tnl = fd->data;
183 result = gnutls_handshake(tnl->session);
185 if(gnutls_error_is_fatal(result)) {
186 logger(LOG_ERR, "tnl: handshake error: %s\n", gnutls_strerror(result));
191 /* check other stuff? */
195 logger(LOG_DEBUG, _("tnl: handshake finished"));
197 if(!tnl_authenticate(tnl))
200 tnl->status == TNL_STATUS_UP;
201 tnl->fd.read = tnl_recv_handler;
206 static bool tnl_send_meta(tnl_t *tnl, const void *buf, int len) {
207 tnl_record_t record = {
208 .type = TNL_RECORD_META,
212 return tnl_send(tnl, &record, sizeof record) && tnl_send(tnl, buf, len);
215 static bool tnl_send_packet(tnl_t *tnl, const void *buf, int len) {
216 tnl_record_t record = {
217 .type = TNL_RECORD_PACKET,
221 return tnl_send(tnl, &record, sizeof record) && tnl_send(tnl, buf, len);
224 static bool tnl_close(tnl_t *tnl) {
226 gnutls_bye(tnl->session, GNUTLS_SHUT_RDWR);
227 gnutls_deinit(tnl->session);
236 static bool tnl_accept_error(tnl_t *tnl, int errnum) {
237 logger(LOG_ERR, _("tnl: error %d on accepted tunnel"));
241 static bool tnl_accept_handler(fd_t *fd) {
242 tnl_listen_t *listener = fd->data;
244 struct sockaddr_storage ss;
245 socklen_t len = sizeof ss;
248 sock = accept(fd->fd, sa(&ss), &len);
251 logger(LOG_ERR, _("tnl: could not accept incoming connection: %s"), strerror(errno));
255 logger(LOG_DEBUG, _("tnl: accepted incoming connection"));
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;
272 tnl->fd.read = tnl_handshake_handler;
275 fcntl(sock, F_SETFL, fcntl(sock, F_GETFL) | O_NONBLOCK);
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);
285 tnl->accept = listener->accept;
292 static bool tnl_connect_handler(fd_t *fd) {
293 tnl_t *tnl = fd->data;
298 getsockopt(fd->fd, SOL_SOCKET, SO_ERROR, &result, &len);
300 logger(LOG_ERR, "tnl: error while connecting: %s", strerror(result));
301 tnl->error(tnl, result);
306 fcntl(tnl->fd.fd, F_SETFL, fcntl(tnl->fd.fd, F_GETFL) | O_NONBLOCK);
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);
317 tnl->fd.write = NULL;
318 tnl->fd.read = tnl_handshake_handler;
321 logger(LOG_DEBUG, _("tnl: connected"));
326 bool tnl_connect(tnl_t *tnl) {
329 sock = socket(sa_family(&tnl->remote.address), tnl->type, tnl->protocol);
332 logger(LOG_ERR, _("tnl: could not create socket: %s"), strerror(errno));
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));
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));
350 tnl->status = TNL_STATUS_CONNECTING;
353 tnl->fd.write = tnl_connect_handler;
356 tnl->send_packet = tnl_send_packet;
357 tnl->send_meta = tnl_send_meta;
358 tnl->close = tnl_close;
365 static bool tnl_listen_close(tnl_listen_t *listener) {
366 fd_del(&listener->fd);
367 close(listener->fd.fd);
371 bool tnl_listen(tnl_listen_t *listener) {
374 sock = socket(sa_family(&listener->local.address), listener->type, listener->protocol);
377 logger(LOG_ERR, _("tnl: could not create listener socket: %s"), strerror(errno));
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));
386 if(listen(sock, 10) == -1) {
387 logger(LOG_ERR, _("tnl: could not listen on listener socket: %s"), strerror(errno));
391 listener->fd.fd = sock;
392 listener->fd.read = tnl_accept_handler;
393 listener->fd.data = listener;
394 listener->close = tnl_listen_close;
396 fd_add(&listener->fd);