static bool ClientConnect(SslHandle *ssl, const char *host) { if (SecIsValidHandle(&ssl->hContext)) { g_pSSPI->DeleteSecurityContext(&ssl->hContext); SecInvalidateHandle(&ssl->hContext); } if (MySslEmptyCache) MySslEmptyCache(); DWORD dwSSPIFlags = ISC_REQ_SEQUENCE_DETECT | ISC_REQ_REPLAY_DETECT | ISC_REQ_CONFIDENTIALITY | ISC_REQ_EXTENDED_ERROR | ISC_REQ_ALLOCATE_MEMORY | ISC_REQ_STREAM; // Initiate a ClientHello message and generate a token. SecBuffer OutBuffers[1]; OutBuffers[0].pvBuffer = NULL; OutBuffers[0].BufferType = SECBUFFER_TOKEN; OutBuffers[0].cbBuffer = 0; SecBufferDesc OutBuffer; OutBuffer.cBuffers = _countof(OutBuffers); OutBuffer.pBuffers = OutBuffers; OutBuffer.ulVersion = SECBUFFER_VERSION; TimeStamp tsExpiry; DWORD dwSSPIOutFlags; SECURITY_STATUS scRet = g_pSSPI->InitializeSecurityContext(&hCreds, NULL, _A2T(host), dwSSPIFlags, 0, 0, NULL, 0, &ssl->hContext, &OutBuffer, &dwSSPIOutFlags, &tsExpiry); if (scRet != SEC_I_CONTINUE_NEEDED) { ReportSslError(scRet, __LINE__); return 0; } // Send response to server if there is one. if (OutBuffers[0].cbBuffer != 0 && OutBuffers[0].pvBuffer != NULL) { DWORD cbData = send(ssl->s, (char*)OutBuffers[0].pvBuffer, OutBuffers[0].cbBuffer, 0); if (cbData == SOCKET_ERROR || cbData == 0) { Netlib_Logf(NULL, "SSL failure sending connection data (%d %d)", ssl->s, WSAGetLastError()); g_pSSPI->FreeContextBuffer(OutBuffers[0].pvBuffer); return 0; } // Free output buffer. g_pSSPI->FreeContextBuffer(OutBuffers[0].pvBuffer); OutBuffers[0].pvBuffer = NULL; } return ClientHandshakeLoop(ssl, TRUE) == SEC_E_OK; }
// Initiate a ClientHello message and generate a token. SECURITY_STATUS chs_hello (void) { DWORD dwFlagsIn, dwFlagsOut; SecBuffer sb[1]; SecBufferDesc hs; dwFlagsIn = ISC_REQ_SEQUENCE_DETECT | ISC_REQ_REPLAY_DETECT | ISC_REQ_CONFIDENTIALITY | ISC_RET_EXTENDED_ERROR | ISC_REQ_ALLOCATE_MEMORY | ISC_REQ_STREAM; sb[0].pvBuffer = NULL; sb[0].BufferType = SECBUFFER_TOKEN; sb[0].cbBuffer = 0; hs.cBuffers = 1; hs.pBuffers = sb; hs.ulVersion = SECBUFFER_VERSION; ss = sspi->InitializeSecurityContextA (&hClientCreds, NULL, pszServer, dwFlagsIn, 0, SECURITY_NATIVE_DREP, NULL, 0, &hContext, &hs, &dwFlagsOut, &ts); // should indicate continuing if (ss==SEC_I_CONTINUE_NEEDED) { // send data if (sb[0].cbBuffer != 0) { send (s, sb[0].pvBuffer, sb[0].cbBuffer, 0); ss = sspi->FreeContextBuffer (sb[0].pvBuffer); } } return ss; }
void NetlibSslShutdown(SslHandle *ssl) { if (ssl == NULL || !SecIsValidHandle(&ssl->hContext)) return; DWORD dwType = SCHANNEL_SHUTDOWN; SecBuffer OutBuffers[1]; OutBuffers[0].pvBuffer = &dwType; OutBuffers[0].BufferType = SECBUFFER_TOKEN; OutBuffers[0].cbBuffer = sizeof(dwType); SecBufferDesc OutBuffer; OutBuffer.cBuffers = _countof(OutBuffers); OutBuffer.pBuffers = OutBuffers; OutBuffer.ulVersion = SECBUFFER_VERSION; SECURITY_STATUS scRet = g_pSSPI->ApplyControlToken(&ssl->hContext, &OutBuffer); if (FAILED(scRet)) return; // Build an SSL close notify message. DWORD dwSSPIFlags = ISC_REQ_SEQUENCE_DETECT | ISC_REQ_REPLAY_DETECT | ISC_REQ_CONFIDENTIALITY | ISC_RET_EXTENDED_ERROR | ISC_REQ_ALLOCATE_MEMORY | ISC_REQ_STREAM; OutBuffers[0].pvBuffer = NULL; OutBuffers[0].BufferType = SECBUFFER_TOKEN; OutBuffers[0].cbBuffer = 0; OutBuffer.cBuffers = 1; OutBuffer.pBuffers = OutBuffers; OutBuffer.ulVersion = SECBUFFER_VERSION; TimeStamp tsExpiry; DWORD dwSSPIOutFlags; scRet = g_pSSPI->InitializeSecurityContext(&hCreds, &ssl->hContext, NULL, dwSSPIFlags, 0, 0, NULL, 0, &ssl->hContext, &OutBuffer, &dwSSPIOutFlags, &tsExpiry); if (FAILED(scRet)) return; // Send the close notify message to the server. if (OutBuffers[0].pvBuffer != NULL && OutBuffers[0].cbBuffer != 0) { send(ssl->s, (char*)OutBuffers[0].pvBuffer, OutBuffers[0].cbBuffer, 0); g_pSSPI->FreeContextBuffer(OutBuffers[0].pvBuffer); } }
HANDLE NetlibInitSecurityProvider(const TCHAR* szProvider, const TCHAR* szPrincipal) { HANDLE hSecurity = NULL; if (_tcsicmp(szProvider, _T("Basic")) == 0) { NtlmHandleType* hNtlm = (NtlmHandleType*)mir_calloc(sizeof(NtlmHandleType)); hNtlm->szProvider = mir_tstrdup(szProvider); SecInvalidateHandle(&hNtlm->hClientContext); SecInvalidateHandle(&hNtlm->hClientCredential); ntlmCnt++; return hNtlm; } WaitForSingleObject(hSecMutex, INFINITE); if (secCnt == 0 ) { LoadSecurityLibrary(); secCnt += g_hSecurity != NULL; } else secCnt++; if (g_pSSPI != NULL) { PSecPkgInfo ntlmSecurityPackageInfo; bool isGSSAPI = _tcsicmp(szProvider, _T("GSSAPI")) == 0; const TCHAR *szProviderC = isGSSAPI ? _T("Kerberos") : szProvider; SECURITY_STATUS sc = g_pSSPI->QuerySecurityPackageInfo((LPTSTR)szProviderC, &ntlmSecurityPackageInfo); if (sc == SEC_E_OK) { NtlmHandleType* hNtlm; hSecurity = hNtlm = (NtlmHandleType*)mir_calloc(sizeof(NtlmHandleType)); hNtlm->cbMaxToken = ntlmSecurityPackageInfo->cbMaxToken; g_pSSPI->FreeContextBuffer(ntlmSecurityPackageInfo); hNtlm->szProvider = mir_tstrdup(szProvider); hNtlm->szPrincipal = mir_tstrdup(szPrincipal ? szPrincipal : _T("")); SecInvalidateHandle(&hNtlm->hClientContext); SecInvalidateHandle(&hNtlm->hClientCredential); ntlmCnt++; } } ReleaseMutex(hSecMutex); return hSecurity; }
/* * Query specified package for it's maximum token size. */ static int getMaxTokenSize(char *package, ULONG * maxTokenSize) { SECURITY_STATUS status; SecPkgInfo *packageSecurityInfo = NULL; status = pSFT->QuerySecurityPackageInfo(package, &packageSecurityInfo); if (status == SEC_E_OK) { *maxTokenSize = packageSecurityInfo->cbMaxToken; if (pSFT->FreeContextBuffer(packageSecurityInfo) != SEC_E_OK) { NE_DEBUG(NE_DBG_HTTPAUTH, "sspi: Unable to free security package info."); } } else { NE_DEBUG(NE_DBG_HTTPAUTH, "sspi: QuerySecurityPackageInfo [failed] [%x].", status); return -1; } return 0; }
static SECURITY_STATUS ClientHandshakeLoop(SslHandle *ssl, BOOL fDoInitialRead) { DWORD dwSSPIFlags = ISC_REQ_SEQUENCE_DETECT | ISC_REQ_REPLAY_DETECT | ISC_REQ_CONFIDENTIALITY | ISC_REQ_EXTENDED_ERROR | ISC_REQ_ALLOCATE_MEMORY | ISC_REQ_STREAM; ssl->cbIoBuffer = 0; BOOL fDoRead = fDoInitialRead; SECURITY_STATUS scRet = SEC_I_CONTINUE_NEEDED; // Loop until the handshake is finished or an error occurs. while (scRet == SEC_I_CONTINUE_NEEDED || scRet == SEC_E_INCOMPLETE_MESSAGE || scRet == SEC_I_INCOMPLETE_CREDENTIALS) { // Read server data if (0 == ssl->cbIoBuffer || scRet == SEC_E_INCOMPLETE_MESSAGE) { if (fDoRead) { static const TIMEVAL tv = { 6, 0 }; fd_set fd; // If buffer not large enough reallocate buffer if (ssl->sbIoBuffer <= ssl->cbIoBuffer) { ssl->sbIoBuffer += 4096; ssl->pbIoBuffer = (PUCHAR)mir_realloc(ssl->pbIoBuffer, ssl->sbIoBuffer); } FD_ZERO(&fd); FD_SET(ssl->s, &fd); if (select(1, &fd, NULL, NULL, &tv) != 1) { Netlib_Logf(NULL, "SSL Negotiation failure recieving data (timeout) (bytes %u)", ssl->cbIoBuffer); scRet = ERROR_NOT_READY; break; } DWORD cbData = recv(ssl->s, (char*)ssl->pbIoBuffer + ssl->cbIoBuffer, ssl->sbIoBuffer - ssl->cbIoBuffer, 0); if (cbData == SOCKET_ERROR) { Netlib_Logf(NULL, "SSL Negotiation failure recieving data (%d)", WSAGetLastError()); scRet = ERROR_NOT_READY; break; } if (cbData == 0) { Netlib_Logf(NULL, "SSL Negotiation connection gracefully closed"); scRet = ERROR_NOT_READY; break; } ssl->cbIoBuffer += cbData; } else fDoRead = TRUE; } // Set up the input buffers. Buffer 0 is used to pass in data // received from the server. Schannel will consume some or all // of this. Leftover data (if any) will be placed in buffer 1 and // given a buffer type of SECBUFFER_EXTRA. SecBuffer InBuffers[2]; InBuffers[0].pvBuffer = ssl->pbIoBuffer; InBuffers[0].cbBuffer = ssl->cbIoBuffer; InBuffers[0].BufferType = SECBUFFER_TOKEN; InBuffers[1].pvBuffer = NULL; InBuffers[1].cbBuffer = 0; InBuffers[1].BufferType = SECBUFFER_EMPTY; SecBufferDesc InBuffer; InBuffer.cBuffers = _countof(InBuffers); InBuffer.pBuffers = InBuffers; InBuffer.ulVersion = SECBUFFER_VERSION; // Set up the output buffers. These are initialized to NULL // so as to make it less likely we'll attempt to free random // garbage later. SecBuffer OutBuffers[1]; OutBuffers[0].pvBuffer = NULL; OutBuffers[0].BufferType = SECBUFFER_TOKEN; OutBuffers[0].cbBuffer = 0; SecBufferDesc OutBuffer; OutBuffer.cBuffers = _countof(OutBuffers); OutBuffer.pBuffers = OutBuffers; OutBuffer.ulVersion = SECBUFFER_VERSION; TimeStamp tsExpiry; DWORD dwSSPIOutFlags; scRet = g_pSSPI->InitializeSecurityContext(&hCreds, &ssl->hContext, NULL, dwSSPIFlags, 0, 0, &InBuffer, 0, NULL, &OutBuffer, &dwSSPIOutFlags, &tsExpiry); // If success (or if the error was one of the special extended ones), // send the contents of the output buffer to the server. if (scRet == SEC_E_OK || scRet == SEC_I_CONTINUE_NEEDED || (FAILED(scRet) && (dwSSPIOutFlags & ISC_RET_EXTENDED_ERROR))) { if (OutBuffers[0].cbBuffer != 0 && OutBuffers[0].pvBuffer != NULL) { DWORD cbData = send(ssl->s, (char*)OutBuffers[0].pvBuffer, OutBuffers[0].cbBuffer, 0); if (cbData == SOCKET_ERROR || cbData == 0) { Netlib_Logf(NULL, "SSL Negotiation failure sending data (%d)", WSAGetLastError()); g_pSSPI->FreeContextBuffer(OutBuffers[0].pvBuffer); return SEC_E_INTERNAL_ERROR; } // Free output buffer. g_pSSPI->FreeContextBuffer(OutBuffers[0].pvBuffer); OutBuffers[0].pvBuffer = NULL; } } // we need to read more data from the server and try again. if (scRet == SEC_E_INCOMPLETE_MESSAGE) continue; // handshake completed successfully. if (scRet == SEC_E_OK) { // Store remaining data for further use if (InBuffers[1].BufferType == SECBUFFER_EXTRA) { memmove(ssl->pbIoBuffer, ssl->pbIoBuffer + (ssl->cbIoBuffer - InBuffers[1].cbBuffer), InBuffers[1].cbBuffer); ssl->cbIoBuffer = InBuffers[1].cbBuffer; } else ssl->cbIoBuffer = 0; break; } // Check for fatal error. if (FAILED(scRet)) break; // server just requested client authentication. if (scRet == SEC_I_INCOMPLETE_CREDENTIALS) { // Server has requested client authentication and // GetNewClientCredentials(ssl); // Go around again. fDoRead = FALSE; scRet = SEC_I_CONTINUE_NEEDED; continue; } // Copy any leftover data from the buffer, and go around again. if (InBuffers[1].BufferType == SECBUFFER_EXTRA) { memmove(ssl->pbIoBuffer, ssl->pbIoBuffer + (ssl->cbIoBuffer - InBuffers[1].cbBuffer), InBuffers[1].cbBuffer); ssl->cbIoBuffer = InBuffers[1].cbBuffer; } else ssl->cbIoBuffer = 0; } // Delete the security context in the case of a fatal error. ReportSslError(scRet, __LINE__); if (ssl->cbIoBuffer == 0) { mir_free(ssl->pbIoBuffer); ssl->pbIoBuffer = NULL; ssl->sbIoBuffer = 0; } return scRet; }
// perform SSL handshake with remote system SECURITY_STATUS chs (void) { DWORD dwFlagsIn, dwFlagsOut; SecBuffer ib[2], ob[1]; SecBufferDesc in, out; DWORD cbIoBuffer=0; PBYTE IoBuffer; int len; BOOL bRead=TRUE; // 8192 should be enough for handshake but // if you see any errors, try increasing it. IoBuffer = LocalAlloc (LMEM_FIXED, 8192); if ((ss=chs_hello())!=SEC_E_OK) { return ss; } dwFlagsIn = ISC_REQ_SEQUENCE_DETECT | ISC_REQ_REPLAY_DETECT | ISC_REQ_CONFIDENTIALITY | ISC_RET_EXTENDED_ERROR | ISC_REQ_ALLOCATE_MEMORY | ISC_REQ_STREAM; ss=SEC_I_CONTINUE_NEEDED; while (ss==SEC_I_CONTINUE_NEEDED || ss==SEC_E_INCOMPLETE_MESSAGE) { if (ss==SEC_E_INCOMPLETE_MESSAGE || cbIoBuffer==0) { if (bRead) { len=recv (s, &IoBuffer[cbIoBuffer], 8192, 0); // some socket error if (len<=0) { break; } cbIoBuffer += len; } else { bRead=TRUE; } } ib[0].pvBuffer = IoBuffer; ib[0].cbBuffer = cbIoBuffer; ib[0].BufferType = SECBUFFER_TOKEN; ib[1].pvBuffer = NULL; ib[1].cbBuffer = 0; ib[1].BufferType = SECBUFFER_EMPTY; in.cBuffers = 2; in.pBuffers = ib; in.ulVersion = SECBUFFER_VERSION; ob[0].pvBuffer = NULL; ob[0].BufferType = SECBUFFER_TOKEN; ob[0].cbBuffer = 0; out.cBuffers = 1; out.pBuffers = ob; out.ulVersion = SECBUFFER_VERSION; ss = sspi->InitializeSecurityContextA (&hClientCreds, &hContext, NULL, dwFlagsIn, 0, SECURITY_NATIVE_DREP, &in, 0, NULL, &out, &dwFlagsOut, &ts); // might get SEC_E_ILLEGAL_MESSAGE here if (ss==SEC_E_OK || ss==SEC_I_CONTINUE_NEEDED || FAILED (ss) && (dwFlagsOut & ISC_RET_EXTENDED_ERROR)) { if (ob[0].cbBuffer != 0) { len=send (s, ob[0].pvBuffer, ob[0].cbBuffer, 0); // socket error if (len<=0) { break; } sspi->FreeContextBuffer (ob[0].pvBuffer); } } if (ss==SEC_E_INCOMPLETE_MESSAGE) continue; if (ss==SEC_E_OK) { if (ib[1].BufferType==SECBUFFER_EXTRA) { // i don't handle extra data here but it should be. } break; } // Copy any leftover data from the "extra" buffer, and go around again. if ( ib[1].BufferType == SECBUFFER_EXTRA ) { MoveMemory (IoBuffer, &IoBuffer[cbIoBuffer - ib[1].cbBuffer], ib[1].cbBuffer); cbIoBuffer = ib[1].cbBuffer; } else cbIoBuffer = 0; } LocalFree (IoBuffer); return ss; }