Try using OpenSSL directly
authorDavid Woodhouse <David.Woodhouse@intel.com>
Sun, 21 Sep 2008 19:59:05 +0000 (12:59 -0700)
committerDavid Woodhouse <David.Woodhouse@intel.com>
Sun, 21 Sep 2008 19:59:05 +0000 (12:59 -0700)
cisco.c

diff --git a/cisco.c b/cisco.c
index cf0d5a1..8b4f00f 100644 (file)
--- a/cisco.c
+++ b/cisco.c
@@ -1,4 +1,5 @@
 #include <stdio.h>
+#include <stdarg.h>
 #include <stdlib.h>
 #include <string.h>
 #include <errno.h>
@@ -8,6 +9,14 @@
 #include <net/if.h>
 #include <sys/ioctl.h>
 #include <linux/if_tun.h>
+#include <openssl/ssl.h>
+#include <sys/utsname.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netdb.h>
+
+#define _GNU_SOURCE
+#include <getopt.h>
 
 #if 0
 /* If the cookie expires, you can get another one by connecting with 
@@ -23,31 +32,28 @@ X-Transcend-Version: 1
 
 #endif
 
-char request[] = "CONNECT /CSCOSSLC/tunnel HTTP/1.1\r\n"
-       "Host: vpnserver\r\n"
-       "User-Agent: Cisco AnyConnect VPN Agent for Windows 2.2.0\r\n"
-       "Cookie: webvpn=835267836@921600@1221512527@6BC73D90EB2F59E242F75B424D42F223D0912984\r\n"
-       "X-CSTP-Version: 1\r\n"
-       "X-CSTP-Hostname: macbook.infradead.org\r\n"
-       "X-CSTP-Accept-Encoding: xxdeflate;q=1.0\r\n"
-       "X-CSTP-MTU: 1406\r\n"
-       "X-CSTP-Address-Type: IPv6,IPv4\r\n"
-       "X-DTLS-Master-Secret: 5A5A5A5A5A5A5A5A5A5A5A5A5A5A5A5A5A5A5A5A5A5A5A5A5A5A5A5A5A5A5A5A5A5A5A5A5A5A5A5A5A5A5A5A5A5A5A5A\r\n"
-       "X-DTLS-CipherSuite: AES256-SHA:AES128-SHA:DES-CBC3-SHA:DES-CBC-SHA\r\n\r\n";
-
 /* The master-secret is generated randomly by the client. The server
    responds with a DTLS Session-ID. These are enough to 'resume' the DTLS
    session, bypassing all the initial setup of a normal DTLS connection.
    Or you can just send traffic over the HTTPS connection... */
 
-int main(int argc, char **argv)
+struct cstp_option {
+       const char *option;
+       const char *value;
+       struct cstp_option *next;
+};
+
+char *cookie;
+char *hostname;
+unsigned char dtls_secret[48];
+int mtu = 1406;
+int deflate;
+const char *useragent = "Cisco AnyConnect VPN Agent for Windows 2.2.0";
+int verbose;
+
+/* Set up a tuntap device. */
+int setup_tun(struct cstp_option *options)
 {
-       int in_pipes[2];
-       int out_pipes[2];
-       pid_t ssl_pid;
-       unsigned char buf[65536 + 8];
-       int buflen;
-       int state = 0;
        struct ifreq ifr;
        int tun_fd;
 
@@ -63,38 +69,210 @@ int main(int argc, char **argv)
                perror("TUNSETIFF");
                exit(1);
        }
