InitializeSecurityContext (Schannel) неожиданно возвращает SEC_I_INCOMPLETE_CREDENTIALS

Я реализовал код для создания безопасного соединения с использованием SSPI. Я использую его для отправки почты по SMTP. Он отлично работает с smtp.gmail.com и некоторыми другими серверами, с которыми я пробовал. Но это не работает с smtp.live.com. Второй вызов InitializeSecurityContext() возвращает мне SEC_I_INCOMPLETE_CREDENTIALS. Насколько я понимаю, это означает, что мне нужно предоставить сертификат клиента. Но это общедоступный бесплатный почтовый сервер. Какой клиентский сертификат он может потребовать?? Или, может быть, я использую какие-то неправильные параметры или флаги?

Подключаюсь к smtp.live.com:587 Изначально соединение незашифрованное, обычный текст. Сначала я отправляю команду EHLO, затем отправляю команду STARTTLS. После этого я выполняю рукопожатие SSL, используя приведенный ниже код.

Вот код:

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
} 

person CITBL    schedule 22.11.2017    source источник
comment
Пробовали ли вы проверить, какие CA сервер примет (вызвав функцию QueryContextAttributes (Schannel) с SECPKG_ATTR_ISSUER_LIST_EX). Затем вы можете просмотреть сертификаты на машине, чтобы увидеть, совпадет ли один из них. (Я предполагаю, что вам удалось подключиться к проблемному серверу с помощью другого клиента.   -  person SoronelHaetir    schedule 23.11.2017
comment
к какому порту вы подключаетесь на smtp.live.com? на 995 я просматриваю сервис ssl pop3 и здесь все ок, на 25 я просматриваю обычный текст..   -  person RbMm    schedule 23.11.2017
comment
@RbMm В порт 587   -  person CITBL    schedule 23.11.2017
comment
я получил обычный текст на этом порту - 220 AM5PR0602CA0024.outlook.office365.com Microsoft ESMTP MAIL Service ready at Thu, 23 Nov 2017 ... - вообще без шифрования   -  person RbMm    schedule 23.11.2017
comment
@RbMm Да, потому что сначала вам нужно отправить команду EHLO, а затем переключиться на SSL с помощью команды STARTTLS. Рукопожатие выполняется после STARTTLS   -  person CITBL    schedule 23.11.2017
comment
@SoronelHaetir Я пытался позвонить QueryContextAttributes с помощью SECPKG_ATTR_ISSUER_LIST_EX. Он вернул SEC_E_OK. Структура SecPkgContext_IssuerListInfoEx имеет ноль как для cIssuers, так и для aIssuers. Да, я могу отправлять электронную почту с помощью Thundernird через smtp.live.com, тот же порт   -  person CITBL    schedule 23.11.2017
comment
Кроме того, поскольку это запрашивает сертификат клиента, вы пытались просто удалить SCH_CRED_NO_DEFAULT_CREDS и посмотреть, может ли schannel придумать все, что хочет удаленный конец?   -  person SoronelHaetir    schedule 24.11.2017
comment
@SoronelHaetir Да, я пробовал. Не помогает.   -  person CITBL    schedule 24.11.2017


Ответы (1)


Отвечая на мой собственный вопрос. Я перехватил трафик с помощью Wireshark, и да, smtp.live.com действительно запрашивает сертификат клиента во время рукопожатия TLS. Но достаточно отправить ему пустой (нулевой длины) сертификат, чтобы продолжить рукопожатие. Для этого сразу после получения SEC_I_INCOMPLETE_CREDENTIALS я просто снова вызываю InitializeSecurityContext() с теми же параметрами. Другими словами, после получения SEC_I_INCOMPLETE_CREDENTIALS я продолжаю цикл рукопожатия, но пропускаю получение входных буферов для следующей итерации. Если я получаю SEC_I_INCOMPLETE_CREDENTIALS во второй раз, я завершаю работу с ошибкой, потому что мой клиент не поддерживает аутентификацию с помощью сертификата. (На самом деле я не уверен, что можно получить SEC_I_INCOMPLETE_CREDENTIALS дважды)

person CITBL    schedule 24.11.2017