From 8a2ce3a31365f6179f126912a4df85d07a59ca85 Mon Sep 17 00:00:00 2001 From: Richard Levitte Date: Thu, 18 Aug 2016 15:42:42 +0200 Subject: VMS: synchronise tests with Unix Reviewed-by: Rich Salz --- test/maketests.com | 9 +++++++-- test/tests.com | 24 +++++++++++++++++++++--- 2 files changed, 28 insertions(+), 5 deletions(-) (limited to 'test') diff --git a/test/maketests.com b/test/maketests.com index 7e2d939bbe..3d16443ba7 100644 --- a/test/maketests.com +++ b/test/maketests.com @@ -147,9 +147,10 @@ $ TEST_FILES = "BNTEST,ECTEST,ECDSATEST,ECDHTEST,IDEATEST,"+ - "MDC2TEST,RMDTEST,"+ - "RANDTEST,DHTEST,ENGINETEST,"+ - "BFTEST,CASTTEST,SSLTEST,EXPTEST,DSATEST,RSA_TEST,"+ - - "EVP_TEST,IGETEST,JPAKETEST,SRPTEST,"+ - + "EVP_TEST,EVP_EXTRA_TEST,IGETEST,JPAKETEST,SRPTEST,"+ - "ASN1TEST,V3NAMETEST,HEARTBEAT_TEST,"+ - - "CONSTANT_TIME_TEST" + "CONSTANT_TIME_TEST,VERIFY_EXTRA_TEST,"+ - + "CLIENTHELLOTEST,SSLV2CONFTEST" $! Should we add MTTEST,PQ_TEST,LH_TEST,DIVTEST,TABTEST as well? $! $! Additional directory information. @@ -183,6 +184,7 @@ $ T_D_EXPTEST := [-.crypto.bn] $ T_D_DSATEST := [-.crypto.dsa] $ T_D_RSA_TEST := [-.crypto.rsa] $ T_D_EVP_TEST := [-.crypto.evp] +$ T_D_EVP_EXTRA_TEST := [-.crypto.evp] $ T_D_IGETEST := [-.test] $ T_D_JPAKETEST := [-.crypto.jpake] $ T_D_SRPTEST := [-.crypto.srp] @@ -190,6 +192,9 @@ $ T_D_V3NAMETEST := [-.crypto.x509v3] $ T_D_ASN1TEST := [-.test] $ T_D_HEARTBEAT_TEST := [-.ssl] $ T_D_CONSTANT_TIME_TEST := [-.crypto] +$ T_D_VERIFY_EXTRA_TEST := [-.crypto.x509] +$ T_D_CLIENTHELLOTEST := [-.ssl] +$ T_D_SSLV2CONFTEST := [-.ssl] $! $ TCPIP_PROGRAMS = ",," $ IF COMPILER .EQS. "VAXC" THEN - diff --git a/test/tests.com b/test/tests.com index 6e1c818e7a..9361659982 100644 --- a/test/tests.com +++ b/test/tests.com @@ -55,9 +55,9 @@ $ tests := - test_rand,test_bn,test_ec,test_ecdsa,test_ecdh,- test_enc,test_x509,test_rsa,test_crl,test_sid,- test_gen,test_req,test_pkcs7,test_verify,test_dh,test_dsa,- - test_ss,test_ca,test_engine,test_evp,test_ssl,test_tsa,test_ige,- + test_ss,test_ca,test_engine,test_evp,test_evp_extra,test_ssl,test_tsa,test_ige,- test_jpake,test_srp,test_cms,test_ocsp,test_v3name,test_heartbeat,- - test_constant_time + test_constant_time,test_verify_extra,test_clienthello,test_sslv2conftest $ endif $ tests = f$edit(tests,"COLLAPSE") $ @@ -92,6 +92,7 @@ $ SSLTEST := ssltest $ RSATEST := rsa_test $ ENGINETEST := enginetest $ EVPTEST := evp_test +$ EVPEXTRATEST := evp_extra_test $ IGETEST := igetest $ JPAKETEST := jpaketest $ SRPTEST := srptest @@ -99,6 +100,9 @@ $ V3NAMETEST := v3nametest $ ASN1TEST := asn1test $ HEARTBEATTEST := heartbeat_test $ CONSTTIMETEST := constant_time_test +$ VERIFYEXTRATEST := verify_extra_test +$ CLIENTHELLOTEST := clienthellotest +$ SSLV2CONFTEST := sslv2conftest $! $ tests_i = 0 $ loop_tests: @@ -112,6 +116,9 @@ $ $ test_evp: $ mcr 'texe_dir''evptest' 'ROOT'.CRYPTO.EVP]evptests.txt $ return +$ test_evp_extra: +$ mcr 'texe_dir''evpextratest' +$ return $ test_des: $ mcr 'texe_dir''destest' $ return @@ -386,7 +393,18 @@ $ test_constant_time: $ write sys$output "Test constant time utilities" $ mcr 'texe_dir''consttimetest' $ return -$ +$ test_verify_extra: +$ write sys$output "''START' test_verify_extra" +$ mcr 'texe_dir''verifyextratest' +$ return +$ test_clienthello: +$ write sys$output "''START' test_clienthello" +$ mcr 'texe_dir''clienthellotest' +$ return +$ test_sslv2conftest: +$ write sys$output "''START' test_sslv2conftest" +$ mcr 'texe_dir''sslv2conftest' +$ return $ $ exit: $ on error then goto exit2 ! In case openssl.exe didn't build. -- cgit v1.2.3 From 48e8df6e399ec1bef53500457f16b54d798198d3 Mon Sep 17 00:00:00 2001 From: Matt Caswell Date: Tue, 5 Jul 2016 11:36:10 +0100 Subject: Back port ssltestlib code to 1.0.2 Enables the testing of DTLS code in 1.0.2 Reviewed-by: Richard Levitte --- .gitignore | 1 + test/ssltestlib.c | 687 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ test/ssltestlib.h | 36 +++ 3 files changed, 724 insertions(+) create mode 100644 test/ssltestlib.c create mode 100644 test/ssltestlib.h (limited to 'test') diff --git a/.gitignore b/.gitignore index 36c3a37507..cf8877916f 100644 --- a/.gitignore +++ b/.gitignore @@ -31,6 +31,7 @@ !/test/igetest.c !/test/r160test.c !/test/fips_algvs.c +!/test/ssltestlib.c /test/*.ss /test/*.srl diff --git a/test/ssltestlib.c b/test/ssltestlib.c new file mode 100644 index 0000000000..1592514a38 --- /dev/null +++ b/test/ssltestlib.c @@ -0,0 +1,687 @@ +/* + * Copyright 2016 The OpenSSL Project Authors. All Rights Reserved. + * + * Licensed under the OpenSSL license (the "License"). You may not use + * this file except in compliance with the License. You can obtain a copy + * in the file LICENSE in the source distribution or at + * https://www.openssl.org/source/license.html + */ + +#include +#include + +#include "ssltestlib.h" + +#define SSL_IS_DTLS(s) (s->method->version == DTLS_ANY_VERSION \ + || s->method->version == DTLS1_2_VERSION \ + || s->method->version == DTLS1_VERSION) + +static int tls_dump_new(BIO *bi); +static int tls_dump_free(BIO *a); +static int tls_dump_read(BIO *b, char *out, int outl); +static int tls_dump_write(BIO *b, const char *in, int inl); +static long tls_dump_ctrl(BIO *b, int cmd, long num, void *ptr); +static int tls_dump_gets(BIO *bp, char *buf, int size); +static int tls_dump_puts(BIO *bp, const char *str); + +/* Choose a sufficiently large type likely to be unused for this custom BIO */ +# define BIO_TYPE_TLS_DUMP_FILTER (0x80 | BIO_TYPE_FILTER) + +# define BIO_TYPE_MEMPACKET_TEST 0x81 + +static BIO_METHOD method_tls_dump = { + BIO_TYPE_TLS_DUMP_FILTER, + "TLS dump filter", + tls_dump_write, + tls_dump_read, + tls_dump_puts, + tls_dump_gets, + tls_dump_ctrl, + tls_dump_new, + tls_dump_free +}; + +BIO_METHOD *bio_f_tls_dump_filter(void) +{ + return &method_tls_dump; +} + +static int tls_dump_new(BIO *bio) +{ + bio->init = 1; + return 1; +} + +static int tls_dump_free(BIO *bio) +{ + bio->init = 0; + + return 1; +} + +static void copy_flags(BIO *bio) +{ + int flags; + BIO *next = BIO_next(bio); + + flags = BIO_test_flags(next, BIO_FLAGS_SHOULD_RETRY | BIO_FLAGS_RWS); + BIO_clear_flags(bio, BIO_FLAGS_SHOULD_RETRY | BIO_FLAGS_RWS); + BIO_set_flags(bio, flags); +} + +#define RECORD_CONTENT_TYPE 0 +#define RECORD_VERSION_HI 1 +#define RECORD_VERSION_LO 2 +#define RECORD_EPOCH_HI 3 +#define RECORD_EPOCH_LO 4 +#define RECORD_SEQUENCE_START 5 +#define RECORD_SEQUENCE_END 10 +#define RECORD_LEN_HI 11 +#define RECORD_LEN_LO 12 + +#define MSG_TYPE 0 +#define MSG_LEN_HI 1 +#define MSG_LEN_MID 2 +#define MSG_LEN_LO 3 +#define MSG_SEQ_HI 4 +#define MSG_SEQ_LO 5 +#define MSG_FRAG_OFF_HI 6 +#define MSG_FRAG_OFF_MID 7 +#define MSG_FRAG_OFF_LO 8 +#define MSG_FRAG_LEN_HI 9 +#define MSG_FRAG_LEN_MID 10 +#define MSG_FRAG_LEN_LO 11 + + +static void dump_data(const char *data, int len) +{ + int rem, i, content, reclen, msglen, fragoff, fraglen, epoch; + unsigned char *rec; + + printf("---- START OF PACKET ----\n"); + + rem = len; + rec = (unsigned char *)data; + + while (rem > 0) { + if (rem != len) + printf("*\n"); + printf("*---- START OF RECORD ----\n"); + if (rem < DTLS1_RT_HEADER_LENGTH) { + printf("*---- RECORD TRUNCATED ----\n"); + break; + } + content = rec[RECORD_CONTENT_TYPE]; + printf("** Record Content-type: %d\n", content); + printf("** Record Version: %02x%02x\n", + rec[RECORD_VERSION_HI], rec[RECORD_VERSION_LO]); + epoch = (rec[RECORD_EPOCH_HI] << 8) | rec[RECORD_EPOCH_LO]; + printf("** Record Epoch: %d\n", epoch); + printf("** Record Sequence: "); + for (i = RECORD_SEQUENCE_START; i <= RECORD_SEQUENCE_END; i++) + printf("%02x", rec[i]); + reclen = (rec[RECORD_LEN_HI] << 8) | rec[RECORD_LEN_LO]; + printf("\n** Record Length: %d\n", reclen); + + /* Now look at message */ + rec += DTLS1_RT_HEADER_LENGTH; + rem -= DTLS1_RT_HEADER_LENGTH; + if (content == SSL3_RT_HANDSHAKE) { + printf("**---- START OF HANDSHAKE MESSAGE FRAGMENT ----\n"); + if (epoch > 0) { + printf("**---- HANDSHAKE MESSAGE FRAGMENT ENCRYPTED ----\n"); + } else if (rem < DTLS1_HM_HEADER_LENGTH + || reclen < DTLS1_HM_HEADER_LENGTH) { + printf("**---- HANDSHAKE MESSAGE FRAGMENT TRUNCATED ----\n"); + } else { + printf("*** Message Type: %d\n", rec[MSG_TYPE]); + msglen = (rec[MSG_LEN_HI] << 16) | (rec[MSG_LEN_MID] << 8) + | rec[MSG_LEN_LO]; + printf("*** Message Length: %d\n", msglen); + printf("*** Message sequence: %d\n", + (rec[MSG_SEQ_HI] << 8) | rec[MSG_SEQ_LO]); + fragoff = (rec[MSG_FRAG_OFF_HI] << 16) + | (rec[MSG_FRAG_OFF_MID] << 8) + | rec[MSG_FRAG_OFF_LO]; + printf("*** Message Fragment offset: %d\n", fragoff); + fraglen = (rec[MSG_FRAG_LEN_HI] << 16) + | (rec[MSG_FRAG_LEN_MID] << 8) + | rec[MSG_FRAG_LEN_LO]; + printf("*** Message Fragment len: %d\n", fraglen); + if (fragoff + fraglen > msglen) + printf("***---- HANDSHAKE MESSAGE FRAGMENT INVALID ----\n"); + else if(reclen < fraglen) + printf("**---- HANDSHAKE MESSAGE FRAGMENT TRUNCATED ----\n"); + else + printf("**---- END OF HANDSHAKE MESSAGE FRAGMENT ----\n"); + } + } + if (rem < reclen) { + printf("*---- RECORD TRUNCATED ----\n"); + rem = 0; + } else { + rec += reclen; + rem -= reclen; + printf("*---- END OF RECORD ----\n"); + } + } + printf("---- END OF PACKET ----\n\n"); + fflush(stdout); +} + +static int tls_dump_read(BIO *bio, char *out, int outl) +{ + int ret; + BIO *next = BIO_next(bio); + + ret = BIO_read(next, out, outl); + copy_flags(bio); + + if (ret > 0) { + dump_data(out, ret); + } + + return ret; +} + +static int tls_dump_write(BIO *bio, const char *in, int inl) +{ + int ret; + BIO *next = BIO_next(bio); + + ret = BIO_write(next, in, inl); + copy_flags(bio); + + return ret; +} + +static long tls_dump_ctrl(BIO *bio, int cmd, long num, void *ptr) +{ + long ret; + BIO *next = BIO_next(bio); + + if (next == NULL) + return 0; + + switch (cmd) { + case BIO_CTRL_DUP: + ret = 0L; + break; + default: + ret = BIO_ctrl(next, cmd, num, ptr); + break; + } + return ret; +} + +static int tls_dump_gets(BIO *bio, char *buf, int size) +{ + /* We don't support this - not needed anyway */ + return -1; +} + +static int tls_dump_puts(BIO *bio, const char *str) +{ + return tls_dump_write(bio, str, strlen(str)); +} + + +typedef struct mempacket_st { + unsigned char *data; + int len; + unsigned int num; + unsigned int type; +} MEMPACKET; + +/* + * These defines would normally be auto-generated and in safestack.h...but this + * is just for tests so its probably not an appropriate place + */ +# define sk_MEMPACKET_new(cmp) SKM_sk_new(MEMPACKET, (cmp)) +# define sk_MEMPACKET_new_null() SKM_sk_new_null(MEMPACKET) +# define sk_MEMPACKET_free(st) SKM_sk_free(MEMPACKET, (st)) +# define sk_MEMPACKET_num(st) SKM_sk_num(MEMPACKET, (st)) +# define sk_MEMPACKET_value(st, i) SKM_sk_value(MEMPACKET, (st), (i)) +# define sk_MEMPACKET_set(st, i, val) SKM_sk_set(MEMPACKET, (st), (i), (val)) +# define sk_MEMPACKET_zero(st) SKM_sk_zero(MEMPACKET, (st)) +# define sk_MEMPACKET_push(st, val) SKM_sk_push(MEMPACKET, (st), (val)) +# define sk_MEMPACKET_unshift(st, val) SKM_sk_unshift(MEMPACKET, (st), (val)) +# define sk_MEMPACKET_find(st, val) SKM_sk_find(MEMPACKET, (st), (val)) +# define sk_MEMPACKET_find_ex(st, val) SKM_sk_find_ex(MEMPACKET, (st), (val)) +# define sk_MEMPACKET_delete(st, i) SKM_sk_delete(MEMPACKET, (st), (i)) +# define sk_MEMPACKET_delete_ptr(st, ptr) SKM_sk_delete_ptr(MEMPACKET, (st), (ptr)) +# define sk_MEMPACKET_insert(st, val, i) SKM_sk_insert(MEMPACKET, (st), (val), (i)) +# define sk_MEMPACKET_set_cmp_func(st, cmp) SKM_sk_set_cmp_func(MEMPACKET, (st), (cmp)) +# define sk_MEMPACKET_dup(st) SKM_sk_dup(MEMPACKET, st) +# define sk_MEMPACKET_pop_free(st, free_func) SKM_sk_pop_free(MEMPACKET, (st), (free_func)) +# define sk_MEMPACKET_deep_copy(st, copy_func, free_func) SKM_sk_deep_copy(MEMPACKET, (st), (copy_func), (free_func)) +# define sk_MEMPACKET_shift(st) SKM_sk_shift(MEMPACKET, (st)) +# define sk_MEMPACKET_pop(st) SKM_sk_pop(MEMPACKET, (st)) +# define sk_MEMPACKET_sort(st) SKM_sk_sort(MEMPACKET, (st)) +# define sk_MEMPACKET_is_sorted(st) SKM_sk_is_sorted(MEMPACKET, (st)) + +static void mempacket_free(MEMPACKET *pkt) +{ + if (pkt->data != NULL) + OPENSSL_free(pkt->data); + OPENSSL_free(pkt); +} + +typedef struct mempacket_test_ctx_st { + STACK_OF(MEMPACKET) *pkts; + unsigned int epoch; + unsigned int currrec; + unsigned int currpkt; + unsigned int lastpkt; + unsigned int noinject; +} MEMPACKET_TEST_CTX; + +static int mempacket_test_new(BIO *bi); +static int mempacket_test_free(BIO *a); +static int mempacket_test_read(BIO *b, char *out, int outl); +static int mempacket_test_write(BIO *b, const char *in, int inl); +static long mempacket_test_ctrl(BIO *b, int cmd, long num, void *ptr); +static int mempacket_test_gets(BIO *bp, char *buf, int size); +static int mempacket_test_puts(BIO *bp, const char *str); + +static BIO_METHOD method_mempacket_test = { + BIO_TYPE_MEMPACKET_TEST, + "Mem Packet Test", + mempacket_test_write, + mempacket_test_read, + mempacket_test_puts, + mempacket_test_gets, + mempacket_test_ctrl, + mempacket_test_new, + mempacket_test_free +}; + +BIO_METHOD *bio_s_mempacket_test(void) +{ + return &method_mempacket_test; +} + +static int mempacket_test_new(BIO *bio) +{ + MEMPACKET_TEST_CTX *ctx = OPENSSL_malloc(sizeof(*ctx)); + if (ctx == NULL) + return 0; + memset(ctx, 0, sizeof(*ctx)); + + ctx->pkts = sk_MEMPACKET_new_null(); + if (ctx->pkts == NULL) { + OPENSSL_free(ctx); + return 0; + } + bio->init = 1; + bio->ptr = ctx; + return 1; +} + +static int mempacket_test_free(BIO *bio) +{ + MEMPACKET_TEST_CTX *ctx = bio->ptr; + + sk_MEMPACKET_pop_free(ctx->pkts, mempacket_free); + OPENSSL_free(ctx); + bio->ptr = NULL; + bio->init = 0; + + return 1; +} + +/* Record Header values */ +#define EPOCH_HI 4 +#define EPOCH_LO 5 +#define RECORD_SEQUENCE 10 +#define RECORD_LEN_HI 11 +#define RECORD_LEN_LO 12 + +#define STANDARD_PACKET 0 + +static int mempacket_test_read(BIO *bio, char *out, int outl) +{ + MEMPACKET_TEST_CTX *ctx = bio->ptr; + MEMPACKET *thispkt; + unsigned char *rec; + int rem; + unsigned int seq, offset, len, epoch; + + BIO_clear_retry_flags(bio); + + thispkt = sk_MEMPACKET_value(ctx->pkts, 0); + if (thispkt == NULL || thispkt->num != ctx->currpkt) { + /* Probably run out of data */ + BIO_set_retry_read(bio); + return -1; + } + sk_MEMPACKET_shift(ctx->pkts); + ctx->currpkt++; + + if (outl > thispkt->len) + outl = thispkt->len; + + if (thispkt->type != INJECT_PACKET_IGNORE_REC_SEQ) { + /* + * Overwrite the record sequence number. We strictly number them in + * the order received. Since we are actually a reliable transport + * we know that there won't be any re-ordering. We overwrite to deal + * with any packets that have been injected + */ + rem = thispkt->len; + rec = thispkt->data; + while (rem > 0) { + if (rem < DTLS1_RT_HEADER_LENGTH) { + return -1; + } + epoch = (rec[EPOCH_HI] << 8) | rec[EPOCH_LO]; + if (epoch != ctx->epoch) { + ctx->epoch = epoch; + ctx->currrec = 0; + } + seq = ctx->currrec; + offset = 0; + do { + rec[RECORD_SEQUENCE - offset] = seq & 0xFF; + seq >>= 8; + offset++; + } while (seq > 0); + ctx->currrec++; + + len = ((rec[RECORD_LEN_HI] << 8) | rec[RECORD_LEN_LO]) + + DTLS1_RT_HEADER_LENGTH; + + rec += len; + rem -= len; + } + } + + memcpy(out, thispkt->data, outl); + + mempacket_free(thispkt); + + return outl; +} + +int mempacket_test_inject(BIO *bio, const char *in, int inl, int pktnum, + int type) +{ + MEMPACKET_TEST_CTX *ctx = bio->ptr; + MEMPACKET *thispkt, *looppkt, *nextpkt; + int i; + + if (ctx == NULL) + return -1; + + /* We only allow injection before we've started writing any data */ + if (pktnum >= 0) { + if (ctx->noinject) + return -1; + } else { + ctx->noinject = 1; + } + + thispkt = OPENSSL_malloc(sizeof(MEMPACKET)); + if (thispkt == NULL) + return -1; + + thispkt->data = OPENSSL_malloc(inl); + if (thispkt->data == NULL) { + mempacket_free(thispkt); + return -1; + } + + memcpy(thispkt->data, in, inl); + thispkt->len = inl; + thispkt->num = (pktnum >= 0) ? (unsigned int)pktnum : ctx->lastpkt; + thispkt->type = type; + + for(i = 0; (looppkt = sk_MEMPACKET_value(ctx->pkts, i)) != NULL; i++) { + /* Check if we found the right place to insert this packet */ + if (looppkt->num > thispkt->num) { + if (sk_MEMPACKET_insert(ctx->pkts, thispkt, i) == 0) { + mempacket_free(thispkt); + return -1; + } + /* If we're doing up front injection then we're done */ + if (pktnum >= 0) + return inl; + /* + * We need to do some accounting on lastpkt. We increment it first, + * but it might now equal the value of injected packets, so we need + * to skip over those + */ + ctx->lastpkt++; + do { + i++; + nextpkt = sk_MEMPACKET_value(ctx->pkts, i); + if (nextpkt != NULL && nextpkt->num == ctx->lastpkt) + ctx->lastpkt++; + else + return inl; + } while(1); + } else if(looppkt->num == thispkt->num) { + if (!ctx->noinject) { + /* We injected two packets with the same packet number! */ + return -1; + } + ctx->lastpkt++; + thispkt->num++; + } + } + /* + * We didn't find any packets with a packet number equal to or greater than + * this one, so we just add it onto the end + */ + if (!sk_MEMPACKET_push(ctx->pkts, thispkt)) { + mempacket_free(thispkt); + return -1; + } + + if (pktnum < 0) + ctx->lastpkt++; + + return inl; +} + +static int mempacket_test_write(BIO *bio, const char *in, int inl) +{ + return mempacket_test_inject(bio, in, inl, -1, STANDARD_PACKET); +} + +static long mempacket_test_ctrl(BIO *bio, int cmd, long num, void *ptr) +{ + long ret = 1; + MEMPACKET_TEST_CTX *ctx = bio->ptr; + MEMPACKET *thispkt; + + switch (cmd) { + case BIO_CTRL_EOF: + ret = (long)(sk_MEMPACKET_num(ctx->pkts) == 0); + break; + case BIO_CTRL_GET_CLOSE: + ret = bio->shutdown; + break; + case BIO_CTRL_SET_CLOSE: + bio->shutdown = (int)num; + break; + case BIO_CTRL_WPENDING: + ret = 0L; + break; + case BIO_CTRL_PENDING: + thispkt = sk_MEMPACKET_value(ctx->pkts, 0); + if (thispkt == NULL) + ret = 0; + else + ret = thispkt->len; + break; + case BIO_CTRL_FLUSH: + ret = 1; + break; + case BIO_CTRL_RESET: + case BIO_CTRL_DUP: + case BIO_CTRL_PUSH: + case BIO_CTRL_POP: + default: + ret = 0; + break; + } + return ret; +} + +static int mempacket_test_gets(BIO *bio, char *buf, int size) +{ + /* We don't support this - not needed anyway */ + return -1; +} + +static int mempacket_test_puts(BIO *bio, const char *str) +{ + return mempacket_test_write(bio, str, strlen(str)); +} + +int create_ssl_ctx_pair(const SSL_METHOD *sm, const SSL_METHOD *cm, + SSL_CTX **sctx, SSL_CTX **cctx, char *certfile, + char *privkeyfile) +{ + SSL_CTX *serverctx = NULL; + SSL_CTX *clientctx = NULL; + + serverctx = SSL_CTX_new(sm); + clientctx = SSL_CTX_new(cm); + if (serverctx == NULL || clientctx == NULL) { + printf("Failed to create SSL_CTX\n"); + goto err; + } + + if (SSL_CTX_use_certificate_file(serverctx, certfile, + SSL_FILETYPE_PEM) <= 0) { + printf("Failed to load server certificate\n"); + goto err; + } + if (SSL_CTX_use_PrivateKey_file(serverctx, privkeyfile, + SSL_FILETYPE_PEM) <= 0) { + printf("Failed to load server private key\n"); + } + if (SSL_CTX_check_private_key(serverctx) <= 0) { + printf("Failed to check private key\n"); + goto err; + } + + *sctx = serverctx; + *cctx = clientctx; + + return 1; + err: + SSL_CTX_free(serverctx); + SSL_CTX_free(clientctx); + return 0; +} + +#define MAXLOOPS 100000 + +/* + * NOTE: Transfers control of the BIOs - this function will free them on error + */ +int create_ssl_objects(SSL_CTX *serverctx, SSL_CTX *clientctx, SSL **sssl, + SSL **cssl, BIO *s_to_c_fbio, BIO *c_to_s_fbio) +{ + SSL *serverssl, *clientssl; + BIO *s_to_c_bio = NULL, *c_to_s_bio = NULL; + + serverssl = SSL_new(serverctx); + clientssl = SSL_new(clientctx); + + if (serverssl == NULL || clientssl == NULL) { + printf("Failed to create SSL object\n"); + goto error; + } + + if (SSL_IS_DTLS(clientssl)) { + s_to_c_bio = BIO_new(bio_s_mempacket_test()); + c_to_s_bio = BIO_new(bio_s_mempacket_test());; + } else { + s_to_c_bio = BIO_new(BIO_s_mem()); + c_to_s_bio = BIO_new(BIO_s_mem()); + } + if (s_to_c_bio == NULL || c_to_s_bio == NULL) { + printf("Failed to create mem BIOs\n"); + goto error; + } + + if (s_to_c_fbio != NULL) + s_to_c_bio = BIO_push(s_to_c_fbio, s_to_c_bio); + if (c_to_s_fbio != NULL) + c_to_s_bio = BIO_push(c_to_s_fbio, c_to_s_bio); + if (s_to_c_bio == NULL || c_to_s_bio == NULL) { + printf("Failed to create chained BIOs\n"); + goto error; + } + + /* Set Non-blocking IO behaviour */ + BIO_set_mem_eof_return(s_to_c_bio, -1); + BIO_set_mem_eof_return(c_to_s_bio, -1); + + /* Up ref these as we are passing them to two SSL objects */ + CRYPTO_add(&s_to_c_bio->references, 1, CRYPTO_LOCK_BIO); + CRYPTO_add(&c_to_s_bio->references, 1, CRYPTO_LOCK_BIO); + + SSL_set_bio(serverssl, c_to_s_bio, s_to_c_bio); + SSL_set_bio(clientssl, s_to_c_bio, c_to_s_bio); + + /* BIOs will now be freed when SSL objects are freed */ + s_to_c_bio = c_to_s_bio = NULL; + s_to_c_fbio = c_to_s_fbio = NULL; + + *sssl = serverssl; + *cssl = clientssl; + + return 1; + + error: + SSL_free(serverssl); + SSL_free(clientssl); + BIO_free(s_to_c_bio); + BIO_free(c_to_s_bio); + BIO_free(s_to_c_fbio); + BIO_free(c_to_s_fbio); + + return 0; +} + +int create_ssl_connection(SSL *serverssl, SSL *clientssl) +{ + int retc = -1, rets = -1, err, abortctr = 0; + + do { + err = SSL_ERROR_WANT_WRITE; + while (retc <= 0 && err == SSL_ERROR_WANT_WRITE) { + retc = SSL_connect(clientssl); + if (retc <= 0) + err = SSL_get_error(clientssl, retc); + } + + if (retc <= 0 && err != SSL_ERROR_WANT_READ) { + printf("SSL_connect() failed %d, %d\n", retc, err); + return 0; + } + + err = SSL_ERROR_WANT_WRITE; + while (rets <= 0 && err == SSL_ERROR_WANT_WRITE) { + rets = SSL_accept(serverssl); + if (rets <= 0) + err = SSL_get_error(serverssl, rets); + } + + if (rets <= 0 && err != SSL_ERROR_WANT_READ) { + printf("SSL_accept() failed %d, %d\n", retc, err); + return 0; + } + if (++abortctr == MAXLOOPS) { + printf("No progress made\n"); + return 0; + } + } while (retc <=0 || rets <= 0); + + return 1; +} diff --git a/test/ssltestlib.h b/test/ssltestlib.h new file mode 100644 index 0000000000..b23d8f4d10 --- /dev/null +++ b/test/ssltestlib.h @@ -0,0 +1,36 @@ +/* + * Copyright 2016 The OpenSSL Project Authors. All Rights Reserved. + * + * Licensed under the OpenSSL license (the "License"). You may not use + * this file except in compliance with the License. You can obtain a copy + * in the file LICENSE in the source distribution or at + * https://www.openssl.org/source/license.html + */ + +#ifndef HEADER_SSLTESTLIB_H +# define HEADER_SSLTESTLIB_H + +# include + +int create_ssl_ctx_pair(const SSL_METHOD *sm, const SSL_METHOD *cm, + SSL_CTX **sctx, SSL_CTX **cctx, char *certfile, + char *privkeyfile); +int create_ssl_objects(SSL_CTX *serverctx, SSL_CTX *clientctx, SSL **sssl, + SSL **cssl, BIO *s_to_c_fbio, BIO *c_to_s_fbio); +int create_ssl_connection(SSL *serverssl, SSL *clientssl); + +/* Note: Not thread safe! */ +BIO_METHOD *bio_f_tls_dump_filter(void); +void bio_f_tls_dump_filter_free(void); + +BIO_METHOD *bio_s_mempacket_test(void); +void bio_s_mempacket_test_free(void); + +/* Packet types - value 0 is reserved */ +#define INJECT_PACKET 1 +#define INJECT_PACKET_IGNORE_REC_SEQ 2 + +int mempacket_test_inject(BIO *bio, const char *in, int inl, int pktnum, + int type); + +#endif /* HEADER_SSLTESTLIB_H */ -- cgit v1.2.3 From bc4d7e1230bfb10cc3154b3d11ac2ff95f48bc94 Mon Sep 17 00:00:00 2001 From: Matt Caswell Date: Tue, 5 Jul 2016 11:37:40 +0100 Subject: Add a DTLS unprocesed records test Add a test to inject a record from the next epoch during the handshake and make sure it doesn't get processed immediately. Reviewed-by: Richard Levitte --- ssl/Makefile | 2 +- ssl/dtlstest.c | 135 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++ test/Makefile | 58 ++++++++++++++++++++++--- 3 files changed, 188 insertions(+), 7 deletions(-) create mode 100644 ssl/dtlstest.c (limited to 'test') diff --git a/ssl/Makefile b/ssl/Makefile index b6dee5b5ea..775d8ec980 100644 --- a/ssl/Makefile +++ b/ssl/Makefile @@ -15,7 +15,7 @@ KRB5_INCLUDES= CFLAGS= $(INCLUDES) $(CFLAG) GENERAL=Makefile README ssl-lib.com install.com -TEST=ssltest.c heartbeat_test.c clienthellotest.c sslv2conftest.c +TEST=ssltest.c heartbeat_test.c clienthellotest.c sslv2conftest.c dtlstest.c APPS= LIB=$(TOP)/libssl.a diff --git a/ssl/dtlstest.c b/ssl/dtlstest.c new file mode 100644 index 0000000000..148cc2e0fb --- /dev/null +++ b/ssl/dtlstest.c @@ -0,0 +1,135 @@ +/* + * Copyright 2016 The OpenSSL Project Authors. All Rights Reserved. + * + * Licensed under the OpenSSL license (the "License"). You may not use + * this file except in compliance with the License. You can obtain a copy + * in the file LICENSE in the source distribution or at + * https://www.openssl.org/source/license.html + */ + +#include +#include +#include +#include + +#include "ssltestlib.h" +#include "testutil.h" + +static char *cert = NULL; +static char *privkey = NULL; + + +#define DUMMY_CERT_STATUS_LEN 12 + +unsigned char certstatus[] = { + SSL3_RT_HANDSHAKE, /* Content type */ + 0xfe, 0xfd, /* Record version */ + 0, 1, /* Epoch */ + 0, 0, 0, 0, 0, 0x0f, /* Record sequence number */ + 0, DTLS1_HM_HEADER_LENGTH + DUMMY_CERT_STATUS_LEN - 2, + SSL3_MT_CERTIFICATE_STATUS, /* Cert Status handshake message type */ + 0, 0, DUMMY_CERT_STATUS_LEN, /* Message len */ + 0, 5, /* Message sequence */ + 0, 0, 0, /* Fragment offset */ + 0, 0, DUMMY_CERT_STATUS_LEN - 2, /* Fragment len */ + 0x80, 0x80, 0x80, 0x80, 0x80, + 0x80, 0x80, 0x80, 0x80, 0x80 /* Dummy data */ +}; + +static int test_dtls_unprocessed(void) +{ + SSL_CTX *sctx = NULL, *cctx = NULL; + SSL *serverssl1 = NULL, *clientssl1 = NULL; + BIO *c_to_s_fbio, *c_to_s_mempacket; + int testresult = 0; + + if (!create_ssl_ctx_pair(DTLS_server_method(), DTLS_client_method(), &sctx, + &cctx, cert, privkey)) { + printf("Unable to create SSL_CTX pair\n"); + return 0; + } + + if (!SSL_CTX_set_ecdh_auto(sctx, 1)) { + printf("Failed configuring auto ECDH\n"); + } + + if (!SSL_CTX_set_cipher_list(cctx, "ECDHE-RSA-AES256-SHA384")) { + printf("Failed setting cipher list\n"); + } + + c_to_s_fbio = BIO_new(bio_f_tls_dump_filter()); + if (c_to_s_fbio == NULL) { + printf("Failed to create filter BIO\n"); + goto end; + } + + /* BIO is freed by create_ssl_connection on error */ + if (!create_ssl_objects(sctx, cctx, &serverssl1, &clientssl1, NULL, + c_to_s_fbio)) { + printf("Unable to create SSL objects\n"); + ERR_print_errors_fp(stdout); + goto end; + } + + /* + * Inject a dummy record from the next epoch. This should never get used + * because the message sequence number is too big + */ + c_to_s_mempacket = SSL_get_wbio(clientssl1); + c_to_s_mempacket = BIO_next(c_to_s_mempacket); + mempacket_test_inject(c_to_s_mempacket, (char *)certstatus, + sizeof(certstatus), 1, INJECT_PACKET_IGNORE_REC_SEQ); + + if (!create_ssl_connection(serverssl1, clientssl1)) { + printf("Unable to create SSL connection\n"); + ERR_print_errors_fp(stdout); + goto end; + } + + testresult = 1; + end: + SSL_free(serverssl1); + SSL_free(clientssl1); + SSL_CTX_free(sctx); + SSL_CTX_free(cctx); + + return testresult; +} + +int main(int argc, char *argv[]) +{ + BIO *err = NULL; + int testresult = 0; + + if (argc != 3) { + printf("Invalid argument count\n"); + return 1; + } + + cert = argv[1]; + privkey = argv[2]; + + err = BIO_new_fp(stderr, BIO_NOCLOSE | BIO_FP_TEXT); + + SSL_library_init(); + SSL_load_error_strings(); + + CRYPTO_malloc_debug_init(); + CRYPTO_dbg_set_options(V_CRYPTO_MDEBUG_ALL); + CRYPTO_mem_ctrl(CRYPTO_MEM_CHECK_ON); + + if (!test_dtls_unprocessed()) + testresult = 1; + + ERR_free_strings(); + ERR_remove_thread_state(NULL); + EVP_cleanup(); + CRYPTO_cleanup_all_ex_data(); + CRYPTO_mem_leaks(err); + BIO_free(err); + + if (!testresult) + printf("PASS\n"); + + return testresult; +} diff --git a/test/Makefile b/test/Makefile index e566babfa5..a3a7483d9a 100644 --- a/test/Makefile +++ b/test/Makefile @@ -71,6 +71,7 @@ CONSTTIMETEST= constant_time_test VERIFYEXTRATEST= verify_extra_test CLIENTHELLOTEST= clienthellotest SSLV2CONFTEST = sslv2conftest +DTLSTEST = dtlstest TESTS= alltests @@ -84,7 +85,7 @@ EXE= $(BNTEST)$(EXE_EXT) $(ECTEST)$(EXE_EXT) $(ECDSATEST)$(EXE_EXT) $(ECDHTEST) $(EVPTEST)$(EXE_EXT) $(EVPEXTRATEST)$(EXE_EXT) $(IGETEST)$(EXE_EXT) $(JPAKETEST)$(EXE_EXT) $(SRPTEST)$(EXE_EXT) \ $(ASN1TEST)$(EXE_EXT) $(V3NAMETEST)$(EXE_EXT) $(HEARTBEATTEST)$(EXE_EXT) \ $(CONSTTIMETEST)$(EXE_EXT) $(VERIFYEXTRATEST)$(EXE_EXT) \ - $(CLIENTHELLOTEST)$(EXE_EXT) $(SSLV2CONFTEST)$(EXE_EXT) + $(CLIENTHELLOTEST)$(EXE_EXT) $(SSLV2CONFTEST)$(EXE_EXT) $(DTLSTEST)$(EXE_EXT) # $(METHTEST)$(EXE_EXT) @@ -98,7 +99,7 @@ OBJ= $(BNTEST).o $(ECTEST).o $(ECDSATEST).o $(ECDHTEST).o $(IDEATEST).o \ $(BFTEST).o $(SSLTEST).o $(DSATEST).o $(EXPTEST).o $(RSATEST).o \ $(EVPTEST).o $(EVPEXTRATEST).o $(IGETEST).o $(JPAKETEST).o $(ASN1TEST).o $(V3NAMETEST).o \ $(HEARTBEATTEST).o $(CONSTTIMETEST).o $(VERIFYEXTRATEST).o \ - $(CLIENTHELLOTEST).o $(SSLV2CONFTEST).o + $(CLIENTHELLOTEST).o $(SSLV2CONFTEST).o $(DTLSTEST).o ssltestlib.o SRC= $(BNTEST).c $(ECTEST).c $(ECDSATEST).c $(ECDHTEST).c $(IDEATEST).c \ $(MD2TEST).c $(MD4TEST).c $(MD5TEST).c \ @@ -109,10 +110,10 @@ SRC= $(BNTEST).c $(ECTEST).c $(ECDSATEST).c $(ECDHTEST).c $(IDEATEST).c \ $(BFTEST).c $(SSLTEST).c $(DSATEST).c $(EXPTEST).c $(RSATEST).c \ $(EVPTEST).c $(EVPEXTRATEST).c $(IGETEST).c $(JPAKETEST).c $(SRPTEST).c $(ASN1TEST).c \ $(V3NAMETEST).c $(HEARTBEATTEST).c $(CONSTTIMETEST).c $(VERIFYEXTRATEST).c \ - $(CLIENTHELLOTEST).c $(SSLV2CONFTEST).c + $(CLIENTHELLOTEST).c $(SSLV2CONFTEST).c $(DTLSTEST).c ssltestlib.c EXHEADER= -HEADER= testutil.h $(EXHEADER) +HEADER= testutil.h ssltestlib.h $(EXHEADER) ALL= $(GENERAL) $(SRC) $(HEADER) @@ -153,7 +154,8 @@ alltests: \ test_gen test_req test_pkcs7 test_verify test_dh test_dsa \ test_ss test_ca test_engine test_evp test_evp_extra test_ssl test_tsa test_ige \ test_jpake test_srp test_cms test_ocsp test_v3name test_heartbeat \ - test_constant_time test_verify_extra test_clienthello test_sslv2conftest + test_constant_time test_verify_extra test_clienthello test_sslv2conftest \ + test_dtls test_evp: $(EVPTEST)$(EXE_EXT) evptests.txt ../util/shlib_wrap.sh ./$(EVPTEST) evptests.txt @@ -366,6 +368,10 @@ test_sslv2conftest: $(SSLV2CONFTEST)$(EXE_EXT) @echo $(START) $@ ../util/shlib_wrap.sh ./$(SSLV2CONFTEST) +test_dtls: $(DTLSTEST)$(EXE_EXT) + @echo $(START) $@ + ../util/shlib_wrap.sh ./$(DTLSTEST) ../apps/server.pem ../apps/server.pem + lint: lint -DLINT $(INCLUDES) $(SRC)>fluff @@ -397,7 +403,7 @@ BUILD_CMD=shlib_target=; if [ -n "$(SHARED_LIBS)" ]; then \ fi; \ LIBRARIES="$(LIBSSL) $(LIBCRYPTO) $(LIBKRB5)"; \ $(MAKE) -f $(TOP)/Makefile.shared -e \ - CC="$${CC}" APPNAME=$$target$(EXE_EXT) OBJECTS="$$target.o" \ + CC="$${CC}" APPNAME=$$target$(EXE_EXT) OBJECTS="$$target.o $$exobj" \ LIBDEPS="$(PEX_LIBS) $$LIBRARIES $(EX_LIBS)" \ link_app.$${shlib_target} @@ -546,6 +552,9 @@ $(CLIENTHELLOTEST)$(EXE_EXT): $(CLIENTHELLOTEST).o $(SSLV2CONFTEST)$(EXE_EXT): $(SSLV2CONFTEST).o @target=$(SSLV2CONFTEST) $(BUILD_CMD) +$(DTLSTEST)$(EXE_EXT): $(DTLSTEST).o ssltestlib.o $(DLIBSSL) $(DLIBCRYPTO) + @target=$(DTLSTEST); exobj=ssltestlib.o; $(BUILD_CMD) + #$(AESTEST).o: $(AESTEST).c # $(CC) -c $(CFLAGS) -DINTERMEDIATE_VALUE_KAT -DTRACE_KAT_MCT $(AESTEST).c @@ -634,6 +643,25 @@ dsatest.o: ../include/openssl/opensslconf.h ../include/openssl/opensslv.h dsatest.o: ../include/openssl/ossl_typ.h ../include/openssl/rand.h dsatest.o: ../include/openssl/safestack.h ../include/openssl/stack.h dsatest.o: ../include/openssl/symhacks.h dsatest.c +dtlstest.o: ../include/openssl/asn1.h ../include/openssl/bio.h +dtlstest.o: ../include/openssl/buffer.h ../include/openssl/comp.h +dtlstest.o: ../include/openssl/crypto.h ../include/openssl/dtls1.h +dtlstest.o: ../include/openssl/e_os2.h ../include/openssl/ec.h +dtlstest.o: ../include/openssl/ecdh.h ../include/openssl/ecdsa.h +dtlstest.o: ../include/openssl/err.h ../include/openssl/evp.h +dtlstest.o: ../include/openssl/hmac.h ../include/openssl/kssl.h +dtlstest.o: ../include/openssl/lhash.h ../include/openssl/obj_mac.h +dtlstest.o: ../include/openssl/objects.h ../include/openssl/opensslconf.h +dtlstest.o: ../include/openssl/opensslv.h ../include/openssl/ossl_typ.h +dtlstest.o: ../include/openssl/pem.h ../include/openssl/pem2.h +dtlstest.o: ../include/openssl/pkcs7.h ../include/openssl/pqueue.h +dtlstest.o: ../include/openssl/safestack.h ../include/openssl/sha.h +dtlstest.o: ../include/openssl/srtp.h ../include/openssl/ssl.h +dtlstest.o: ../include/openssl/ssl2.h ../include/openssl/ssl23.h +dtlstest.o: ../include/openssl/ssl3.h ../include/openssl/stack.h +dtlstest.o: ../include/openssl/symhacks.h ../include/openssl/tls1.h +dtlstest.o: ../include/openssl/x509.h ../include/openssl/x509_vfy.h dtlstest.c +dtlstest.o: ssltestlib.h testutil.h ecdhtest.o: ../e_os.h ../include/openssl/asn1.h ../include/openssl/bio.h ecdhtest.o: ../include/openssl/bn.h ../include/openssl/crypto.h ecdhtest.o: ../include/openssl/e_os2.h ../include/openssl/ec.h @@ -856,6 +884,24 @@ ssltest.o: ../include/openssl/ssl3.h ../include/openssl/stack.h ssltest.o: ../include/openssl/symhacks.h ../include/openssl/tls1.h ssltest.o: ../include/openssl/x509.h ../include/openssl/x509_vfy.h ssltest.o: ../include/openssl/x509v3.h ssltest.c +ssltestlib.o: ../include/openssl/asn1.h ../include/openssl/bio.h +ssltestlib.o: ../include/openssl/buffer.h ../include/openssl/comp.h +ssltestlib.o: ../include/openssl/crypto.h ../include/openssl/dtls1.h +ssltestlib.o: ../include/openssl/e_os2.h ../include/openssl/ec.h +ssltestlib.o: ../include/openssl/ecdh.h ../include/openssl/ecdsa.h +ssltestlib.o: ../include/openssl/evp.h ../include/openssl/hmac.h +ssltestlib.o: ../include/openssl/kssl.h ../include/openssl/lhash.h +ssltestlib.o: ../include/openssl/obj_mac.h ../include/openssl/objects.h +ssltestlib.o: ../include/openssl/opensslconf.h ../include/openssl/opensslv.h +ssltestlib.o: ../include/openssl/ossl_typ.h ../include/openssl/pem.h +ssltestlib.o: ../include/openssl/pem2.h ../include/openssl/pkcs7.h +ssltestlib.o: ../include/openssl/pqueue.h ../include/openssl/safestack.h +ssltestlib.o: ../include/openssl/sha.h ../include/openssl/srtp.h +ssltestlib.o: ../include/openssl/ssl.h ../include/openssl/ssl2.h +ssltestlib.o: ../include/openssl/ssl23.h ../include/openssl/ssl3.h +ssltestlib.o: ../include/openssl/stack.h ../include/openssl/symhacks.h +ssltestlib.o: ../include/openssl/tls1.h ../include/openssl/x509.h +ssltestlib.o: ../include/openssl/x509_vfy.h ssltestlib.c ssltestlib.h sslv2conftest.o: ../include/openssl/asn1.h ../include/openssl/bio.h sslv2conftest.o: ../include/openssl/buffer.h ../include/openssl/comp.h sslv2conftest.o: ../include/openssl/crypto.h ../include/openssl/dtls1.h -- cgit v1.2.3 From 66e708326524929a0e1631f8d1ef6e63c153922c Mon Sep 17 00:00:00 2001 From: Richard Levitte Date: Fri, 19 Aug 2016 14:19:00 +0100 Subject: Have dtlstest run on VMS as well Reviewed-by: Matt Caswell --- test/maketests.com | 24 +++++++++++++++++++++--- test/tests.com | 7 ++++++- 2 files changed, 27 insertions(+), 4 deletions(-) (limited to 'test') diff --git a/test/maketests.com b/test/maketests.com index 3d16443ba7..6223659fb0 100644 --- a/test/maketests.com +++ b/test/maketests.com @@ -150,7 +150,7 @@ $ TEST_FILES = "BNTEST,ECTEST,ECDSATEST,ECDHTEST,IDEATEST,"+ - "EVP_TEST,EVP_EXTRA_TEST,IGETEST,JPAKETEST,SRPTEST,"+ - "ASN1TEST,V3NAMETEST,HEARTBEAT_TEST,"+ - "CONSTANT_TIME_TEST,VERIFY_EXTRA_TEST,"+ - - "CLIENTHELLOTEST,SSLV2CONFTEST" + "CLIENTHELLOTEST,SSLV2CONFTEST,DTLSTEST" $! Should we add MTTEST,PQ_TEST,LH_TEST,DIVTEST,TABTEST as well? $! $! Additional directory information. @@ -195,6 +195,9 @@ $ T_D_CONSTANT_TIME_TEST := [-.crypto] $ T_D_VERIFY_EXTRA_TEST := [-.crypto.x509] $ T_D_CLIENTHELLOTEST := [-.ssl] $ T_D_SSLV2CONFTEST := [-.ssl] +$ T_D_DTLSTEST := [-.ssl] +$ +$ EXOBJ_DTLSTEST := SSLTESTLIB $! $ TCPIP_PROGRAMS = ",," $ IF COMPILER .EQS. "VAXC" THEN - @@ -227,10 +230,21 @@ $! $! Create The Object File Name. $! $ OBJECT_FILE = OBJ_DIR + FILE_NAME + ".OBJ" +$ OBJECT_FILES = OBJECT_FILE $! $! Create The Executable File Name. $! $ EXE_FILE = EXE_DIR + FILE_NAME + ".EXE" +$! +$! Do the same for the possible extra unit +$! +$ IF F$TYPE(EXOBJ_'FILE_NAME') .NES. "" +$ THEN +$ EXOBJ_SOURCE_FILE = "SYS$DISK:" + EXOBJ_'FILE_NAME' + ".C" +$ EXOBJ_OBJECT_FILE = OBJ_DIR + EXOBJ_'FILE_NAME' + ".OBJ" +$ OBJECT_FILES = OBJECT_FILES + "," + EXOBJ_OBJECT_FILE +$ ENDIF +$! $ ON WARNING THEN GOTO NEXT_FILE $! $! Check To See If The File We Want To Compile Actually Exists. @@ -257,6 +271,10 @@ $! Compile The File. $! $ ON ERROR THEN GOTO NEXT_FILE $ CC /OBJECT='OBJECT_FILE' 'SOURCE_FILE' +$ IF F$TYPE(EXOBJ_'FILE_NAME') .NES. "" +$ THEN +$ CC /OBJECT='EXOBJ_OBJECT_FILE' 'EXOBJ_SOURCE_FILE' +$ ENDIF $ ON WARNING THEN GOTO NEXT_FILE $! $! Check If What We Are About To Compile Works Without A TCP/IP Library. @@ -280,7 +298,7 @@ $! $! Don't Link With The RSAREF Routines And TCP/IP Library. $! $ LINK /'DEBUGGER' /'LINKMAP' /'TRACEBACK' /EXECTABLE = 'EXE_FILE' - - 'OBJECT_FILE', - + 'OBJECT_FILES', - 'SSL_LIB' /LIBRARY, - 'CRYPTO_LIB' /LIBRARY - 'TCPIP_LIB' - @@ -480,7 +498,7 @@ $ CHECK_OPTIONS: $! $! Set basic C compiler /INCLUDE directories. $! -$ CC_INCLUDES = "SYS$DISK:[-],SYS$DISK:[-.CRYPTO]" +$ CC_INCLUDES = "SYS$DISK:[-],SYS$DISK:[-.CRYPTO],SYS$DISK:[-.TEST]" $! $! Check To See If P1 Is Blank. $! diff --git a/test/tests.com b/test/tests.com index 9361659982..59745e14c7 100644 --- a/test/tests.com +++ b/test/tests.com @@ -57,7 +57,7 @@ $ tests := - test_gen,test_req,test_pkcs7,test_verify,test_dh,test_dsa,- test_ss,test_ca,test_engine,test_evp,test_evp_extra,test_ssl,test_tsa,test_ige,- test_jpake,test_srp,test_cms,test_ocsp,test_v3name,test_heartbeat,- - test_constant_time,test_verify_extra,test_clienthello,test_sslv2conftest + test_constant_time,test_verify_extra,test_clienthello,test_sslv2conftest,test_dtls $ endif $ tests = f$edit(tests,"COLLAPSE") $ @@ -103,6 +103,7 @@ $ CONSTTIMETEST := constant_time_test $ VERIFYEXTRATEST := verify_extra_test $ CLIENTHELLOTEST := clienthellotest $ SSLV2CONFTEST := sslv2conftest +$ DTLSTEST := dtlstest $! $ tests_i = 0 $ loop_tests: @@ -405,6 +406,10 @@ $ test_sslv2conftest: $ write sys$output "''START' test_sslv2conftest" $ mcr 'texe_dir''sslv2conftest' $ return +$ test_dtls: +$ write sys$output "''START' test_dtls" +$ mcr 'texe_dir''dtlstest' 'ROOT'.APPS]server.pem 'ROOT'.APPS]server.pem +$ return $ $ exit: $ on error then goto exit2 ! In case openssl.exe didn't build. -- cgit v1.2.3 From 91dc6054582d1b7f263a67527ebbe2c050d178fc Mon Sep 17 00:00:00 2001 From: Richard Levitte Date: Mon, 22 Aug 2016 14:02:31 +0200 Subject: ssltestlib: Tell compiler we don't care about the value when we don't In mempacket_test_read(), we've already fetched the top value of the stack, so when we shift the stack, we don't care for the value. The compiler needs to be told, or it will complain harshly when we tell it to be picky. Reviewed-by: Matt Caswell (cherry picked from commit 1c288878af42650fbda911b702ae7b551a545b1c) --- test/ssltestlib.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'test') diff --git a/test/ssltestlib.c b/test/ssltestlib.c index 1592514a38..a52669c82a 100644 --- a/test/ssltestlib.c +++ b/test/ssltestlib.c @@ -355,7 +355,7 @@ static int mempacket_test_read(BIO *bio, char *out, int outl) BIO_set_retry_read(bio); return -1; } - sk_MEMPACKET_shift(ctx->pkts); + (void)sk_MEMPACKET_shift(ctx->pkts); ctx->currpkt++; if (outl > thispkt->len) -- cgit v1.2.3 From 50c30153d3fe887d0f6c8c0514bc825c4f3dec6a Mon Sep 17 00:00:00 2001 From: David Woodhouse Date: Wed, 3 Aug 2016 18:25:07 +0100 Subject: Add basic test for Cisco DTLS1_BAD_VER and record replay handling (Modified for 1.0.2 by adding selected PACKET_xx() functions and PRF, and subsequent cleanup from commit eb633d03fe2db3666840dee8d0a2dbe491672dfc) Reviewed-by: Rich Salz Reviewed-by: Matt Caswell (cherry picked from commit 40425899200a3dea9ec3684d3eb80bcf50c99baf) --- ssl/Makefile | 2 +- ssl/bad_dtls_test.c | 923 ++++++++++++++++++++++++++++++++++++++++++++++++++++ test/Makefile | 39 ++- 3 files changed, 959 insertions(+), 5 deletions(-) create mode 100644 ssl/bad_dtls_test.c (limited to 'test') diff --git a/ssl/Makefile b/ssl/Makefile index 775d8ec980..dd12962250 100644 --- a/ssl/Makefile +++ b/ssl/Makefile @@ -15,7 +15,7 @@ KRB5_INCLUDES= CFLAGS= $(INCLUDES) $(CFLAG) GENERAL=Makefile README ssl-lib.com install.com -TEST=ssltest.c heartbeat_test.c clienthellotest.c sslv2conftest.c dtlstest.c +TEST=ssltest.c heartbeat_test.c clienthellotest.c sslv2conftest.c dtlstest.c bad_dtls_test.c APPS= LIB=$(TOP)/libssl.a diff --git a/ssl/bad_dtls_test.c b/ssl/bad_dtls_test.c new file mode 100644 index 0000000000..d42817fc32 --- /dev/null +++ b/ssl/bad_dtls_test.c @@ -0,0 +1,923 @@ +/* + * Copyright 2016 The OpenSSL Project Authors. All Rights Reserved. + * + * Licensed under the OpenSSL license (the "License"). You may not use + * this file except in compliance with the License. You can obtain a copy + * in the file LICENSE in the source distribution or at + * https://www.openssl.org/source/license.html + */ + +/* + * Unit test for Cisco DTLS1_BAD_VER session resume, as used by + * AnyConnect VPN protocol. + * + * This is designed to exercise the code paths in + * http://git.infradead.org/users/dwmw2/openconnect.git/blob/HEAD:/dtls.c + * which have frequently been affected by regressions in DTLS1_BAD_VER + * support. + * + * Note that unlike other SSL tests, we don't test against our own SSL + * server method. Firstly because we don't have one; we *only* support + * DTLS1_BAD_VER as a client. And secondly because even if that were + * fixed up it's the wrong thing to test against — because if changes + * are made in generic DTLS code which don't take DTLS1_BAD_VER into + * account, there's plenty of scope for making those changes such that + * they break *both* the client and the server in the same way. + * + * So we handle the server side manually. In a session resume there isn't + * much to be done anyway. + */ +#include + +/* On Windows this will include and thus it needs to be + * included *before* anything that includes . Ick. */ +#include "e_os.h" /* for 'inline' */ + +#include +#include +#include +#include +#include +#include + +/* PACKET functions lifted from OpenSSL 1.1's ssl/packet_locl.h */ +typedef struct { + /* Pointer to where we are currently reading from */ + const unsigned char *curr; + /* Number of bytes remaining */ + size_t remaining; +} PACKET; + +/* Internal unchecked shorthand; don't use outside this file. */ +static inline void packet_forward(PACKET *pkt, size_t len) +{ + pkt->curr += len; + pkt->remaining -= len; +} + +/* + * Returns the number of bytes remaining to be read in the PACKET + */ +static inline size_t PACKET_remaining(const PACKET *pkt) +{ + return pkt->remaining; +} + +/* + * Initialise a PACKET with |len| bytes held in |buf|. This does not make a + * copy of the data so |buf| must be present for the whole time that the PACKET + * is being used. + */ +static inline int PACKET_buf_init(PACKET *pkt, + const unsigned char *buf, + size_t len) +{ + /* Sanity check for negative values. */ + if (len > (size_t)65536) + return 0; + + pkt->curr = buf; + pkt->remaining = len; + return 1; +} + +/* + * Returns 1 if the packet has length |num| and its contents equal the |num| + * bytes read from |ptr|. Returns 0 otherwise (lengths or contents not equal). + * If lengths are equal, performs the comparison in constant time. + */ +static inline int PACKET_equal(const PACKET *pkt, const void *ptr, + size_t num) +{ + if (PACKET_remaining(pkt) != num) + return 0; + return CRYPTO_memcmp(pkt->curr, ptr, num) == 0; +} + +/* + * Peek ahead at 2 bytes in network order from |pkt| and store the value in + * |*data| + */ +static inline int PACKET_peek_net_2(const PACKET *pkt, + unsigned int *data) +{ + if (PACKET_remaining(pkt) < 2) + return 0; + + *data = ((unsigned int)(*pkt->curr)) << 8; + *data |= *(pkt->curr + 1); + + return 1; +} + +/* Equivalent of n2s */ +/* Get 2 bytes in network order from |pkt| and store the value in |*data| */ +static inline int PACKET_get_net_2(PACKET *pkt, + unsigned int *data) +{ + if (!PACKET_peek_net_2(pkt, data)) + return 0; + + packet_forward(pkt, 2); + + return 1; +} + +/* Peek ahead at 1 byte from |pkt| and store the value in |*data| */ +static inline int PACKET_peek_1(const PACKET *pkt, + unsigned int *data) +{ + if (!PACKET_remaining(pkt)) + return 0; + + *data = *pkt->curr; + + return 1; +} + +/* Get 1 byte from |pkt| and store the value in |*data| */ +static inline int PACKET_get_1(PACKET *pkt, unsigned int *data) +{ + if (!PACKET_peek_1(pkt, data)) + return 0; + + packet_forward(pkt, 1); + + return 1; +} + +/* + * Peek ahead at |len| bytes from the |pkt| and store a pointer to them in + * |*data|. This just points at the underlying buffer that |pkt| is using. The + * caller should not free this data directly (it will be freed when the + * underlying buffer gets freed + */ +static inline int PACKET_peek_bytes(const PACKET *pkt, + const unsigned char **data, + size_t len) +{ + if (PACKET_remaining(pkt) < len) + return 0; + + *data = pkt->curr; + + return 1; +} + +/* + * Read |len| bytes from the |pkt| and store a pointer to them in |*data|. This + * just points at the underlying buffer that |pkt| is using. The caller should + * not free this data directly (it will be freed when the underlying buffer gets + * freed + */ +static inline int PACKET_get_bytes(PACKET *pkt, + const unsigned char **data, + size_t len) +{ + if (!PACKET_peek_bytes(pkt, data, len)) + return 0; + + packet_forward(pkt, len); + + return 1; +} + +/* Peek ahead at |len| bytes from |pkt| and copy them to |data| */ +static inline int PACKET_peek_copy_bytes(const PACKET *pkt, + unsigned char *data, + size_t len) +{ + if (PACKET_remaining(pkt) < len) + return 0; + + memcpy(data, pkt->curr, len); + + return 1; +} + +/* + * Read |len| bytes from |pkt| and copy them to |data|. + * The caller is responsible for ensuring that |data| can hold |len| bytes. + */ +static inline int PACKET_copy_bytes(PACKET *pkt, + unsigned char *data, + size_t len) +{ + if (!PACKET_peek_copy_bytes(pkt, data, len)) + return 0; + + packet_forward(pkt, len); + + return 1; +} + + +/* Move the current reading position forward |len| bytes */ +static inline int PACKET_forward(PACKET *pkt, size_t len) +{ + if (PACKET_remaining(pkt) < len) + return 0; + + packet_forward(pkt, len); + + return 1; +} + +/* + * Reads a variable-length vector prefixed with a one-byte length, and stores + * the contents in |subpkt|. |pkt| can equal |subpkt|. + * Data is not copied: the |subpkt| packet will share its underlying buffer with + * the original |pkt|, so data wrapped by |pkt| must outlive the |subpkt|. + * Upon failure, the original |pkt| and |subpkt| are not modified. + */ +static inline int PACKET_get_length_prefixed_1(PACKET *pkt, + PACKET *subpkt) +{ + unsigned int length; + const unsigned char *data; + PACKET tmp = *pkt; + if (!PACKET_get_1(&tmp, &length) || + !PACKET_get_bytes(&tmp, &data, (size_t)length)) { + return 0; + } + + *pkt = tmp; + subpkt->curr = data; + subpkt->remaining = length; + + return 1; +} + +#define OSSL_NELEM(x) (sizeof(x)/sizeof(x[0])) + +/* For DTLS1_BAD_VER packets the MAC doesn't include the handshake header */ +#define MAC_OFFSET (DTLS1_RT_HEADER_LENGTH + DTLS1_HM_HEADER_LENGTH) + +static unsigned char client_random[SSL3_RANDOM_SIZE]; +static unsigned char server_random[SSL3_RANDOM_SIZE]; + +/* These are all generated locally, sized purely according to our own whim */ +static unsigned char session_id[32]; +static unsigned char master_secret[48]; +static unsigned char cookie[20]; + +/* We've hard-coded the cipher suite; we know it's 104 bytes */ +static unsigned char key_block[104]; +#define mac_key (key_block + 20) +#define dec_key (key_block + 40) +#define enc_key (key_block + 56) + +static EVP_MD_CTX handshake_md5; +static EVP_MD_CTX handshake_sha1; + +/* PRF lifted from ssl/t1_enc.c since we can't easily use it directly */ +static int tls1_P_hash(const EVP_MD *md, const unsigned char *sec, + int sec_len, + const void *seed1, int seed1_len, + const void *seed2, int seed2_len, + const void *seed3, int seed3_len, + unsigned char *out, int olen) +{ + int chunk; + size_t j; + EVP_MD_CTX ctx, ctx_tmp, ctx_init; + EVP_PKEY *prf_mac_key; + unsigned char A1[EVP_MAX_MD_SIZE]; + size_t A1_len; + int ret = 0; + + chunk = EVP_MD_size(md); + OPENSSL_assert(chunk >= 0); + + EVP_MD_CTX_init(&ctx); + EVP_MD_CTX_init(&ctx_tmp); + EVP_MD_CTX_init(&ctx_init); + EVP_MD_CTX_set_flags(&ctx_init, EVP_MD_CTX_FLAG_NON_FIPS_ALLOW); + prf_mac_key = EVP_PKEY_new_mac_key(EVP_PKEY_HMAC, NULL, sec, sec_len); + if (!prf_mac_key) + goto err; + if (!EVP_DigestSignInit(&ctx_init, NULL, md, NULL, prf_mac_key)) + goto err; + if (!EVP_MD_CTX_copy_ex(&ctx, &ctx_init)) + goto err; + if (seed1 && !EVP_DigestSignUpdate(&ctx, seed1, seed1_len)) + goto err; + if (seed2 && !EVP_DigestSignUpdate(&ctx, seed2, seed2_len)) + goto err; + if (seed3 && !EVP_DigestSignUpdate(&ctx, seed3, seed3_len)) + goto err; + if (!EVP_DigestSignFinal(&ctx, A1, &A1_len)) + goto err; + + for (;;) { + /* Reinit mac contexts */ + if (!EVP_MD_CTX_copy_ex(&ctx, &ctx_init)) + goto err; + if (!EVP_DigestSignUpdate(&ctx, A1, A1_len)) + goto err; + if (olen > chunk && !EVP_MD_CTX_copy_ex(&ctx_tmp, &ctx)) + goto err; + if (seed1 && !EVP_DigestSignUpdate(&ctx, seed1, seed1_len)) + goto err; + if (seed2 && !EVP_DigestSignUpdate(&ctx, seed2, seed2_len)) + goto err; + if (seed3 && !EVP_DigestSignUpdate(&ctx, seed3, seed3_len)) + goto err; + + if (olen > chunk) { + if (!EVP_DigestSignFinal(&ctx, out, &j)) + goto err; + out += j; + olen -= j; + /* calc the next A1 value */ + if (!EVP_DigestSignFinal(&ctx_tmp, A1, &A1_len)) + goto err; + } else { /* last one */ + + if (!EVP_DigestSignFinal(&ctx, A1, &A1_len)) + goto err; + memcpy(out, A1, olen); + break; + } + } + ret = 1; + err: + EVP_PKEY_free(prf_mac_key); + EVP_MD_CTX_cleanup(&ctx); + EVP_MD_CTX_cleanup(&ctx_tmp); + EVP_MD_CTX_cleanup(&ctx_init); + OPENSSL_cleanse(A1, sizeof(A1)); + return ret; +} + +/* seed1 through seed5 are virtually concatenated */ +static int do_PRF(const void *seed1, int seed1_len, + const void *seed2, int seed2_len, + const void *seed3, int seed3_len, + unsigned char *out, int olen) +{ + unsigned char out2[104]; + int i, len; + + if (olen > (int)sizeof(out2)) + return 0; + + len = sizeof(master_secret) / 2; + + if (!tls1_P_hash(EVP_md5(), master_secret, len, + seed1, seed1_len, seed2, seed2_len, seed3, + seed3_len, out, olen)) + return 0; + + if (!tls1_P_hash(EVP_sha1(), master_secret + len, len, + seed1, seed1_len, seed2, seed2_len, seed3, + seed3_len, out2, olen)) + return 0; + + for (i = 0; i < olen; i++) { + out[i] ^= out2[i]; + } + + return 1; +} + +static SSL_SESSION *client_session(void) +{ + static unsigned char session_asn1[] = { + 0x30, 0x5F, /* SEQUENCE, length 0x5F */ + 0x02, 0x01, 0x01, /* INTEGER, SSL_SESSION_ASN1_VERSION */ + 0x02, 0x02, 0x01, 0x00, /* INTEGER, DTLS1_BAD_VER */ + 0x04, 0x02, 0x00, 0x2F, /* OCTET_STRING, AES128-SHA */ + 0x04, 0x20, /* OCTET_STRING, session id */ +#define SS_SESSID_OFS 15 /* Session ID goes here */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x04, 0x30, /* OCTET_STRING, master secret */ +#define SS_SECRET_OFS 49 /* Master secret goes here */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + }; + const unsigned char *p = session_asn1; + + /* Copy the randomly-generated fields into the above ASN1 */ + memcpy(session_asn1 + SS_SESSID_OFS, session_id, sizeof(session_id)); + memcpy(session_asn1 + SS_SECRET_OFS, master_secret, sizeof(master_secret)); + + return d2i_SSL_SESSION(NULL, &p, sizeof(session_asn1)); +} + +/* Returns 1 for initial ClientHello, 2 for ClientHello with cookie */ +static int validate_client_hello(BIO *wbio) +{ + PACKET pkt, pkt2; + long len; + unsigned char *data; + int cookie_found = 0; + unsigned int u; + + len = BIO_get_mem_data(wbio, (char **)&data); + if (!PACKET_buf_init(&pkt, data, len)) + return 0; + + /* Check record header type */ + if (!PACKET_get_1(&pkt, &u) || u != SSL3_RT_HANDSHAKE) + return 0; + /* Version */ + if (!PACKET_get_net_2(&pkt, &u) || u != DTLS1_BAD_VER) + return 0; + /* Skip the rest of the record header */ + if (!PACKET_forward(&pkt, DTLS1_RT_HEADER_LENGTH - 3)) + return 0; + + /* Check it's a ClientHello */ + if (!PACKET_get_1(&pkt, &u) || u != SSL3_MT_CLIENT_HELLO) + return 0; + /* Skip the rest of the handshake message header */ + if (!PACKET_forward(&pkt, DTLS1_HM_HEADER_LENGTH - 1)) + return 0; + + /* Check client version */ + if (!PACKET_get_net_2(&pkt, &u) || u != DTLS1_BAD_VER) + return 0; + + /* Store random */ + if (!PACKET_copy_bytes(&pkt, client_random, SSL3_RANDOM_SIZE)) + return 0; + + /* Check session id length and content */ + if (!PACKET_get_length_prefixed_1(&pkt, &pkt2) || + !PACKET_equal(&pkt2, session_id, sizeof(session_id))) + return 0; + + /* Check cookie */ + if (!PACKET_get_length_prefixed_1(&pkt, &pkt2)) + return 0; + if (PACKET_remaining(&pkt2)) { + if (!PACKET_equal(&pkt2, cookie, sizeof(cookie))) + return 0; + cookie_found = 1; + } + + /* Skip ciphers */ + if (!PACKET_get_net_2(&pkt, &u) || !PACKET_forward(&pkt, u)) + return 0; + + /* Skip compression */ + if (!PACKET_get_1(&pkt, &u) || !PACKET_forward(&pkt, u)) + return 0; + + /* Skip extensions */ + if (!PACKET_get_net_2(&pkt, &u) || !PACKET_forward(&pkt, u)) + return 0; + + /* Now we are at the end */ + if (PACKET_remaining(&pkt)) + return 0; + + /* Update handshake MAC for second ClientHello (with cookie) */ + if (cookie_found && (!EVP_DigestUpdate(&handshake_md5, data + MAC_OFFSET, + len - MAC_OFFSET) || + !EVP_DigestUpdate(&handshake_sha1, data + MAC_OFFSET, + len - MAC_OFFSET))) + printf("EVP_DigestUpdate() failed\n"); + + (void)BIO_reset(wbio); + + return 1 + cookie_found; +} + +static int send_hello_verify(BIO *rbio) +{ + static unsigned char hello_verify[] = { + 0x16, /* Handshake */ + 0x01, 0x00, /* DTLS1_BAD_VER */ + 0x00, 0x00, /* Epoch 0 */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* Seq# 0 */ + 0x00, 0x23, /* Length */ + 0x03, /* Hello Verify */ + 0x00, 0x00, 0x17, /* Length */ + 0x00, 0x00, /* Seq# 0 */ + 0x00, 0x00, 0x00, /* Fragment offset */ + 0x00, 0x00, 0x17, /* Fragment length */ + 0x01, 0x00, /* DTLS1_BAD_VER */ + 0x14, /* Cookie length */ +#define HV_COOKIE_OFS 28 /* Cookie goes here */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + }; + + memcpy(hello_verify + HV_COOKIE_OFS, cookie, sizeof(cookie)); + + BIO_write(rbio, hello_verify, sizeof(hello_verify)); + + return 1; +} + +static int send_server_hello(BIO *rbio) +{ + static unsigned char server_hello[] = { + 0x16, /* Handshake */ + 0x01, 0x00, /* DTLS1_BAD_VER */ + 0x00, 0x00, /* Epoch 0 */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, /* Seq# 1 */ + 0x00, 0x52, /* Length */ + 0x02, /* Server Hello */ + 0x00, 0x00, 0x46, /* Length */ + 0x00, 0x01, /* Seq# */ + 0x00, 0x00, 0x00, /* Fragment offset */ + 0x00, 0x00, 0x46, /* Fragment length */ + 0x01, 0x00, /* DTLS1_BAD_VER */ +#define SH_RANDOM_OFS 27 /* Server random goes here */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x20, /* Session ID length */ +#define SH_SESSID_OFS 60 /* Session ID goes here */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x2f, /* Cipher suite AES128-SHA */ + 0x00, /* Compression null */ + }; + static unsigned char change_cipher_spec[] = { + 0x14, /* Change Cipher Spec */ + 0x01, 0x00, /* DTLS1_BAD_VER */ + 0x00, 0x00, /* Epoch 0 */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, /* Seq# 2 */ + 0x00, 0x03, /* Length */ + 0x01, 0x00, 0x02, /* Message */ + }; + + memcpy(server_hello + SH_RANDOM_OFS, server_random, sizeof(server_random)); + memcpy(server_hello + SH_SESSID_OFS, session_id, sizeof(session_id)); + + if (!EVP_DigestUpdate(&handshake_md5, server_hello + MAC_OFFSET, + sizeof(server_hello) - MAC_OFFSET) || + !EVP_DigestUpdate(&handshake_sha1, server_hello + MAC_OFFSET, + sizeof(server_hello) - MAC_OFFSET)) + printf("EVP_DigestUpdate() failed\n"); + + BIO_write(rbio, server_hello, sizeof(server_hello)); + BIO_write(rbio, change_cipher_spec, sizeof(change_cipher_spec)); + + return 1; +} + +/* Create header, HMAC, pad, encrypt and send a record */ +static int send_record(BIO *rbio, unsigned char type, unsigned long seqnr, + const void *msg, size_t len) +{ + /* Note that the order of the record header fields on the wire, + * and in the HMAC, is different. So we just keep them in separate + * variables and handle them individually. */ + static unsigned char epoch[2] = { 0x00, 0x01 }; + static unsigned char seq[6] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; + static unsigned char ver[2] = { 0x01, 0x00 }; /* DTLS1_BAD_VER */ + unsigned char lenbytes[2]; + HMAC_CTX ctx; + EVP_CIPHER_CTX enc_ctx; + unsigned char iv[16]; + unsigned char pad; + unsigned char *enc; + +#ifdef SIXTY_FOUR_BIT_LONG + seq[0] = (seqnr >> 40) & 0xff; + seq[1] = (seqnr >> 32) & 0xff; +#endif + seq[2] = (seqnr >> 24) & 0xff; + seq[3] = (seqnr >> 16) & 0xff; + seq[4] = (seqnr >> 8) & 0xff; + seq[5] = seqnr & 0xff; + + pad = 15 - ((len + SHA_DIGEST_LENGTH) % 16); + enc = OPENSSL_malloc(len + SHA_DIGEST_LENGTH + 1 + pad); + if (enc == NULL) + return 0; + + /* Copy record to encryption buffer */ + memcpy(enc, msg, len); + + /* Append HMAC to data */ + HMAC_Init(&ctx, mac_key, 20, EVP_sha1()); + HMAC_Update(&ctx, epoch, 2); + HMAC_Update(&ctx, seq, 6); + HMAC_Update(&ctx, &type, 1); + HMAC_Update(&ctx, ver, 2); /* Version */ + lenbytes[0] = len >> 8; + lenbytes[1] = len & 0xff; + HMAC_Update(&ctx, lenbytes, 2); /* Length */ + HMAC_Update(&ctx, enc, len); /* Finally the data itself */ + HMAC_Final(&ctx, enc + len, NULL); + HMAC_CTX_cleanup(&ctx); + + /* Append padding bytes */ + len += SHA_DIGEST_LENGTH; + do { + enc[len++] = pad; + } while (len % 16); + + /* Generate IV, and encrypt */ + RAND_bytes(iv, sizeof(iv)); + EVP_CIPHER_CTX_init(&enc_ctx); + EVP_CipherInit_ex(&enc_ctx, EVP_aes_128_cbc(), NULL, enc_key, iv, 1); + EVP_Cipher(&enc_ctx, enc, enc, len); + EVP_CIPHER_CTX_cleanup(&enc_ctx); + + /* Finally write header (from fragmented variables), IV and encrypted record */ + BIO_write(rbio, &type, 1); + BIO_write(rbio, ver, 2); + BIO_write(rbio, epoch, 2); + BIO_write(rbio, seq, 6); + lenbytes[0] = (len + sizeof(iv)) >> 8; + lenbytes[1] = (len + sizeof(iv)) & 0xff; + BIO_write(rbio, lenbytes, 2); + + BIO_write(rbio, iv, sizeof(iv)); + BIO_write(rbio, enc, len); + + OPENSSL_free(enc); + return 1; +} + +static int send_finished(SSL *s, BIO *rbio) +{ + static unsigned char finished_msg[DTLS1_HM_HEADER_LENGTH + + TLS1_FINISH_MAC_LENGTH] = { + 0x14, /* Finished */ + 0x00, 0x00, 0x0c, /* Length */ + 0x00, 0x03, /* Seq# 3 */ + 0x00, 0x00, 0x00, /* Fragment offset */ + 0x00, 0x00, 0x0c, /* Fragment length */ + /* Finished MAC (12 bytes) */ + }; + unsigned char handshake_hash[EVP_MAX_MD_SIZE * 2]; + + /* Derive key material */ + do_PRF(TLS_MD_KEY_EXPANSION_CONST, TLS_MD_KEY_EXPANSION_CONST_SIZE, + server_random, SSL3_RANDOM_SIZE, + client_random, SSL3_RANDOM_SIZE, + key_block, sizeof(key_block)); + + /* Generate Finished MAC */ + if (!EVP_DigestFinal_ex(&handshake_md5, handshake_hash, NULL) || + !EVP_DigestFinal_ex(&handshake_sha1, handshake_hash + EVP_MD_CTX_size(&handshake_md5), NULL)) + printf("EVP_DigestFinal_ex() failed\n"); + + do_PRF(TLS_MD_SERVER_FINISH_CONST, TLS_MD_SERVER_FINISH_CONST_SIZE, + handshake_hash, EVP_MD_CTX_size(&handshake_md5) + EVP_MD_CTX_size(&handshake_sha1), + NULL, 0, + finished_msg + DTLS1_HM_HEADER_LENGTH, TLS1_FINISH_MAC_LENGTH); + + return send_record(rbio, SSL3_RT_HANDSHAKE, 0, + finished_msg, sizeof(finished_msg)); +} + +static int validate_ccs(BIO *wbio) +{ + PACKET pkt; + long len; + unsigned char *data; + unsigned int u; + + len = BIO_get_mem_data(wbio, (char **)&data); + if (!PACKET_buf_init(&pkt, data, len)) + return 0; + + /* Check record header type */ + if (!PACKET_get_1(&pkt, &u) || u != SSL3_RT_CHANGE_CIPHER_SPEC) + return 0; + /* Version */ + if (!PACKET_get_net_2(&pkt, &u) || u != DTLS1_BAD_VER) + return 0; + /* Skip the rest of the record header */ + if (!PACKET_forward(&pkt, DTLS1_RT_HEADER_LENGTH - 3)) + return 0; + + /* Check ChangeCipherSpec message */ + if (!PACKET_get_1(&pkt, &u) || u != SSL3_MT_CCS) + return 0; + /* A DTLS1_BAD_VER ChangeCipherSpec also contains the + * handshake sequence number (which is 2 here) */ + if (!PACKET_get_net_2(&pkt, &u) || u != 0x0002) + return 0; + + /* Now check the Finished packet */ + if (!PACKET_get_1(&pkt, &u) || u != SSL3_RT_HANDSHAKE) + return 0; + if (!PACKET_get_net_2(&pkt, &u) || u != DTLS1_BAD_VER) + return 0; + + /* Check epoch is now 1 */ + if (!PACKET_get_net_2(&pkt, &u) || u != 0x0001) + return 0; + + /* That'll do for now. If OpenSSL accepted *our* Finished packet + * then it's evidently remembered that DTLS1_BAD_VER doesn't + * include the handshake header in the MAC. There's not a lot of + * point in implementing decryption here, just to check that it + * continues to get it right for one more packet. */ + + return 1; +} + +#define NODROP(x) { x##UL, 0 } +#define DROP(x) { x##UL, 1 } + +static struct { + unsigned long seq; + int drop; +} tests[] = { + NODROP(1), NODROP(3), NODROP(2), + NODROP(0x1234), NODROP(0x1230), NODROP(0x1235), + NODROP(0xffff), NODROP(0x10001), NODROP(0xfffe), NODROP(0x10000), + DROP(0x10001), DROP(0xff), NODROP(0x100000), NODROP(0x800000), NODROP(0x7fffe1), + NODROP(0xffffff), NODROP(0x1000000), NODROP(0xfffffe), DROP(0xffffff), NODROP(0x1000010), + NODROP(0xfffffd), NODROP(0x1000011), DROP(0x12), NODROP(0x1000012), + NODROP(0x1ffffff), NODROP(0x2000000), DROP(0x1ff00fe), NODROP(0x2000001), + NODROP(0x20fffff), NODROP(0x2105500), DROP(0x20ffffe), NODROP(0x21054ff), + NODROP(0x211ffff), DROP(0x2110000), NODROP(0x2120000) + /* The last test should be NODROP, because a DROP wouldn't get tested. */ +}; + +int main(int argc, char *argv[]) +{ + SSL_SESSION *sess; + SSL_CTX *ctx; + SSL *con; + BIO *rbio; + BIO *wbio; + BIO *err; + int testresult = 0; + int ret; + int i; + + SSL_library_init(); + SSL_load_error_strings(); + + err = BIO_new_fp(stderr, BIO_NOCLOSE | BIO_FP_TEXT); + + CRYPTO_malloc_debug_init(); + CRYPTO_set_mem_debug_options(V_CRYPTO_MDEBUG_ALL); + CRYPTO_mem_ctrl(CRYPTO_MEM_CHECK_ON); + + RAND_bytes(session_id, sizeof(session_id)); + RAND_bytes(master_secret, sizeof(master_secret)); + RAND_bytes(cookie, sizeof(cookie)); + RAND_bytes(server_random + 4, sizeof(server_random) - 4); + time((void *)server_random); + + sess = client_session(); + if (sess == NULL) { + printf("Failed to generate SSL_SESSION\n"); + goto end; + } + + if (!EVP_DigestInit_ex(&handshake_md5, EVP_md5(), NULL) || + !EVP_DigestInit_ex(&handshake_sha1, EVP_sha1(), NULL)) { + printf("Failed to initialise handshake_md\n"); + goto end; + } + + ctx = SSL_CTX_new(DTLSv1_client_method()); + if (ctx == NULL) { + printf("Failed to allocate SSL_CTX\n"); + goto end_md; + } + SSL_CTX_set_options(ctx, SSL_OP_CISCO_ANYCONNECT); + + if (!SSL_CTX_set_cipher_list(ctx, "AES128-SHA")) { + printf("SSL_CTX_set_cipher_list() failed\n"); + goto end_ctx; + } + + con = SSL_new(ctx); + if (!SSL_set_session(con, sess)) { + printf("SSL_set_session() failed\n"); + goto end_con; + } + SSL_SESSION_free(sess); + + rbio = BIO_new(BIO_s_mem()); + wbio = BIO_new(BIO_s_mem()); + + BIO_set_nbio(rbio, 1); + BIO_set_nbio(wbio, 1); + + SSL_set_bio(con, rbio, wbio); + SSL_set_connect_state(con); + + /* Send initial ClientHello */ + ret = SSL_do_handshake(con); + if (ret > 0 || SSL_get_error(con, ret) != SSL_ERROR_WANT_READ) { + printf("Unexpected handshake result at initial call!\n"); + goto end_con; + } + + if (validate_client_hello(wbio) != 1) { + printf("Initial ClientHello failed validation\n"); + goto end_con; + } + if (send_hello_verify(rbio) != 1) { + printf("Failed to send HelloVerify\n"); + goto end_con; + } + ret = SSL_do_handshake(con); + if (ret > 0 || SSL_get_error(con, ret) != SSL_ERROR_WANT_READ) { + printf("Unexpected handshake result after HelloVerify!\n"); + goto end_con; + } + if (validate_client_hello(wbio) != 2) { + printf("Second ClientHello failed validation\n"); + goto end_con; + } + if (send_server_hello(rbio) != 1) { + printf("Failed to send ServerHello\n"); + goto end_con; + } + ret = SSL_do_handshake(con); + if (ret > 0 || SSL_get_error(con, ret) != SSL_ERROR_WANT_READ) { + printf("Unexpected handshake result after ServerHello!\n"); + goto end_con; + } + if (send_finished(con, rbio) != 1) { + printf("Failed to send Finished\n"); + goto end_con; + } + ret = SSL_do_handshake(con); + if (ret < 1) { + printf("Handshake not successful after Finished!\n"); + goto end_con; + } + if (validate_ccs(wbio) != 1) { + printf("Failed to validate client CCS/Finished\n"); + goto end_con; + } + + /* While we're here and crafting packets by hand, we might as well do a + bit of a stress test on the DTLS record replay handling. Not Cisco-DTLS + specific but useful anyway for the general case. It's been broken + before, and in fact was broken even for a basic 0, 2, 1 test case + when this test was first added.... */ + for (i = 0; i < (int)OSSL_NELEM(tests); i++) { + unsigned long recv_buf[2]; + + if (send_record(rbio, SSL3_RT_APPLICATION_DATA, tests[i].seq, + &tests[i].seq, sizeof(unsigned long)) != 1) { + printf("Failed to send data seq #0x%lx (%d)\n", + tests[i].seq, i); + goto end_con; + } + + if (tests[i].drop) + continue; + + ret = SSL_read(con, recv_buf, 2 * sizeof(unsigned long)); + if (ret != sizeof(unsigned long)) { + printf("SSL_read failed or wrong size on seq#0x%lx (%d)\n", + tests[i].seq, i); + goto end_con; + } + if (recv_buf[0] != tests[i].seq) { + printf("Wrong data packet received (0x%lx not 0x%lx) at packet %d\n", + recv_buf[0], tests[i].seq, i); + goto end_con; + } + } + if (tests[i-1].drop) { + printf("Error: last test cannot be DROP()\n"); + goto end_con; + } + testresult=1; + + end_con: + SSL_free(con); + end_ctx: + SSL_CTX_free(ctx); + end_md: + EVP_MD_CTX_cleanup(&handshake_md5); + EVP_MD_CTX_cleanup(&handshake_sha1); + end: + ERR_print_errors_fp(stderr); + + if (!testresult) { + printf("Cisco BadDTLS test: FAILED\n"); + } + + ERR_free_strings(); + ERR_remove_thread_state(NULL); + EVP_cleanup(); + CRYPTO_cleanup_all_ex_data(); + CRYPTO_mem_leaks(err); + BIO_free(err); + + return testresult?0:1; +} diff --git a/test/Makefile b/test/Makefile index a3a7483d9a..bda1899bab 100644 --- a/test/Makefile +++ b/test/Makefile @@ -70,6 +70,7 @@ HEARTBEATTEST= heartbeat_test CONSTTIMETEST= constant_time_test VERIFYEXTRATEST= verify_extra_test CLIENTHELLOTEST= clienthellotest +BADDTLSTEST= bad_dtls_test SSLV2CONFTEST = sslv2conftest DTLSTEST = dtlstest @@ -85,7 +86,8 @@ EXE= $(BNTEST)$(EXE_EXT) $(ECTEST)$(EXE_EXT) $(ECDSATEST)$(EXE_EXT) $(ECDHTEST) $(EVPTEST)$(EXE_EXT) $(EVPEXTRATEST)$(EXE_EXT) $(IGETEST)$(EXE_EXT) $(JPAKETEST)$(EXE_EXT) $(SRPTEST)$(EXE_EXT) \ $(ASN1TEST)$(EXE_EXT) $(V3NAMETEST)$(EXE_EXT) $(HEARTBEATTEST)$(EXE_EXT) \ $(CONSTTIMETEST)$(EXE_EXT) $(VERIFYEXTRATEST)$(EXE_EXT) \ - $(CLIENTHELLOTEST)$(EXE_EXT) $(SSLV2CONFTEST)$(EXE_EXT) $(DTLSTEST)$(EXE_EXT) + $(CLIENTHELLOTEST)$(EXE_EXT) $(SSLV2CONFTEST)$(EXE_EXT) $(DTLSTEST)$(EXE_EXT) \ + $(BADDTLSTEST)$(EXE_EXT) # $(METHTEST)$(EXE_EXT) @@ -99,7 +101,8 @@ OBJ= $(BNTEST).o $(ECTEST).o $(ECDSATEST).o $(ECDHTEST).o $(IDEATEST).o \ $(BFTEST).o $(SSLTEST).o $(DSATEST).o $(EXPTEST).o $(RSATEST).o \ $(EVPTEST).o $(EVPEXTRATEST).o $(IGETEST).o $(JPAKETEST).o $(ASN1TEST).o $(V3NAMETEST).o \ $(HEARTBEATTEST).o $(CONSTTIMETEST).o $(VERIFYEXTRATEST).o \ - $(CLIENTHELLOTEST).o $(SSLV2CONFTEST).o $(DTLSTEST).o ssltestlib.o + $(CLIENTHELLOTEST).o $(SSLV2CONFTEST).o $(DTLSTEST).o ssltestlib.o \ + $(BADDTLSTEST).o SRC= $(BNTEST).c $(ECTEST).c $(ECDSATEST).c $(ECDHTEST).c $(IDEATEST).c \ $(MD2TEST).c $(MD4TEST).c $(MD5TEST).c \ @@ -110,7 +113,8 @@ SRC= $(BNTEST).c $(ECTEST).c $(ECDSATEST).c $(ECDHTEST).c $(IDEATEST).c \ $(BFTEST).c $(SSLTEST).c $(DSATEST).c $(EXPTEST).c $(RSATEST).c \ $(EVPTEST).c $(EVPEXTRATEST).c $(IGETEST).c $(JPAKETEST).c $(SRPTEST).c $(ASN1TEST).c \ $(V3NAMETEST).c $(HEARTBEATTEST).c $(CONSTTIMETEST).c $(VERIFYEXTRATEST).c \ - $(CLIENTHELLOTEST).c $(SSLV2CONFTEST).c $(DTLSTEST).c ssltestlib.c + $(CLIENTHELLOTEST).c $(SSLV2CONFTEST).c $(DTLSTEST).c ssltestlib.c \ + $(BADDTLSTEST).c EXHEADER= HEADER= testutil.h ssltestlib.h $(EXHEADER) @@ -155,7 +159,7 @@ alltests: \ test_ss test_ca test_engine test_evp test_evp_extra test_ssl test_tsa test_ige \ test_jpake test_srp test_cms test_ocsp test_v3name test_heartbeat \ test_constant_time test_verify_extra test_clienthello test_sslv2conftest \ - test_dtls + test_dtls test_bad_dtls test_evp: $(EVPTEST)$(EXE_EXT) evptests.txt ../util/shlib_wrap.sh ./$(EVPTEST) evptests.txt @@ -364,6 +368,10 @@ test_clienthello: $(CLIENTHELLOTEST)$(EXE_EXT) @echo $(START) $@ ../util/shlib_wrap.sh ./$(CLIENTHELLOTEST) +test_bad_dtls: $(BADDTLSTEST)$(EXE_EXT) + @echo $(START) $@ + ../util/shlib_wrap.sh ./$(BADDTLSTEST) + test_sslv2conftest: $(SSLV2CONFTEST)$(EXE_EXT) @echo $(START) $@ ../util/shlib_wrap.sh ./$(SSLV2CONFTEST) @@ -549,6 +557,9 @@ $(VERIFYEXTRATEST)$(EXE_EXT): $(VERIFYEXTRATEST).o $(CLIENTHELLOTEST)$(EXE_EXT): $(CLIENTHELLOTEST).o @target=$(CLIENTHELLOTEST) $(BUILD_CMD) +$(BADDTLSTEST)$(EXE_EXT): $(BADDTLSTEST).o + @target=$(BADDTLSTEST) $(BUILD_CMD) + $(SSLV2CONFTEST)$(EXE_EXT): $(SSLV2CONFTEST).o @target=$(SSLV2CONFTEST) $(BUILD_CMD) @@ -582,6 +593,26 @@ asn1test.o: ../include/openssl/pkcs7.h ../include/openssl/safestack.h asn1test.o: ../include/openssl/sha.h ../include/openssl/stack.h asn1test.o: ../include/openssl/symhacks.h ../include/openssl/x509.h asn1test.o: ../include/openssl/x509_vfy.h asn1test.c +bad_dtls_test.o: ../e_os.h ../include/openssl/asn1.h ../include/openssl/bio.h +bad_dtls_test.o: ../include/openssl/bn.h ../include/openssl/buffer.h +bad_dtls_test.o: ../include/openssl/comp.h ../include/openssl/crypto.h +bad_dtls_test.o: ../include/openssl/dtls1.h ../include/openssl/e_os2.h +bad_dtls_test.o: ../include/openssl/ec.h ../include/openssl/ecdh.h +bad_dtls_test.o: ../include/openssl/ecdsa.h ../include/openssl/err.h +bad_dtls_test.o: ../include/openssl/evp.h ../include/openssl/hmac.h +bad_dtls_test.o: ../include/openssl/kssl.h ../include/openssl/lhash.h +bad_dtls_test.o: ../include/openssl/obj_mac.h ../include/openssl/objects.h +bad_dtls_test.o: ../include/openssl/opensslconf.h ../include/openssl/opensslv.h +bad_dtls_test.o: ../include/openssl/ossl_typ.h ../include/openssl/pem.h +bad_dtls_test.o: ../include/openssl/pem2.h ../include/openssl/pkcs7.h +bad_dtls_test.o: ../include/openssl/pqueue.h ../include/openssl/rand.h +bad_dtls_test.o: ../include/openssl/safestack.h ../include/openssl/sha.h +bad_dtls_test.o: ../include/openssl/srtp.h ../include/openssl/ssl.h +bad_dtls_test.o: ../include/openssl/ssl2.h ../include/openssl/ssl23.h +bad_dtls_test.o: ../include/openssl/ssl3.h ../include/openssl/stack.h +bad_dtls_test.o: ../include/openssl/symhacks.h ../include/openssl/tls1.h +bad_dtls_test.o: ../include/openssl/x509.h ../include/openssl/x509_vfy.h +bad_dtls_test.o: bad_dtls_test.c bftest.o: ../e_os.h ../include/openssl/blowfish.h ../include/openssl/e_os2.h bftest.o: ../include/openssl/opensslconf.h bftest.c bntest.o: ../e_os.h ../include/openssl/asn1.h ../include/openssl/bio.h -- cgit v1.2.3