1

When performing a DTLS handshake using the SChannel SSPI in Windows 10 - for which there is no documentation - how should the application handle a SEC_I_MESSAGE_FRAGMENT result from AcceptSecurityContext (ASC) or InitializeSecurityContext (ISC)?

I understand that I am meant to send the recieved fragment to the other party and call ASC/ISC again to obtain the next fragment, but when I call ASC again, this time with an empty input SECBUFFER_TOKEN, I receive nothing in the output token buffer, and it returns SEC_I_MESSAGE_FRAGMENT - suggesting it is expecting input data.

Presumably I am not successfully indicating to ASC that I want it to give me the next fragment, so how do I do this?

I have created a standalone example that reproduces my issue in this GitHub gist: https://gist.github.com/haddoncd/381c5e9542e977ca238ff16229bd9a0e/c881132ced94995402b617f38a5c8b6f8669b637

I have also included in the gist example output from the program which shows in detail the inputs that lead to the issue: https://gist.github.com/haddoncd/381c5e9542e977ca238ff16229bd9a0e/c881132ced94995402b617f38a5c8b6f8669b637#file-example_output-txt

Haddon CD.
  • 169
  • 8
  • I suggest that you handle it as if you received a SEC_E_INCOMPLETE_MESSAGE, they look very similar. – SoronelHaetir May 10 '21 at 16:01
  • @SoronelHaetir - As I understand it, SEC_E_INCOMPLETE_MESSAGE is used to indicate that the input message was incomplete, and that more data must be read from the other party before continuing. The meaning of SEC_I_MESSAGE_FRAGMENT is almost the opposite - there's more data to be obtained from ASC/ISC without needing to read from the socket, because it has produced output that does not fit in a single datagram, and so has fragmented it, returning only the first fragment. – Haddon CD. May 10 '21 at 16:32
  • As Hsu Pu shows in their answer below, I was handling SEC_I_MESSAGE_FRAGMENT correctly. The issue with my code was that I was passing nullptr as the phNewContext parameter on ISC/ASC calls after the first. I had assumed that the first call would assign a context handle, which would then be reused (through the phContext parameter) on subsequent calls. This, however, is apparently not how the API is implemented. I've updated my gist with working code for the benefit of anyone else that finds this question: https://gist.github.com/haddoncd/381c5e9542e977ca238ff16229bd9a0e – Haddon CD. Sep 11 '21 at 10:46
  • Additional notice here: Schannel seems to have memory leak issue when work with DTLS in Windows 10 / Server 2019. – Hsu Pu Apr 08 '22 at 07:06

2 Answers2

1

According to my test, the only WRONG thing you did is forget to RESET context.handle once initialized during handshaking.

isc_status = InitializeSecurityContextW(
    &creds,
    context.initialized ? &context.handle : nullptr,
    nullptr,
    context_reqs,
    0,
    SECURITY_NATIVE_DREP,
    isc_input_buffers,
    0,
    &context.handle, // HERE
    &out_buffer_desc,
    &context.attrs,
    &context.expiry
);

asc_status = AcceptSecurityContext(
    &creds,
    context.initialized ? &context.handle : nullptr,
    &in_buffer_desc,
    context_reqs,
    SECURITY_NATIVE_DREP,
    &context.handle, // HERE
    &out_buffer_desc,
    &context.attrs,
    &context.expiry
);

BTW, you don't need to call DeleteSecurityContext on the old CtxtHandle because it's been invalid after the call.

Hsu Pu
  • 81
  • 9
  • Hi Hsu Pu, thanks for your reply, looks like that was exactly it. It seems I was mislead by this line from the docs for InitializeSecurityContextW: "When using the Schannel SSP, on calls after the first call, pass the handle returned here as the phContext parameter and specify NULL for phNewContext." (https://learn.microsoft.com/en-us/windows/win32/api/sspi/nf-sspi-initializesecuritycontextw) – Haddon CD. Sep 11 '21 at 10:37
0

A questions regarding your implementation: I noticed you didn't specify the protocol that you're using.

As said in the SCHANNEL_CRED docs, you can do so by setting schannel_cred.grbitEnabledProtocols = SP_PROT_DTLS1_2_CLIENT (or 1.1, server etc.), and then calling AcquireCredentialsHandle. How did you verify the connection used DTLS? The docs says that "If this member is zero, Schannel selects the protocol".

Shahrzad
  • 1
  • 1