|  | /* | 
|  | * 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 <string.h> | 
|  |  | 
|  | #include <openssl/opensslconf.h> | 
|  | #include <openssl/bio.h> | 
|  | #include <openssl/crypto.h> | 
|  | #include <openssl/evp.h> | 
|  | #include <openssl/ssl.h> | 
|  | #include <openssl/err.h> | 
|  | #include <openssl/rand.h> | 
|  | #include <openssl/kdf.h> | 
|  |  | 
|  | #include "../ssl/packet_locl.h" | 
|  | #include "../e_os.h" /* for OSSL_NELEM() */ | 
|  |  | 
|  | /* 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_md; | 
|  |  | 
|  | 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) | 
|  | { | 
|  | EVP_PKEY_CTX *pctx = EVP_PKEY_CTX_new_id(EVP_PKEY_TLS1_PRF, NULL); | 
|  | size_t outlen = olen; | 
|  |  | 
|  | /* No error handling. If it all screws up, the test will fail anyway */ | 
|  | EVP_PKEY_derive_init(pctx); | 
|  | EVP_PKEY_CTX_set_tls1_prf_md(pctx, EVP_md5_sha1()); | 
|  | EVP_PKEY_CTX_set1_tls1_prf_secret(pctx, master_secret, sizeof(master_secret)); | 
|  | EVP_PKEY_CTX_add1_tls1_prf_seed(pctx, seed1, seed1_len); | 
|  | EVP_PKEY_CTX_add1_tls1_prf_seed(pctx, seed2, seed2_len); | 
|  | EVP_PKEY_CTX_add1_tls1_prf_seed(pctx, seed3, seed3_len); | 
|  | EVP_PKEY_derive(pctx, out, &outlen); | 
|  | EVP_PKEY_CTX_free(pctx); | 
|  | 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_md, 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_md, 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 */ | 
|  | ctx = HMAC_CTX_new(); | 
|  | HMAC_Init_ex(ctx, mac_key, 20, EVP_sha1(), NULL); | 
|  | 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_free(ctx); | 
|  |  | 
|  | /* Append padding bytes */ | 
|  | len += SHA_DIGEST_LENGTH; | 
|  | do { | 
|  | enc[len++] = pad; | 
|  | } while (len % 16); | 
|  |  | 
|  | /* Generate IV, and encrypt */ | 
|  | RAND_bytes(iv, sizeof(iv)); | 
|  | enc_ctx = EVP_CIPHER_CTX_new(); | 
|  | EVP_CipherInit_ex(enc_ctx, EVP_aes_128_cbc(), NULL, enc_key, iv, 1); | 
|  | EVP_Cipher(enc_ctx, enc, enc, len); | 
|  | EVP_CIPHER_CTX_free(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]; | 
|  |  | 
|  | /* 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_md, handshake_hash, 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_md), | 
|  | 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; | 
|  |  | 
|  | err = BIO_new_fp(stderr, BIO_NOCLOSE | BIO_FP_TEXT); | 
|  |  | 
|  | CRYPTO_set_mem_debug(1); | 
|  | 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; | 
|  | } | 
|  |  | 
|  | handshake_md = EVP_MD_CTX_new(); | 
|  | if (handshake_md == NULL || | 
|  | !EVP_DigestInit_ex(handshake_md, EVP_md5_sha1(), NULL)) { | 
|  | printf("Failed to initialise handshake_md\n"); | 
|  | goto end; | 
|  | } | 
|  |  | 
|  | ctx = SSL_CTX_new(DTLS_client_method()); | 
|  | if (ctx == NULL) { | 
|  | printf("Failed to allocate SSL_CTX\n"); | 
|  | goto end_md; | 
|  | } | 
|  | if (!SSL_CTX_set_min_proto_version(ctx, DTLS1_BAD_VER)) { | 
|  | printf("SSL_CTX_set_min_proto_version() failed\n"); | 
|  | goto end_ctx; | 
|  | } | 
|  | if (!SSL_CTX_set_max_proto_version(ctx, DTLS1_BAD_VER)) { | 
|  | printf("SSL_CTX_set_max_proto_version() failed\n"); | 
|  | goto end_ctx; | 
|  | } | 
|  |  | 
|  | 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_free(handshake_md); | 
|  | end: | 
|  | ERR_print_errors_fp(stderr); | 
|  |  | 
|  | if (!testresult) { | 
|  | printf("Cisco BadDTLS test: FAILED\n"); | 
|  | } | 
|  |  | 
|  |  | 
|  | #ifndef OPENSSL_NO_CRYPTO_MDEBUG | 
|  | if (CRYPTO_mem_leaks(err) <= 0) | 
|  | testresult = 0; | 
|  | #endif | 
|  | BIO_free(err); | 
|  |  | 
|  | return testresult?0:1; | 
|  | } |