]> git.ipfire.org Git - people/ms/strongswan.git/commitdiff
Support output fragmentation of TLS records
authorMartin Willi <martin@revosec.ch>
Tue, 31 Aug 2010 06:57:26 +0000 (08:57 +0200)
committerMartin Willi <martin@revosec.ch>
Tue, 31 Aug 2010 13:54:37 +0000 (15:54 +0200)
src/libcharon/plugins/eap_tls/eap_tls.c
src/libcharon/plugins/eap_ttls/eap_ttls.c
src/libtls/tls.c
src/libtls/tls.h
src/libtls/tls_socket.c

index 8b5d4cb19f8d0f018bb2edc7f3549afd5c026407..155038056f1bf6901b1f81f987d3f02a7ade1261 100644 (file)
@@ -251,7 +251,7 @@ static status_t process_buf(private_eap_tls_t *this)
 {
        status_t status;
 
-       status = this->tls->process(this->tls, this->input);
+       status = this->tls->process(this->tls, this->input.ptr, this->input.len);
        if (status != NEED_MORE)
        {
                return status;
@@ -260,7 +260,8 @@ static status_t process_buf(private_eap_tls_t *this)
        this->inpos = 0;
 
        chunk_free(&this->output);
-       return this->tls->build(this->tls, &this->output);
+       this->output = chunk_alloc(EAP_TLS_FRAGMENT_LEN);
+       return this->tls->build(this->tls, this->output.ptr, &this->output.len, NULL);
 }
 
 METHOD(eap_method_t, process, status_t,
index a3cfcd1f74a5f216635d83d2a1d922de64cf455c..e2f09a70c5041d997ae42ea41a9b33382404cb13 100644 (file)
@@ -254,7 +254,7 @@ static status_t process_buf(private_eap_ttls_t *this)
 {
        status_t status;
 
-       status = this->tls->process(this->tls, this->input);
+       status = this->tls->process(this->tls, this->input.ptr, this->input.len);
        if (status != NEED_MORE)
        {
                return status;
@@ -263,7 +263,8 @@ static status_t process_buf(private_eap_ttls_t *this)
        this->inpos = 0;
 
        chunk_free(&this->output);
-       return this->tls->build(this->tls, &this->output);
+       this->output = chunk_alloc(EAP_TTLS_FRAGMENT_LEN);
+       return this->tls->build(this->tls, this->output.ptr, &this->output.len, NULL);
 }
 
 METHOD(eap_method_t, process, status_t,
index 91d89db8f3819bce7316e67fa0ae8c6ef5c7b033..61787e366838d896e6058c6acc2272806e82f112 100644 (file)
@@ -137,6 +137,16 @@ struct private_tls_t {
         * Number of bytes read in input buffer
         */
        size_t inpos;
+
+       /**
+        * Allocated output buffer
+        */
+       chunk_t output;
+
+       /**
+        * Number of bytes processed from output buffer
+        */
+       size_t outpos;
 };
 
 /**
@@ -150,23 +160,27 @@ typedef struct __attribute__((packed)) {
 } tls_record_t;
 
 METHOD(tls_t, process, status_t,
-       private_tls_t *this, chunk_t data)
+       private_tls_t *this, void *buf, size_t buflen)
 {
        tls_record_t *record;
        status_t status;
        u_int len;
 
-       while (data.len > sizeof(tls_record_t))
+       while (buflen)
        {
                if (this->input.len == 0)
                {
+                       if (buflen < sizeof(tls_record_t))
+                       {
+                               break;
+                       }
                        while (TRUE)
                        {
                                /* try to process records inline */
-                               record = (tls_record_t*)data.ptr;
+                               record = buf;
                                len = untoh16(&record->length);
 
-                               if (len + sizeof(tls_record_t) > data.len)
+                               if (len + sizeof(tls_record_t) > buflen)
                                {       /* not a full record, read to buffer */
                                        this->input = chunk_alloc(len + sizeof(tls_record_t));
                                        this->inpos = 0;
@@ -180,16 +194,18 @@ METHOD(tls_t, process, status_t,
                                {
                                        return status;
                                }
-                               data = chunk_skip(data, len + sizeof(tls_record_t));
-                               if (data.len == 0)
+                               buf += len + sizeof(tls_record_t);
+                               buflen -= len + sizeof(tls_record_t);
+                               if (buflen == 0)
                                {
                                        return NEED_MORE;
                                }
                        }
                }
-               len = min(data.len, this->input.len - this->inpos);
-               memcpy(this->input.ptr + this->inpos, data.ptr, len);
-               data = chunk_skip(data, len);
+               len = min(buflen, this->input.len - this->inpos);
+               memcpy(this->input.ptr + this->inpos, buf, len);
+               buf += len;
+               buflen -= len;
                this->inpos += len;
                DBG2(DBG_TLS, "buffering %d bytes, %d bytes of %d byte record received",
                         len, this->inpos, this->input.len);
@@ -210,7 +226,7 @@ METHOD(tls_t, process, status_t,
                        }
                }
        }
-       if (data.len != 0)
+       if (buflen != 0)
        {
                DBG1(DBG_TLS, "received incomplete TLS record header");
                return FAILED;
@@ -219,34 +235,68 @@ METHOD(tls_t, process, status_t,
 }
 
 METHOD(tls_t, build, status_t,
-       private_tls_t *this, chunk_t *data)
+       private_tls_t *this, void *buf, size_t *buflen, size_t *msglen)
 {
+       tls_content_type_t type;
        tls_record_t record;
        status_t status;
+       chunk_t data;
+       size_t len;
 
-       *data = chunk_empty;
-       while (TRUE)
+       len = *buflen;
+       if (this->output.len == 0)
        {
-               tls_content_type_t type;
-               chunk_t body;
-
-               status = this->protection->build(this->protection, &type, &body);
-               switch (status)
+               /* query upper layers for new records, as many as we can get */
+               while (TRUE)
                {
-                       case INVALID_STATE:
-                               return NEED_MORE;
-                       case NEED_MORE:
-                               break;
-                       default:
-                               return status;
+                       status = this->protection->build(this->protection, &type, &data);
+                       switch (status)
+                       {
+                               case NEED_MORE:
+                                       record.type = type;
+                                       htoun16(&record.version, this->version);
+                                       htoun16(&record.length, data.len);
+                                       this->output = chunk_cat("mcm", this->output,
+                                                                                        chunk_from_thing(record), data);
+                                       DBG2(DBG_TLS, "sending TLS %N record (%d bytes)",
+                                                tls_content_type_names, type, data.len);
+                                       continue;
+                               case INVALID_STATE:
+                                       if (this->output.len == 0)
+                                       {
+                                               return INVALID_STATE;
+                                       }
+                                       break;
+                               default:
+                                       return status;
+                       }
+                       break;
+               }
+               if (msglen)
+               {
+                       *msglen = this->output.len;
                }
-               record.type = type;
-               htoun16(&record.version, this->version);
-               htoun16(&record.length, body.len);
-               *data = chunk_cat("mcm", *data, chunk_from_thing(record), body);
-               DBG2(DBG_TLS, "sending TLS %N record (%u bytes)",
-                        tls_content_type_names, type, sizeof(tls_record_t) + body.len);
        }
+       else
+       {
+               DBG2(DBG_TLS, "sending %d bytes buffered fragment",
+                        min(*buflen, this->output.len - this->outpos));
+               if (msglen)
+               {
+                       *msglen = 0;
+               }
+       }
+       len = min(len, this->output.len - this->outpos);
+       memcpy(buf, this->output.ptr + this->outpos, len);
+       this->outpos += len;
+       *buflen = len;
+       if (this->outpos == this->output.len)
+       {
+               chunk_free(&this->output);
+               this->outpos = 0;
+               return ALREADY_DONE;
+       }
+       return NEED_MORE;
 }
 
 METHOD(tls_t, is_server, bool,
@@ -323,6 +373,7 @@ METHOD(tls_t, destroy, void,
        this->alert->destroy(this->alert);
 
        free(this->input.ptr);
+       free(this->output.ptr);
 
        free(this);
 }
index ec8d04eeee28db434a2a9c462e7fa33ee1110de1..ba9ede99b3a4b71f03a157aa66c4f8f2f7ba0d4f 100644 (file)
@@ -108,24 +108,36 @@ struct tls_t {
        /**
         * Process one or more TLS records, pass it to upper layers.
         *
-        * @param data          TLS record data, including headers
+        * @param buf           TLS record data, including headers
+        * @param buflen        number of bytes in buf to process
         * @return
         *                                      - SUCCESS if TLS negotiation complete
         *                                      - FAILED if TLS handshake failed
         *                                      - NEED_MORE if more invocations to process/build needed
         */
-       status_t (*process)(tls_t *this, chunk_t data);
+       status_t (*process)(tls_t *this, void *buf, size_t buflen);
 
        /**
-        * Query upper layer for TLS record, build protected record.
+        * Query upper layer for one or more TLS records, build fragments.
         *
-        * @param data          allocated data of the built TLS record
+        * The TLS stack automatically fragments the records to the given buffer
+        * size. Fragmentation is indicated by the reclen ouput parameter and
+        * the return value. For the first fragment of a TLS record, a non-zero
+        * record length is returned in reclen. If more fragments follow, NEED_MORE
+        * is returned. A return value of ALREADY_DONE indicates that the final
+        * fragment has been returned.
+        *
+        * @param buf           buffer to write TLS record fragments to
+        * @param buflen        size of buffer, receives bytes written
+        * @param msglen        receives size of all TLS fragments
         * @return
         *                                      - SUCCESS if TLS negotiation complete
         *                                      - FAILED if TLS handshake failed
-        *                                      - NEED_MORE if more input records required
+        *                                      - INVALID_STATE if more input data required
+        *                                      - NEED_MORE if more fragments available
+        *                                      - ALREADY_DONE if the last available fragment returned
         */
-       status_t (*build)(tls_t *this, chunk_t *data);
+       status_t (*build)(tls_t *this, void *buf, size_t *buflen, size_t *msglen);
 
        /**
         * Check if TLS stack is acting as a server.
index 6aa776879af9ce354ccb63da9aabd87a0240430a..e0c440a4cc86d918482cc96d0129843d3c33de50 100644 (file)
@@ -96,25 +96,31 @@ METHOD(tls_application_t, build, status_t,
  */
 static bool exchange(private_tls_socket_t *this, bool wr)
 {
-       chunk_t data;
-       char buf[2048];
+       char buf[1024];
        ssize_t len;
        int round = 0;
 
        for (round = 0; TRUE; round++)
        {
-               if (this->tls->build(this->tls, &data) != NEED_MORE)
-               {
-                       return FALSE;
-               }
-               if (data.len)
+               while (TRUE)
                {
-                       len = write(this->fd, data.ptr, data.len);
-                       free(data.ptr);
-                       if (len != data.len)
+                       len = sizeof(buf);
+                       switch (this->tls->build(this->tls, buf, &len, NULL))
                        {
-                               return FALSE;
+                               case NEED_MORE:
+                               case ALREADY_DONE:
+                                       len = write(this->fd, buf, len);
+                                       if (len == -1)
+                                       {
+                                               return FALSE;
+                                       }
+                                       continue;
+                               case INVALID_STATE:
+                                       break;
+                               default:
+                                       return FALSE;
                        }
+                       break;
                }
                if (wr)
                {
@@ -139,7 +145,7 @@ static bool exchange(private_tls_socket_t *this, bool wr)
                {
                        return FALSE;
                }
-               if (this->tls->process(this->tls, chunk_create(buf, len)) != NEED_MORE)
+               if (this->tls->process(this->tls, buf, len) != NEED_MORE)
                {
                        return FALSE;
                }