mainGnuTLS - Support: sr #108712, mutiple DTLS records in one UDP...

 
 

sr #108712: mutiple DTLS records in one UDP packet not handled correctly

Submitter:  Andreas Schultz <roadrunnr>
Submitted:  Tue 30 Dec 2014 02:12:06 PM UTC
   
 
Category:  Core library Priority:  5 - Normal
Severity:  3 - Normal Status:  Done
Privacy:  Public Assigned to:  None
Open/Closed:  Closed Operating System:  GNU/Linux
* Mandatory Fields

Add a New Comment Rich Markup
   

Jump to the original submission

Thu 08 Jan 2015 08:42:18 AM UTC, comment #13: 
Nikos Mavrogiannopoulos <nmav>
Group administrator
Wed 07 Jan 2015 05:00:23 PM UTC, comment #12: 


> I believe the issue is due to your reproducer. In select() you use the wrong parameters and that's why it blocks there.


Yes, the select is wrong, but the problem is still there with the fixed select, only the DTLS re-transmission hides it.
I attach a new version of mini-dtls-record-asym.c. This version makes sure the select times out before the DTLS re-transmission kicks in.

(file #32782)

Andreas Schultz <roadrunnr>
Tue 06 Jan 2015 08:34:05 PM UTC, comment #11: 

I believe the issue is due to your reproducer. In select() you use the wrong parameters and that's why it blocks there.

I fixed that in:
https://gitorious.org/gnutls/gnutls/commit/066db0b051a048e005f7bb344621b5ea931aeea3

https://gitorious.org/gnutls/gnutls/source/HEAD:tests/mini-dtls-record-asym.c

and it seems to work. There is no issue handling these packets, whether combined in a single record or not.

Nikos Mavrogiannopoulos <nmav>
Group administrator
Tue 06 Jan 2015 10:18:04 AM UTC, comment #10: 


> Interesting case. Here you are artificially setting the client's MTU to 400 bytes, and the server's to 1500. The certificate is 558 so the client cannot receive it with the size of its buffers. In a real world case (when ICMP packets are allowed) the server should have received GNUTLS_E_LARGE_PACKET and should have adjusted its view of MTU size.


Correction, I set the servers MTU to 400, the client's is left at 1500. Artificial lowering the permitted MTU below the Path MTU is IMO a valid use case and I have seen at least on real world server insisting on such a low MTU (Cisco Virtual Wireless Controller)

> Is there some particular merit in addressing that? I mean are there real scenarios where this case could occur?


Well, this sample is contrive to simulate a specific scenario that would not occur with GNUTLS alone.

What the test does is to generate a payload that the client sees as one big datagram containing multiple fragmented DTLS records. GNUTLS does not generate this kind of datagram, but they are valid and OpenSSL does generate them. So for the test to be reproducible without involving OpenSSL I had to come up with some contrived code. Particularity, running DTLS over a streaming socket that does not preserve the packet boundaries of sending side (this simulates the "big datagram containing multiple DTLS records") and setting a low sender side MTU to get the fragmentation.

So, to answer your question, yes there are real world scenarios where this case will occur.

Andreas Schultz <roadrunnr>
Mon 05 Jan 2015 06:20:00 PM UTC, comment #9: 

Interesting case. Here you are artificially setting the client's MTU to 400 bytes, and the server's to 1500. The certificate is 558 so the client cannot receive it with the size of its buffers. In a real world case (when ICMP packets are allowed) the server should have received GNUTLS_E_LARGE_PACKET and should have adjusted its view of MTU size.

Is there some particular merit in addressing that? I mean are there real scenarios where this case could occur?

Nikos Mavrogiannopoulos <nmav>
Group administrator
Mon 05 Jan 2015 03:59:59 PM UTC, comment #8: 

Bad wording on my side.

I was able to reproduce it. Test case to reproduce is in file #32761

Seems I'm not allowed to change the status of the bug (or I'm unable to find out how).

Andreas Schultz <roadrunnr>
Mon 05 Jan 2015 03:52:49 PM UTC, comment #7: 

I thought you mentioned that you couldn't reproduce it in comment #4. Feel free to re-open it if I misunderstood.

Nikos Mavrogiannopoulos <nmav>
Group administrator
Mon 05 Jan 2015 03:43:17 PM UTC, comment #6: 

Why is this "invalid" ????

Could you point out what in the test case is incorrect usage, please?

Andreas Schultz <roadrunnr>
Mon 05 Jan 2015 12:07:43 PM UTC, comment #5: 

Yes, would be nice. If you have a reproducer please attach it as well.

Nikos Mavrogiannopoulos <nmav>
Group administrator
Mon 05 Jan 2015 11:11:01 AM UTC, comment #4: 

Got a working demo and it doesn't even the non-blocking socket.

However, when looking more closely at the socket code, I found another potential problem. DTLS UDP sockets use writev to push out all parts for the handshake flight. writev will combine all iovec's into one single UDP datagram when the path MTU permits it. This defeats the purpose of gnutls_dtls_set_mtu.

Should I open a new ticket for this?

