--- Configure 2010-01-19 22:40:54.000000000 +0100 +++ Configure 2010-01-21 17:11:28.000000000 +0100 @@ -10,7 +10,7 @@ # see INSTALL for instructions. -my $usage="Usage: Configure [no- ...] [enable- ...] [experimental- ...] [-Dxxx] [-lxxx] [-Lxxx] [-fxxx] [-Kxxx] [no-hw-xxx|no-hw] [[no-]threads] [[no-]shared] [[no-]zlib|zlib-dynamic] [no-asm] [no-dso] [no-krb5] [386] [--prefix=DIR] [--openssldir=OPENSSLDIR] [--with-xxx[=vvv]] [--test-sanity] os/compiler[:flags]\n"; +my $usage="Usage: Configure [no- ...] [enable- ...] [experimental- ...] [-Dxxx] [-lxxx] [-Lxxx] [-fxxx] [-Kxxx] [no-hw-xxx|no-hw] [[no-]threads] [[no-]shared] [[no-]zlib|zlib-dynamic] [no-asm] [no-dso] [no-krb5] [sctp] [386] [--prefix=DIR] [--openssldir=OPENSSLDIR] [--with-xxx[=vvv]] [--test-sanity] os/compiler[:flags]\n"; # Options: # @@ -56,6 +56,7 @@ # [no-]zlib [don't] compile support for zlib compression. # zlib-dynamic Like "zlib", but the zlib library is expected to be a shared # library and will be loaded in run-time by the OpenSSL library. +# sctp include SCTP support # 386 generate 80386 code # no-sse2 disables IA-32 SSE2 code, above option implies no-sse2 # no- build without specified algorithm (rsa, idea, rc5, ...) @@ -634,6 +635,7 @@ my $no_asm=0; my $no_dso=0; my $no_gmp=0; +my $sctp=0; my @skip=(); my $Makefile="Makefile"; my $des_locl="crypto/des/des_locl.h"; @@ -811,6 +813,10 @@ # The check for the option is there so scripts aren't # broken } + elsif (/^sctp$/) + { + $sctp = 1; + } elsif (/^[-+]/) { if (/^-[lL](.*)$/ or /^-Wl,/) @@ -1294,6 +1300,26 @@ } } +if ($sctp) + { + $openssl_other_defines .= "#define OPENSSL_SCTP\n"; + + if ($target =~ /^darwin/ || $target =~ /^debug-darwin/) + { + $lflags = "$lflags -lsctp"; + } + + if ($target =~ /^linux/) + { + $lflags = "$lflags -lsctp"; + } + + if ($target =~ /^solaris/) + { + $lflags = "$lflags -lsctp"; + } + } + $cpuid_obj.=" uplink.o uplink-cof.o" if ($cflags =~ /\-DOPENSSL_USE_APPLINK/); # @@ -1767,6 +1793,7 @@ print "RC2 uses u$type[$rc2_int]\n" if $rc2_int != $def_int; print "BF_PTR used\n" if $bf_ptr == 1; print "BF_PTR2 used\n" if $bf_ptr == 2; +print "SCTP used\n" if $sctp; if($IsMK1MF) { open (OUT,">crypto/buildinf.h") || die "Can't open buildinf.h"; --- crypto/bio/bio.h 2009-09-04 19:42:05.000000000 +0200 +++ crypto/bio/bio.h 2010-01-21 17:11:28.000000000 +0100 @@ -95,6 +95,9 @@ #define BIO_TYPE_BIO (19|0x0400) /* (half a) BIO pair */ #define BIO_TYPE_LINEBUFFER (20|0x0200) /* filter */ #define BIO_TYPE_DGRAM (21|0x0400|0x0100) +#ifdef OPENSSL_SCTP +#define BIO_TYPE_DGRAM_SCTP (24|0x0400|0x0100) +#endif #define BIO_TYPE_ASN1 (22|0x0200) /* filter */ #define BIO_TYPE_COMP (23|0x0200) /* filter */ @@ -161,7 +164,23 @@ #define BIO_CTRL_DGRAM_SET_PEER 44 /* Destination for the data */ #define BIO_CTRL_DGRAM_SET_NEXT_TIMEOUT 45 /* Next DTLS handshake timeout to - * adjust socket timeouts */ + * adjust socket timeouts */ + +/* SCTP stuff */ +#ifdef OPENSSL_SCTP +#define BIO_CTRL_DGRAM_SCTP_SET_IN_HANDSHAKE 50 +#define BIO_CTRL_DGRAM_SCTP_ADD_AUTH_KEY 51 +#define BIO_CTRL_DGRAM_SCTP_NEXT_AUTH_KEY 52 +#define BIO_CTRL_DGRAM_SCTP_AUTH_CCS_RCVD 53 +#define BIO_CTRL_DGRAM_SCTP_GET_SNDINFO 55 +#define BIO_CTRL_DGRAM_SCTP_SET_SNDINFO 56 +#define BIO_CTRL_DGRAM_SCTP_GET_RCVINFO 57 +#define BIO_CTRL_DGRAM_SCTP_SET_RCVINFO 58 +#define BIO_CTRL_DGRAM_SCTP_GET_SNDFLAGS 59 +#define BIO_CTRL_DGRAM_SCTP_SET_SNDFLAGS 60 +#define BIO_CTRL_DGRAM_SCTP_GET_RCVFLAGS 61 +#define BIO_CTRL_DGRAM_SCTP_SAVE_SHUTDOWN 62 +#endif /* modifiers */ #define BIO_FP_READ 0x02 @@ -619,6 +638,9 @@ BIO_METHOD *BIO_f_nbio_test(void); #ifndef OPENSSL_NO_DGRAM BIO_METHOD *BIO_s_datagram(void); +#ifdef OPENSSL_SCTP +BIO_METHOD *BIO_s_datagram_sctp(void); +#endif #endif /* BIO_METHOD *BIO_f_ber(void); */ @@ -661,6 +683,14 @@ BIO *BIO_new_socket(int sock, int close_flag); BIO *BIO_new_dgram(int fd, int close_flag); +#ifdef OPENSSL_SCTP +BIO *BIO_new_dgram_sctp(int fd, int close_flag); +int BIO_dgram_sctp_notification_cb(BIO *b, + void (*handle_notifications)(BIO *bio, void *context, void *buf), + void *context); +int BIO_dgram_sctp_wait_for_dry(BIO *b); +int BIO_dgram_sctp_msg_waiting(BIO *b); +#endif BIO *BIO_new_fd(int fd, int close_flag); BIO *BIO_new_connect(char *host_port); BIO *BIO_new_accept(char *host_port); --- crypto/bio/bss_dgram.c 2010-01-07 11:44:21.000000000 +0100 +++ crypto/bio/bss_dgram.c 2010-01-21 17:11:28.000000000 +0100 @@ -70,6 +70,11 @@ #include #endif +#ifdef OPENSSL_SCTP +#include +#include +#endif + #ifdef OPENSSL_SYS_LINUX #define IP_MTU 14 /* linux is lame */ #endif @@ -88,6 +93,15 @@ static int dgram_free(BIO *data); static int dgram_clear(BIO *bio); +#ifdef OPENSSL_SCTP +static int dgram_sctp_write(BIO *h, const char *buf, int num); +static int dgram_sctp_read(BIO *h, char *buf, int size); +static int dgram_sctp_puts(BIO *h, const char *str); +static long dgram_sctp_ctrl(BIO *h, int cmd, long arg1, void *arg2); +static int dgram_sctp_new(BIO *h); +static int dgram_sctp_free(BIO *data); +#endif + static int BIO_dgram_should_retry(int s); static void get_current_time(struct timeval *t); @@ -106,6 +120,22 @@ NULL, }; +#ifdef OPENSSL_SCTP +static BIO_METHOD methods_dgramp_sctp= + { + BIO_TYPE_DGRAM_SCTP, + "datagram sctp socket", + dgram_sctp_write, + dgram_sctp_read, + dgram_sctp_puts, + NULL, /* dgram_gets, */ + dgram_sctp_ctrl, + dgram_sctp_new, + dgram_sctp_free, + NULL, + }; +#endif + typedef struct bio_dgram_data_st { union { @@ -122,11 +152,51 @@ struct timeval socket_timeout; } bio_dgram_data; +#ifdef OPENSSL_SCTP +typedef struct bio_dgram_sctp_save_message_st + { + BIO *bio; + char *data; + int length; + } bio_dgram_sctp_save_message; + +typedef struct bio_dgram_sctp_data_st + { +#if OPENSSL_USE_IPV6 + struct sockaddr_storage peer; +#else + struct sockaddr_in peer; +#endif + unsigned int connected; + unsigned int _errno; + unsigned int mtu; + struct sctp_sndrcvinfo sndinfo; + struct sctp_sndrcvinfo rcvinfo; + int sndflags; + int rcvflags; + void (*handle_notifications)(BIO *bio, void *context, void *buf); + void* notification_context; + int in_handshake; + int ccs_rcvd; + int ccs_sent; + int save_shutdown; + int peer_auth_tested; + bio_dgram_sctp_save_message saved_message; + } bio_dgram_sctp_data; +#endif + BIO_METHOD *BIO_s_datagram(void) { return(&methods_dgramp); } +#ifdef OPENSSL_SCTP +BIO_METHOD *BIO_s_datagram_sctp(void) + { + return(&methods_dgramp_sctp); + } +#endif + BIO *BIO_new_dgram(int fd, int close_flag) { BIO *ret; @@ -137,6 +207,71 @@ return(ret); } +#ifdef OPENSSL_SCTP +BIO *BIO_new_dgram_sctp(int fd, int close_flag) + { + BIO *bio; + int ret, optval = 20000; + int auth_data = 0, auth_forward = 0; + unsigned char *p; + struct sctp_authchunk auth; + struct sctp_authchunks *authchunks; + struct sctp_event_subscribe event; + socklen_t sockopt_len; + + bio=BIO_new(BIO_s_datagram_sctp()); + if (bio == NULL) return(NULL); + BIO_set_fd(bio,fd,close_flag); + + /* Activate SCTP-AUTH for DATA and FORWARD-TSN chunks */ + auth.sauth_chunk = SCTP_DATA; + ret = setsockopt(fd, IPPROTO_SCTP, SCTP_AUTH_CHUNK, &auth, sizeof(struct sctp_authchunk)); + OPENSSL_assert(ret >= 0); + auth.sauth_chunk = SCTP_FORWARD_CUM_TSN; + ret = setsockopt(fd, IPPROTO_SCTP, SCTP_AUTH_CHUNK, &auth, sizeof(struct sctp_authchunk)); + OPENSSL_assert(ret >= 0); + + /* Test if activation was successful. When using accept(), + * SCTP-AUTH has to be activated for the listening socket + * already, otherwise the connected socket won't use it. */ + sockopt_len = (socklen_t)(sizeof(sctp_assoc_t) + 256 * sizeof(uint8_t)); + authchunks = OPENSSL_malloc(sockopt_len); + memset(authchunks, 0, sizeof(struct sctp_authchunks)); + ret = sctp_opt_info(fd, 0, SCTP_LOCAL_AUTH_CHUNKS, authchunks, &sockopt_len); + OPENSSL_assert(ret >= 0); + + for (p = (unsigned char*) authchunks + sizeof(sctp_assoc_t); + p < (unsigned char*) authchunks + sockopt_len; + p += sizeof(uint8_t)) + { + if (*p == SCTP_DATA) auth_data = 1; + if (*p == SCTP_FORWARD_CUM_TSN) auth_forward = 1; + } + + OPENSSL_free(authchunks); + + OPENSSL_assert(auth_data); + OPENSSL_assert(auth_forward); + + sockopt_len = (socklen_t) sizeof(struct sctp_event_subscribe); + ret = getsockopt(fd, IPPROTO_SCTP, SCTP_EVENTS, &event, &sockopt_len); + OPENSSL_assert(ret >= 0); + + event.sctp_authentication_event = 1; + + ret = setsockopt(fd, IPPROTO_SCTP, SCTP_EVENTS, &event, sizeof(event)); + OPENSSL_assert(ret >= 0); + + /* Disable partial delivery by setting the min size + * larger than the max record size of 2^14 + 2048 + 13 + */ + ret = setsockopt(fd, IPPROTO_SCTP, SCTP_PARTIAL_DELIVERY_POINT, &optval, sizeof(optval)); + OPENSSL_assert(ret >= 0); + + return(bio); + } +#endif + static int dgram_new(BIO *bi) { bio_dgram_data *data = NULL; @@ -153,6 +288,24 @@ return(1); } +#ifdef OPENSSL_SCTP +static int dgram_sctp_new(BIO *bi) + { + bio_dgram_sctp_data *data = NULL; + + bi->init=0; + bi->num=0; + data = OPENSSL_malloc(sizeof(bio_dgram_sctp_data)); + if (data == NULL) + return 0; + memset(data, 0x00, sizeof(bio_dgram_sctp_data)); + bi->ptr = data; + + bi->flags=0; + return(1); + } +#endif + static int dgram_free(BIO *a) { bio_dgram_data *data; @@ -167,6 +320,22 @@ return(1); } +#ifdef OPENSSL_SCTP +static int dgram_sctp_free(BIO *a) + { + bio_dgram_sctp_data *data; + + if (a == NULL) return(0); + if ( ! dgram_clear(a)) + return 0; + + data = (bio_dgram_sctp_data *)a->ptr; + if(data != NULL) OPENSSL_free(data); + + return(1); + } +#endif + static int dgram_clear(BIO *a) { if (a == NULL) return(0); @@ -326,6 +495,178 @@ return(ret); } +#ifdef OPENSSL_SCTP +void dgram_sctp_handle_auth_free_key_event(BIO *b, union sctp_notification *snp) + { + unsigned int sockopt_len = 0; + int ret; + struct sctp_authkey_event* authkeyevent = &snp->sn_auth_event; + + if (authkeyevent->auth_indication == SCTP_AUTH_FREE_KEY) + { + struct sctp_authkeyid authkeyid; + + /* delete key */ + authkeyid.scact_keynumber = authkeyevent->auth_keynumber; + sockopt_len = sizeof(struct sctp_authkeyid); + ret = setsockopt(b->num, IPPROTO_SCTP, SCTP_AUTH_DELETE_KEY, + &authkeyid, sockopt_len); + } + } + +static int dgram_sctp_read(BIO *b, char *out, int outl) + { + int ret=0, n=0, i, optval; + socklen_t optlen; + bio_dgram_sctp_data *data = (bio_dgram_sctp_data *)b->ptr; + union sctp_notification *snp; + + if (out != NULL) + { + clear_socket_error(); + /* Last arg in recvfrom is signed on some platforms and + * unsigned on others. It is of type socklen_t on some + * but this is not universal. Cast to (void *) to avoid + * compiler warnings. + */ + + do + { + memset(&data->rcvinfo, 0x00, sizeof(struct sctp_sndrcvinfo)); + data->rcvflags = 0; + n=sctp_recvmsg(b->num,(void*) out, outl, NULL, NULL,&data->rcvinfo,&data->rcvflags); + if (n <= 0) + { + if (n < 0) + ret = n; + break; + } + + if (data->rcvflags & MSG_NOTIFICATION) + { + snp = (union sctp_notification*) out; + if (snp->sn_header.sn_type == SCTP_SENDER_DRY_EVENT) + { + struct sctp_event_subscribe event; + socklen_t eventsize; + /* If a message has been delayed until the socket + * is dry, it can be sent now. + */ + if (data->saved_message.length > 0) + { + dgram_sctp_write(data->saved_message.bio, data->saved_message.data, + data->saved_message.length); + OPENSSL_free(data->saved_message.data); + data->saved_message.length = 0; + } + + /* disable sender dry event */ + eventsize = sizeof(struct sctp_event_subscribe); + i = getsockopt(b->num, IPPROTO_SCTP, SCTP_EVENTS, &event, &eventsize); + OPENSSL_assert(i >= 0); + + event.sctp_sender_dry_event = 0; + + i = setsockopt(b->num, IPPROTO_SCTP, SCTP_EVENTS, &event, sizeof(event)); + OPENSSL_assert(i >= 0); + } + + if (snp->sn_header.sn_type == SCTP_AUTHENTICATION_EVENT) + dgram_sctp_handle_auth_free_key_event(b, snp); + + if (data->handle_notifications != NULL) + data->handle_notifications(b, data->notification_context, (void*) out); + + memset(out, 0, outl); + } + else + ret += n; + } + while ((data->rcvflags & MSG_NOTIFICATION) && (data->rcvflags & MSG_EOR) && (ret < outl)); + + if (ret > 0 && !(data->rcvflags & MSG_EOR)) + { + /* Partial message read, this should never happen! */ + + /* The buffer was too small, this means the peer sent + * a message that was larger than allowed. */ + if (ret == outl) + return -1; + + /* Test if socket buffer can handle max record + * size (2^14 + 2048 + 13) + */ + optlen = (socklen_t) sizeof(int); + ret = getsockopt(b->num, SOL_SOCKET, SO_RCVBUF, &optval, &optlen); + OPENSSL_assert(ret >= 0); + OPENSSL_assert(optval >= 18445); + + /* Test if SCTP doesn't partially deliver below + * max record size (2^14 + 2048 + 13) + */ + optlen = (socklen_t) sizeof(int); + ret = getsockopt(b->num, IPPROTO_SCTP, SCTP_PARTIAL_DELIVERY_POINT, + &optval, &optlen); + OPENSSL_assert(ret >= 0); + OPENSSL_assert(optval >= 18445); + + /* Partially delivered notification??? Probably a bug.... */ + OPENSSL_assert(!(data->rcvflags & MSG_NOTIFICATION)); + + /* Everything seems ok till now, so it's most likely + * a message dropped by PR-SCTP. + */ + memset(out, 0, outl); + BIO_set_retry_read(b); + return -1; + } + + /* Test if peer uses SCTP-AUTH before continuing */ + if (!data->peer_auth_tested) + { + int i, auth_data = 0, auth_forward = 0; + unsigned char *p; + struct sctp_authchunks *authchunks; + + optlen = (socklen_t)(sizeof(sctp_assoc_t) + 256 * sizeof(uint8_t)); + authchunks = OPENSSL_malloc(optlen); + memset(authchunks, 0, sizeof(struct sctp_authchunks)); + i = sctp_opt_info(b->num, 0, SCTP_PEER_AUTH_CHUNKS, authchunks, &optlen); + OPENSSL_assert(i >= 0); + + for (p = (unsigned char*) authchunks + sizeof(sctp_assoc_t); + p < (unsigned char*) authchunks + optlen; + p += sizeof(uint8_t)) + { + if (*p == SCTP_DATA) auth_data = 1; + if (*p == SCTP_FORWARD_CUM_TSN) auth_forward = 1; + } + + OPENSSL_free(authchunks); + + if (!auth_data || !auth_forward) + { + BIOerr(BIO_F_BIO_READ,BIO_R_CONNECT_ERROR); + return -1; + } + + data->peer_auth_tested = 1; + } + + BIO_clear_retry_flags(b); + if (ret < 0) + { + if (BIO_dgram_should_retry(ret)) + { + BIO_set_retry_read(b); + data->_errno = get_last_socket_error(); + } + } + } + return(ret); + } +#endif + static int dgram_write(BIO *b, const char *in, int inl) { int ret; @@ -369,6 +710,58 @@ return(ret); } +#ifdef OPENSSL_SCTP +static int dgram_sctp_write(BIO *b, const char *in, int inl) + { + int ret; + bio_dgram_sctp_data *data = (bio_dgram_sctp_data *)b->ptr; + struct sctp_sndrcvinfo *sinfo = &(data->sndinfo); + struct sctp_sndrcvinfo handshake_sinfo; + + sinfo->sinfo_flags = data->sndflags; + clear_socket_error(); + + /* If we're send anything else than application data, + * disable all user parameters and flags. + */ + if (in[0] != 23) { + memset(&handshake_sinfo, 0x00, sizeof(struct sctp_sndrcvinfo)); +#ifdef SCTP_SACK_IMMEDIATELY + handshake_sinfo.sinfo_flags = SCTP_SACK_IMMEDIATELY; +#endif + sinfo = &handshake_sinfo; + } + + /* If we have to send a shutdown alert message and the + * socket is not dry yet, we have to save it and send it + * as soon as the socket gets dry. + */ + if (data->save_shutdown && !BIO_dgram_sctp_wait_for_dry(b)) + { + data->saved_message.bio = b; + data->saved_message.length = inl; + data->saved_message.data = OPENSSL_malloc(inl); + memcpy(data->saved_message.data, in, inl); + return inl; + } + + ret = sctp_sendmsg(b->num, (const void *) in, inl, NULL, 0, + sinfo->sinfo_ppid, sinfo->sinfo_flags, sinfo->sinfo_stream, + sinfo->sinfo_timetolive, sinfo->sinfo_context); + + BIO_clear_retry_flags(b); + if (ret <= 0) + { + if (BIO_dgram_should_retry(ret)) + { + BIO_set_retry_write(b); + data->_errno = get_last_socket_error(); + } + } + return(ret); + } +#endif + static long dgram_ctrl(BIO *b, int cmd, long num, void *ptr) { long ret=1; @@ -555,7 +948,6 @@ break; case BIO_CTRL_DGRAM_SET_CONNECTED: to = (struct sockaddr *)ptr; - if ( to != NULL) { data->connected = 1; @@ -728,6 +1120,360 @@ return(ret); } +#ifdef OPENSSL_SCTP +static long dgram_sctp_ctrl(BIO *b, int cmd, long num, void *ptr) + { + long ret=1; + bio_dgram_sctp_data *data = NULL; + unsigned int sockopt_len = 0; + struct sctp_authkeyid authkeyid; + struct sctp_authkey *authkey; + + data = (bio_dgram_sctp_data *)b->ptr; + + switch (cmd) + { + case BIO_CTRL_DGRAM_QUERY_MTU: + /* Set to maximum (2^14) + * and ignore user input to enable transport + * protocol fragmentation. + * Returns always 2^14. + */ + data->mtu = 16384; + ret = data->mtu; + break; + case BIO_CTRL_DGRAM_SET_MTU: + /* Set to maximum (2^14) + * and ignore input to enable transport + * protocol fragmentation. + * Returns always 2^14. + */ + data->mtu = 16384; + ret = data->mtu; + break; + case BIO_CTRL_DGRAM_SET_CONNECTED: + case BIO_CTRL_DGRAM_CONNECT: + /* Returns always -1. */ + ret = -1; + break; + case BIO_CTRL_DGRAM_SET_NEXT_TIMEOUT: + /* SCTP doesn't need the DTLS timer + * Returns always 1. + */ + break; + case BIO_CTRL_DGRAM_SCTP_SET_IN_HANDSHAKE: + if (num > 0) + data->in_handshake = 1; + else + data->in_handshake = 0; + + ret = setsockopt(b->num, IPPROTO_SCTP, SCTP_NODELAY, &data->in_handshake, sizeof(int)); + break; + case BIO_CTRL_DGRAM_SCTP_ADD_AUTH_KEY: + /* New shared key for SCTP AUTH. + * Returns 0 on success, -1 otherwise. + */ + + /* Get active key */ + sockopt_len = sizeof(struct sctp_authkeyid); + ret = sctp_opt_info(b->num, 0, SCTP_AUTH_ACTIVE_KEY, &authkeyid, &sockopt_len); + if (ret < 0) break; + + /* Add new key */ + authkey = OPENSSL_malloc(sizeof(struct sctp_authkey) + 64 * sizeof(uint8_t)); + authkey->sca_keynumber = authkeyid.scact_keynumber + 1; + memset(&authkey->sca_key[0], 0x00, 65 * sizeof(uint8_t)); + memcpy(&authkey->sca_key[0], ptr, 32 * sizeof(uint8_t)); + + sockopt_len = sizeof(struct sctp_authkey) + 63 * sizeof(uint8_t); + ret = setsockopt(b->num, IPPROTO_SCTP, SCTP_AUTH_KEY, authkey, sockopt_len); + if (ret < 0) break; + + /* Reset active key */ + ret = setsockopt(b->num, IPPROTO_SCTP, SCTP_AUTH_ACTIVE_KEY, + &authkeyid, sizeof(struct sctp_authkeyid)); + if (ret < 0) break; + + break; + case BIO_CTRL_DGRAM_SCTP_NEXT_AUTH_KEY: + /* Returns 0 on success, -1 otherwise. */ + + /* Get active key */ + sockopt_len = sizeof(struct sctp_authkeyid); + ret = sctp_opt_info(b->num, 0, SCTP_AUTH_ACTIVE_KEY, &authkeyid, &sockopt_len); + if (ret < 0) break; + + /* Set active key */ + authkeyid.scact_keynumber = authkeyid.scact_keynumber + 1; + ret = setsockopt(b->num, IPPROTO_SCTP, SCTP_AUTH_ACTIVE_KEY, + &authkeyid, sizeof(struct sctp_authkeyid)); + if (ret < 0) break; + + /* CCS has been sent, so remember that and fall through + * to check if we need to deactivate an old key + */ + data->ccs_sent = 1; + + case BIO_CTRL_DGRAM_SCTP_AUTH_CCS_RCVD: + /* Returns 0 on success, -1 otherwise. */ + + /* Has this command really been called or is this just a fall-through? */ + if (cmd == BIO_CTRL_DGRAM_SCTP_AUTH_CCS_RCVD) + data->ccs_rcvd = 1; + + /* CSS has been both, received and sent, so deactivate an old key */ + if (data->ccs_rcvd == 1 && data->ccs_sent == 1) + { + /* Get active key */ + sockopt_len = sizeof(struct sctp_authkeyid); + ret = sctp_opt_info(b->num, 0, SCTP_AUTH_ACTIVE_KEY, &authkeyid, &sockopt_len); + if (ret < 0) break; + + /* deactivate key */ + authkeyid.scact_keynumber = authkeyid.scact_keynumber - 1; + sockopt_len = sizeof(struct sctp_authkeyid); + ret = setsockopt(b->num, IPPROTO_SCTP, SCTP_AUTH_DEACTIVATE_KEY, + &authkeyid, sockopt_len); + if (ret < 0) break; + + data->ccs_rcvd = 0; + data->ccs_sent = 0; + } + break; + case BIO_CTRL_DGRAM_SCTP_GET_SNDINFO: + /* Returns the size of the copied struct. */ + if (num > sizeof(struct sctp_sndrcvinfo)) + num = sizeof(struct sctp_sndrcvinfo); + + memcpy(ptr, &(data->sndinfo), num); + ret = num; + break; + case BIO_CTRL_DGRAM_SCTP_SET_SNDINFO: + /* Returns the size of the copied struct. */ + if (num > sizeof(struct sctp_sndrcvinfo)) + num = sizeof(struct sctp_sndrcvinfo); + + memcpy(&(data->sndinfo), ptr, num); + break; + case BIO_CTRL_DGRAM_SCTP_GET_RCVINFO: + /* Returns the size of the copied struct. */ + if (num > sizeof(struct sctp_sndrcvinfo)) + num = sizeof(struct sctp_sndrcvinfo); + + memcpy(ptr, &data->rcvinfo, num); + + ret = num; + break; + case BIO_CTRL_DGRAM_SCTP_SET_RCVINFO: + /* Returns the size of the copied struct. */ + if (num > sizeof(struct sctp_sndrcvinfo)) + num = sizeof(struct sctp_sndrcvinfo); + + memcpy(&(data->rcvinfo), ptr, num); + break; + case BIO_CTRL_DGRAM_SCTP_GET_SNDFLAGS: + /* Returns the size of the copied struct. */ + if (num > sizeof(int)) + num = sizeof(int); + + memcpy(ptr, &(data->sndflags), num); + ret = num; + break; + case BIO_CTRL_DGRAM_SCTP_SET_SNDFLAGS: + /* Returns the size of the copied struct. */ + if (num > sizeof(int)) + num = sizeof(int); + + memcpy(&(data->sndflags), ptr, num); + break; + case BIO_CTRL_DGRAM_SCTP_GET_RCVFLAGS: + /* Returns the size of the copied struct. */ + if (num > sizeof(int)) + num = sizeof(int); + + memcpy(ptr, &(data->rcvflags), num); + ret = num; + break; + case BIO_CTRL_DGRAM_SCTP_SAVE_SHUTDOWN: + /* Returns always 1. */ + if (num > 0) + data->save_shutdown = 1; + else + data->save_shutdown = 0; + break; + + default: + /* Pass to default ctrl function to + * process SCTP unspecific commands + */ + ret=dgram_ctrl(b, cmd, num, ptr); + break; + } + return(ret); + } + +int BIO_dgram_sctp_notification_cb(BIO *b, + void (*handle_notifications)(BIO *bio, void *context, void *buf), + void *context) + { + bio_dgram_sctp_data *data = (bio_dgram_sctp_data *) b->ptr; + + if (handle_notifications != NULL) + { + data->handle_notifications = handle_notifications; + data->notification_context = context; + } + else + return -1; + + return 0; + } + +int BIO_dgram_sctp_wait_for_dry(BIO *b) +{ + int flags, is_dry = 0; + int n, sockflags, ret; + union sctp_notification snp; + struct sctp_event_subscribe event; + socklen_t eventsize; + bio_dgram_sctp_data *data = (bio_dgram_sctp_data *)b->ptr; + + /* set sender dry event */ + eventsize = sizeof(struct sctp_event_subscribe); + ret = getsockopt(b->num, IPPROTO_SCTP, SCTP_EVENTS, &event, &eventsize); + if (ret < 0) + return -1; + + event.sctp_sender_dry_event = 1; + + ret = setsockopt(b->num, IPPROTO_SCTP, SCTP_EVENTS, &event, sizeof(event)); + if (ret < 0) + return -1; + + /* peek for notification */ + flags = MSG_PEEK; + n = sctp_recvmsg(b->num,(void*)&snp,sizeof(union sctp_notification),NULL,NULL,0,&flags); + if (n <= 0) + { + if ((n < 0) && (get_last_socket_error() != EAGAIN) && (get_last_socket_error() != EWOULDBLOCK)) + return -1; + else + return 0; + } + + /* if we find a notification, process it and try again if necessary */ + while (flags & MSG_NOTIFICATION) + { + memset(&snp, 0x00, sizeof(union sctp_notification)); + flags = 0; + n = sctp_recvmsg(b->num,(void*)&snp,sizeof(union sctp_notification),NULL,NULL,0,&flags); + if (n <= 0) + { + if ((n < 0) && (get_last_socket_error() != EAGAIN) && (get_last_socket_error() != EWOULDBLOCK)) + return -1; + else + return is_dry; + } + + if (snp.sn_header.sn_type == SCTP_SENDER_DRY_EVENT) + { + is_dry = 1; + + /* disable sender dry event */ + eventsize = (socklen_t) sizeof(struct sctp_event_subscribe); + ret = getsockopt(b->num, IPPROTO_SCTP, SCTP_EVENTS, &event, &eventsize); + if (ret < 0) + return -1; + + event.sctp_sender_dry_event = 0; + + ret = setsockopt(b->num, IPPROTO_SCTP, SCTP_EVENTS, &event, sizeof(event)); + if (ret < 0) + return -1; + } + + if (snp.sn_header.sn_type == SCTP_AUTHENTICATION_EVENT) + dgram_sctp_handle_auth_free_key_event(b, &snp); + + if (data->handle_notifications != NULL) + data->handle_notifications(b, data->notification_context, (void*) &snp); + + if (is_dry == 1) + { + /* check for more notifications */ + sockflags = fcntl(b->num, F_GETFL, 0); + fcntl(b->num, F_SETFL, O_NONBLOCK); + flags = MSG_PEEK; + n = sctp_recvmsg(b->num,(void*)&snp,sizeof(union sctp_notification),NULL,NULL,0,&flags); + fcntl(b->num, F_SETFL, sockflags); + + /* if nothing more is waiting just quit. otherwise read again. */ + if (n <= 0) + { + if ((n < 0) && (get_last_socket_error() != EAGAIN) && (get_last_socket_error() != EWOULDBLOCK)) + return -1; + else + return is_dry; + } + } + + /* found notification but no dry event, peek again */ + memset(&snp, 0x00, sizeof(union sctp_notification)); + flags = MSG_PEEK; + n = sctp_recvmsg(b->num,(void*)&snp,sizeof(union sctp_notification),NULL,NULL,0,&flags); + if (n <= 0) + { + if ((n < 0) && (get_last_socket_error() != EAGAIN) && (get_last_socket_error() != EWOULDBLOCK)) + return -1; + else + return is_dry; + } + } + + /* read anything else */ + return is_dry; +} + +int BIO_dgram_sctp_msg_waiting(BIO *b) + { + int n, sockflags, flags; + union sctp_notification snp; + bio_dgram_sctp_data *data = (bio_dgram_sctp_data *)b->ptr; + + /* Check if there are any messages waiting to be read */ + do + { + sockflags = fcntl(b->num, F_GETFL, 0); + fcntl(b->num, F_SETFL, O_NONBLOCK); + flags = MSG_PEEK; + n = sctp_recvmsg(b->num,(void*)&snp,sizeof(union sctp_notification),NULL,NULL,0,&flags); + fcntl(b->num, F_SETFL, sockflags); + + /* if notification, process and try again */ + if (n > 0 && (flags & MSG_NOTIFICATION)) + { + if (snp.sn_header.sn_type == SCTP_AUTHENTICATION_EVENT) + dgram_sctp_handle_auth_free_key_event(b, &snp); + + flags = 0; + n = sctp_recvmsg(b->num,(void*)&snp,sizeof(union sctp_notification),NULL,NULL,0,&flags); + + if (data->handle_notifications != NULL) + data->handle_notifications(b, data->notification_context, (void*) &snp); + + memset(&snp, 0x00, sizeof(union sctp_notification)); + } + + } while (n > 0 && (flags & MSG_NOTIFICATION)); + + /* Return 1 if there is a message to be read, return 0 otherwise. */ + if (n > 0) + return 1; + else + return 0; + } +#endif + static int dgram_puts(BIO *bp, const char *str) { int n,ret; @@ -737,6 +1483,17 @@ return(ret); } +#ifdef OPENSSL_SCTP +static int dgram_sctp_puts(BIO *bp, const char *str) + { + int n,ret; + + n=strlen(str); + ret=dgram_sctp_write(bp,str,n); + return(ret); + } +#endif + static int BIO_dgram_should_retry(int i) { int err; --- ssl/d1_both.c 2009-12-01 18:41:42.000000000 +0100 +++ ssl/d1_both.c 2010-01-21 17:11:28.000000000 +0100 @@ -1265,3 +1265,24 @@ ccs_hdr->type = *(data++); } + +int dtls1_shutdown(SSL *s) + { + int ret; +#ifdef OPENSSL_SCTP + if (SSL_get_wbio(s)->method->type == BIO_TYPE_DGRAM_SCTP && + !(s->shutdown & SSL_SENT_SHUTDOWN)) + { + ret = BIO_dgram_sctp_wait_for_dry(SSL_get_wbio(s)); + if (ret < 0) return -1; + + if (ret == 0) + BIO_ctrl(SSL_get_wbio(s), BIO_CTRL_DGRAM_SCTP_SAVE_SHUTDOWN, 1, NULL); + } +#endif + ret = ssl3_shutdown(s); +#ifdef OPENSSL_SCTP + BIO_ctrl(SSL_get_wbio(s), BIO_CTRL_DGRAM_SCTP_SAVE_SHUTDOWN, 0, NULL); +#endif + return ret; + } --- ssl/d1_clnt.c 2010-01-21 17:10:59.000000000 +0100 +++ ssl/d1_clnt.c 2010-01-21 17:11:28.000000000 +0100 @@ -152,6 +152,10 @@ void (*cb)(const SSL *ssl,int type,int val)=NULL; int ret= -1; int new_state,state,skip=0;; +#ifdef OPENSSL_SCTP + unsigned char sctpauthkey[64]; + unsigned char labelbuffer[25], contextbuffer[8]; +#endif RAND_add(&Time,sizeof(Time),0); ERR_clear_error(); @@ -165,6 +169,14 @@ s->in_handshake++; if (!SSL_in_init(s) || SSL_in_before(s)) SSL_clear(s); +#ifdef OPENSSL_SCTP + /* Notify SCTP BIO socket to enter handshake + * mode and prevent stream identifier other + * than 0. Will be ignored if no SCTP is used. + */ + BIO_ctrl(SSL_get_wbio(s), BIO_CTRL_DGRAM_SCTP_SET_IN_HANDSHAKE, s->in_handshake, NULL); +#endif + for (;;) { state=s->state; @@ -227,6 +239,42 @@ s->hit = 0; break; +#ifdef OPENSSL_SCTP + case DTLS1_SCTP_ST_CR_READ_SOCK: + + if (BIO_dgram_sctp_msg_waiting(SSL_get_rbio(s))) + { + s->s3->in_read_app_data=2; + s->rwstate=SSL_READING; + BIO_clear_retry_flags(SSL_get_rbio(s)); + BIO_set_retry_read(SSL_get_rbio(s)); + ret = -1; + goto end; + } + + s->state=s->s3->tmp.next_state; + break; + + case DTLS1_SCTP_ST_CW_WRITE_SOCK: + /* read app data until dry event */ + + ret = BIO_dgram_sctp_wait_for_dry(SSL_get_wbio(s)); + if (ret < 0) goto end; + + if (ret == 0) + { + s->s3->in_read_app_data=2; + s->rwstate=SSL_READING; + BIO_clear_retry_flags(SSL_get_rbio(s)); + BIO_set_retry_read(SSL_get_rbio(s)); + ret = -1; + goto end; + } + + s->state=s->d1->next_state; + break; +#endif + case SSL3_ST_CW_CLNT_HELLO_A: case SSL3_ST_CW_CLNT_HELLO_B: @@ -249,9 +297,17 @@ s->init_num=0; - /* turn on buffering for the next lot of output */ - if (s->bbio != s->wbio) - s->wbio=BIO_push(s->bbio,s->wbio); +#ifdef OPENSSL_SCTP + /* Disable buffering for SCTP */ + if (SSL_get_wbio(s)->method->type != BIO_TYPE_DGRAM_SCTP) + { +#endif + /* turn on buffering for the next lot of output */ + if (s->bbio != s->wbio) + s->wbio=BIO_push(s->bbio,s->wbio); +#ifdef OPENSSL_SCTP + } +#endif break; @@ -263,7 +319,24 @@ { dtls1_stop_timer(s); if (s->hit) + { +#ifdef OPENSSL_SCTP + /* Add new shared key for SCTP-Auth, + * will be ignored if no SCTP used. + */ + snprintf((char*) labelbuffer, 25, "EXTRACTOR_dtlssctpauthkey"); + snprintf((char*) contextbuffer, 8, "dtlssctp"); + + SSL_tls1_key_extractor(s, labelbuffer, sizeof(labelbuffer), + contextbuffer, sizeof(contextbuffer), + sctpauthkey, sizeof(sctpauthkey)); + + BIO_ctrl(SSL_get_wbio(s), BIO_CTRL_DGRAM_SCTP_ADD_AUTH_KEY, + sizeof(sctpauthkey), sctpauthkey); +#endif + s->state=SSL3_ST_CR_FINISHED_A; + } else s->state=DTLS1_ST_CR_HELLO_VERIFY_REQUEST_A; } @@ -356,11 +429,18 @@ ret=ssl3_get_server_done(s); if (ret <= 0) goto end; if (s->s3->tmp.cert_req) - s->state=SSL3_ST_CW_CERT_A; + s->s3->tmp.next_state=SSL3_ST_CW_CERT_A; else - s->state=SSL3_ST_CW_KEY_EXCH_A; + s->s3->tmp.next_state=SSL3_ST_CW_KEY_EXCH_A; s->init_num=0; +#ifdef OPENSSL_SCTP + if (SSL_get_wbio(s)->method->type == BIO_TYPE_DGRAM_SCTP && + state == SSL_ST_RENEGOTIATE) + s->state=DTLS1_SCTP_ST_CR_READ_SOCK; + else +#endif + s->state=s->s3->tmp.next_state; break; case SSL3_ST_CW_CERT_A: @@ -379,6 +459,22 @@ dtls1_start_timer(s); ret=dtls1_send_client_key_exchange(s); if (ret <= 0) goto end; + +#ifdef OPENSSL_SCTP + /* Add new shared key for SCTP-Auth, + * will be ignored if no SCTP used. + */ + snprintf((char*) labelbuffer, 25, "EXTRACTOR_dtlssctpauthkey"); + snprintf((char*) contextbuffer, 8, "dtlssctp"); + + SSL_tls1_key_extractor(s, labelbuffer, sizeof(labelbuffer), + contextbuffer, sizeof(contextbuffer), + sctpauthkey, sizeof(sctpauthkey)); + + BIO_ctrl(SSL_get_wbio(s), BIO_CTRL_DGRAM_SCTP_ADD_AUTH_KEY, + sizeof(sctpauthkey), sctpauthkey); +#endif + /* EAY EAY EAY need to check for DH fix cert * sent back */ /* For TLS, cert_req is set to 2, so a cert chain @@ -389,7 +485,15 @@ } else { - s->state=SSL3_ST_CW_CHANGE_A; +#ifdef OPENSSL_SCTP + if (SSL_get_wbio(s)->method->type == BIO_TYPE_DGRAM_SCTP) + { + s->d1->next_state=SSL3_ST_CW_CHANGE_A; + s->state=DTLS1_SCTP_ST_CW_WRITE_SOCK; + } + else +#endif + s->state=SSL3_ST_CW_CHANGE_A; s->s3->change_cipher_spec=0; } @@ -401,7 +505,15 @@ dtls1_start_timer(s); ret=dtls1_send_client_verify(s); if (ret <= 0) goto end; - s->state=SSL3_ST_CW_CHANGE_A; +#ifdef OPENSSL_SCTP + if (SSL_get_wbio(s)->method->type == BIO_TYPE_DGRAM_SCTP) + { + s->d1->next_state=SSL3_ST_CW_CHANGE_A; + s->state=DTLS1_SCTP_ST_CW_WRITE_SOCK; + } + else +#endif + s->state=SSL3_ST_CW_CHANGE_A; s->init_num=0; s->s3->change_cipher_spec=0; break; @@ -412,6 +524,14 @@ ret=dtls1_send_change_cipher_spec(s, SSL3_ST_CW_CHANGE_A,SSL3_ST_CW_CHANGE_B); if (ret <= 0) goto end; + +#ifdef OPENSSL_SCTP + /* Change to new shared key of SCTP-Auth, + * will be ignored if no SCTP used. + */ + BIO_ctrl(SSL_get_wbio(s), BIO_CTRL_DGRAM_SCTP_NEXT_AUTH_KEY, 0, NULL); +#endif + s->state=SSL3_ST_CW_FINISHED_A; s->init_num=0; @@ -456,9 +576,23 @@ if (s->hit) { s->s3->tmp.next_state=SSL_ST_OK; +#ifdef OPENSSL_SCTP + if (SSL_get_wbio(s)->method->type == BIO_TYPE_DGRAM_SCTP) + { + s->d1->next_state = s->s3->tmp.next_state; + s->s3->tmp.next_state=DTLS1_SCTP_ST_CW_WRITE_SOCK; + } +#endif if (s->s3->flags & SSL3_FLAGS_DELAY_CLIENT_FINISHED) { s->state=SSL_ST_OK; +#ifdef OPENSSL_SCTP + if (SSL_get_wbio(s)->method->type == BIO_TYPE_DGRAM_SCTP) + { + s->d1->next_state = SSL_ST_OK; + s->state=DTLS1_SCTP_ST_CW_WRITE_SOCK; + } +#endif s->s3->flags|=SSL3_FLAGS_POP_BUFFER; s->s3->delay_buf_pop_ret=0; } @@ -507,6 +641,16 @@ s->state=SSL3_ST_CW_CHANGE_A; else s->state=SSL_ST_OK; + +#ifdef OPENSSL_SCTP + if (SSL_get_wbio(s)->method->type == BIO_TYPE_DGRAM_SCTP && + state == SSL_ST_RENEGOTIATE) + { + s->d1->next_state=s->state; + s->state=DTLS1_SCTP_ST_CW_WRITE_SOCK; + } +#endif + s->init_num=0; break; @@ -589,6 +733,15 @@ } end: s->in_handshake--; + +#ifdef OPENSSL_SCTP + /* Notify SCTP BIO socket to leave handshake + * mode and allow stream identifier other + * than 0. Will be ignored if no SCTP is used. + */ + BIO_ctrl(SSL_get_wbio(s), BIO_CTRL_DGRAM_SCTP_SET_IN_HANDSHAKE, s->in_handshake, NULL); +#endif + if (buf != NULL) BUF_MEM_free(buf); if (cb != NULL) --- ssl/d1_lib.c 2009-12-08 12:38:17.000000000 +0100 +++ ssl/d1_lib.c 2010-01-21 17:11:28.000000000 +0100 @@ -237,6 +237,15 @@ void dtls1_start_timer(SSL *s) { +#ifdef OPENSSL_SCTP + /* Disable timer for SCTP */ + if (SSL_get_wbio(s)->method->type == BIO_TYPE_DGRAM_SCTP) + { + memset(&(s->d1->next_timeout), 0, sizeof(struct timeval)); + return; + } +#endif + /* If timer is not set, initialize duration with 1 second */ if (s->d1->next_timeout.tv_sec == 0 && s->d1->next_timeout.tv_usec == 0) { --- ssl/d1_pkt.c 2010-01-21 17:10:59.000000000 +0100 +++ ssl/d1_pkt.c 2010-01-21 17:11:28.000000000 +0100 @@ -229,6 +229,14 @@ item->data = rdata; +#ifdef OPENSSL_SCTP + /* Store sctp_sndrcvinfo struct */ + if (SSL_get_rbio(s)->method->type == BIO_TYPE_DGRAM_SCTP && + (s->state == SSL3_ST_SR_FINISHED_A || s->state == SSL3_ST_CR_FINISHED_A)) { + BIO_ctrl(SSL_get_rbio(s), BIO_CTRL_DGRAM_SCTP_GET_RCVINFO, sizeof(rdata->recordinfo), &rdata->recordinfo); + } +#endif + /* insert should not fail, since duplicates are dropped */ if (pqueue_insert(queue->q, item) == NULL) { @@ -648,20 +656,28 @@ goto again; /* get another record */ } - /* Check whether this is a repeat, or aged record. - * Don't check if we're listening and this message is - * a ClientHello. They can look as if they're replayed, - * since they arrive from different connections and - * would be dropped unnecessarily. - */ - if (!(s->d1->listen && rr->type == SSL3_RT_HANDSHAKE && - *p == SSL3_MT_CLIENT_HELLO) && - !dtls1_record_replay_check(s, bitmap)) - { - rr->length = 0; - s->packet_length=0; /* dump this record */ - goto again; /* get another record */ - } +#ifdef OPENSSL_SCTP + /* Only do replay check if no SCTP bio */ + if (SSL_get_rbio(s)->method->type != BIO_TYPE_DGRAM_SCTP) + { +#endif + /* Check whether this is a repeat, or aged record. + * Don't check if we're listening and this message is + * a ClientHello. They can look as if they're replayed, + * since they arrive from different connections and + * would be dropped unnecessarily. + */ + if (!(s->d1->listen && rr->type == SSL3_RT_HANDSHAKE && + *p == SSL3_MT_CLIENT_HELLO) && + !dtls1_record_replay_check(s, bitmap)) + { + rr->length = 0; + s->packet_length=0; /* dump this record */ + goto again; /* get another record */ + } +#ifdef OPENSSL_SCTP + } +#endif /* just read a 0 length packet */ if (rr->length == 0) goto again; @@ -741,7 +757,17 @@ /* Now s->d1->handshake_fragment_len == 0 if type == SSL3_RT_HANDSHAKE. */ +#ifdef OPENSSL_SCTP + /* Continue handshake if it had to be interrupted to read + * app data with SCTP. + */ + if ((!s->in_handshake && SSL_in_init(s)) || + (SSL_get_rbio(s)->method->type == BIO_TYPE_DGRAM_SCTP && + (s->state == DTLS1_SCTP_ST_SR_READ_SOCK || s->state == DTLS1_SCTP_ST_CR_READ_SOCK) && + s->s3->in_read_app_data != 2)) +#else if (!s->in_handshake && SSL_in_init(s)) +#endif { /* type == SSL3_RT_APPLICATION_DATA */ i=s->handshake_func(s); @@ -772,6 +798,15 @@ item = pqueue_pop(s->d1->buffered_app_data.q); if (item) { +#ifdef OPENSSL_SCTP + /* Restore sctp_sndrcvinfo struct */ + if (SSL_get_rbio(s)->method->type == BIO_TYPE_DGRAM_SCTP) + { + DTLS1_RECORD_DATA *rdata = (DTLS1_RECORD_DATA *) item->data; + BIO_ctrl(SSL_get_rbio(s), BIO_CTRL_DGRAM_SCTP_SET_RCVINFO, sizeof(rdata->recordinfo), &rdata->recordinfo); + } +#endif + dtls1_copy_record(s, item); OPENSSL_free(item->data); @@ -854,6 +889,31 @@ rr->off=0; } } + +#ifdef OPENSSL_SCTP + /* We were about to renegotiate but had to read + * belated application data first, so retry. + */ + if (SSL_get_rbio(s)->method->type == BIO_TYPE_DGRAM_SCTP && + rr->type == SSL3_RT_APPLICATION_DATA && + (s->state == DTLS1_SCTP_ST_SR_READ_SOCK || s->state == DTLS1_SCTP_ST_CR_READ_SOCK)) + { + s->rwstate=SSL_READING; + BIO_clear_retry_flags(SSL_get_rbio(s)); + BIO_set_retry_read(SSL_get_rbio(s)); + } + + /* We might had to delay a close_notify alert because + * of reordered app data. If there was an alert and there + * is no message to read anymore, finally set shutdown. + */ + if (SSL_get_rbio(s)->method->type == BIO_TYPE_DGRAM_SCTP && + s->d1->shutdown_received && !BIO_dgram_sctp_msg_waiting(SSL_get_rbio(s))) + { + s->shutdown |= SSL_RECEIVED_SHUTDOWN; + return(0); + } +#endif return(n); } @@ -1026,6 +1086,21 @@ s->s3->warn_alert = alert_descr; if (alert_descr == SSL_AD_CLOSE_NOTIFY) { +#ifdef OPENSSL_SCTP + /* With SCTP and streams the socket may deliver app data + * after a close_notify alert. We have to check this + * first so that nothing gets discarded. + */ + if (SSL_get_rbio(s)->method->type == BIO_TYPE_DGRAM_SCTP && + BIO_dgram_sctp_msg_waiting(SSL_get_rbio(s))) + { + s->d1->shutdown_received = 1; + s->rwstate=SSL_READING; + BIO_clear_retry_flags(SSL_get_rbio(s)); + BIO_set_retry_read(SSL_get_rbio(s)); + return -1; + } +#endif s->shutdown |= SSL_RECEIVED_SHUTDOWN; return(0); } @@ -1132,6 +1207,15 @@ if (s->version == DTLS1_BAD_VER) s->d1->handshake_read_seq++; +#ifdef OPENSSL_SCTP + /* Remember that a CCS has been received, + * so that an old key of SCTP-Auth can be + * deleted when a CCS is sent. Will be ignored + * if no SCTP is used + */ + BIO_ctrl(SSL_get_wbio(s), BIO_CTRL_DGRAM_SCTP_AUTH_CCS_RCVD, 1, NULL); +#endif + goto start; } @@ -1267,7 +1351,16 @@ { int i; - if (SSL_in_init(s) && !s->in_handshake) +#ifdef OPENSSL_SCTP + /* Check if we have to continue an interrupted handshake + * for reading belated app data with SCTP. + */ + if ((SSL_in_init(s) && !s->in_handshake) || + (SSL_get_wbio(s)->method->type == BIO_TYPE_DGRAM_SCTP && + (s->state == DTLS1_SCTP_ST_SR_READ_SOCK || s->state == DTLS1_SCTP_ST_CR_READ_SOCK))) +#else + if (SSL_in_init(s) && !s->in_handshake) +#endif { i=s->handshake_func(s); if (i < 0) return(i); --- ssl/d1_srvr.c 2010-01-21 17:10:59.000000000 +0100 +++ ssl/d1_srvr.c 2010-01-21 17:15:21.000000000 +0100 @@ -151,6 +151,10 @@ unsigned long alg_k; int ret= -1; int new_state,state,skip=0; +#ifdef OPENSSL_SCTP + unsigned char sctpauthkey[64]; + unsigned char labelbuffer[25], contextbuffer[8]; +#endif RAND_add(&Time,sizeof(Time),0); ERR_clear_error(); @@ -165,6 +169,14 @@ s->in_handshake++; if (!SSL_in_init(s) || SSL_in_before(s)) SSL_clear(s); +#ifdef OPENSSL_SCTP + /* Notify SCTP BIO socket to enter handshake + * mode and prevent stream identifier other + * than 0. Will be ignored if no SCTP is used. + */ + BIO_ctrl(SSL_get_wbio(s), BIO_CTRL_DGRAM_SCTP_SET_IN_HANDSHAKE, s->in_handshake, NULL); +#endif + if (s->cert == NULL) { SSLerr(SSL_F_DTLS1_ACCEPT,SSL_R_NO_CERTIFICATE_SET); @@ -223,8 +235,12 @@ { /* Ok, we now need to push on a buffering BIO so that * the output is sent in a way that TCP likes :-) + * ...but not with SCTP :-) */ - if (!ssl_init_wbio_buffer(s,1)) { ret= -1; goto end; } +#ifdef OPENSSL_SCTP + if (SSL_get_wbio(s)->method->type != BIO_TYPE_DGRAM_SCTP) +#endif + if (!ssl_init_wbio_buffer(s,1)) { ret= -1; goto end; } ssl3_init_finished_mac(s); s->state=SSL3_ST_SR_CLNT_HELLO_A; @@ -273,7 +289,7 @@ s->state = SSL3_ST_SW_SRVR_HELLO_A; s->init_num=0; - + /* If we're just listening, stop here */ if (s->d1->listen && s->state == SSL3_ST_SW_SRVR_HELLO_A) { @@ -298,6 +314,43 @@ ssl3_init_finished_mac(s); break; +#ifdef OPENSSL_SCTP + case DTLS1_SCTP_ST_SR_READ_SOCK: + + if (BIO_dgram_sctp_msg_waiting(SSL_get_rbio(s))) + { + s->s3->in_read_app_data=2; + s->rwstate=SSL_READING; + BIO_clear_retry_flags(SSL_get_rbio(s)); + BIO_set_retry_read(SSL_get_rbio(s)); + ret = -1; + goto end; + } + + s->state=SSL3_ST_SR_FINISHED_A; + break; + + case DTLS1_SCTP_ST_SW_WRITE_SOCK: + ret = BIO_dgram_sctp_wait_for_dry(SSL_get_wbio(s)); + if (ret < 0) goto end; + + if (ret == 0) + { + if (s->d1->next_state != SSL_ST_OK) + { + s->s3->in_read_app_data=2; + s->rwstate=SSL_READING; + BIO_clear_retry_flags(SSL_get_rbio(s)); + BIO_set_retry_read(SSL_get_rbio(s)); + ret = -1; + goto end; + } + } + + s->state=s->d1->next_state; + break; +#endif + case SSL3_ST_SW_SRVR_HELLO_A: case SSL3_ST_SW_SRVR_HELLO_B: s->renegotiate = 2; @@ -305,18 +358,31 @@ ret=dtls1_send_server_hello(s); if (ret <= 0) goto end; -#ifndef OPENSSL_NO_TLSEXT if (s->hit) { +#ifdef OPENSSL_SCTP + /* Add new shared key for SCTP-Auth, + * will be ignored if no SCTP used. + */ + snprintf((char*) labelbuffer, 25, "EXTRACTOR_dtlssctpauthkey"); + snprintf((char*) contextbuffer, 8, "dtlssctp"); + + SSL_tls1_key_extractor(s, labelbuffer, sizeof(labelbuffer), + contextbuffer, sizeof(contextbuffer), + sctpauthkey, sizeof(sctpauthkey)); + + BIO_ctrl(SSL_get_wbio(s), BIO_CTRL_DGRAM_SCTP_ADD_AUTH_KEY, + sizeof(sctpauthkey), sctpauthkey); +#endif +#ifndef OPENSSL_NO_TLSEXT if (s->tlsext_ticket_expected) s->state=SSL3_ST_SW_SESSION_TICKET_A; else s->state=SSL3_ST_SW_CHANGE_A; - } #else - if (s->hit) - s->state=SSL3_ST_SW_CHANGE_A; + s->state=SSL3_ST_SW_CHANGE_A; #endif + } else s->state=SSL3_ST_SW_CERT_A; s->init_num=0; @@ -426,6 +492,13 @@ skip=1; s->s3->tmp.cert_request=0; s->state=SSL3_ST_SW_SRVR_DONE_A; +#ifdef OPENSSL_SCTP + if (SSL_get_wbio(s)->method->type == BIO_TYPE_DGRAM_SCTP) + { + s->d1->next_state = SSL3_ST_SW_SRVR_DONE_A; + s->state = DTLS1_SCTP_ST_SW_WRITE_SOCK; + } +#endif } else { @@ -435,9 +508,23 @@ if (ret <= 0) goto end; #ifndef NETSCAPE_HANG_BUG s->state=SSL3_ST_SW_SRVR_DONE_A; +#ifdef OPENSSL_SCTP + if (SSL_get_wbio(s)->method->type == BIO_TYPE_DGRAM_SCTP) + { + s->d1->next_state = SSL3_ST_SW_SRVR_DONE_A; + s->state = DTLS1_SCTP_ST_SW_WRITE_SOCK; + } +#endif #else s->state=SSL3_ST_SW_FLUSH; s->s3->tmp.next_state=SSL3_ST_SR_CERT_A; +#ifdef OPENSSL_SCTP + if (SSL_get_wbio(s)->method->type == BIO_TYPE_DGRAM_SCTP) + { + s->d1->next_state = s->s3->tmp.next_state; + s->s3->tmp.next_state=DTLS1_SCTP_ST_SW_WRITE_SOCK; + } +#endif #endif s->init_num=0; } @@ -492,6 +579,21 @@ ret=ssl3_get_client_key_exchange(s); if (ret <= 0) goto end; dtls1_stop_timer(s); +#ifdef OPENSSL_SCTP + /* Add new shared key for SCTP-Auth, + * will be ignored if no SCTP used. + */ + snprintf((char *) labelbuffer, 25, "EXTRACTOR_dtlssctpauthkey"); + snprintf((char *) contextbuffer, 8, "dtlssctp"); + + SSL_tls1_key_extractor(s, labelbuffer, sizeof(labelbuffer), + contextbuffer, sizeof(contextbuffer), + sctpauthkey, sizeof(sctpauthkey)); + + BIO_ctrl(SSL_get_wbio(s), BIO_CTRL_DGRAM_SCTP_ADD_AUTH_KEY, + sizeof(sctpauthkey), sctpauthkey); +#endif + s->state=SSL3_ST_SR_CERT_VRFY_A; s->init_num=0; @@ -529,8 +631,13 @@ ret=ssl3_get_cert_verify(s); if (ret <= 0) goto end; dtls1_stop_timer(s); - - s->state=SSL3_ST_SR_FINISHED_A; +#ifdef OPENSSL_SCTP + if (SSL_get_wbio(s)->method->type == BIO_TYPE_DGRAM_SCTP && + state == SSL_ST_RENEGOTIATE) + s->state=DTLS1_SCTP_ST_SR_READ_SOCK; + else +#endif + s->state=SSL3_ST_SR_FINISHED_A; s->init_num=0; break; @@ -582,6 +689,14 @@ SSL3_ST_SW_CHANGE_A,SSL3_ST_SW_CHANGE_B); if (ret <= 0) goto end; + +#ifdef OPENSSL_SCTP + /* Change to new shared key of SCTP-Auth, + * will be ignored if no SCTP used. + */ + BIO_ctrl(SSL_get_wbio(s), BIO_CTRL_DGRAM_SCTP_NEXT_AUTH_KEY, 0, NULL); +#endif + s->state=SSL3_ST_SW_FINISHED_A; s->init_num=0; @@ -606,7 +721,16 @@ if (s->hit) s->s3->tmp.next_state=SSL3_ST_SR_FINISHED_A; else + { s->s3->tmp.next_state=SSL_ST_OK; +#ifdef OPENSSL_SCTP + if (SSL_get_wbio(s)->method->type == BIO_TYPE_DGRAM_SCTP) + { + s->d1->next_state = s->s3->tmp.next_state; + s->s3->tmp.next_state=DTLS1_SCTP_ST_SW_WRITE_SOCK; + } +#endif + } s->init_num=0; break; @@ -680,6 +804,14 @@ /* BIO_flush(s->wbio); */ s->in_handshake--; +#ifdef OPENSSL_SCTP + /* Notify SCTP BIO socket to leave handshake + * mode and prevent stream identifier other + * than 0. Will be ignored if no SCTP is used. + */ + BIO_ctrl(SSL_get_wbio(s), BIO_CTRL_DGRAM_SCTP_SET_IN_HANDSHAKE, s->in_handshake, NULL); +#endif + if (cb != NULL) cb(s,SSL_CB_ACCEPT_EXIT,ret); return(ret); --- ssl/dtls1.h 2009-09-09 19:05:42.000000000 +0200 +++ ssl/dtls1.h 2010-01-21 17:11:28.000000000 +0100 @@ -74,6 +74,9 @@ #else #include #endif +#ifdef OPENSSL_SCTP +#include +#endif #ifdef __cplusplus extern "C" { @@ -241,6 +244,13 @@ unsigned int retransmitting; unsigned int change_cipher_spec_ok; + +#ifdef OPENSSL_SCTP + /* used when SSL_ST_XX_FLUSH is entered */ + int next_state; + + int shutdown_received; +#endif } DTLS1_STATE; @@ -250,6 +260,9 @@ unsigned int packet_length; SSL3_BUFFER rbuf; SSL3_RECORD rrec; +#ifdef OPENSSL_SCTP + struct sctp_sndrcvinfo recordinfo; +#endif } DTLS1_RECORD_DATA; --- ssl/ssl3.h 2010-01-06 18:37:38.000000000 +0100 +++ ssl/ssl3.h 2010-01-21 17:11:28.000000000 +0100 @@ -519,6 +519,10 @@ /*client */ /* extra state */ #define SSL3_ST_CW_FLUSH (0x100|SSL_ST_CONNECT) +#ifdef OPENSSL_SCTP +#define DTLS1_SCTP_ST_CW_WRITE_SOCK (0x310|SSL_ST_CONNECT) +#define DTLS1_SCTP_ST_CR_READ_SOCK (0x320|SSL_ST_CONNECT) +#endif /* write to server */ #define SSL3_ST_CW_CLNT_HELLO_A (0x110|SSL_ST_CONNECT) #define SSL3_ST_CW_CLNT_HELLO_B (0x111|SSL_ST_CONNECT) @@ -561,6 +565,10 @@ /* server */ /* extra state */ #define SSL3_ST_SW_FLUSH (0x100|SSL_ST_ACCEPT) +#ifdef OPENSSL_SCTP +#define DTLS1_SCTP_ST_SW_WRITE_SOCK (0x310|SSL_ST_ACCEPT) +#define DTLS1_SCTP_ST_SR_READ_SOCK (0x320|SSL_ST_ACCEPT) +#endif /* read from client */ /* Do not change the number values, they do matter */ #define SSL3_ST_SR_CLNT_HELLO_A (0x110|SSL_ST_ACCEPT) --- ssl/ssl_locl.h 2009-12-08 12:38:18.000000000 +0100 +++ ssl/ssl_locl.h 2010-01-21 17:11:28.000000000 +0100 @@ -752,7 +752,7 @@ ssl3_read, \ ssl3_peek, \ ssl3_write, \ - ssl3_shutdown, \ + dtls1_shutdown, \ ssl3_renegotiate, \ ssl3_renegotiate_check, \ dtls1_get_message, \ @@ -1012,6 +1012,7 @@ void dtls1_free(SSL *s); void dtls1_clear(SSL *s); long dtls1_ctrl(SSL *s,int cmd, long larg, void *parg); +int dtls1_shutdown(SSL *s); long dtls1_get_message(SSL *s, int st1, int stn, int mt, long max, int *ok); int dtls1_get_record(SSL *s);