Fix secure operation; some name and logical tidies

Secure sending code was passing the remote address as the source
address, bizarrely. This records the local address last used as the
destination for incoming packets and uses that.

Other fix-ups:

* dest_addr (ie remote address) removed from internal_socket_t; this isn't
  safe, as one socket talks to multiple remote peers. Use address in
  secure_session_t instead.
* Some renaming: listen_socket->socket, send/receive callbacks,
  remote_address+remote_port -> remote_host
pull/3240/head
Kevin Bracey 2016-10-27 17:50:36 +03:00
parent 115aa3eee2
commit 6d263723c1
1 changed files with 51 additions and 55 deletions

View File

@ -27,13 +27,12 @@ typedef struct internal_socket_s {
uint32_t timeout_min;
uint32_t timeout_max;
uint16_t listen_port;
int8_t listen_socket;
uint16_t listen_port; // 0 for ephemeral-port sockets
ns_address_t dest_addr;
int16_t data_len;
uint8_t *data;
int8_t socket;
bool real_socket;
uint8_t usage_counter;
bool is_secure;
@ -64,8 +63,9 @@ typedef struct secure_session {
coap_security_t *sec_handler; //owned
internal_socket_t *parent; //not owned
uint8_t remote_address[16];
uint16_t remote_port;
ns_address_t remote_host;
uint8_t local_address[16];
// local port is fixed by socket
secure_timer_t timer;
@ -75,8 +75,8 @@ typedef struct secure_session {
} secure_session_t;
static NS_LIST_DEFINE(secure_session_list, secure_session_t, link);
static int send_to_socket(int8_t socket_id, void *handle, const void *buf, size_t len);
static int receive_from_socket(int8_t socket_id, unsigned char *buf, size_t len);
static int secure_session_sendto(int8_t socket_id, void *handle, const void *buf, size_t len);
static int secure_session_recvfrom(int8_t socket_id, unsigned char *buf, size_t len);
static void start_timer(int8_t timer_id, uint32_t int_ms, uint32_t fin_ms);
static int timer_status(int8_t timer_id);
@ -95,7 +95,7 @@ static secure_session_t *secure_session_find_by_timer_id(int8_t timer_id)
static void secure_session_delete(secure_session_t *this)
{
if (this) {
transactions_delete_all(this->parent->dest_addr.address, this->parent->dest_addr.identifier);
transactions_delete_all(this->remote_host.address, this->remote_host.identifier);
ns_list_remove(&secure_session_list, this);
if( this->sec_handler ){
coap_security_destroy(this->sec_handler);
@ -150,11 +150,12 @@ static secure_session_t *secure_session_create(internal_socket_t *parent, const
timer_id++;
}
this->timer.id = timer_id;
memcpy(this->remote_address, address_ptr, 16);
this->remote_port = port;
this->remote_host.type = ADDRESS_IPV6;
memcpy(this->remote_host.address, address_ptr, 16);
this->remote_host.identifier = port;
this->sec_handler = coap_security_create(parent->listen_socket, this->timer.id, this, ECJPAKE,
&send_to_socket, &receive_from_socket, &start_timer, &timer_status);
this->sec_handler = coap_security_create(parent->socket, this->timer.id, this, ECJPAKE,
&secure_session_sendto, &secure_session_recvfrom, &start_timer, &timer_status);
if( !this->sec_handler ){
ns_dyn_mem_free(this);
return NULL;
@ -184,10 +185,9 @@ static secure_session_t *secure_session_find(internal_socket_t *parent, const ui
secure_session_t *this = NULL;
ns_list_foreach(secure_session_t, cur_ptr, &secure_session_list) {
if( cur_ptr->sec_handler ){
if (cur_ptr->parent == parent && cur_ptr->remote_port == port &&
memcmp(cur_ptr->remote_address, address_ptr, 16) == 0) {
if (cur_ptr->parent == parent && cur_ptr->remote_host.identifier == port &&
memcmp(cur_ptr->remote_host.address, address_ptr, 16) == 0) {
this = cur_ptr;
// hack_save_remote_address(address_ptr, port);
break;
}
}
@ -217,36 +217,36 @@ static internal_socket_t *int_socket_create(uint16_t listen_port, bool use_ephem
this->listen_port = listen_port;
this->real_socket = real_socket;
this->bypass_link_sec = bypassSec;
this->listen_socket = -1;
this->socket = -1;
if( real_socket ){
if( use_ephemeral_port ){ //socket_api creates ephemeral port if the one provided is 0
listen_port = 0;
}
if( !is_secure ){
this->listen_socket = socket_open(SOCKET_UDP, listen_port, recv_sckt_msg);
this->socket = socket_open(SOCKET_UDP, listen_port, recv_sckt_msg);
}else{
#ifdef COAP_SECURITY_AVAILABLE
this->listen_socket = socket_open(SOCKET_UDP, listen_port, secure_recv_sckt_msg);
this->socket = socket_open(SOCKET_UDP, listen_port, secure_recv_sckt_msg);
#else
tr_err("Secure CoAP unavailable - SSL library not configured, possibly due to lack of entropy source");
#endif
}
// Socket create failed
if(this->listen_socket < 0){
if(this->socket < 0){
ns_dyn_mem_free(this);
return NULL;
}
socket_setsockopt(this->listen_socket, SOCKET_IPPROTO_IPV6, SOCKET_LINK_LAYER_SECURITY, &(const int8_t) {bypassSec ? 0 : 1}, sizeof(int8_t));
socket_setsockopt(this->socket, SOCKET_IPPROTO_IPV6, SOCKET_LINK_LAYER_SECURITY, &(const int8_t) {bypassSec ? 0 : 1}, sizeof(int8_t));
// XXX API for this? May want to get clever to do recommended first query = 1 hop, retries = whole PAN
socket_setsockopt(this->listen_socket, SOCKET_IPPROTO_IPV6, SOCKET_IPV6_MULTICAST_HOPS, &(const int16_t) {16}, sizeof(int16_t));
socket_setsockopt(this->socket, SOCKET_IPPROTO_IPV6, SOCKET_IPV6_MULTICAST_HOPS, &(const int16_t) {16}, sizeof(int16_t));
// Set socket option to receive packet info
socket_setsockopt(this->listen_socket, SOCKET_IPPROTO_IPV6, SOCKET_IPV6_RECVPKTINFO, &(const bool) {1}, sizeof(bool));
socket_setsockopt(this->socket, SOCKET_IPPROTO_IPV6, SOCKET_IPV6_RECVPKTINFO, &(const bool) {1}, sizeof(bool));
}else{
this->listen_socket = -1;
this->socket = -1;
}
ns_list_add_to_start(&socket_list, this);
@ -259,7 +259,7 @@ static void int_socket_delete(internal_socket_t *this)
this->usage_counter--;
if(this->usage_counter == 0){
clear_secure_sessions(this);
socket_free(this->listen_socket);
socket_free(this->socket);
ns_list_remove(&socket_list, this);
if( this->data ){
ns_dyn_mem_free(this->data);
@ -277,7 +277,7 @@ static internal_socket_t *int_socket_find_by_socket_id(int8_t id)
{
internal_socket_t *this = NULL;
ns_list_foreach(internal_socket_t, cur_ptr, &socket_list) {
if( cur_ptr->listen_socket == id ) {
if( cur_ptr->socket == id ) {
this = cur_ptr;
break;
}
@ -339,7 +339,7 @@ static int8_t send_to_real_socket(int8_t socket_id, const ns_address_t *address,
return socket_sendmsg(socket_id, &msghdr, 0);
}
static int send_to_socket(int8_t socket_id, void *handle, const void *buf, size_t len)
static int secure_session_sendto(int8_t socket_id, void *handle, const void *buf, size_t len)
{
secure_session_t *session = handle;
internal_socket_t *sock = int_socket_find_by_socket_id(socket_id);
@ -348,7 +348,7 @@ static int send_to_socket(int8_t socket_id, void *handle, const void *buf, size_
}
if(!sock->real_socket){
// Send to virtual socket cb
int ret = sock->parent->_send_cb(sock->listen_socket, session->remote_address, session->remote_port, buf, len);
int ret = sock->parent->_send_cb(sock->socket, session->remote_host.address, session->remote_host.identifier, buf, len);
if( ret < 0 )
return ret;
return len;
@ -359,19 +359,19 @@ static int send_to_socket(int8_t socket_id, void *handle, const void *buf, size_
if (sock->bypass_link_sec) {
securityLinkLayer = 0;
}
socket_setsockopt(sock->listen_socket, SOCKET_IPPROTO_IPV6, SOCKET_IPV6_ADDR_PREFERENCES, &opt_name, sizeof(int));
socket_setsockopt(sock->listen_socket, SOCKET_IPPROTO_IPV6, SOCKET_LINK_LAYER_SECURITY, &securityLinkLayer, sizeof(int8_t));
socket_setsockopt(sock->socket, SOCKET_IPPROTO_IPV6, SOCKET_IPV6_ADDR_PREFERENCES, &opt_name, sizeof(int));
socket_setsockopt(sock->socket, SOCKET_IPPROTO_IPV6, SOCKET_LINK_LAYER_SECURITY, &securityLinkLayer, sizeof(int8_t));
//For some reason socket_sendto returns 0 in success, while other socket impls return number of bytes sent!!!
//TODO: check if address_ptr is valid and use that instead if it is
int8_t ret = send_to_real_socket(sock->listen_socket, &sock->dest_addr, session->remote_address, buf, len);
int8_t ret = send_to_real_socket(sock->socket, &session->remote_host, session->local_address, buf, len);
if (ret < 0) {
return ret;
}
return len;
}
static int receive_from_socket(int8_t socket_id, unsigned char *buf, size_t len)
static int secure_session_recvfrom(int8_t socket_id, unsigned char *buf, size_t len)
{
(void)len;
internal_socket_t *sock = int_socket_find_by_socket_id(socket_id);
@ -537,21 +537,23 @@ static void secure_recv_sckt_msg(void *cb_res)
// Create session
if (!session) {
memcpy( sock->dest_addr.address, src_address.address, 16 );
sock->dest_addr.identifier = src_address.identifier;
sock->dest_addr.type = src_address.type;
session = secure_session_create(sock, src_address.address, src_address.identifier);
}
if (!session) {
tr_err("secure_recv_sckt_msg session creation failed - OOM");
return;
}
// Record the destination. We are not strict on local address - all
// session_find calls match only on remote address and port. But we
// record the last-used destination address to use it as the source of
// outgoing packets.
memcpy(session->local_address, dst_address, 16);
session->last_contact_time = coap_service_get_internal_timer_ticks();
// Start handshake
if (!coap_security_handler_is_started(session->sec_handler) ){
uint8_t *pw = ns_dyn_mem_alloc(64);
uint8_t pw_len;
if( sock->parent->_get_password_cb && 0 == sock->parent->_get_password_cb(sock->listen_socket, src_address.address, src_address.identifier, pw, &pw_len)){
if( sock->parent->_get_password_cb && 0 == sock->parent->_get_password_cb(sock->socket, src_address.address, src_address.identifier, pw, &pw_len)){
//TODO: get_password_cb should support certs and PSK also
coap_security_keys_t keys;
keys._priv = pw;
@ -570,7 +572,7 @@ static void secure_recv_sckt_msg(void *cb_res)
session->timer.timer = NULL;
session->session_state = SECURE_SESSION_OK;
if( sock->parent->_security_done_cb ){
sock->parent->_security_done_cb(sock->listen_socket, src_address.address,
sock->parent->_security_done_cb(sock->socket, src_address.address,
src_address.identifier,
(void *)coap_security_handler_keyblock(session->sec_handler));
}
@ -592,7 +594,7 @@ static void secure_recv_sckt_msg(void *cb_res)
ns_dyn_mem_free(data);
} else {
if (sock->parent->_recv_cb) {
sock->parent->_recv_cb(sock->listen_socket, src_address.address, src_address.identifier, dst_address, data, len);
sock->parent->_recv_cb(sock->socket, src_address.address, src_address.identifier, dst_address, data, len);
}
ns_dyn_mem_free(data);
}
@ -610,7 +612,7 @@ static void recv_sckt_msg(void *cb_res)
if (sock && read_data(sckt_data, sock, &src_address, dst_address) == 0) {
if (sock->parent && sock->parent->_recv_cb) {
sock->parent->_recv_cb(sock->listen_socket, src_address.address, src_address.identifier, dst_address, sock->data, sock->data_len);
sock->parent->_recv_cb(sock->socket, src_address.address, src_address.identifier, dst_address, sock->data, sock->data_len);
}
ns_dyn_mem_free(sock->data);
sock->data = NULL;
@ -656,7 +658,7 @@ int coap_connection_handler_virtual_recv(coap_conn_handler_t *handler, uint8_t a
if (!coap_security_handler_is_started(session->sec_handler)) {
uint8_t *pw = ns_dyn_mem_alloc(64);
uint8_t pw_len;
if (sock->parent->_get_password_cb && 0 == sock->parent->_get_password_cb(sock->listen_socket, address, port, pw, &pw_len)) {
if (sock->parent->_get_password_cb && 0 == sock->parent->_get_password_cb(sock->socket, address, port, pw, &pw_len)) {
//TODO: get_password_cb should support certs and PSK also
coap_security_keys_t keys;
keys._priv = pw;
@ -675,7 +677,7 @@ int coap_connection_handler_virtual_recv(coap_conn_handler_t *handler, uint8_t a
if(ret == 0){
session->session_state = SECURE_SESSION_OK;
if( handler->_security_done_cb ){
handler->_security_done_cb(sock->listen_socket,
handler->_security_done_cb(sock->socket,
address, port,
(void *)coap_security_handler_keyblock(session->sec_handler));
}
@ -700,7 +702,7 @@ int coap_connection_handler_virtual_recv(coap_conn_handler_t *handler, uint8_t a
return 0;
} else {
if (sock->parent->_recv_cb) {
sock->parent->_recv_cb(sock->listen_socket, address, port, ns_in6addr_any, data, len);
sock->parent->_recv_cb(sock->socket, address, port, ns_in6addr_any, data, len);
}
ns_dyn_mem_free(data);
data = NULL;
@ -711,7 +713,7 @@ int coap_connection_handler_virtual_recv(coap_conn_handler_t *handler, uint8_t a
} else {
/* unsecure*/
if (sock->parent->_recv_cb) {
sock->parent->_recv_cb(sock->listen_socket, address, port, ns_in6addr_any, sock->data, sock->data_len);
sock->parent->_recv_cb(sock->socket, address, port, ns_in6addr_any, sock->data, sock->data_len);
}
if (sock->data) {
ns_dyn_mem_free(sock->data);
@ -757,7 +759,7 @@ void connection_handler_close_secure_connection( coap_conn_handler_t *handler, u
{
if (handler) {
if (handler->socket && handler->socket->is_secure) {
secure_session_t *session = secure_session_find( handler->socket, destination_addr_ptr, port);
secure_session_t *session = secure_session_find( handler->socket, destination_addr_ptr, port );
if (session) {
coap_security_send_close_alert( session->sec_handler );
session->session_state = SECURE_SESSION_CLOSED;
@ -806,9 +808,6 @@ int coap_connection_handler_send_data(coap_conn_handler_t *handler, const ns_add
}
if (handler->socket->is_secure) {
handler->socket->bypass_link_sec = bypass_link_sec;
memcpy(handler->socket->dest_addr.address, dest_addr->address, 16);
handler->socket->dest_addr.identifier = dest_addr->identifier;
handler->socket->dest_addr.type = dest_addr->type;
secure_session_t *session = secure_session_find(handler->socket, dest_addr->address, dest_addr->identifier);
if (!session) {
session = secure_session_create(handler->socket, dest_addr->address, dest_addr->identifier);
@ -816,16 +815,13 @@ int coap_connection_handler_send_data(coap_conn_handler_t *handler, const ns_add
return -1;
}
session->last_contact_time = coap_service_get_internal_timer_ticks();
memcpy( handler->socket->dest_addr.address, dest_addr->address, 16 );
handler->socket->dest_addr.identifier = dest_addr->identifier;
handler->socket->dest_addr.type = dest_addr->type;
uint8_t *pw = ns_dyn_mem_alloc(64);
if (!pw) {
//todo: free secure session?
return -1;
}
uint8_t pw_len;
if (handler->_get_password_cb && 0 == handler->_get_password_cb(handler->socket->listen_socket, (uint8_t*)dest_addr->address, dest_addr->identifier, pw, &pw_len)) {
if (handler->_get_password_cb && 0 == handler->_get_password_cb(handler->socket->socket, (uint8_t*)dest_addr->address, dest_addr->identifier, pw, &pw_len)) {
//TODO: get_password_cb should support certs and PSK also
coap_security_keys_t keys;
keys._priv = pw;
@ -847,7 +843,7 @@ int coap_connection_handler_send_data(coap_conn_handler_t *handler, const ns_add
return -1;
}else{
if (!handler->socket->real_socket && handler->_send_cb) {
return handler->_send_cb((int8_t)handler->socket->listen_socket, dest_addr->address, dest_addr->identifier, data_ptr, data_len);
return handler->_send_cb((int8_t)handler->socket->socket, dest_addr->address, dest_addr->identifier, data_ptr, data_len);
}
int opt_name = SOCKET_IPV6_PREFER_SRC_6LOWPAN_SHORT;
int8_t securityLinkLayer = 1;
@ -855,10 +851,10 @@ int coap_connection_handler_send_data(coap_conn_handler_t *handler, const ns_add
securityLinkLayer = 0;
}
socket_setsockopt(handler->socket->listen_socket, SOCKET_IPPROTO_IPV6, SOCKET_IPV6_ADDR_PREFERENCES, &opt_name, sizeof(int));
socket_setsockopt(handler->socket->listen_socket, SOCKET_IPPROTO_IPV6, SOCKET_LINK_LAYER_SECURITY, &securityLinkLayer, sizeof(int8_t));
socket_setsockopt(handler->socket->socket, SOCKET_IPPROTO_IPV6, SOCKET_IPV6_ADDR_PREFERENCES, &opt_name, sizeof(int));
socket_setsockopt(handler->socket->socket, SOCKET_IPPROTO_IPV6, SOCKET_LINK_LAYER_SECURITY, &securityLinkLayer, sizeof(int8_t));
return send_to_real_socket(handler->socket->listen_socket, dest_addr, src_address, data_ptr, data_len);
return send_to_real_socket(handler->socket->socket, dest_addr, src_address, data_ptr, data_len);
}
}
@ -868,7 +864,7 @@ bool coap_connection_handler_socket_belongs_to(coap_conn_handler_t *handler, int
return false;
}
if( handler->socket->listen_socket == socket_id){
if( handler->socket->socket == socket_id){
return true;
}
return false;