0

The client is using SChannel to handle the DTLS connection, where the server uses OpenSSL. Everything works fine until the client tries to close the connection (I used this guide).

Here is the client shutdown routine:

         SecBufferDesc BuffDesc;
         SecBuffer Buffer;
         SECURITY_STATUS sspi_ret;
         SecBuffer outbuf;
         SecBufferDesc outbuf_desc;

         DWORD dwshut = SCHANNEL_SHUTDOWN;

         Buffer->BufferType = SECBUFFER_TOKEN;
         Buffer->pvBuffer = &dwshut;
         Buffer->cbBuffer = sizeof(dwshut);
         init_sec_buffer_desc(&BuffDesc, &Buffer, 1);

         sspi_ret = ApplyControlToken(&c->ctxt_handle, &BuffDesc);
         if (sspi_ret != SEC_E_OK)
             log(h, LOG_ERROR, "ApplyControlToken failed\n");


         outbuf->BufferType = SECBUFFER_EMPTY;
         outbuf->pvBuffer = NULL;
         outbuf->cbBuffer = 0;
         init_sec_buffer_desc(&outbuf_desc, &outbuf, 1);

         sspi_ret = InitializeSecurityContext(&c->cred_handle, &c->ctxt_handle, s->host,
                                              c->request_flags, 0, 0, NULL, 0, &c->ctxt_handle,
                                              &outbuf_desc, &c->context_flags, &c->ctxt_timestamp);
         if (sspi_ret == SEC_E_OK || sspi_ret == SEC_I_CONTEXT_EXPIRED) {
             ret = write(s->tcp, outbuf.pvBuffer, outbuf.cbBuffer);
             FreeContextBuffer(outbuf.pvBuffer);
             if (ret < 0 || ret != outbuf.cbBuffer)
                 log(h, LOG_ERROR, "Failed to send close message\n");
         }

The thing is that InitializeSecurityContext returns SEC_E_INVALID_TOKEN and doesn't init the output buffer - so I don't have a close_notify msg to send to the server.

At this point i'm not sure if i'm doing something wrong, or that I cannot expect SChannel to work accordingly to OpenSSL's shutdown routine. Any help will be appreciated!

EDIT

As suggested, I tried using a different token type - SECBUFFER_TOKEN. Because I do not use the ISC_REQ_ALLOCATE_MEMORY flag, I allocated a buffer. I got the SEC_I_MESSAGE_FRAGMENT, so I added a loop.

Now it returns SEC_I_MESSAGE_FRAGMENT twice, and on the third call returns SEC_E_INVALID_HANDLE. Any ideas?

         SecBufferDesc BuffDesc;
         SecBuffer Buffer;
         SECURITY_STATUS sspi_ret;
         SecBuffer outbuf;
         SecBufferDesc outbuf_desc;

         DWORD dwshut = SCHANNEL_SHUTDOWN;

         Buffer->BufferType = SECBUFFER_TOKEN;
         Buffer->pvBuffer = &dwshut;
         Buffer->cbBuffer = sizeof(dwshut);
         init_sec_buffer_desc(&BuffDesc, &Buffer, 1);

         sspi_ret = ApplyControlToken(&c->ctxt_handle, &BuffDesc);
         if (sspi_ret != SEC_E_OK)
             log(h, LOG_ERROR, "ApplyControlToken failed\n");

         BYTE output_msg[MAX_MSG_SIZE];
         memset(output_msg, 0, MAX_MSG_SIZE);
         outbuf->BufferType = SECBUFFER_TOKEN;
         outbuf->pvBuffer = output_msg;
         outbuf->cbBuffer = MAX_MSG_SIZE;
         init_sec_buffer_desc(&outbuf_desc, &outbuf, 1);
         int size = 0;

         do {
             sspi_ret = InitializeSecurityContext(&c->cred_handle, &c->ctxt_handle, s->host,
                                              c->request_flags, 0, 0, NULL, 0, &c->ctxt_handle,
                                              &outbuf_desc, &c->context_flags, &c->ctxt_timestamp);
             if (sspi_ret == SEC_I_MESSAGE_FRAGMENT) {
                 size += outbuf->cbBuffer;
                 outbuf.pvBuffer = output_msg + size;
                 outbuf.cbBuffer = MAX_MSG_SIZE;
             }
         } while (sspi_ret == SEC_I_MESSAGE_FRAGMENT);

         if (sspi_ret == SEC_E_OK || sspi_ret == SEC_I_CONTEXT_EXPIRED) {
             ret = write(s->tcp, outbuf.pvBuffer, outbuf.cbBuffer);
             FreeContextBuffer(outbuf.pvBuffer);
             if (ret < 0 || ret != outbuf.cbBuffer)
                 log(h, LOG_ERROR, "Failed to send close message\n");
         }
Shahrzad
  • 1
  • 1
  • 1
    replace `outbuf->BufferType = SECBUFFER_EMPTY;` to `outbuf->BufferType = SECBUFFER_TOKEN;` – RbMm Jan 06 '22 at 08:37
  • Already tried that, got: SEC_E_INSUFFICIENT_MEMORY - Not enough memory is available to complete this request. Then I added the allocate memory for me flag, it returned SEC_I_MESSAGE_FRAGMENT. So I looped and called InitializeSecurityContext several times until the same SEC_E_INVALID_TOKEN returned again. – Shahrzad Jan 09 '22 at 07:03
  • in this case you have some another errors. for code with outbuf->BufferType = SECBUFFER_TOKEN work ok – RbMm Jan 09 '22 at 09:23
  • What flags are you using? do you indicate outbuf->pvBuffer = NULL and outbuf->cbBuffer = 0 ? – Shahrzad Jan 09 '22 at 09:29
  • Please review the new edit. – Shahrzad Jan 09 '22 at 10:04
  • i use [this](https://github.com/rbmm/LIB/blob/master/ASIO/ssl.cpp#L458) code for shutdown and it work ok for me – RbMm Jan 09 '22 at 10:25
  • Thanks, but is it for DTLS? it seems that the shutdown routine is not relevant - https://stackoverflow.com/questions/47678618/dtls-using-schannel?rq=1 – Shahrzad Jan 11 '22 at 11:03
  • this is for general windows schannel. you got exactly error from here. what is transport ( tcp, udp, etc ) - not relevant here – RbMm Jan 11 '22 at 11:50
  • Why isn't it relevant? it seems in the link I sent that it is and therefore shutting down this way won't work. – Shahrzad Jan 11 '22 at 13:08
  • you got error from schannel. schannel work with input and output data. it not care about transport. how you send/receiver data to another side. you not got error from remote side, but from local api call – RbMm Jan 11 '22 at 13:29
  • Curl uses SECBUFFER_EMPTY https://github.com/curl/curl/blob/21248e052dbd0db33e8999aeeb919fb6f32c9567/lib/vtls/schannel.c#L2193 – Валерий Заподовников Feb 09 '23 at 11:10

0 Answers0