windowswinapisspischannel

InitializeSecurityContext (Schannel) returns SEC_I_INCOMPLETE_CREDENTIALS unexpectedly


I have implemented a code for creating a secure connection using SSPI. I use it for sending mail over SMTP. It works fine with smtp.gmail.com and some other servers which I tried it with. But it doesn't work with smtp.live.com. The second call of InitializeSecurityContext() returns me SEC_I_INCOMPLETE_CREDENTIALS. As I understand this means that I need to provide a client certificate. But this is a public free mail server. What client certificate can it require?? Or maybe I use some wrong parameters or flags?

I connect to smtp.live.com:587 Initially the connection is unencrypted, plain text. First, I send EHLO command, then I send STARTTLS command. After that I perform an SSL handshake using the code below

Here is the code :

SCHANNEL_CRED schCred;
memset(&schCred,0,sizeof(schCred));
schCred.dwVersion=SCHANNEL_CRED_VERSION; // == 4
schCred.dwFlags=SCH_CRED_NO_DEFAULT_CREDS;

// acquire credentials
SECURITY_STATUS status=
    AcquireCredentialsHandle(NULL,UNISP_NAME,SECPKG_CRED_OUTBOUND,NULL,
                &schCred,NULL,NULL,&Creds,NULL);

// then I create a context 
const DWORD dwSSPIFlags = ISC_REQ_SEQUENCE_DETECT   |
                ISC_REQ_REPLAY_DETECT     |
                ISC_REQ_CONFIDENTIALITY   |
                ISC_RET_EXTENDED_ERROR    |
                ISC_REQ_ALLOCATE_MEMORY   |
                ISC_REQ_STREAM;

SECURITY_STATUS scRet =
          InitializeSecurityContextW(&Creds, NULL, targetName, dwSSPIFlags,
                0, 0 , NULL, 0, &Context,&outBuffer, &dwSSPIOutFlags, &tsExpiry); 

// scRet==SEC_I_CONTINUE_NEEDED

SendOutBuffer(outBuffers);
ReceiveInputBuffers(inBuffers);

// Then I call InitializeSecurityContext for the second time
scRet = InitializeSecurityContext(&Creds, &Context, NULL, dwSSPIFlags,
              0, 0, &inBuffer, 0, NULL, &outBuffer, &dwSSPIOutFlags, &tsExpiry);


if( ( SEC_E_OK == scRet || SEC_I_CONTINUE_NEEDED == scRet ||
 FAILED(scRet) && (dwSSPIOutFlags & ISC_RET_EXTENDED_ERROR) ) 
    && BufferNotEmpty(outBuffers)) {        

    SendOutBuffer(outBuffers);                
} 

if(SEC_I_INCOMPLETE_CREDENTIALS == scRet) {                  
 // This is where I am when connecting to smtp.live.com
} 

Solution

  • Answering my own question. I captured traffic with Wireshark and yes, smtp.live.com does ask for a client certificate during a TLS handshake. But it's enough to send it an empty (zero length) certificate to proceed with the handshake. To do so, right after getting SEC_I_INCOMPLETE_CREDENTIALS, I just call InitializeSecurityContext() again with the same parameters. In other words, after getting SEC_I_INCOMPLETE_CREDENTIALS, I continue the handshake loop but skip the receiving of the input buffers for the next one itteration. If I'm getting SEC_I_INCOMPLETE_CREDENTIALS for a second time, I quit with an error because my client does not support authentication with a certificate. (Actually, I'm not sure if it's possible to get SEC_I_INCOMPLETE_CREDENTIALS twice)