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]

Reply via email to