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");
}