Сколько данных нужно получить с сервера в SSL-квитировании перед вызовом InitializeSecurityContext? - PullRequest
2 голосов
/ 26 июля 2011

В нашем приложении для Windows C ++ я использую клиентскую часть InitializeSecurityContext (), чтобы открыть канальное соединение с сервером, на котором работает stunnel SSL-прокси.Мой код теперь работает, но только с помощью хака, который я хотел бы устранить.

Я начал с этого примера кода: http://msdn.microsoft.com/en-us/library/aa380536%28v=VS.85%29.aspx

В примере кода посмотрите на SendMsg и ReceiveMsg.Первые 4 байта любого отправленного или полученного сообщения указывают длину сообщения.Это хорошо для образца, где серверная часть образца соответствует тому же соглашению.

stunnel не использует это соглашение.Когда клиент получает данные во время рукопожатия, как он узнает, когда прекратить получать и сделать еще один вызов InitializeSecurityContext ()?

Так я структурировал свой код, основываясь на том, что я мог почерпнуть из документации.:

1. call InitializeSecurityContext which returns an output buffer
2. Send output buffer to server
3. Receive response from server
4. call InitializeSecurityContext(server_response) which returns an output buffer
5. if SEC_E_INCOMPLETE_MESSAGE, go back to step 3, 
   if SEC_I_CONTINUE_NEEDED go back to step 2

Я ожидал, что InitializeSecurityContext на шаге 4 вернет SEC_E_INCOMPLETE_MESSAGE, если на шаге 3 было прочитано недостаточно данных с сервера. Вместо этого я получаю SEC_I_CONTINUE_NEEDED, но пустой буфер вывода.Я экспериментировал с несколькими способами обработки этого случая (например, вернитесь к шагу 3), но ни один из них, похоже, не работал, и что более важно, я не вижу такого поведения документированным.

На шаге 3, если я добавлюЦикл, который получает данные до истечения времени ожидания, все отлично работает в моей тестовой среде.Но должен быть более надежный способ.

Как правильно узнать, сколько данных получить на шаге 3?

Ответы [ 3 ]

4 голосов
/ 26 июля 2011

SChannel отличается от пакета безопасности согласования. Вам нужно получить как минимум 5 байтов, то есть размер заголовка записи SSL / TLS:

struct {
    ContentType type;
    ProtocolVersion version;
    uint16 length;
    opaque fragment[TLSPlaintext.length];
} TLSPlaintext;

ContentType - 1 байт, ProtocolVersion - 2 байта, а длина записи - 2 байта. Как только вы прочитаете эти 5 байтов, SChannel вернет SEC_E_INCOMPLETE_MESSAGE и сообщит вам, сколько еще байтов ожидать:

SEC_E_INCOMPLETE_MESSAGE
Data for the whole message was not read from the wire.

When this value is returned, the pInput buffer contains a SecBuffer structure with a BufferType member of SECBUFFER_MISSING. The cbBuffer member of SecBuffer contains a value that indicates the number of additional bytes that the function must read from the client before this function succeeds. 

Как только вы получите этот вывод, вы точно знаете, сколько читать из сети.

1 голос
/ 10 мая 2016

SChannel SSP возвращает SEC_E_INCOMPLETE_MESSAGE из InitializeSecurityContext и DecryptMessage, когда недостаточно данных прочитано.

Тип сообщения SECBUFFER_MISSING возвращается из DecryptMessage со значением cbBuffer количества требуемых байтов.

Но на практике я не использовал значение «отсутствующие данные». В документации указано, что значение не гарантируется, что оно является правильным, и является лишь подсказкой, которую разработчики могут использовать для сокращения вызовов.

InitalizeSecurityContext MSDN doc :

Хотя этот номер не всегда точен, его использование может помочь повысить производительность, избегая многократных вызовов этой функции.

Поэтому я безоговорочно считываю больше данных в тот же буфер каждый раз, когда возвращается SEC_E_INCOMPLETE_MESSAGE. Чтение нескольких байтов одновременно из сокета.

Требовалось дополнительное управление входным буфером, чтобы добавить больше прочитанных данных и сохранить правильную длину. DecryptMessage изменит свойства cbBuffer входных буферов при сбое, что меня удивило.

Распечатка буферов и возвращаемый результат после вызова InitializeSecurityContext показывает следующее:

read socket:bytes(5).
InitializeSecurityContext:result(80090318). // SEC_E_INCOMPLETE_MESSAGE
inBuffers[0]:type(2),bytes(5).
inBuffers[1]:type(0),bytes(0).   // no indication of missing data
outBuffer[0]:type(2),bytes(0).

read socket:bytes(74).
InitializeSecurityContext:result(00090312). // SEC_I_CONTINUE_NEEDED
inBuffers[0]:type(2),bytes(79).  // notice 74 + 5 from before
inBuffers[1]:type(0),bytes(0).
outBuffer[0]:type(2),bytes(0).

А для функции DecryptMessage вход всегда находится в dataBuf[0], остальные обнуляются.

read socket:bytes(5).
DecryptMessage:len 5, bytes(17030201). // SEC_E_INCOMPLETE_MESSAGE
DecryptMessage:dataBuf[0].BufferType 4, 8  // notice input buffer modified
DecryptMessage:dataBuf[1].BufferType 4, 8
DecryptMessage:dataBuf[2].BufferType 0, 0
DecryptMessage:dataBuf[3].BufferType 0, 0

read socket:bytes(8).
DecryptMessage:len 13, bytes(17030201). // SEC_E_INCOMPLETE_MESSAGE
DecryptMessage:dataBuf[0].BufferType 4, 256
DecryptMessage:dataBuf[1].BufferType 4, 256
DecryptMessage:dataBuf[2].BufferType 0, 0
DecryptMessage:dataBuf[3].BufferType 0, 0

read socket:bytes(256).
DecryptMessage:len 269, bytes(17030201). // SEC_E_OK

Мы видим, что мой узел TLS-сервера отправляет заголовки TLS (5 байт) в одном пакете, а затем сообщение TLS (8 для данных приложения), а затем полезную нагрузку данных приложения в третьем.

1 голос
/ 27 июля 2011

Я нашел проблему.

Я нашел этот образец:

http://www.codeproject.com/KB/IP/sslsocket.aspx

Мне не хватало обработки SECBUFFER_EXTRA (строка 987 SslSocket.cpp)

...