1

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
} 
CITBL
  • 1,326
  • 3
  • 17
  • 31
  • Have you tried checking what CAs the server would accept (by calling QueryContextAttributes (Schannel) function with SECPKG_ATTR_ISSUER_LIST_EX). You could then look through the certificates on the machine to see if one of them would match. (I am assuming here that you have succeeded in connecting to the problem server using some other client. – SoronelHaetir Nov 23 '17 at 05:41
  • to which port you connect at *smtp.live.com* ? on 995 i view ssl pop3 service and here all ok, on 25 i view plain text.. – RbMm Nov 23 '17 at 16:08
  • @RbMm To port 587 – CITBL Nov 23 '17 at 16:31
  • i got plain text on this port - `220 AM5PR0602CA0024.outlook.office365.com Microsoft ESMTP MAIL Service ready at Thu, 23 Nov 2017 ...` - no encryption at all – RbMm Nov 23 '17 at 16:34
  • @RbMm Yes, because first you need to send EHLO command then switch to SSL with STARTTLS command. Handshake is performed after STARTTLS – CITBL Nov 23 '17 at 16:45
  • @SoronelHaetir I've tried to call `QueryContextAttributes` with `SECPKG_ATTR_ISSUER_LIST_EX`. It returned `SEC_E_OK`. The `SecPkgContext_IssuerListInfoEx` structure has zero for both `cIssuers` and `aIssuers`. Yes, I can send email using Thundernird via smtp.live.com, same port – CITBL Nov 23 '17 at 18:24
  • Also, since this is asking for a client certificate have you tried simply removing the SCH_CRED_NO_DEFAULT_CREDS and seeing if schannel can come up with whatever the remote end wants? – SoronelHaetir Nov 23 '17 at 22:49
  • @SoronelHaetir Yes I tried that. Doesn't help. – CITBL Nov 24 '17 at 07:59

1 Answers1

1

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)

CITBL
  • 1,326
  • 3
  • 17
  • 31