freewtp/lib/dtls.c

635 lines
18 KiB
C

#include "capwap.h"
#include "dtls.h"
#include "protocol.h"
#include <wolfssl/options.h>
#include <wolfssl/ssl.h>
#include <wolfssl/wolfcrypt/sha.h>
/* */
static const char g_char2hex[] = {
0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
-1, -1, -1, -1, -1, -1, -1,
10, 11, 12, 13, 14, 15, /* Upper Case A - F */
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
10, 11, 12, 13, 14, 15 /* Lower Case a - f */
};
static const int g_char2hex_length = sizeof(g_char2hex) / sizeof(g_char2hex[0]);
/* */
static int capwap_bio_method_recv(WOLFSSL* ssl, char* buffer, int length, void* context) {
struct capwap_dtls* dtls = (struct capwap_dtls*)context;
struct capwap_dtls_header* dtlspreamble;
int size;
/* Check read packet */
if ((dtls->length < sizeof(struct capwap_dtls_header)) || !dtls->buffer) {
if (!dtls->length && !dtls->buffer) {
return WOLFSSL_CBIO_ERR_WANT_READ; /* Notify empty buffer */
}
return WOLFSSL_CBIO_ERR_GENERAL;
}
/* Check DTLS Capwap Preamble */
dtlspreamble = (struct capwap_dtls_header*)dtls->buffer;
if ((dtlspreamble->preamble.version != CAPWAP_PROTOCOL_VERSION) || (dtlspreamble->preamble.type != CAPWAP_PREAMBLE_DTLS_HEADER)) {
log_printf(LOG_DEBUG, "Wrong DTLS Capwap Preamble");
return WOLFSSL_CBIO_ERR_GENERAL; /* Wrong DTLS Capwap Preamble */
}
/* */
size = dtls->length - sizeof(struct capwap_dtls_header);
dtls->length = 0;
dtls->buffer += sizeof(struct capwap_dtls_header);
if (size > length) {
dtls->buffer = NULL;
return WOLFSSL_CBIO_ERR_GENERAL;
}
/* Copy DTLS packet */
memcpy(buffer, dtls->buffer, size);
dtls->buffer = NULL;
return size;
}
/* */
static int capwap_bio_method_send(WOLFSSL* ssl, char* buffer, int length, void* context) {
int err;
char data[CAPWAP_MAX_PACKET_SIZE];
struct capwap_dtls* dtls = (struct capwap_dtls*)context;
struct capwap_dtls_header* dtlspreamble = (struct capwap_dtls_header*)data;
/* Check for maxium size of packet */
if (length > (CAPWAP_MAX_PACKET_SIZE - sizeof(struct capwap_dtls_header))) {
return WOLFSSL_CBIO_ERR_GENERAL;
}
/* Create DTLS Capwap Preamble */
dtlspreamble->preamble.version = CAPWAP_PROTOCOL_VERSION;
dtlspreamble->preamble.type = CAPWAP_PREAMBLE_DTLS_HEADER;
dtlspreamble->reserved1 = dtlspreamble->reserved2 = dtlspreamble->reserved3 = 0;
memcpy(&data[0] + sizeof(struct capwap_dtls_header), buffer, length);
/* Send packet */
err = capwap_sendto(dtls->sock, data, length + sizeof(struct capwap_dtls_header), &dtls->peeraddr);
if (err <= 0) {
log_printf(LOG_WARNING, "Unable to send crypt packet, sentto return error %d", err);
return WOLFSSL_CBIO_ERR_GENERAL;
}
/* Don't return size of DTLS Capwap Preamble */
return length;
}
/* */
int capwap_crypt_init() {
int result;
/* Init library */
result = wolfSSL_Init();
if (result != SSL_SUCCESS) {
return -1;
}
return 0;
}
/* */
void capwap_crypt_free() {
wolfSSL_Cleanup();
}
/* */
static int capwap_crypt_verifycertificate(int preverify_ok, WOLFSSL_X509_STORE_CTX* ctx) {
return preverify_ok;
}
/* */
static unsigned int capwap_crypt_psk_client(WOLFSSL* ssl, const char* hint, char* identity, unsigned int max_identity_len, unsigned char* psk, unsigned int max_psk_len) {
struct capwap_dtls* dtls = (struct capwap_dtls*)wolfSSL_GetIOReadCtx(ssl);
ASSERT(dtls != NULL);
ASSERT(dtls->dtlscontext != NULL);
/* */
if ((max_identity_len < strlen(dtls->dtlscontext->presharedkey.identity)) || (max_psk_len < dtls->dtlscontext->presharedkey.pskkeylength)) {
return 0;
}
/* */
strcpy(identity, dtls->dtlscontext->presharedkey.identity);
memcpy(psk, dtls->dtlscontext->presharedkey.pskkey, dtls->dtlscontext->presharedkey.pskkeylength);
return dtls->dtlscontext->presharedkey.pskkeylength;
}
/* */
static unsigned int capwap_crypt_psk_server(WOLFSSL* ssl, const char* identity, unsigned char* psk, unsigned int max_psk_len) {
struct capwap_dtls* dtls = (struct capwap_dtls*)wolfSSL_GetIOReadCtx(ssl);
ASSERT(dtls != NULL);
ASSERT(dtls->dtlscontext != NULL);
/* */
if (strcmp(identity, dtls->dtlscontext->presharedkey.identity) || (max_psk_len < dtls->dtlscontext->presharedkey.pskkeylength)) {
return 0;
}
/* */
memcpy(psk, dtls->dtlscontext->presharedkey.pskkey, dtls->dtlscontext->presharedkey.pskkeylength);
return dtls->dtlscontext->presharedkey.pskkeylength;
}
/* */
static unsigned int capwap_crypt_psk_to_bin(char* pskkey, unsigned char** pskbin) {
int i, j;
int length;
int result;
unsigned char* buffer;
/* Convert string to hex */
length = strlen(pskkey);
if (!length || (length % 2)) {
return 0;
}
/* */
result = length / 2;
buffer = (unsigned char*)capwap_alloc(result);
for (i = 0, j = 0; i < length; i += 2, j++) {
char valuehi = pskkey[i] - 48;
char valuelo = pskkey[i + 1] - 48;
/* Check value */
if ((valuehi < 0) || (valuehi >= g_char2hex_length) || (valuelo < 0) || (valuelo >= g_char2hex_length)) {
capwap_free(buffer);
return 0;
}
/* */
valuehi = g_char2hex[(int)valuehi];
valuelo = g_char2hex[(int)valuelo];
/* Check value */
if ((valuehi < 0) || (valuelo < 0)) {
capwap_free(buffer);
return 0;
}
/* */
buffer[j] = (unsigned char)(((unsigned char)valuehi << 4) | (unsigned char)valuelo);
}
/* */
*pskbin = buffer;
return result;
}
/* */
static int capwap_crypt_createcookie(WOLFSSL* ssl, unsigned char* buffer, int size, void* context) {
int length;
unsigned char temp[32];
Sha sha;
byte digest[SHA_DIGEST_SIZE];
struct capwap_dtls* dtls = (struct capwap_dtls*)context;
if (size != SHA_DIGEST_SIZE) {
return -1;
}
/* Create buffer with peer's address and port */
if (dtls->peeraddr.ss.ss_family == AF_INET) {
length = sizeof(struct in_addr) + sizeof(in_port_t);
memcpy(temp, &dtls->peeraddr.sin.sin_port, sizeof(in_port_t));
memcpy(temp + sizeof(in_port_t), &dtls->peeraddr.sin.sin_addr, sizeof(struct in_addr));
} else if (dtls->peeraddr.ss.ss_family == AF_INET6) {
length = sizeof(struct in6_addr) + sizeof(in_port_t);
memcpy(temp, &dtls->peeraddr.sin6.sin6_port, sizeof(in_port_t));
memcpy(temp + sizeof(in_port_t), &dtls->peeraddr.sin6.sin6_addr, sizeof(struct in6_addr));
} else {
return -1;
}
/* */
if (wc_InitSha(&sha)) {
return -1;
}
wc_ShaUpdate(&sha, temp, length);
wc_ShaFinal(&sha, digest);
/* */
memcpy(buffer, digest, SHA_DIGEST_SIZE);
return SHA_DIGEST_SIZE;
}
/* */
int capwap_crypt_createcontext(struct capwap_dtls_context* dtlscontext, struct capwap_dtls_param* param) {
ASSERT(dtlscontext != NULL);
ASSERT(param != NULL);
memset(dtlscontext, 0, sizeof(struct capwap_dtls_context));
dtlscontext->type = param->type;
dtlscontext->mode = param->mode;
/* Alloc context */
dtlscontext->sslcontext = (void*)wolfSSL_CTX_new(((param->type == CAPWAP_DTLS_SERVER) ? wolfDTLSv1_server_method() : wolfDTLSv1_client_method()));
if (!dtlscontext->sslcontext) {
log_printf(LOG_DEBUG, "Error to initialize dtls context");
return 0;
}
/* Set context IO */
wolfSSL_SetIORecv((WOLFSSL_CTX*)dtlscontext->sslcontext, capwap_bio_method_recv);
wolfSSL_SetIOSend((WOLFSSL_CTX*)dtlscontext->sslcontext, capwap_bio_method_send);
wolfSSL_CTX_SetGenCookie((WOLFSSL_CTX*)dtlscontext->sslcontext, capwap_crypt_createcookie);
/* */
if (dtlscontext->mode == CAPWAP_DTLS_MODE_CERTIFICATE) {
/* Check context */
if (!param->cert.filecert || !strlen(param->cert.filecert)) {
log_printf(LOG_DEBUG, "Error, request certificate file");
capwap_crypt_freecontext(dtlscontext);
return 0;
} else if (!param->cert.filekey || !strlen(param->cert.filekey)) {
log_printf(LOG_DEBUG, "Error, request privatekey file");
capwap_crypt_freecontext(dtlscontext);
return 0;
} else if (!param->cert.fileca || !strlen(param->cert.fileca)) {
log_printf(LOG_DEBUG, "Error, request ca file");
capwap_crypt_freecontext(dtlscontext);
return 0;
}
/* Public certificate */
if (!wolfSSL_CTX_use_certificate_file((WOLFSSL_CTX*)dtlscontext->sslcontext, param->cert.filecert, SSL_FILETYPE_PEM)) {
log_printf(LOG_DEBUG, "Error to load certificate file");
capwap_crypt_freecontext(dtlscontext);
return 0;
}
/* Private key */
if (!wolfSSL_CTX_use_PrivateKey_file((WOLFSSL_CTX*)dtlscontext->sslcontext, param->cert.filekey, SSL_FILETYPE_PEM)) {
log_printf(LOG_DEBUG, "Error to load private key file");
capwap_crypt_freecontext(dtlscontext);
return 0;
}
if (!wolfSSL_CTX_check_private_key((WOLFSSL_CTX*)dtlscontext->sslcontext)) {
log_printf(LOG_DEBUG, "Error to check private key");
capwap_crypt_freecontext(dtlscontext);
return 0;
}
/* Certificate Authority */
if (!wolfSSL_CTX_load_verify_locations((WOLFSSL_CTX*)dtlscontext->sslcontext, param->cert.fileca, NULL)) {
log_printf(LOG_DEBUG, "Error to load ca file");
capwap_crypt_freecontext(dtlscontext);
return 0;
}
/* Verify certificate callback */
wolfSSL_CTX_set_verify((WOLFSSL_CTX*)dtlscontext->sslcontext, ((param->type == CAPWAP_DTLS_SERVER) ? SSL_VERIFY_PEER | SSL_VERIFY_FAIL_IF_NO_PEER_CERT : SSL_VERIFY_PEER), capwap_crypt_verifycertificate);
/* Cipher list:
TLS_RSA_WITH_AES_128_CBC_SHA
TLS_DHE_RSA_WITH_AES_128_CBC_SHA
TLS_RSA_WITH_AES_256_CBC_SHA
TLS_DHE_RSA_WITH_AES_256_CBC_SHA
*/
if (!wolfSSL_CTX_set_cipher_list((WOLFSSL_CTX*)dtlscontext->sslcontext, "AES128-SHA:DHE-RSA-AES128-SHA:AES256-SHA:DHE-RSA-AES256-SHA")) {
log_printf(LOG_DEBUG, "Error to select cipher list");
capwap_crypt_freecontext(dtlscontext);
return 0;
}
} else if (dtlscontext->mode == CAPWAP_DTLS_MODE_PRESHAREDKEY) {
/* Cipher list:
TLS_PSK_WITH_AES_128_CBC_SHA
TLS_DHE_PSK_WITH_AES_128_CBC_SHA
TLS_PSK_WITH_AES_256_CBC_SHA
TLS_DHE_PSK_WITH_AES_256_CBC_SHA
*/
if (!wolfSSL_CTX_set_cipher_list((WOLFSSL_CTX*)dtlscontext->sslcontext, "PSK-AES128-CBC-SHA:PSK-AES256-CBC-SHA")) {
log_printf(LOG_DEBUG, "Error to select cipher list");
capwap_crypt_freecontext(dtlscontext);
return 0;
}
/* */
if (dtlscontext->type == CAPWAP_DTLS_SERVER) {
if (param->presharedkey.hint) {
wolfSSL_CTX_use_psk_identity_hint((WOLFSSL_CTX*)dtlscontext->sslcontext, param->presharedkey.hint);
} else {
log_printf(LOG_DEBUG, "Error to presharedkey hint");
capwap_crypt_freecontext(dtlscontext);
return 0;
}
}
/* */
dtlscontext->presharedkey.identity = capwap_duplicate_string(param->presharedkey.identity);
dtlscontext->presharedkey.pskkeylength = capwap_crypt_psk_to_bin(param->presharedkey.pskkey, &dtlscontext->presharedkey.pskkey);
if (!dtlscontext->presharedkey.pskkeylength) {
log_printf(LOG_DEBUG, "Error to presharedkey");
capwap_crypt_freecontext(dtlscontext);
return 0;
}
/* */
if (dtlscontext->type == CAPWAP_DTLS_SERVER) {
wolfSSL_CTX_set_psk_server_callback((WOLFSSL_CTX*)dtlscontext->sslcontext, capwap_crypt_psk_server);
} else {
wolfSSL_CTX_set_psk_client_callback((WOLFSSL_CTX*)dtlscontext->sslcontext, capwap_crypt_psk_client);
}
} else {
log_printf(LOG_DEBUG, "Invalid DTLS mode");
capwap_crypt_freecontext(dtlscontext);
return 0;
}
return 1;
}
/* */
void capwap_crypt_freecontext(struct capwap_dtls_context* dtlscontext) {
ASSERT(dtlscontext != NULL);
/* */
if (dtlscontext->mode == CAPWAP_DTLS_MODE_PRESHAREDKEY) {
if (dtlscontext->presharedkey.identity) {
capwap_free(dtlscontext->presharedkey.identity);
}
if (dtlscontext->presharedkey.pskkey) {
capwap_free(dtlscontext->presharedkey.pskkey);
}
}
/* Free context */
if (dtlscontext->sslcontext) {
wolfSSL_CTX_free((WOLFSSL_CTX*)dtlscontext->sslcontext);
}
memset(dtlscontext, 0, sizeof(struct capwap_dtls_context));
}
/* */
int capwap_crypt_createsession(struct capwap_dtls* dtls, struct capwap_dtls_context* dtlscontext) {
ASSERT(dtls != NULL);
ASSERT(dtlscontext != NULL);
ASSERT(dtlscontext->sslcontext != NULL);
/* Create ssl session */
dtls->sslsession = (void*)wolfSSL_new((WOLFSSL_CTX*)dtlscontext->sslcontext);
if (!dtls->sslsession) {
log_printf(LOG_DEBUG, "Error to initialize dtls session");
return 0;
}
/* */
wolfSSL_set_using_nonblock((WOLFSSL*)dtls->sslsession, 1);
wolfSSL_SetIOReadCtx((WOLFSSL*)dtls->sslsession, (void*)dtls);
wolfSSL_SetIOWriteCtx((WOLFSSL*)dtls->sslsession, (void*)dtls);
wolfSSL_SetCookieCtx((WOLFSSL*)dtls->sslsession, (void*)dtls);
/* */
dtls->action = CAPWAP_DTLS_ACTION_NONE;
dtls->dtlscontext = dtlscontext;
dtls->enable = 1;
dtls->buffer = NULL;
dtls->length = 0;
return 1;
}
/* */
static int capwap_crypt_handshake(struct capwap_dtls* dtls) {
int result;
ASSERT(dtls != NULL);
ASSERT(dtls->enable != 0);
ASSERT((dtls->action == CAPWAP_DTLS_ACTION_NONE) || (dtls->action == CAPWAP_DTLS_ACTION_HANDSHAKE));
/* */
if (dtls->dtlscontext->type == CAPWAP_DTLS_SERVER) {
result = wolfSSL_accept((WOLFSSL*)dtls->sslsession);
} else {
result = wolfSSL_connect((WOLFSSL*)dtls->sslsession);
}
/* */
if (result != SSL_SUCCESS) {
char buffer[WOLFSSL_MAX_ERROR_SZ];
result = wolfSSL_get_error((WOLFSSL*)dtls->sslsession, 0);
if ((result == SSL_ERROR_WANT_READ) || (result == SSL_ERROR_WANT_WRITE)) {
/* Incomplete handshake */
dtls->action = CAPWAP_DTLS_ACTION_HANDSHAKE;
return CAPWAP_HANDSHAKE_CONTINUE;
}
log_printf(LOG_DEBUG, "Error in DTLS handshake: %s",
wolfSSL_ERR_error_string(result, buffer));
/* Handshake error */
dtls->action = CAPWAP_DTLS_ACTION_ERROR;
return CAPWAP_HANDSHAKE_ERROR;
}
/* Handshake complete */
dtls->action = CAPWAP_DTLS_ACTION_DATA;
return CAPWAP_HANDSHAKE_COMPLETE;
}
/* */
void capwap_crypt_setconnection(struct capwap_dtls* dtls, int sock, union sockaddr_capwap* localaddr, union sockaddr_capwap* peeraddr) {
ASSERT(sock >= 0);
ASSERT(localaddr != NULL);
ASSERT(peeraddr != NULL);
dtls->sock = sock;
/* */
memcpy(&dtls->localaddr, localaddr, sizeof(union sockaddr_capwap));
if (dtls->localaddr.ss.ss_family == AF_INET6) {
capwap_ipv4_mapped_ipv6(&dtls->localaddr);
}
/* */
memcpy(&dtls->peeraddr, peeraddr, sizeof(union sockaddr_capwap));
if (dtls->peeraddr.ss.ss_family == AF_INET6) {
capwap_ipv4_mapped_ipv6(&dtls->peeraddr);
}
}
/* */
int capwap_crypt_open(struct capwap_dtls* dtls) {
return capwap_crypt_handshake(dtls);
}
/* */
void capwap_crypt_close(struct capwap_dtls* dtls) {
ASSERT(dtls != NULL);
ASSERT(dtls->enable != 0);
if (dtls->sslsession) {
wolfSSL_shutdown((WOLFSSL*)dtls->sslsession);
}
}
/* */
void capwap_crypt_freesession(struct capwap_dtls* dtls) {
ASSERT(dtls != NULL);
/* Free SSL session */
if (dtls->sslsession) {
wolfSSL_free((WOLFSSL*)dtls->sslsession);
}
/* */
memset(dtls, 0, sizeof(struct capwap_dtls));
}
/* */
int capwap_crypt_sendto(struct capwap_dtls* dtls, void* buffer, int size) {
int err;
ASSERT(dtls != NULL);
ASSERT(dtls->sock >= 0);
ASSERT(buffer != NULL);
ASSERT(size > 0);
if (!dtls->enable) {
err = capwap_sendto(dtls->sock, buffer, size, &dtls->peeraddr);
if (err <= 0) {
log_printf(LOG_WARNING, "Unable to send plain packet, sentto return error %d", err);
}
return err;
}
/* Valid DTLS status */
if (dtls->action != CAPWAP_DTLS_ACTION_DATA) {
return -ENOTCONN;
}
return wolfSSL_write((WOLFSSL*)dtls->sslsession, buffer, size);
}
/* */
int capwap_crypt_sendto_fragmentpacket(struct capwap_dtls* dtls, struct capwap_list* fragmentlist) {
int err;
struct capwap_list_item* item;
ASSERT(dtls != NULL);
ASSERT(dtls->sock >= 0);
ASSERT(fragmentlist != NULL);
/* */
if (!dtls->enable) {
return capwap_sendto_fragmentpacket(dtls->sock, fragmentlist, &dtls->peeraddr);
}
/* */
item = fragmentlist->first;
while (item) {
struct capwap_fragment_packet_item* fragmentpacket = (struct capwap_fragment_packet_item*)item->item;
ASSERT(fragmentpacket != NULL);
ASSERT(fragmentpacket->offset > 0);
err = capwap_crypt_sendto(dtls, fragmentpacket->buffer, fragmentpacket->offset);
if (err <= 0) {
log_printf(LOG_WARNING, "Unable to send crypt fragment, sentto return error %d", err);
return 0;
}
/* */
item = item->next;
}
return 1;
}
/* */
int capwap_decrypt_packet(struct capwap_dtls* dtls, void* encrybuffer, int size, void* plainbuffer, int maxsize) {
int sslerror;
int result = -1;
char* clone = NULL;
ASSERT(dtls != NULL);
ASSERT(dtls->enable != 0);
ASSERT((dtls->action == CAPWAP_DTLS_ACTION_HANDSHAKE) || (dtls->action == CAPWAP_DTLS_ACTION_DATA));
ASSERT(dtls->buffer == NULL);
ASSERT(dtls->length == 0);
ASSERT(encrybuffer != NULL);
ASSERT(size > 0);
ASSERT(maxsize > 0);
/* */
if (!plainbuffer) {
clone = capwap_clone(encrybuffer, size);
}
dtls->buffer = (clone ? clone : encrybuffer);
dtls->length = size;
/* */
if (dtls->action == CAPWAP_DTLS_ACTION_HANDSHAKE) {
if (capwap_crypt_handshake(dtls) == CAPWAP_HANDSHAKE_ERROR) {
result = CAPWAP_ERROR_CLOSE; /* Error handshake */
} else {
result = CAPWAP_ERROR_AGAIN; /* Don't parsing DTLS packet */
}
} else if (dtls->action == CAPWAP_DTLS_ACTION_DATA) {
result = wolfSSL_read((WOLFSSL*)dtls->sslsession, (plainbuffer ? plainbuffer : encrybuffer), maxsize);
if (!result) {
dtls->action = CAPWAP_DTLS_ACTION_SHUTDOWN;
result = CAPWAP_ERROR_SHUTDOWN;
} else if (result < 0) {
/* Check error */
sslerror = wolfSSL_get_error((WOLFSSL*)dtls->sslsession, 0);
if ((sslerror == SSL_ERROR_WANT_READ) || (sslerror == SSL_ERROR_WANT_WRITE)) {
result = CAPWAP_ERROR_AGAIN; /* DTLS Renegotiation */
} else {
result = CAPWAP_ERROR_CLOSE;
}
}
}
/* Verify BIO read */
ASSERT(dtls->buffer == NULL);
ASSERT(dtls->length == 0);
/* Free clone */
if (clone) {
capwap_free(clone);
}
return result;
}
/* */
#define SIZEOF_DTLS_LAYERS 14
#define DTLS_RECORD_LAYER_HANDSHAKE_CONTENT_TYPE 22
#define DTLS_1_0_VERSION 0xfeff
#define DTLS_1_2_VERSION 0xfefd
#define DTLS_HANDSHAKE_LAYER_CLIENT_HELLO 1
/* */
int capwap_crypt_has_dtls_clienthello(void* buffer, int buffersize) {
unsigned char* dtlsdata = (unsigned char*)buffer;
/* Read DTLS packet in RAW mode */
if ((buffer != NULL) && (buffersize > SIZEOF_DTLS_LAYERS)) {
if (dtlsdata[0] == DTLS_RECORD_LAYER_HANDSHAKE_CONTENT_TYPE) {
uint16_t version = ntohs(*(uint16_t*)(dtlsdata + 1));
if (((version == DTLS_1_0_VERSION) || (version == DTLS_1_2_VERSION)) && (dtlsdata[13] == DTLS_HANDSHAKE_LAYER_CLIENT_HELLO)) {
return 1;
}
}
}
return 0;
}