From 982cbf5ffcc0266c4a50f31bcf1377e1ab9610c8 Mon Sep 17 00:00:00 2001 From: Olaf Bergmann Date: Fri, 19 Jul 2013 12:27:47 +0200 Subject: [PATCH] started cleanup of dtls.[hc]; new function dtls_connect_peer() --- Makefile.in | 4 +- dtls.c | 163 +++++++++++++++------------------------------------- dtls.h | 74 +++++++++--------------- peer.c | 96 +++++++++++++++++++++++++++++++ peer.h | 101 ++++++++++++++++++++++++++++++++ state.h | 60 +++++++++++++++++++ 6 files changed, 333 insertions(+), 165 deletions(-) create mode 100644 peer.c create mode 100644 peer.h create mode 100644 state.h diff --git a/Makefile.in b/Makefile.in index 7c1ea11..44413f5 100644 --- a/Makefile.in +++ b/Makefile.in @@ -42,10 +42,10 @@ package = @PACKAGE_TARNAME@-@PACKAGE_VERSION@ install := cp # files and flags -SOURCES:= dtls.c crypto.c ccm.c hmac.c debug.c netq.c +SOURCES:= dtls.c crypto.c ccm.c hmac.c debug.c netq.c peer.c OBJECTS:= $(patsubst %.c, %.o, $(SOURCES)) aes/rijndael.o @OPT_OBJS@ HEADERS:=dtls.h hmac.h debug.h config.h uthash.h numeric.h crypto.h global.h ccm.h \ - netq.h t_list.h alert.h utlist.h prng.h + netq.h t_list.h alert.h utlist.h prng.h peer.h state.h CFLAGS:=-Wall -pedantic -std=c99 @CFLAGS@ CPPFLAGS:=@CPPFLAGS@ -DDTLS_CHECK_CONTENTTYPE SUBDIRS:=tests doc sha2 aes diff --git a/dtls.c b/dtls.c index cc8bf6f..6d421f6 100644 --- a/dtls.c +++ b/dtls.c @@ -116,11 +116,6 @@ P += dtls_ ## T ## _to_int(P) + sizeof(T); \ } -#define CURRENT_CONFIG(Peer) (&(Peer)->security_params[(Peer)->config]) -#define OTHER_CONFIG(Peer) (&(Peer)->security_params[!((Peer)->config & 0x01)]) - -#define SWITCH_CONFIG(Peer) ((Peer)->config = !((Peer)->config & 0x01)) - uint8 _clear[DTLS_MAX_BUF]; /* target buffer message decryption */ uint8 _buf[DTLS_MAX_BUF]; /* target buffer for several crypto operations */ @@ -141,43 +136,15 @@ static const unsigned char prf_label_finished[] = " finished"; extern void netq_init(); extern void crypto_init(); +extern void peer_init(); dtls_context_t the_dtls_context; -#ifndef WITH_CONTIKI -static inline dtls_peer_t * -dtls_malloc_peer() { - return (dtls_peer_t *)malloc(sizeof(dtls_peer_t)); -} - -static inline void -dtls_free_peer(dtls_peer_t *peer) { - free(peer); -} -#else /* WITH_CONTIKI */ -PROCESS(dtls_retransmit_process, "DTLS retransmit process"); - -#include "memb.h" -MEMB(peer_storage, dtls_peer_t, DTLS_PEER_MAX); - -static inline dtls_peer_t * -dtls_malloc_peer() { - return memb_alloc(&peer_storage); -} -static inline void -dtls_free_peer(dtls_peer_t *peer) { - memb_free(&peer_storage, peer); -} -#endif /* WITH_CONTIKI */ - void dtls_init() { netq_init(); crypto_init(); - -#ifdef WITH_CONTIKI - memb_init(&peer_storage); -#endif /* WITH_CONTIKI */ + peer_init(); } /* Calls cb_alert() with given arguments if defined, otherwise an @@ -212,7 +179,7 @@ int dtls_send(dtls_context_t *ctx, dtls_peer_t *peer, unsigned char type, void dtls_stop_retransmission(dtls_context_t *context, dtls_peer_t *peer); dtls_peer_t * -dtls_get_peer(struct dtls_context_t *ctx, const session_t *session) { +dtls_get_peer(const dtls_context_t *ctx, const session_t *session) { dtls_peer_t *p = NULL; #ifndef WITH_CONTIKI @@ -226,6 +193,15 @@ dtls_get_peer(struct dtls_context_t *ctx, const session_t *session) { return p; } +void +dtls_add_peer(dtls_context_t *ctx, dtls_peer_t *peer) { +#ifndef WITH_CONTIKI + HASH_ADD_PEER(ctx->peers, session, peer); +#else /* WITH_CONTIKI */ + list_add(ctx->peers, peer); +#endif /* WITH_CONTIKI */ +} + int dtls_write(struct dtls_context_t *ctx, session_t *dst, uint8 *buf, size_t len) { @@ -848,39 +824,6 @@ check_ccs(dtls_context_t *ctx, extern size_t dsrv_print_addr(const session_t *, unsigned char *, size_t); #endif -dtls_peer_t * -dtls_new_peer(dtls_context_t *ctx, - const session_t *session) { - dtls_peer_t *peer; - - peer = dtls_malloc_peer(); - if (peer) { - memset(peer, 0, sizeof(dtls_peer_t)); - memcpy(&peer->session, session, sizeof(session_t)); - -#ifndef NDEBUG - { - unsigned char addrbuf[72]; - dsrv_print_addr(session, addrbuf, sizeof(addrbuf)); - printf("dtls_new_peer: %s\n", addrbuf); - dump((unsigned char *)session, sizeof(session_t)); - printf("\n"); - } -#endif - /* initially allow the NULL cipher */ - CURRENT_CONFIG(peer)->cipher = TLS_NULL_WITH_NULL_NULL; - - /* initialize the handshake hash wrt. the hard-coded DTLS version */ - debug("DTLSv12: initialize HASH_SHA256\n"); - /* TLS 1.2: PRF(secret, label, seed) = P_(secret, label + seed) */ - /* FIXME: we use the default SHA256 here, might need to support other - hash functions as well */ - dtls_hash_init(&peer->hs_state.hs_hash); - } - - return peer; -} - static inline void update_hs_hash(dtls_peer_t *peer, uint8 *data, size_t length) { #ifndef NDEBUG @@ -2045,31 +1988,16 @@ dtls_handle_message(dtls_context_t *ctx, (without MAC and padding) */ /* check if we have DTLS state for addr/port/ifindex */ -#ifndef WITH_CONTIKI - HASH_FIND_PEER(ctx->peers, session, peer); - { - dtls_peer_t *p = NULL; - HASH_FIND_PEER(ctx->peers, session, p); + peer = dtls_get_peer(ctx, session); + #ifndef NDEBUG - if (!p) { - printf("dtls_handle_message: PEER NOT FOUND\n"); - { - unsigned char addrbuf[72]; - dsrv_print_addr(session, addrbuf, sizeof(addrbuf)); - printf(" %s\n", addrbuf); - dump((unsigned char *)session, sizeof(session_t)); - printf("\n"); - } - } else - printf("dtls_handle_message: FOUND PEER\n"); -#endif /* NDEBUG */ + if (peer) { + unsigned char addrbuf[72]; + + dsrv_print_addr(session, addrbuf, sizeof(addrbuf)); + debug("found peer %s\n", addrbuf); } -#else /* WITH_CONTIKI */ - for (peer = list_head(ctx->peers); - peer && !dtls_session_equals(&peer->session, session); - peer = list_item_next(peer)) - ; -#endif /* WITH_CONTIKI */ +#endif /* NDEBUG */ if (!peer) { @@ -2110,7 +2038,7 @@ dtls_handle_message(dtls_context_t *ctx, safely create the server state machine and continue with the handshake. */ - peer = dtls_new_peer(ctx, session); + peer = dtls_new_peer(session); if (!peer) { dsrv_log(LOG_ALERT, "cannot create peer"); /* FIXME: signal internal error */ @@ -2298,44 +2226,28 @@ void dtls_free_context(dtls_context_t *ctx) { } int -dtls_connect(dtls_context_t *ctx, const session_t *dst) { - dtls_peer_t *peer; +dtls_connect_peer(dtls_context_t *ctx, dtls_peer_t *peer) { uint8 *p = ctx->sendbuf; size_t size; int res; - /* check if we have DTLS state for addr/port/ifindex */ -#ifndef WITH_CONTIKI - HASH_FIND_PEER(ctx->peers, dst, peer); -#else /* WITH_CONTIKI */ - for (peer = list_head(ctx->peers); peer; peer = list_item_next(peer)) - if (dtls_session_equals(&peer->session, dst)) - break; -#endif /* WITH_CONTIKI */ - - if (peer) { + assert(peer); + if (!peer) + return -1; + + /* check if the same peer is already in our list */ + if (peer == dtls_get_peer(ctx, &peer->session)) { debug("found peer, try to re-connect\n"); /* FIXME: send HelloRequest if we are server, ClientHello with good cookie if client */ return 0; } - - peer = dtls_new_peer(ctx, dst); - - if (!peer) { - dsrv_log(LOG_CRIT, "cannot create new peer\n"); - return -1; - } /* set peer role to server: */ OTHER_CONFIG(peer)->role = DTLS_SERVER; CURRENT_CONFIG(peer)->role = DTLS_SERVER; -#ifndef WITH_CONTIKI - HASH_ADD_PEER(ctx->peers, session, peer); -#else /* WITH_CONTIKI */ - list_add(ctx->peers, peer); -#endif /* WITH_CONTIKI */ + dtls_add_peer(ctx, peer); /* send ClientHello with some Cookie */ @@ -2395,6 +2307,23 @@ dtls_connect(dtls_context_t *ctx, const session_t *dst) { return res; } +int +dtls_connect(dtls_context_t *ctx, const session_t *dst) { + dtls_peer_t *peer; + + peer = dtls_get_peer(ctx, dst); + + if (!peer) + peer = dtls_new_peer(dst); + + if (!peer) { + dsrv_log(LOG_CRIT, "cannot create new peer\n"); + return -1; + } + + return dtls_connect_peer(ctx, peer); +} + void dtls_retransmit(dtls_context_t *context, netq_t *node) { if (!context || !node) diff --git a/dtls.h b/dtls.h index 5ff2681..d218712 100644 --- a/dtls.h +++ b/dtls.h @@ -1,6 +1,6 @@ /* dtls -- a very basic DTLS implementation * - * Copyright (C) 2011--2012 Olaf Bergmann + * Copyright (C) 2011--2013 Olaf Bergmann * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation @@ -34,6 +34,8 @@ #include #include "t_list.h" +#include "state.h" +#include "peer.h" #ifndef WITH_CONTIKI #include "uthash.h" @@ -57,51 +59,6 @@ */ #define TLS_COMP_NULL 0x00 /* NULL compression */ -typedef enum { - DTLS_STATE_INIT = 0, DTLS_STATE_SERVERHELLO, DTLS_STATE_KEYEXCHANGE, - DTLS_STATE_WAIT_FINISHED, DTLS_STATE_FINISHED, - /* client states */ - DTLS_STATE_CLIENTHELLO, DTLS_STATE_WAIT_SERVERHELLODONE, - DTLS_STATE_WAIT_SERVERFINISHED, - - DTLS_STATE_CONNECTED, - DTLS_STATE_CLOSING, - DTLS_STATE_CLOSED, -} dtls_state_t; - -typedef struct { - uint24 mseq; /**< handshake message sequence number counter */ - - /** pending config that is updated during handshake */ - /* FIXME: dtls_security_parameters_t pending_config; */ - - /* temporary storage for the final handshake hash */ - dtls_hash_ctx hs_hash; -} dtls_hs_state_t; - -/** - * Holds security parameters, local state and the transport address - * for each peer. */ -typedef struct dtls_peer_t { -#ifndef WITH_CONTIKI - UT_hash_handle hh; -#else /* WITH_CONTIKI */ - struct dtls_peer_t *next; -#endif /* WITH_CONTIKI */ - - session_t session; /**< peer address and local interface */ - - dtls_state_t state; /**< DTLS engine state */ - uint16 epoch; /**< counter for cipher state changes*/ - uint48 rseq; /**< sequence number of last record sent */ - - dtls_hs_state_t hs_state; /**< handshake protocol status */ - - dtls_security_parameters_t security_params[2]; - int config; /**< denotes which security params are in effect - FIXME: check if we can use epoch for this */ -} dtls_peer_t; - typedef enum { DTLS_KEY_INVALID=0, DTLS_KEY_PSK=1, DTLS_KEY_RPK=2 } dtls_key_type_t; @@ -259,6 +216,18 @@ static inline void dtls_set_handler(dtls_context_t *ctx, dtls_handler_t *h) { */ int dtls_connect(dtls_context_t *ctx, const session_t *dst); +/** + * Establishes a DTLS channel with the specified remote peer. + * This function returns @c 0 if that channel already exists, a value + * greater than zero when a new ClientHello message was sent, and + * a value less than zero on error. + * + * @param ctx The DTLS context to use. + * @param peer The peer object that describes the session. + * @return A value less than zero on error, greater or equal otherwise. + */ +int dtls_connect_peer(dtls_context_t *ctx, dtls_peer_t *peer); + /** * Closes the DTLS connection associated with @p remote. This function * returns zero on success, and a value less than zero on error. @@ -374,6 +343,19 @@ int dtls_get_cookie(uint8 *hello_msg, int msglen, uint8 **cookie); int dtls_handle_message(dtls_context_t *ctx, session_t *session, uint8 *msg, int msglen); +/** + * Check if @p session is associated with a peer object in @p context. + * This function returns a pointer to the peer if found, NULL otherwise. + * + * @param context The DTLS context to search. + * @param session The remote address and local interface + * @return A pointer to the peer associated with @p session or NULL if + * none exists. + */ +dtls_peer_t *dtls_get_peer(const dtls_context_t *context, + const session_t *session); + + #endif /* _DTLS_H_ */ /** diff --git a/peer.c b/peer.c new file mode 100644 index 0000000..bc171e4 --- /dev/null +++ b/peer.c @@ -0,0 +1,96 @@ +/* dtls -- a very basic DTLS implementation + * + * Copyright (C) 2011--2013 Olaf Bergmann + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "peer.h" +#include "debug.h" + +#ifndef WITH_CONTIKI +void peer_init() +{ +} + +static inline dtls_peer_t * +dtls_malloc_peer() { + return (dtls_peer_t *)malloc(sizeof(dtls_peer_t)); +} + +void +dtls_free_peer(dtls_peer_t *peer) { + free(peer); +} +#else /* WITH_CONTIKI */ +PROCESS(dtls_retransmit_process, "DTLS retransmit process"); + +#include "memb.h" +MEMB(peer_storage, dtls_peer_t, DTLS_PEER_MAX); + +void +peer_init() { + memb_init(&peer_storage); +} + +static inline dtls_peer_t * +dtls_malloc_peer() { + return memb_alloc(&peer_storage); +} + +void +dtls_free_peer(dtls_peer_t *peer) { + memb_free(&peer_storage, peer); +} +#endif /* WITH_CONTIKI */ + +dtls_peer_t * +dtls_new_peer(const session_t *session) { + dtls_peer_t *peer; + + peer = dtls_malloc_peer(); + if (peer) { + memset(peer, 0, sizeof(dtls_peer_t)); + memcpy(&peer->session, session, sizeof(session_t)); + +#ifndef NDEBUG + { + unsigned char addrbuf[72]; + dsrv_print_addr(session, addrbuf, sizeof(addrbuf)); + printf("dtls_new_peer: %s\n", addrbuf); + dump((unsigned char *)session, sizeof(session_t)); + printf("\n"); + } +#endif + /* initially allow the NULL cipher */ + CURRENT_CONFIG(peer)->cipher = TLS_NULL_WITH_NULL_NULL; + + /* initialize the handshake hash wrt. the hard-coded DTLS version */ + debug("DTLSv12: initialize HASH_SHA256\n"); + /* TLS 1.2: PRF(secret, label, seed) = P_(secret, label + seed) */ + /* FIXME: we use the default SHA256 here, might need to support other + hash functions as well */ + dtls_hash_init(&peer->hs_state.hs_hash); + } + + return peer; +} + diff --git a/peer.h b/peer.h new file mode 100644 index 0000000..d15b26f --- /dev/null +++ b/peer.h @@ -0,0 +1,101 @@ +/* dtls -- a very basic DTLS implementation + * + * Copyright (C) 2011--2013 Olaf Bergmann + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +/** + * @file peer.h + * @brief information about peers in a DTLS session + */ + +#ifndef _PEER_H_ +#define _PEER_H_ + +#include "config.h" +#include "global.h" + +#include "state.h" +#include "crypto.h" + +#ifndef WITH_CONTIKI +#include "uthash.h" +#endif /* WITH_CONTIKI */ + +/** + * Holds security parameters, local state and the transport address + * for each peer. */ +typedef struct dtls_peer_t { +#ifndef WITH_CONTIKI + UT_hash_handle hh; +#else /* WITH_CONTIKI */ + struct dtls_peer_t *next; +#endif /* WITH_CONTIKI */ + + session_t session; /**< peer address and local interface */ + + dtls_state_t state; /**< DTLS engine state */ + uint16 epoch; /**< counter for cipher state changes*/ + uint48 rseq; /**< sequence number of last record sent */ + + dtls_hs_state_t hs_state; /**< handshake protocol status */ + + dtls_security_parameters_t security_params[2]; + int config; /**< denotes which security params are in effect */ + /* FIXME: check if we can use epoch for this */ +} dtls_peer_t; + +/** + * Creates a new peer for given @p session. The current configuration + * is initialized with the cipher suite TLS_NULL_WITH_NULL_NULL (i.e. + * no security at all). This function returns a pointer to the new + * peer or NULL on error. The caller is responsible for releasing the + * storage allocated for this peer using dtls_free_peer(). + * + * @param session The remote peer's address and local interface index. + * @return A pointer to a newly created and initialized peer object + * or NULL on error. + */ +dtls_peer_t *dtls_new_peer(const session_t *session); + +/** Releases the storage allocated to @p peer. */ +void dtls_free_peer(dtls_peer_t *peer); + +/** Returns the current state of @p peer. */ +inline dtls_state_t dtls_peer_state(const dtls_peer_t *peer) { + return peer->state; +} + +/** + * Checks if given @p peer is connected. This function returns + * @c 1 if connected, or @c 0 otherwise. + */ +inline int dtls_peer_is_connected(const dtls_peer_t *peer) { + return peer->state == DTLS_STATE_CONNECTED; +} + +#define CURRENT_CONFIG(Peer) (&(Peer)->security_params[(Peer)->config]) +#define OTHER_CONFIG(Peer) (&(Peer)->security_params[!((Peer)->config & 0x01)]) + +#define SWITCH_CONFIG(Peer) ((Peer)->config = !((Peer)->config & 0x01)) + +#endif /* _PEER_H_ */ diff --git a/state.h b/state.h new file mode 100644 index 0000000..aef856a --- /dev/null +++ b/state.h @@ -0,0 +1,60 @@ +/* dtls -- a very basic DTLS implementation + * + * Copyright (C) 2011--2013 Olaf Bergmann + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +/** + * @file state.h + * @brief state information for DTLS FSM + */ + +#ifndef _STATE_H_ +#define _STATE_H_ + +#include "config.h" +#include "global.h" +#include "hmac.h" + +typedef enum { + DTLS_STATE_INIT = 0, DTLS_STATE_SERVERHELLO, DTLS_STATE_KEYEXCHANGE, + DTLS_STATE_WAIT_FINISHED, DTLS_STATE_FINISHED, + /* client states */ + DTLS_STATE_CLIENTHELLO, DTLS_STATE_WAIT_SERVERHELLODONE, + DTLS_STATE_WAIT_SERVERFINISHED, + + DTLS_STATE_CONNECTED, + DTLS_STATE_CLOSING, + DTLS_STATE_CLOSED, +} dtls_state_t; + +typedef struct { + uint24 mseq; /**< handshake message sequence number counter */ + + /** pending config that is updated during handshake */ + /* FIXME: dtls_security_parameters_t pending_config; */ + + /* temporary storage for the final handshake hash */ + dtls_hash_ctx hs_hash; +} dtls_hs_state_t; + +#endif /* _STATE_H_ */