(file #32761)

Andreas Schultz <roadrunnr>
Sun 04 Jan 2015 05:06:03 PM UTC, comment #3: 

I'm unable to reproduce. Are you testing with the latest release? If yes, could you modify the attached program to make it reproduce the issue? It uses the libutils from the tests/ directory in gnutls. You can compile it as:
gcc mini-dtls-srecord.c -lgnutls .libs/libutils.a



(file #32757)

Nikos Mavrogiannopoulos <nmav>
Group administrator
Sun 04 Jan 2015 03:58:22 PM UTC, comment #2: 


> From what I understand from your description gnutls has read 251 bytes out of a 16732 UDP packet


No, gnutls told the pull function that the supplied buffer is 16732 bytes long, the pull function
reads a UDP packet of 251 bytes and copies those 251 bytes into the supplied buffer.

The 251 byte UDP packet does contain a total of three (3) DTLS records.
The first record is 158 bytes long and does contain the last fragment of the server certificate, the second record contains a certificate request and the third record is the server hello done.

gnutls now processes only the first record, ignores the two others and return GNUTLS_E_AGAIN. There is no data in the socket buffer left at this point, all of it was passed to gnutls and is no in gnutls internal buffers.


Andreas Schultz <roadrunnr>
Sat 03 Jan 2015 10:49:37 PM UTC, comment #1: 

Hi,
 From what I understand from your description gnutls has read 251 bytes out of a 16732 UDP packet and returns GNUTLS_E_AGAIN. That seems to be the expected behavior. It doesn't call recv() multiple times to avoid blocks, and expects you to restore the handshake function if you know there are more data in the socket buffers (e.g., using select in standard socket functions). In your case as I understand you do the buffering so you should be able to know whether there are pending data or not.

Is there something I am missing?

Nikos Mavrogiannopoulos <nmav>
Group administrator
Tue 30 Dec 2014 02:12:06 PM UTC, original submission:  

under some very special circumstance, gnutls_handshake does return E_AGAIN even when there are pending DTLS records in the buffer.

I have a CAPWAP DTLS client in GNUTLS_NONBLOCK mode, talking to a server that insists to fragment it's packets to about 544 bytes (before CAPWAP encapsulation). This leads to a server handshake where the last datagram caries three DTLS records (a Certificate Fragment, a Certificate Request and the Server Hello Done).

gnutls_handshake call's the pull func, get the full datagram, handls the Certificate Fragment, reassembles the full certificate chain and then return GNUTLS_E_AGAIN.
The rest of the datagram is left in the internal buffer and handled on the next call to gnutls_handshake.

For the application there is no indication that it should or has to call gnutls_handshake again. It's internal buffer was emptied by the pull func, no more data will arrive and GNUTLS_E_AGAIN means "wait for more data".

Relevant lines from the debug log:

Dec 30 14:44:57.475 capwap-mitm.c:710:dtls_pull_func: 0x15e2810: DTLS pull of size 16732

^^^^ pull empties the application buffer

gnutls[10]: READ: Got 251 bytes from 0x15e2810
gnutls[10]: READ: read 251 bytes from 0x15e2810
gnutls[10]: RB: have 0 bytes into buffer. Adding 251 bytes.

^^^^ gets the 251 bytes from the last datagram

gnutls[10]: RB: Requested 13 bytes
gnutls[5]: REC[0x160b170]: SSL 254.255 Handshake packet received. Epoch 0, length: 145
gnutls[5]: REC[0x160b170]: Expected Packet Handshake(22)
gnutls[5]: REC[0x160b170]: Received Packet Handshake(22) with length: 145
gnutls[5]: REC[0x160b170]: Decrypted Packet[0.7] Handshake(22) with length: 145

^^^^ process the Certificate Fragemtent 158 Bytes (145 + 13 Bytes header)

gnutls[4]: HSK[0x160b170]: CERTIFICATE (11) was received. Length 2645[133], frag offset 2500, frag length: 133, sequence: 2
gnutls[3]: ASSERT: gnutls_buffers.c:1111
gnutls[3]: ASSERT: gnutls_kx.c:630

^^^^ from here one, nothing happens on this session, gnutls_handshake returns GNUTLS_E_AGAIN and the remaining bytes in the buffer are ignored.

I believe gnutls_handshake should continue to process the records in the buffer.

Full debug log and sample DTLS pcap attached (needs up to date wireshark do decode properly), full DTLS application can be found at http://github.com/travelping/capwap-mitm


Andreas Schultz <roadrunnr>

 

(Note: upload size limit is set to 16384 kB, after insertion of the required escape characters.)

Attach Files:
   
   
Comment:
   

Attached Files
file #32761:  mini-dtls-srecord.c added by roadrunnr (8KiB - text/x-csrc)
file #32757:  mini-dtls-srecord.c added by nmav (8KiB - text/x-csrc - sample for reproducer)
file #32734:  capwap-dtls-handshake.pcapng added by roadrunnr (4KiB - application/x-pcapng)

 

Depends on the following items: None found

Items that depend on this one: None found

 

Carbon-Copy List
  • -email is unavailable- added by nmav (Posted a comment)
  • -email is unavailable- added by roadrunnr (Submitted the item)
  •  

    There are 0 votes so far. Votes easily highlight which items people would like to see resolved in priority, independently of the priority of the item set by tracker managers.

    Only logged-in users can vote.

     

    Follow 11 latest changes.

    Date Changed by Updated Field Previous Value => Replaced by
    2015-01-12 nmav StatusIn Progress Done
        Open/ClosedOpen Closed
    2015-01-07 roadrunnr Attached File- Added mini-dtls-record-asym.c, #32782
    2015-01-05 nmav StatusInvalid In Progress
        Open/ClosedClosed Open
    2015-01-05 nmav StatusNone Invalid
        Open/ClosedOpen Closed
    2015-01-05 roadrunnr Attached File- Added mini-dtls-srecord.c, #32761
    2015-01-04 nmav Attached File- Added mini-dtls-srecord.c, #32757
    2014-12-30 roadrunnr Attached File- Added capwap-dtls-handshake.pcapng, #32734
        Attached File- Added capwap-dtls-handshake.log, #32735

    Back to the top

    Powered by Savane 3.13-4448.
    Corresponding source code