2014-08-03 08:46:42 +02:00
|
|
|
/*
|
|
|
|
This file is part of libcapwap.
|
|
|
|
|
|
|
|
libcapwap is free software: you can redistribute it and/or modify
|
|
|
|
it under the terms of the GNU General Public License as published by
|
|
|
|
the Free Software Foundation, either version 3 of the License, or
|
|
|
|
(at your option) any later version.
|
|
|
|
|
|
|
|
libcapwap is distributed in the hope that it will be useful,
|
|
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
GNU General Public License for more details.
|
|
|
|
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
|
|
along with Foobar. If not, see <http://www.gnu.org/licenses/>.
|
|
|
|
|
|
|
|
*/
|
|
|
|
|
2015-02-07 10:54:32 +01:00
|
|
|
#include <stdlib.h>
|
2015-02-08 11:42:01 +01:00
|
|
|
#include <string.h>
|
|
|
|
#include <errno.h>
|
2014-08-03 13:35:09 +02:00
|
|
|
|
|
|
|
#include <gnutls/gnutls.h>
|
2015-02-08 11:42:01 +01:00
|
|
|
#include <gnutls/dtls.h>
|
2014-08-03 13:35:09 +02:00
|
|
|
|
|
|
|
|
2018-03-17 17:29:09 +01:00
|
|
|
|
2014-08-03 13:35:09 +02:00
|
|
|
#include "conn.h"
|
2015-04-11 19:00:51 +02:00
|
|
|
#include "dbg.h"
|
2015-04-10 17:52:01 +02:00
|
|
|
#include "log.h"
|
2015-02-08 11:42:01 +01:00
|
|
|
#include "sock.h"
|
2015-04-10 17:14:55 +02:00
|
|
|
#include "capwap.h"
|
2018-03-02 00:12:38 +01:00
|
|
|
#include "dtls_common.h"
|
2015-02-08 16:28:04 +01:00
|
|
|
#include "dtls_gnutls.h"
|
2015-04-11 19:00:51 +02:00
|
|
|
#include "timer.h"
|
2015-02-07 10:54:32 +01:00
|
|
|
|
2014-08-03 13:35:09 +02:00
|
|
|
|
2018-04-04 10:59:07 +02:00
|
|
|
int psk_creds(gnutls_session_t session, const char *user, gnutls_datum_t *key)
|
|
|
|
{
|
|
|
|
struct conn *conn;
|
|
|
|
|
|
|
|
int rc;
|
|
|
|
uint8_t * psk;
|
|
|
|
int psk_len;
|
|
|
|
|
|
|
|
conn = gnutls_session_get_ptr(session);
|
|
|
|
|
|
|
|
rc = conn->dtls_get_psk(conn,user, &psk,&psk_len);
|
|
|
|
if (!rc)
|
|
|
|
return -1;
|
|
|
|
key->size=psk_len;
|
|
|
|
key->data = gnutls_malloc(key->size);
|
|
|
|
if (key->data == NULL) {
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
memcpy(key->data, psk, psk_len);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2015-02-08 11:42:01 +01:00
|
|
|
int dtls_gnutls_accept(struct conn *conn)
|
2014-08-03 08:46:42 +02:00
|
|
|
{
|
2018-03-05 12:23:16 +01:00
|
|
|
char sock_buf[SOCK_ADDR_BUFSIZE];
|
2018-04-02 01:39:08 +02:00
|
|
|
char cookie_buf[SOCK_ADDR_BUFSIZE];
|
2015-02-07 10:54:32 +01:00
|
|
|
struct dtls_gnutls_data *d;
|
2018-03-09 07:44:17 +01:00
|
|
|
uint8_t buffer[2048];
|
|
|
|
int tlen, rc;
|
|
|
|
time_t c_timer;
|
2018-04-04 00:43:13 +02:00
|
|
|
int bits;
|
2015-02-08 11:42:01 +01:00
|
|
|
|
|
|
|
gnutls_datum_t cookie_key;
|
2018-03-09 07:44:17 +01:00
|
|
|
gnutls_dtls_prestate_st prestate;
|
2018-04-04 00:43:13 +02:00
|
|
|
|
2018-04-04 10:59:07 +02:00
|
|
|
|
|
|
|
|
|
|
|
|
2015-02-08 11:42:01 +01:00
|
|
|
gnutls_key_generate(&cookie_key, GNUTLS_COOKIE_KEY_SIZE);
|
2015-04-12 23:28:55 +02:00
|
|
|
cw_dbg(DBG_DTLS, "Session cookie for %s generated: %s",
|
2018-04-04 10:59:07 +02:00
|
|
|
sock_addr2str(&conn->addr, sock_buf),
|
2018-04-02 01:39:08 +02:00
|
|
|
sock_hwaddrtostr((uint8_t *) (&cookie_key),
|
2018-04-04 10:59:07 +02:00
|
|
|
sizeof(cookie_key), cookie_buf, ""));
|
2018-03-09 07:44:17 +01:00
|
|
|
|
2015-02-08 11:42:01 +01:00
|
|
|
|
|
|
|
memset(&prestate, 0, sizeof(prestate));
|
|
|
|
|
2018-04-04 00:12:20 +02:00
|
|
|
tlen = dtls_gnutls_bio_read(conn, buffer, sizeof(buffer));
|
2015-02-08 11:42:01 +01:00
|
|
|
|
|
|
|
gnutls_dtls_cookie_send(&cookie_key, &conn->addr, sizeof(conn->addr),
|
2018-04-04 10:59:07 +02:00
|
|
|
&prestate, (gnutls_transport_ptr_t) conn,
|
|
|
|
dtls_gnutls_bio_write);
|
2015-02-08 11:42:01 +01:00
|
|
|
|
2018-04-04 10:59:07 +02:00
|
|
|
rc = -1;
|
2018-03-09 07:44:17 +01:00
|
|
|
|
2015-02-08 11:42:01 +01:00
|
|
|
|
2018-03-09 07:44:17 +01:00
|
|
|
|
|
|
|
c_timer = cw_timer_start(10);
|
2015-02-08 11:42:01 +01:00
|
|
|
|
2018-04-04 10:59:07 +02:00
|
|
|
while (!cw_timer_timeout(c_timer)) {
|
2015-02-08 11:42:01 +01:00
|
|
|
|
2018-04-04 10:59:07 +02:00
|
|
|
tlen = conn_q_recv_packet_peek(conn, buffer, sizeof(buffer));
|
2015-02-08 11:42:01 +01:00
|
|
|
|
2018-04-04 10:59:07 +02:00
|
|
|
if (tlen < 0 && errno == EAGAIN)
|
2015-02-08 11:42:01 +01:00
|
|
|
continue;
|
2018-04-04 10:59:07 +02:00
|
|
|
if (tlen < 0) {
|
2018-04-07 19:28:00 +02:00
|
|
|
/* something went wrong, we should log a message */
|
2015-02-08 11:42:01 +01:00
|
|
|
continue;
|
|
|
|
}
|
2018-04-04 10:59:07 +02:00
|
|
|
|
2015-02-08 11:42:01 +01:00
|
|
|
rc = gnutls_dtls_cookie_verify(&cookie_key,
|
|
|
|
&conn->addr,
|
2018-04-04 10:59:07 +02:00
|
|
|
sizeof(conn->addr), buffer + 4, tlen - 4,
|
|
|
|
&prestate);
|
2016-03-12 16:12:36 +01:00
|
|
|
|
2018-04-04 10:59:07 +02:00
|
|
|
if (rc < 0) {
|
|
|
|
cw_dbg(DBG_DTLS, "Cookie couldn't be verified: %s",
|
|
|
|
gnutls_strerror(rc));
|
2015-02-08 11:42:01 +01:00
|
|
|
dtls_gnutls_bio_read(conn, buffer, sizeof(buffer));
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
2018-04-04 10:59:07 +02:00
|
|
|
if (rc < 0) {
|
2015-04-12 23:28:55 +02:00
|
|
|
cw_log(LOG_ERR, "Cookie couldn't be verified: %s", gnutls_strerror(rc));
|
2015-02-08 11:42:01 +01:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2018-04-04 10:59:07 +02:00
|
|
|
cw_dbg(DBG_DTLS, "Cookie verified! Starting handshake with %s ...",
|
|
|
|
sock_addr2str(&conn->addr, sock_buf));
|
2015-04-12 23:28:55 +02:00
|
|
|
|
2015-02-08 11:42:01 +01:00
|
|
|
|
|
|
|
|
2018-04-04 10:59:07 +02:00
|
|
|
d = dtls_gnutls_data_create(conn, GNUTLS_SERVER | GNUTLS_DATAGRAM);
|
2015-02-07 10:54:32 +01:00
|
|
|
if (!d)
|
|
|
|
return 0;
|
|
|
|
|
2018-04-04 00:43:13 +02:00
|
|
|
|
2018-04-04 10:59:07 +02:00
|
|
|
if (conn->dtls_psk_enable) {
|
|
|
|
gnutls_psk_server_credentials_t cred;
|
|
|
|
rc = gnutls_psk_allocate_server_credentials(&cred);
|
|
|
|
if (rc != 0) {
|
|
|
|
cw_log(LOG_ERR,"gnutls_psk_allocate_server_credentials() failed.");
|
|
|
|
}
|
|
|
|
/* GnuTLS will call psk_creds to ask for the key associated with the
|
|
|
|
client's username.*/
|
|
|
|
gnutls_psk_set_server_credentials_function(cred, psk_creds);
|
|
|
|
/* // Pass the "credentials" to the GnuTLS session. GnuTLS does NOT make an
|
|
|
|
// internal copy of the information, so we have to keep the 'cred' structure
|
|
|
|
// in memory (and not modify it) until we're done with this session.*/
|
|
|
|
rc = gnutls_credentials_set(d->session, GNUTLS_CRD_PSK, cred);
|
|
|
|
if (rc != 0) {
|
|
|
|
cw_log(LOG_ERR,"gnutls_credentials_set() failed.\n");
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
2018-04-04 00:43:13 +02:00
|
|
|
|
|
|
|
|
|
|
|
|
2018-04-04 10:59:07 +02:00
|
|
|
/* Generate Diffie-Hellman parameters - for use with DHE
|
|
|
|
* kx algorithms. When short bit length is used, it might
|
|
|
|
* be wise to regenerate parameters often.
|
|
|
|
*/
|
|
|
|
/*bits = gnutls_sec_param_to_pk_bits(GNUTLS_PK_DH, GNUTLS_SEC_PARAM_LEGACY); */
|
|
|
|
bits = conn->dtls_dhbits;
|
|
|
|
|
|
|
|
gnutls_dh_params_init(&d->dh_params);
|
2018-04-04 00:43:13 +02:00
|
|
|
|
2018-04-04 10:59:07 +02:00
|
|
|
cw_dbg(DBG_DTLS, "Generating DH params, %d", bits);
|
|
|
|
gnutls_dh_params_generate2(d->dh_params, bits);
|
|
|
|
cw_dbg(DBG_DTLS, "DH params generated, %d", bits);
|
|
|
|
gnutls_certificate_set_dh_params(d->x509_cred, d->dh_params);
|
2018-04-04 00:43:13 +02:00
|
|
|
|
2018-04-04 10:59:07 +02:00
|
|
|
gnutls_certificate_server_set_request(d->session, GNUTLS_CERT_REQUEST);
|
2015-02-08 11:42:01 +01:00
|
|
|
gnutls_dtls_prestate_set(d->session, &prestate);
|
2015-02-07 10:54:32 +01:00
|
|
|
|
2015-02-08 11:42:01 +01:00
|
|
|
c_timer = cw_timer_start(10);
|
2018-04-04 10:59:07 +02:00
|
|
|
do {
|
2015-02-07 10:54:32 +01:00
|
|
|
rc = gnutls_handshake(d->session);
|
2018-04-04 10:59:07 +02:00
|
|
|
} while (!cw_timer_timeout(c_timer) && rc == GNUTLS_E_AGAIN);
|
|
|
|
|
2015-02-08 11:42:01 +01:00
|
|
|
|
|
|
|
|
2018-04-04 10:59:07 +02:00
|
|
|
if (rc < 0) {
|
|
|
|
cw_log(LOG_ERR, "Error in handshake with %s: %s",
|
|
|
|
sock_addr2str(&conn->addr, sock_buf), gnutls_strerror(rc));
|
2015-02-08 11:42:01 +01:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2015-02-07 10:54:32 +01:00
|
|
|
|
2018-04-04 10:59:07 +02:00
|
|
|
cw_dbg(DBG_DTLS, "Handshake with %s successful.",
|
|
|
|
sock_addr2str(&conn->addr, sock_buf));
|
2014-08-03 13:35:09 +02:00
|
|
|
|
2018-04-04 10:59:07 +02:00
|
|
|
conn->dtls_data = d;
|
2015-02-08 11:42:01 +01:00
|
|
|
conn->read = dtls_gnutls_read;
|
|
|
|
conn->write = dtls_gnutls_write;
|
2014-08-03 13:35:09 +02:00
|
|
|
|
2015-02-08 11:42:01 +01:00
|
|
|
return 1;
|
2014-08-03 08:46:42 +02:00
|
|
|
}
|