+       /* FIXME: Configure it... */
+       /* Better still, use lwip and just provide a SOCKS server rather than
+          telling the kernel at all */
+       return tun_fd;
+}
+
+SSL *open_https(const char *host)
+{
+       SSL_METHOD *ssl3_method;
+       SSL_CTX *https_ctx;
+       SSL *https_ssl;
+       BIO *https_bio;
+       int ssl_sock;
+       int err;
+       struct addrinfo hints, *result, *rp;
+
+       memset(&hints, 0, sizeof(struct addrinfo));
+       hints.ai_family = AF_UNSPEC;    /* Allow IPv4 or IPv6 */
+       hints.ai_socktype = SOCK_STREAM; /* Datagram socket */
+       hints.ai_flags = AI_PASSIVE;    /* For wildcard IP address */
+       hints.ai_protocol = 0;          /* Any protocol */
+       hints.ai_canonname = NULL;
+       hints.ai_addr = NULL;
+       hints.ai_next = NULL;
+
+       err = getaddrinfo(host, "https", &hints, &result);
+       if (err) {
+               fprintf(stderr, "getaddrinfo failed: %s\n", gai_strerror(err));
+               return NULL;
+       }
 
-       pipe(in_pipes);
-       pipe(out_pipes);
+       for (rp = result; rp ; rp = rp->ai_next) {
+               ssl_sock = socket(rp->ai_family, rp->ai_socktype,
+                                 rp->ai_protocol);
+               if (ssl_sock < 0)
+                       continue;
 
-       ssl_pid = fork();
-       if (!ssl_pid) {
-               dup2(out_pipes[0], 0);
-               dup2(in_pipes[1], 1);
+               if (connect(ssl_sock, rp->ai_addr, rp->ai_addrlen) >= 0)
+                       break;
 
-               close(in_pipes[0]);
-               close(in_pipes[1]);
-               close(out_pipes[0]);
-               close(out_pipes[1]);
-               execlp("openssl", "openssl", "s_client", "-quiet", "-connect", "vpnserver:443", NULL);
-               perror("exec");
-               exit(1);
+               close(ssl_sock);
+       }
+       freeaddrinfo(result);
+
+       if (!rp) {
+               fprintf(stderr, "Failed to connect to host %s\n", host);
+               return NULL;
+       }
+
+       ssl3_method = SSLv23_client_method();
+       https_ctx = SSL_CTX_new(ssl3_method);
+       https_ssl = SSL_new(https_ctx);
+               
+       https_bio = BIO_new_socket(ssl_sock, BIO_NOCLOSE);
+       SSL_set_bio(https_ssl, https_bio, https_bio);
+
+       if (SSL_connect(https_ssl) <= 0) {
+               BIO *err_bio = BIO_new_fp(stderr, BIO_NOCLOSE);
+               fprintf(stderr,  "SSL connection failure\n");
+               SSL_free(https_ssl);
+               SSL_CTX_free(https_ctx);
+               return NULL;
+       }
+
+       return https_ssl;
+}
+
+struct cstp_option *start_ssl_connection(SSL *ssl)
+{
+       char buf[65536];
+       int i, state = 0;
+       struct cstp_option *options = NULL, **next_opt = &options;
+       struct utsname utsbuf;
+       BIO *bio = BIO_new(BIO_f_ssl());
+       
+       BIO_set_ssl(bio, ssl, BIO_NOCLOSE);
+       
+       if (uname(&utsbuf))
+               sprintf(utsbuf.nodename, "localhost");
+
+       if (verbose)
+               printf("Connected to HTTPS on %s\n", hostname);
+
+       BIO_printf(bio, "CONNECT /CSCOSSLC/tunnel HTTP/1.1\r\n");
+       BIO_printf(bio, "Host: %s\r\n", hostname);
+       BIO_printf(bio, "User-Agent: %S\r\n", useragent);
+       BIO_printf(bio, "Cookie: webvpn=%s\r\n", cookie);
+       BIO_printf(bio, "X-CSTP-Version: 1\r\n");
+       BIO_printf(bio, "X-CSTP-Hostname: %s\r\n", utsbuf.nodename);
+       if (deflate)
+               BIO_printf(bio, "X-CSTP-Accept-Encoding: deflate;q=1.0\r\n");
+       BIO_printf(bio, "X-CSTP-MTU: %d\r\n", mtu);
+       BIO_printf(bio, "X-CSTP-Address-Type: IPv6,IPv4\r\n");
+       BIO_printf(bio, "X-DTLS-Master-Secret: ");
+       for (i = 0; i < sizeof(dtls_secret); i++)
+               BIO_printf(bio, "%02X", dtls_secret[i]);
+       BIO_printf(bio, "X-DTLS-CipherSuite: AES256-SHA:AES128-SHA:DES-CBC3-SHA:DES-CBC-SHA\r\n\r\n");
+
+       state = 0;
+#if 0
+       state = BIO_gets(bio, buf, sizeof(buf)-1);
+
+       if (state <= 0) {
+               fprintf(stderr, "Error getting HTTP CONNECT response: %d\n", state);
+               BIO_free(bio);
+               return NULL;
        }
+       if (!strncmp(buf, "HTTP/1.1 200 ", 13)) {
+               fprintf(stderr, "Got inappropriate HTTP CONNECT response: %s\n",
+                       buf);
+               BIO_free(bio);
+               return NULL;
+       }
+
+       while (BIO_gets(bio, buf, sizeof(buf)-1)) {
+               char *colon = strchr(buf, ':');
+
+               if (buf[strlen(buf)] == '\r') {
+                       printf("ends \\r\n");
+                       buf[strlen(buf)] = 0;
+               }
 
-       write(out_pipes[1], request, sizeof(request));
-       while (state < 4) {
-               read(in_pipes[0], buf, 1);
-               if ((state == 0 || state == 2) &&
-                   buf[0] == '\r')
-                       state++;
-               else if ((state == 1 || state == 3) &&
-                   buf[0] == '\n')
-                       state++;
-               else state = 0;
-               write(1, buf, 1);
+               if (!strlen(buf))
+                       break;
+
+               fprintf(stderr, "Got: %s\n", buf);
        }
+#else
+        while (state < 4) {
+                SSL_read(ssl, buf, 1);
+                if ((state == 0 || state == 2) &&
+                    buf[0] == '\r')
+                        state++;
+                else if ((state == 1 || state == 3) &&
+                    buf[0] == '\n')
+                        state++;
+                else state = 0;
+                write(1, buf, 1);
+        }
+#endif
+       return NULL;
+}
+
+static struct option long_options[] = {
+       {"cookie", 1, 0, 'c'},
+       {"host", 1, 0, 'h'},
+       {"mtu", 1, 0, 'm'},
+};
+
+int main(int argc, char **argv)
+{
+       SSL *https_ssl;
+       struct cstp_option *opts;
+       int tun_fd;
+       int optind;
+       int opt;
+       char buf[65536 + 8];
+
+       SSL_library_init ();
+       ERR_clear_error ();
+       SSL_load_error_strings ();
+       OpenSSL_add_all_algorithms ();
+
+       while (opt = getopt_long(argc, argv, "c:h:", long_options, &optind)) {
+               if (opt < 0)
+                       break;
+
+               switch (opt) {
+               case 'c':
+                       cookie = optarg;
+                       break;
+
+               case 'h':
+                       hostname = optarg;
+                       break;
+
+               case 'm':
+                       mtu = atol(optarg);
+                       if (mtu < 576) {
+                               fprintf(stderr, "MTU %d too small\n", mtu);
+                               exit(1);
+                       }
+                       break;
+               }
+       }
+       if (!hostname || !cookie) {
+               fprintf(stderr, "Need -h hostname, -c cookie\n");
+               exit(1);
+       }
+
+       https_ssl = open_https(hostname);
+       if (!https_ssl)
+               exit(1);
+       int i;
+       for (i=0; i<48; i++) 
+               dtls_secret[i] = i;
+
+       opts = start_ssl_connection(https_ssl);
+
+       exit(1);
        printf("Connected\n");
 
+       tun_fd = setup_tun(NULL);
+
        if (fork()) {
                while (1) {
                        size_t len;
@@ -113,15 +291,15 @@ int main(int argc, char **argv)
                                buf[4] = len >> 8;
                                buf[5] = len & 0xff;
                        }
-                       write(out_pipes[1], buf, len + 8);
+                       SSL_write(https_ssl, buf, len + 8);
                }
        } else {
                while (1) {
                        int len;
-                       read(in_pipes[0], buf, 8);
+                       SSL_read(https_ssl, buf, 8);
                        
                        len = (buf[4] << 8) + buf [5];
-                       read(in_pipes[0], buf + 8, len);
+                       SSL_read(https_ssl, buf + 8, len);
                        if (buf[0] != 'S' ||
                            buf[1] != 'T' ||
                            buf[2] != 'F' ||