I'm working on a VPN client compatible with Cisco's AnyConnect, which uses SSL + DTLS. My code so far is at git:// or http://git.infradead.org/users/dwmw2/anyconnect.git
It's mostly going quite well, but I'm having trouble with DTLS compatibility and would massively appreciate some help from someone a little more clueful about DTLS. I can only actually get a successful handshake with their server when I use the libssl.so.0.9.8 which is shipped as part Cisco's own client -- I can't get it working with any version of OpenSSL that I build myself. My library always rejects the ServerHello packet: 12293:error:14101119:SSL routines:DTLS1_PROCESS_RECORD:decryption failed or bad record mac:d1_pkt.c:466: Their library seems to be (contains the strings): OpenSSL 0.9.8f 11 Oct 2007 However, the traffic I see on the wire is the pre-RFC version of DTLS (DTLS1_BAD_VER). But OpenSSL 0.9.8f postdates RFC4347 and had already been updated to the official protocol version number -- so whatever they're shipping, it's certainly not a pristine OpenSSL 0.9.8f. I've tried using the last release of OpenSSL which actually supported that version of the DTLS protocol (0.9.8e), and I've tried 0.9.8f and newer versions. None of them work. The only thing that works is using LD_LIBRARY_PATH to run my client against _their_ build of the library. I suspect that they haven't done anything particularly _exciting_ in their version of the library -- they've probably just reverted some of the updates in 0.9.8f which change to the the new behaviour defined in RFC4347. And maybe back-ported some later bug fixes. I've tried starting with 0.9.8f and reverting individual changes to see if I could get it to work, and I've tried some intermediate points between 0.9.8e and 0.9.8f, but haven't really had much luck and I'm mostly clueless about the details -- I would really appreciate some input from someone who actually knows the protocol a little better. I'm kind of hoping it's immediately obvious to someone. Although their own client uses the old version (DTLS1_BAD_VER) of the protocol, their server does actually seem to respond to the new version too. Its responses are slightly different (two extra bytes) according to whether I use DTLS1_BAD_VER or DTLS1_VERSION, so it does seem to be adapting to what the client sends rather than just echoing the version number. But the ultimate result in both cases is exactly the same. I send a Client Hello, get a Hello Verify Request. I send the Client Hello again, get a Server Hello. And then I send a 'Bad Record MAC' alert and the connection is over. I've made a simple test case, by capturing the traffic during a _failed_ session with OpenSSL-0.9.8e. The captured packets _work_ when I play them back against their library, but obviously fail when run against my build of OpenSSL-0.9.8e. This capture is using DTLS1_BAD_VER, of course. I _could_ do a capture using a newer version of OpenSSL and thus with DTLS1_VERSION, but the server side of that might be broken anyway -- I have no reason to believe there are _any_ working clients using that version of the protocol. And I wouldn't be able to show that test case working, either. So I think it's best to work with the old version of the protocol to start with, and work out what's going on. The test program is at http://david.woodhou.se/dtls-test.c and also below, and their version of libssl.so.0.9.8 is next to it on the web server (sha1 21a5704ea4f66dd48ea2b9d6c5b8ab85d4e16d89). [EMAIL PROTECTED] anyconnect]$ LD_LIBRARY_PATH=/opt/cisco/vpn/lib ./dtls-test Found AES128-SHA cipher at 28 SSL_SESSION is 200 bytes Child done. Success [EMAIL PROTECTED] anyconnect]$ LD_LIBRARY_PATH=/home/dwmw2/working/openssl-0.9.8e ./dtls-test Found AES128-SHA cipher at 29 SSL_SESSION is 200 bytes < ... lots of debugging that I added, which didn't enlighten me at all ... > DTLS connection returned 0 12994:error:14101119:SSL routines:DTLS1_PROCESS_RECORD:decryption failed or bad record mac:d1_pkt.c:466: Child done. This is the test case.... /* * Open AnyConnect (SSL + DTLS) client * * © 2008 David Woodhouse <[EMAIL PROTECTED]> * * Permission to use, copy, modify, and/or distribute this software * for any purpose with or without fee is hereby granted, provided * that the above copyright notice and this permission notice appear * in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS * OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, * NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include <errno.h> #include <sys/types.h> #include <sys/socket.h> #include <netdb.h> #include <unistd.h> #include <openssl/ssl.h> #include <openssl/err.h> #include <fcntl.h> #include <time.h> /* We want it to be reproducible */ time_t time(time_t *t) { time_t x = 0x3ab2d948; if (t) *t = x; return x; } int RAND_pseudo_bytes(char *buf, int len) { memset(buf, 0x5a, len); return 1; } int RAND_bytes(char *buf, int len) { memset(buf, 0x5b, len); return 1; } char master_secret[48]; #if 0 /* Test case recorded when _working_ */ char session_id[] = { 0x60, 0x1E, 0xF8, 0x4A, 0xB7, 0x93, 0x48, 0xBB, 0x4A, 0xF9, 0x98, 0x40, 0x5E, 0x89, 0xA5, 0x8A, 0xD7, 0xC6, 0x14, 0x7A, 0xCE, 0x62, 0x4D, 0xEB, 0xB3, 0x8D, 0x15, 0x42, 0x87, 0x62, 0xD9, 0x07 }; unsigned char verify_request[] = { 0x16, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0f, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x01, 0x00, 0x00 }; unsigned char server_hello[] = { 0x16, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x52, 0x02, 0x00, 0x00, 0x46, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x46, 0x01, 0x00, 0x48, 0xd9, 0xb2, 0xf2, 0x0f, 0x9a, 0x91, 0x28, 0x56, 0x77, 0x36, 0xce, 0x1b, 0x26, 0x6b, 0x9b, 0xcf, 0x77, 0x95, 0x20, 0xa6, 0x67, 0x4d, 0x29, 0x62, 0x19, 0x69, 0xec, 0x0a, 0xf3, 0x36, 0x34, 0x20, 0x60, 0x1e, 0xf8, 0x4a, 0xb7, 0x93, 0x48, 0xbb, 0x4a, 0xf9, 0x98, 0x40, 0x5e, 0x89, 0xa5, 0x8a, 0xd7, 0xc6, 0x14, 0x7a, 0xce, 0x62, 0x4d, 0xeb, 0xb3, 0x8d, 0x15, 0x42, 0x87, 0x62, 0xd9, 0x07, 0x00, 0x2f, 0x00, 0x14, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x03, 0x01, 0x00, 0x02, 0x16, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x0d, 0x9d, 0x2e, 0x90, 0x9f, 0x80, 0xb6, 0x2c, 0x55, 0x24, 0x46, 0xa8, 0xf4, 0xf7, 0xd3, 0x8e, 0x86, 0xac, 0x15, 0xf2, 0xfe, 0x91, 0xc4, 0x2e, 0xce, 0x47, 0xbc, 0x23, 0x0b, 0xa9, 0xc7, 0x36, 0x03, 0x07, 0xd8, 0xfc, 0xcb, 0x10, 0x2f, 0xe5, 0xa0, 0xb8, 0x67, 0x6a, 0xc1, 0xc2, 0xe9, 0xcd, 0xdd, 0xcb, 0x12, 0xdd, 0x57, 0x95, 0xe1, 0xbb, 0x74, 0xac, 0x05, 0x00, 0xea, 0xf8, 0x71, 0x59 }; #else /* This one was captured with OpenSSL 0.9.8e on the client side, which rejected the ServerHello. Yet it works fine with Cisco's build of the library */ char session_id[] = { 0x60, 0x1E, 0xF8, 0x4A, 0xB7, 0x93, 0x48, 0xBB, 0x4A, 0xF9, 0x98, 0x40, 0x5E, 0x89, 0xA5, 0x8A, 0xD7, 0xC6, 0x14, 0x7A, 0xCE, 0x62, 0x4D, 0xEB, 0xB3, 0x8D, 0x15, 0x42, 0x87, 0x62, 0xD9, 0x07 }; unsigned char verify_request[] = { 0x16, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0f, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x01, 0x00, 0x00 }; unsigned char server_hello[] = { 0x16, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x52, 0x02, 0x00, 0x00, 0x46, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x46, 0x01, 0x00, 0x48, 0xd9, 0xc9, 0xae, 0x12, 0x1d, 0x7a, 0x84, 0x85, 0x4b, 0x0f, 0x2f, 0x75, 0x09, 0xf3, 0x20, 0xd2, 0x0a, 0xf5, 0x4e, 0x82, 0x9f, 0xb2, 0x60, 0x50, 0xda, 0x91, 0x34, 0x21, 0xd9, 0x32, 0xc0, 0x20, 0x60, 0x1e, 0xf8, 0x4a, 0xb7, 0x93, 0x48, 0xbb, 0x4a, 0xf9, 0x98, 0x40, 0x5e, 0x89, 0xa5, 0x8a, 0xd7, 0xc6, 0x14, 0x7a, 0xce, 0x62, 0x4d, 0xeb, 0xb3, 0x8d, 0x15, 0x42, 0x87, 0x62, 0xd9, 0x07, 0x00, 0x2f, 0x00, 0x14, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x03, 0x01, 0x00, 0x02, 0x16, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0xf8, 0x31, 0xa0, 0x7c, 0x83, 0xb3, 0x9e, 0x7d, 0xbb, 0x23, 0xee, 0x6c, 0x41, 0xdb, 0x70, 0xc2, 0x60, 0x19, 0xc1, 0xc4, 0x82, 0x57, 0x81, 0x52, 0x43, 0x78, 0xb4, 0x7b, 0x7d, 0x5c, 0xd0, 0x57, 0xee, 0xa3, 0x5b, 0x6c, 0xba, 0xa7, 0x8c, 0x2d, 0x76, 0x43, 0x3e, 0xa7, 0x93, 0x2e, 0xbe, 0xba, 0xbe, 0x1f, 0x42, 0x1f, 0x39, 0xb9, 0x40, 0x4e, 0x8a, 0xa1, 0xa0, 0x0b, 0x50, 0x3e, 0x99, 0x85 }; #endif int dump_traffic = 0; void hexdump(unsigned char *buf, int len) { int i; for (i=0; i<len; i++) { if (!(i&15)) printf("\n%04x: ", i); printf("%02x ", buf[i]); } printf("\n"); } void feed_responses(int fd) { char buf[2000]; int len; len = read(fd, buf, 2000); if (len < 0) { perror("read 1"); exit(1); } if (dump_traffic) { printf("Received packet 1:"); hexdump(buf, len); } len = write(fd, verify_request, sizeof(verify_request)); if (dump_traffic) printf("Wrote %d bytes of Hello Verify\n", len); len = read(fd, buf, 2000); if (len < 0) { perror("read 2"); exit(1); } if (dump_traffic) { printf("Received packet 2:"); hexdump(buf, len); } len = write(fd, server_hello, sizeof(server_hello)); if (dump_traffic) printf("Wrote %d bytes of Server Hello\n", len); len = read(fd, buf, 2000); if (len < 0) { perror("read 3"); exit(1); } if (dump_traffic) { printf("Received packet 3:"); hexdump(buf, len); } } extern SSL_CIPHER ssl3_ciphers[]; int main(void) { int sockets[2]; pid_t child_pid; int cipher; SSL_METHOD *dtls_method; SSL_CTX *dtls_ctx; SSL_SESSION *dtls_session; SSL_CIPHER *https_cipher; SSL *dtls_ssl; BIO *dtls_bio; int ret; int aes128_cipher; if (socketpair(AF_UNIX, SOCK_DGRAM, 0, sockets)) { perror("socketpair"); return 1; } child_pid = fork(); if (child_pid < 0) { perror("fork"); return 1; } if (!child_pid) { close(sockets[1]); feed_responses(sockets[0]); printf("Child done.\n"); exit(1); } close(sockets[0]); /* Not really very random. */ RAND_bytes(master_secret, 48); /* Normally we go do an HTTPS exchange here with the server; we tell it our master secret, and it gives us the session-id. Then we "resume" a DTLS session... */ SSL_library_init (); ERR_clear_error (); SSL_load_error_strings (); OpenSSL_add_all_algorithms (); dtls_method = DTLSv1_client_method(); dtls_ctx = SSL_CTX_new(dtls_method); SSL_CTX_set_read_ahead(dtls_ctx, 1); dtls_ssl = SSL_new(dtls_ctx); SSL_set_connect_state(dtls_ssl); /* Normally we use the same cipher as the HTTPS session. */ // https_cipher = SSL_get_current_cipher(vpninfo->https_ssl); while (ssl3_ciphers[aes128_cipher].id != 0x0300002f) aes128_cipher++; printf("Found %s cipher at %d\n", ssl3_ciphers[aes128_cipher].name, aes128_cipher); SSL_set_cipher_list(dtls_ssl, "AES128-SHA"); printf("SSL_SESSION is %d bytes\n", sizeof(*dtls_session)); if (sizeof(*dtls_session) != 200) { printf("WARNING: The version of OpenSSL you're building against is\n"); printf("not ABI-compatible with Cisco's build of libssl.0.9.8.\n"); } dtls_session = SSL_SESSION_new(); dtls_session->ssl_version = DTLS1_BAD_VER; dtls_session->master_key_length = sizeof(master_secret); memcpy(dtls_session->master_key, master_secret, sizeof(master_secret)); dtls_session->session_id_length = sizeof(session_id); memcpy(dtls_session->session_id, session_id, sizeof(session_id)); dtls_session->cipher = &ssl3_ciphers[aes128_cipher]; dtls_session->cipher_id = 0x0300002f; if (!SSL_set_session(dtls_ssl, dtls_session)) { printf("SSL_set_session() failed.\n"); return -EINVAL; } if (!SSL_CTX_add_session(dtls_ctx, dtls_session)) printf("SSL_CTX_add_session() failed\n"); dtls_bio = BIO_new_socket(sockets[1], BIO_NOCLOSE); SSL_set_bio(dtls_ssl, dtls_bio, dtls_bio); ret = SSL_do_handshake(dtls_ssl); if (ret != 1) { fprintf(stderr, "DTLS connection returned %d\n", ret); if (ret < 0) fprintf(stderr, "DTLS handshake error: %d\n", SSL_get_error(dtls_ssl, ret)); ERR_print_errors_fp(stderr); SSL_free(dtls_ssl); SSL_CTX_free(dtls_ctx); close(sockets[1]); exit(1); return -EINVAL; } printf("Success\n"); return 0; } -- dwmw2 ______________________________________________________________________ OpenSSL Project http://www.openssl.org User Support Mailing List openssl-users@openssl.org Automated List Manager [EMAIL PROTECTED]