// 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; }
/* * Resets the context */ static void resetContext(SSPIContext * sspiContext) { pSFT->DeleteSecurityContext(&(sspiContext->context)); #if defined(_MSC_VER) && _MSC_VER <= 1200 pSFT->FreeCredentialHandle(&(sspiContext->credentials)); #else pSFT->FreeCredentialsHandle(&(sspiContext->credentials)); #endif sspiContext->continueNeeded = 0; }
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; }
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; }
static bool AcquireCredentials(void) { SCHANNEL_CRED SchannelCred; TimeStamp tsExpiry; SECURITY_STATUS scRet; memset(&SchannelCred, 0, sizeof(SchannelCred)); SchannelCred.dwVersion = SCHANNEL_CRED_VERSION; SchannelCred.grbitEnabledProtocols = SP_PROT_SSL3TLS1_CLIENTS; SchannelCred.dwFlags |= SCH_CRED_NO_DEFAULT_CREDS | SCH_CRED_MANUAL_CRED_VALIDATION; // Create an SSPI credential. scRet = g_pSSPI->AcquireCredentialsHandle( NULL, // Name of principal UNISP_NAME, // Name of package SECPKG_CRED_OUTBOUND, // Flags indicating use NULL, // Pointer to logon ID &SchannelCred, // Package specific data NULL, // Pointer to GetKey() func NULL, // Value to pass to GetKey() &hCreds, // (out) Cred Handle &tsExpiry); // (out) Lifetime (optional) ReportSslError(scRet, __LINE__); return scRet == SEC_E_OK; }
/* * Wrapper arround initializeSecurityContext. Supplies several * default parameters as well as logging in case of errors. */ static SECURITY_STATUS initializeSecurityContext(CredHandle * credentials, CtxtHandle * context, char *spn, ULONG contextReq, SecBufferDesc * inBuffer, CtxtHandle * newContext, SecBufferDesc * outBuffer) { ULONG contextAttributes; SECURITY_STATUS status; status = pSFT->InitializeSecurityContext(credentials, context, spn, contextReq, 0, SECURITY_NETWORK_DREP, inBuffer, 0, newContext, outBuffer, &contextAttributes, NULL); if (!SEC_SUCCESS(status)) { if (status == SEC_E_INVALID_TOKEN) { NE_DEBUG(NE_DBG_HTTPAUTH, "InitializeSecurityContext [fail] SEC_E_INVALID_TOKEN.\n"); } else if (status == SEC_E_UNSUPPORTED_FUNCTION) { NE_DEBUG(NE_DBG_HTTPAUTH, "InitializeSecurityContext [fail] SEC_E_UNSUPPORTED_FUNCTION.\n"); } else { NE_DEBUG(NE_DBG_HTTPAUTH, "InitializeSecurityContext [fail] [%x].\n", status); } } return status; }
// create credentials SECURITY_STATUS create_creds (void) { DWORD cSupportedAlgs = 0; ALG_ID rgbSupportedAlgs[16]; ZeroMemory (&SchannelCred, sizeof (SchannelCred)); SchannelCred.dwVersion = SCHANNEL_CRED_VERSION; SchannelCred.grbitEnabledProtocols = SP_PROT_SSL3 | SP_PROT_TLS1; if (aiKeyExch) { rgbSupportedAlgs[cSupportedAlgs++] = aiKeyExch; } if (cSupportedAlgs) { SchannelCred.cSupportedAlgs = cSupportedAlgs; SchannelCred.palgSupportedAlgs = rgbSupportedAlgs; } SchannelCred.dwFlags |= SCH_CRED_NO_DEFAULT_CREDS; // We need manual validation SchannelCred.dwFlags |= SCH_CRED_MANUAL_CRED_VALIDATION; ss = sspi->AcquireCredentialsHandleA (NULL, UNISP_NAME_A, SECPKG_CRED_OUTBOUND, NULL, &SchannelCred, NULL, NULL, &hClientCreds, &ts); return ss; }
void UnloadSslModule(void) { if (g_pSSPI && SecIsValidHandle(&hCreds)) g_pSSPI->FreeCredentialsHandle(&hCreds); CloseHandle(g_hSslMutex); if (g_hSchannel) FreeLibrary(g_hSchannel); }
void NetlibSslFree(SslHandle *ssl) { if (ssl == NULL) return; g_pSSPI->DeleteSecurityContext(&ssl->hContext); mir_free(ssl->pbRecDataBuf); mir_free(ssl->pbIoBuffer); memset(ssl, 0, sizeof(SslHandle)); mir_free(ssl); }
/* * 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; }
void secure_info (void) { SecPkgContext_ConnectionInfo ci; ss = sspi->QueryContextAttributes (&hContext, SECPKG_ATTR_CONNECTION_INFO, (PVOID)&ci); if(ss != SEC_E_OK) { printf("Error 0x%x querying connection info\n", ss); return; } printf (" [ Protocol : %s\n", alg2s(ci.dwProtocol)); printf (" [ Cipher : %s-%i\n", alg2s(ci.aiCipher), ci.dwCipherStrength); printf (" [ Hash : %s-%i\n", alg2s(ci.aiHash), ci.dwHashStrength ); printf (" [ Exchange : %s-%i\n\n", alg2s(ci.aiExch), ci.dwExchStrength ); }
SECURITY_STATUS ssl_recv (void) { SecBufferDesc msg; SecBuffer sb[4]; DWORD cbIoBuffer=0; SecBuffer *pData=NULL, *pExtra=NULL; int len, i; ss=SEC_E_INCOMPLETE_MESSAGE; do { if (cbIoBuffer==0 || ss==SEC_E_INCOMPLETE_MESSAGE) { len = recv (s, pbDataIn + cbIoBuffer, cbBufferLen - cbIoBuffer, 0); if (len<=0) break; cbIoBuffer += len; sb[0].pvBuffer = pbDataIn; sb[0].cbBuffer = cbIoBuffer; sb[0].BufferType = SECBUFFER_DATA; sb[1].BufferType = SECBUFFER_EMPTY; sb[2].BufferType = SECBUFFER_EMPTY; sb[3].BufferType = SECBUFFER_EMPTY; msg.ulVersion = SECBUFFER_VERSION; msg.cBuffers = 4; msg.pBuffers = sb; ss = sspi->DecryptMessage (&hContext, &msg, 0, NULL); if (ss == SEC_I_CONTEXT_EXPIRED) break; for (i=0; i<4; i++) { if (pData==NULL && sb[i].BufferType==SECBUFFER_DATA) pData=&sb[i]; if (pExtra==NULL && sb[i].BufferType==SECBUFFER_EXTRA) pExtra=&sb[i]; } if (pData!=NULL) { cbDataIn=pData->cbBuffer; if (cbDataIn!=0) { memcpy (pbDataIn, pData->pvBuffer, cbDataIn); break; } } } } while (1); return SEC_E_OK; }
void NetlibDestroySecurityProvider(HANDLE hSecurity) { if (hSecurity == NULL) return; WaitForSingleObject(hSecMutex, INFINITE); if (ntlmCnt != 0) { NtlmHandleType* hNtlm = (NtlmHandleType*)hSecurity; if (SecIsValidHandle(&hNtlm->hClientContext)) g_pSSPI->DeleteSecurityContext(&hNtlm->hClientContext); if (SecIsValidHandle(&hNtlm->hClientCredential)) g_pSSPI->FreeCredentialsHandle(&hNtlm->hClientCredential); mir_free(hNtlm->szProvider); mir_free(hNtlm->szPrincipal); --ntlmCnt; mir_free(hNtlm); } if (secCnt && --secCnt == 0) FreeSecurityLibrary(); ReleaseMutex(hSecMutex); }
/* * Simplification wrapper arround AcquireCredentialsHandle as most of * the parameters do not change. */ static int acquireCredentialsHandle(CredHandle * credentials, char *package) { SECURITY_STATUS status; TimeStamp timestamp; status = pSFT->AcquireCredentialsHandle(NULL, package, SECPKG_CRED_OUTBOUND, NULL, NULL, NULL, NULL, credentials, ×tamp); if (status != SEC_E_OK) { NE_DEBUG(NE_DBG_HTTPAUTH, "sspi: AcquireCredentialsHandle [fail] [%x].\n", status); return -1; } return 0; }
// encrypt and send data to remote system SECURITY_STATUS ssl_send (void) { SecBufferDesc msg; SecBuffer sb[4]; // stream header sb[0].pvBuffer = pbBufferOut; sb[0].cbBuffer = Sizes.cbHeader; sb[0].BufferType = SECBUFFER_STREAM_HEADER; // stream data sb[1].pvBuffer = pbBufferOut + Sizes.cbHeader; sb[1].cbBuffer = cbDataOut; sb[1].BufferType = SECBUFFER_DATA; // stream trailer sb[2].pvBuffer = pbBufferOut + Sizes.cbHeader + cbDataOut; sb[2].cbBuffer = Sizes.cbTrailer; sb[2].BufferType = SECBUFFER_STREAM_TRAILER; sb[3].pvBuffer = SECBUFFER_EMPTY; sb[3].cbBuffer = SECBUFFER_EMPTY; sb[3].BufferType = SECBUFFER_EMPTY; msg.ulVersion = SECBUFFER_VERSION; msg.cBuffers = 4; msg.pBuffers = sb; // encrypt ss = sspi->EncryptMessage (&hContext, 0, &msg, 0); // send if (ss==SEC_E_OK) { send (s, pbBufferOut, sb[0].cbBuffer + sb[1].cbBuffer + sb[2].cbBuffer, 0); } return ss; }
/* * Processes received authentication tokens as well as supplies the * response token. */ int ne_sspi_authenticate(void *context, const char *base64Token, char **responseToken) { SecBufferDesc outBufferDesc; SecBuffer outBuffer; int status; SECURITY_STATUS securityStatus; ULONG contextFlags; SSPIContext *sspiContext; if (initialized <= 0) { return -1; } status = getContext(context, &sspiContext); if (status) { return status; } /* TODO: Not sure what flags should be set. joe: this needs to be * driven by the ne_auth interface; the GSSAPI code needs similar * flags. */ contextFlags = ISC_REQ_CONFIDENTIALITY | ISC_REQ_MUTUAL_AUTH; initSingleEmptyBuffer(&outBufferDesc, &outBuffer); status = makeBuffer(&outBufferDesc, sspiContext->maxTokenSize); if (status) { return status; } if (base64Token) { SecBufferDesc inBufferDesc; SecBuffer inBuffer; if (!sspiContext->continueNeeded) { freeBuffer(&outBufferDesc); NE_DEBUG(NE_DBG_HTTPAUTH, "sspi: Got an unexpected token.\n"); return -1; } initSingleEmptyBuffer(&inBufferDesc, &inBuffer); status = base64ToBuffer(base64Token, &inBufferDesc); if (status) { freeBuffer(&outBufferDesc); return status; } securityStatus = initializeSecurityContext(&sspiContext->credentials, &(sspiContext->context), sspiContext->serverName, contextFlags, &inBufferDesc, &(sspiContext->context), &outBufferDesc); if (securityStatus == SEC_E_OK) { sspiContext->authfinished = 1; } freeBuffer(&inBufferDesc); } else { if (sspiContext->continueNeeded) { freeBuffer(&outBufferDesc); NE_DEBUG(NE_DBG_HTTPAUTH, "sspi: Expected a token from server.\n"); return -1; } if (sspiContext->authfinished && (sspiContext->credentials.dwLower || sspiContext->credentials.dwUpper)) { if (sspiContext->authfinished) { freeBuffer(&outBufferDesc); sspiContext->authfinished = 0; NE_DEBUG(NE_DBG_HTTPAUTH,"sspi: failing because starting over from failed try.\n"); return -1; } sspiContext->authfinished = 0; } /* Reset any existing context since we are starting over */ resetContext(sspiContext); if (acquireCredentialsHandle (&sspiContext->credentials, sspiContext->mechanism) != SEC_E_OK) { freeBuffer(&outBufferDesc); NE_DEBUG(NE_DBG_HTTPAUTH, "sspi: acquireCredentialsHandle failed.\n"); return -1; } securityStatus = initializeSecurityContext(&sspiContext->credentials, NULL, sspiContext->serverName, contextFlags, NULL, &(sspiContext->context), &outBufferDesc); } if (securityStatus == SEC_I_COMPLETE_AND_CONTINUE || securityStatus == SEC_I_COMPLETE_NEEDED) { SECURITY_STATUS compleStatus = pSFT->CompleteAuthToken(&(sspiContext->context), &outBufferDesc); if (compleStatus != SEC_E_OK) { freeBuffer(&outBufferDesc); NE_DEBUG(NE_DBG_HTTPAUTH, "sspi: CompleteAuthToken failed.\n"); return -1; } } if (securityStatus == SEC_I_COMPLETE_AND_CONTINUE || securityStatus == SEC_I_CONTINUE_NEEDED) { sspiContext->continueNeeded = 1; } else { sspiContext->continueNeeded = 0; } if (!(securityStatus == SEC_I_COMPLETE_AND_CONTINUE || securityStatus == SEC_I_COMPLETE_NEEDED || securityStatus == SEC_I_CONTINUE_NEEDED || securityStatus == SEC_E_OK)) { NE_DEBUG(NE_DBG_HTTPAUTH, "sspi: initializeSecurityContext [failed] [%x].\n", securityStatus); freeBuffer(&outBufferDesc); return -1; } *responseToken = ne_base64(outBufferDesc.pBuffers->pvBuffer, outBufferDesc.pBuffers->cbBuffer); freeBuffer(&outBufferDesc); return 0; }
static bool VerifyCertificate(SslHandle *ssl, PCSTR pszServerName, DWORD dwCertFlags) { static LPSTR rgszUsages[] = { szOID_PKIX_KP_SERVER_AUTH, szOID_SERVER_GATED_CRYPTO, szOID_SGC_NETSCAPE }; CERT_CHAIN_PARA ChainPara = { 0 }; HTTPSPolicyCallbackData polHttps = { 0 }; CERT_CHAIN_POLICY_PARA PolicyPara = { 0 }; CERT_CHAIN_POLICY_STATUS PolicyStatus = { 0 }; PCCERT_CHAIN_CONTEXT pChainContext = NULL; PCCERT_CONTEXT pServerCert = NULL; DWORD scRet; PWSTR pwszServerName = mir_a2u(pszServerName); scRet = g_pSSPI->QueryContextAttributes(&ssl->hContext, SECPKG_ATTR_REMOTE_CERT_CONTEXT, &pServerCert); if (scRet != SEC_E_OK) goto cleanup; if (pServerCert == NULL) { scRet = SEC_E_WRONG_PRINCIPAL; goto cleanup; } ChainPara.cbSize = sizeof(ChainPara); ChainPara.RequestedUsage.dwType = USAGE_MATCH_TYPE_OR; ChainPara.RequestedUsage.Usage.cUsageIdentifier = _countof(rgszUsages); ChainPara.RequestedUsage.Usage.rgpszUsageIdentifier = rgszUsages; if (!CertGetCertificateChain(NULL, pServerCert, NULL, pServerCert->hCertStore, &ChainPara, 0, NULL, &pChainContext)) { scRet = GetLastError(); goto cleanup; } polHttps.cbStruct = sizeof(HTTPSPolicyCallbackData); polHttps.dwAuthType = AUTHTYPE_SERVER; polHttps.fdwChecks = dwCertFlags; polHttps.pwszServerName = pwszServerName; PolicyPara.cbSize = sizeof(PolicyPara); PolicyPara.pvExtraPolicyPara = &polHttps; PolicyStatus.cbSize = sizeof(PolicyStatus); if (!CertVerifyCertificateChainPolicy(CERT_CHAIN_POLICY_SSL, pChainContext, &PolicyPara, &PolicyStatus)) { scRet = GetLastError(); goto cleanup; } if (PolicyStatus.dwError) { scRet = PolicyStatus.dwError; goto cleanup; } scRet = SEC_E_OK; cleanup: if (pChainContext) CertFreeCertificateChain(pChainContext); if (pServerCert) CertFreeCertificateContext(pServerCert); mir_free(pwszServerName); ReportSslError(scRet, __LINE__, true); return scRet == SEC_E_OK; }
SECURITY_STATUS ReadDecrypt (void) // calls recv() - blocking socket read // http://msdn.microsoft.com/en-us/library/ms740121(VS.85).aspx // The encrypted message is decrypted in place, overwriting the original contents of its buffer. // http://msdn.microsoft.com/en-us/library/aa375211(VS.85).aspx { SecBuffer ExtraBuffer; SecBuffer *pDataBuffer, *pExtraBuffer; SECURITY_STATUS scRet; // unsigned long cbBuffer; // Size of the buffer, in bytes SecBufferDesc Message; // unsigned long BufferType; // Type of the buffer (below) SecBuffer Buffers[4]; // void SEC_FAR * pvBuffer; // Pointer to the buffer DWORD cbIoBuffer, cbData, length; PBYTE buff; int i; // Read data from server until done. cbIoBuffer = 0; scRet = 0; while(TRUE) // Read some data. { if( cbIoBuffer == 0 || scRet == SEC_E_INCOMPLETE_MESSAGE ) // get the data { cbDataIn = recv(s, pbBufferIn + cbIoBuffer, cbBufferLen - cbIoBuffer, 0); if(cbDataIn == SOCKET_ERROR) { printf("**** Error %d reading data from server\n", WSAGetLastError()); scRet = SEC_E_INTERNAL_ERROR; break; } else if(cbDataIn == 0) // Server disconnected. { if(cbIoBuffer) { printf("**** Server unexpectedly disconnected\n"); scRet = SEC_E_INTERNAL_ERROR; return scRet; } else break; // All Done } else // success { cbIoBuffer += cbDataIn; } } // Decrypt the received data. Buffers[0].pvBuffer = pbBufferIn; Buffers[0].cbBuffer = cbIoBuffer; Buffers[0].BufferType = SECBUFFER_DATA; // Initial Type of the buffer 1 Buffers[1].BufferType = SECBUFFER_EMPTY; // Initial Type of the buffer 2 Buffers[2].BufferType = SECBUFFER_EMPTY; // Initial Type of the buffer 3 Buffers[3].BufferType = SECBUFFER_EMPTY; // Initial Type of the buffer 4 Message.ulVersion = SECBUFFER_VERSION; // Version number Message.cBuffers = 4; // Number of buffers - must contain four SecBuffer structures. Message.pBuffers = Buffers; // Pointer to array of buffers scRet = sspi->DecryptMessage(&hContext, &Message, 0, NULL); if( scRet == SEC_I_CONTEXT_EXPIRED ) break; // Server signalled end of session // if( scRet == SEC_E_INCOMPLETE_MESSAGE - Input buffer has partial encrypted record, read more if( scRet != SEC_E_OK && scRet != SEC_I_RENEGOTIATE && scRet != SEC_I_CONTEXT_EXPIRED ) { printf("**** DecryptMessage "); return scRet; } // Locate data and (optional) extra buffers. pDataBuffer = NULL; pExtraBuffer = NULL; for(i = 1; i < 4; i++) { if( pDataBuffer == NULL && Buffers[i].BufferType == SECBUFFER_DATA ) pDataBuffer = &Buffers[i]; if( pExtraBuffer == NULL && Buffers[i].BufferType == SECBUFFER_EXTRA ) pExtraBuffer = &Buffers[i]; } if (pDataBuffer!=NULL) { cbDataIn=pDataBuffer->cbBuffer; if (cbDataIn!=0) { memcpy (pbDataIn, pDataBuffer->pvBuffer, cbDataIn); } } // Move any "extra" data to the input buffer. if(pExtraBuffer) { printf ("extra"); MoveMemory(pbBufferIn, pExtraBuffer->pvBuffer, pExtraBuffer->cbBuffer); cbIoBuffer = pExtraBuffer->cbBuffer; // printf("cbIoBuffer= %d \n", cbIoBuffer); } else cbIoBuffer = 0; printf ("\nhello"); } return SEC_E_OK; }
// 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; }
/* * Resets the context */ static void resetContext(SSPIContext * sspiContext) { pSFT->DeleteSecurityContext(&(sspiContext->context)); pSFT->FreeCredentialsHandle(&(sspiContext->credentials)); sspiContext->continueNeeded = 0; }
int main (int argc, char *argv[]) { INIT_SECURITY_INTERFACE pInitSecurityInterface; // set buffer width of console setw (300); puts ("\n [ cms v0.1 - Copyleft 2015 (x) @Odzhan\n"); // set up default values args.address = NULL; args.ai_family = AF_INET; args.port = DEFAULT_PORT; args.port_nbr = atoi(args.port); pInitSecurityInterface = (INIT_SECURITY_INTERFACE)GetProcAddress(LoadLibrary("Secur32"), "InitSecurityInterfaceA" ); if (pInitSecurityInterface==NULL) printf ("didn't resolve"); sspi = pInitSecurityInterface(); // process command line parse_args(argc, argv); // resolve address and open socket if (open_tcp ()) { start_handler (); // create credentials if (create_creds()==SEC_E_OK) { // connect to server if (connect (s, ai_addr, ai_addrlen) != SOCKET_ERROR) { // perform the handshake if (chs () == SEC_E_OK) { printf (" [ connected\n\n"); secure_info(); ss=sspi->QueryContextAttributes (&hContext, SECPKG_ATTR_STREAM_SIZES, &Sizes ); cbBufferLen = Sizes.cbHeader + Sizes.cbMaximumMessage + Sizes.cbTrailer; pbBufferIn = LocalAlloc(LMEM_FIXED, cbBufferLen); pbBufferOut = LocalAlloc(LMEM_FIXED, cbBufferLen); pbDataIn=pbBufferIn + Sizes.cbHeader; pbDataOut=pbBufferOut + Sizes.cbHeader; cbBufferLen = Sizes.cbMaximumMessage; printf (" [ running cmd\n"); cmd(); } else { printf (" [ handshake failed\n"); } } else { printf (" [ unable to connect\n"); } } else { printf (" [ error creating credentials\n"); } stop_handler (); close_tcp(); } return 0; }
char* CompleteGssapi(HANDLE hSecurity, unsigned char *szChallenge, unsigned chlsz) { if (!szChallenge || !szChallenge[0]) return NULL; NtlmHandleType* hNtlm = (NtlmHandleType*)hSecurity; unsigned char inDataBuffer[1024]; SecBuffer inBuffers[2] = { { sizeof(inDataBuffer), SECBUFFER_DATA, inDataBuffer }, { chlsz, SECBUFFER_STREAM, szChallenge }, }; SecBufferDesc inBuffersDesc = { SECBUFFER_VERSION, 2, inBuffers }; unsigned long qop = 0; SECURITY_STATUS sc = g_pSSPI->DecryptMessage(&hNtlm->hClientContext, &inBuffersDesc, 0, &qop); if (sc != SEC_E_OK) { ReportSecError(sc, __LINE__); return NULL; } unsigned char LayerMask = inDataBuffer[0]; unsigned int MaxMessageSize = htonl(*(unsigned*)&inDataBuffer[1]); SecPkgContext_Sizes sizes; sc = g_pSSPI->QueryContextAttributes(&hNtlm->hClientContext, SECPKG_ATTR_SIZES, &sizes); if (sc != SEC_E_OK) { ReportSecError(sc, __LINE__); return NULL; } unsigned char *tokenBuffer = (unsigned char*)alloca(sizes.cbSecurityTrailer); unsigned char *paddingBuffer = (unsigned char*)alloca(sizes.cbBlockSize); unsigned char outDataBuffer[4] = { 1, 0, 16, 0 }; SecBuffer outBuffers[3] = { { sizes.cbSecurityTrailer, SECBUFFER_TOKEN, tokenBuffer }, { sizeof(outDataBuffer), SECBUFFER_DATA, outDataBuffer }, { sizes.cbBlockSize, SECBUFFER_PADDING, paddingBuffer } }; SecBufferDesc outBuffersDesc = { SECBUFFER_VERSION, 3, outBuffers }; sc = g_pSSPI->EncryptMessage(&hNtlm->hClientContext, SECQOP_WRAP_NO_ENCRYPT, &outBuffersDesc, 0); if (sc != SEC_E_OK) { ReportSecError(sc, __LINE__); return NULL; } unsigned i, ressz = 0; for (i = 0; i < outBuffersDesc.cBuffers; i++) ressz += outBuffersDesc.pBuffers[i].cbBuffer; unsigned char *response = (unsigned char*)alloca(ressz), *p = response; for (i = 0; i < outBuffersDesc.cBuffers; i++) { memcpy(p, outBuffersDesc.pBuffers[i].pvBuffer, outBuffersDesc.pBuffers[i].cbBuffer); p += outBuffersDesc.pBuffers[i].cbBuffer; } NETLIBBASE64 nlb64; nlb64.cbDecoded = ressz; nlb64.pbDecoded = response; nlb64.cchEncoded = Netlib_GetBase64EncodedBufferSize(nlb64.cbDecoded); nlb64.pszEncoded = (char*)alloca(nlb64.cchEncoded); if (!NetlibBase64Encode(0,(LPARAM)&nlb64)) return NULL; return mir_strdup(nlb64.pszEncoded); }
char* NtlmCreateResponseFromChallenge(HANDLE hSecurity, const char *szChallenge, const TCHAR* login, const TCHAR* psw, bool http, unsigned& complete) { SECURITY_STATUS sc; SecBufferDesc outputBufferDescriptor,inputBufferDescriptor; SecBuffer outputSecurityToken,inputSecurityToken; TimeStamp tokenExpiration; ULONG contextAttributes; NETLIBBASE64 nlb64 = { 0 }; NtlmHandleType* hNtlm = (NtlmHandleType*)hSecurity; if (hSecurity == NULL || ntlmCnt == 0) return NULL; if (_tcsicmp(hNtlm->szProvider, _T("Basic"))) { bool isGSSAPI = _tcsicmp(hNtlm->szProvider, _T("GSSAPI")) == 0; TCHAR *szProvider = isGSSAPI ? _T("Kerberos") : hNtlm->szProvider; bool hasChallenge = szChallenge != NULL && szChallenge[0] != '\0'; if (hasChallenge) { nlb64.cchEncoded = lstrlenA(szChallenge); nlb64.pszEncoded = (char*)szChallenge; nlb64.cbDecoded = Netlib_GetBase64DecodedBufferSize(nlb64.cchEncoded); nlb64.pbDecoded = (PBYTE)alloca(nlb64.cbDecoded); if (!NetlibBase64Decode(0, (LPARAM)&nlb64)) return NULL; if (isGSSAPI && complete) return CompleteGssapi(hSecurity, nlb64.pbDecoded, nlb64.cbDecoded); inputBufferDescriptor.cBuffers = 1; inputBufferDescriptor.pBuffers = &inputSecurityToken; inputBufferDescriptor.ulVersion = SECBUFFER_VERSION; inputSecurityToken.BufferType = SECBUFFER_TOKEN; inputSecurityToken.cbBuffer = nlb64.cbDecoded; inputSecurityToken.pvBuffer = nlb64.pbDecoded; // try to decode the domain name from the NTLM challenge if (login != NULL && login[0] != '\0' && !hNtlm->hasDomain) { NtlmType2packet* pkt = ( NtlmType2packet* )nlb64.pbDecoded; if (!strncmp(pkt->sign, "NTLMSSP", 8) && pkt->type == 2) { #ifdef UNICODE wchar_t* domainName = (wchar_t*)&nlb64.pbDecoded[pkt->targetName.offset]; int domainLen = pkt->targetName.len; // Negotiate ANSI? if yes, convert the ANSI name to unicode if ((pkt->flags & 1) == 0) { int bufsz = MultiByteToWideChar(CP_ACP, 0, (char*)domainName, domainLen, NULL, 0); wchar_t* buf = (wchar_t*)alloca(bufsz * sizeof(wchar_t)); domainLen = MultiByteToWideChar(CP_ACP, 0, (char*)domainName, domainLen, buf, bufsz) - 1; domainName = buf; } else domainLen /= sizeof(wchar_t); #else char* domainName = (char*)&nlb64.pbDecoded[pkt->targetName.offset]; int domainLen = pkt->targetName.len; // Negotiate Unicode? if yes, convert the unicode name to ANSI if (pkt->flags & 1) { int bufsz = WideCharToMultiByte(CP_ACP, 0, (WCHAR*)domainName, domainLen, NULL, 0, NULL, NULL); char* buf = (char*)alloca(bufsz); domainLen = WideCharToMultiByte(CP_ACP, 0, (WCHAR*)domainName, domainLen, buf, bufsz, NULL, NULL) - 1; domainName = buf; } #endif if (domainLen) { size_t newLoginLen = _tcslen(login) + domainLen + 1; TCHAR *newLogin = (TCHAR*)alloca(newLoginLen * sizeof(TCHAR)); _tcsncpy(newLogin, domainName, domainLen); newLogin[domainLen] = '\\'; _tcscpy(newLogin + domainLen + 1, login); char* szChl = NtlmCreateResponseFromChallenge(hSecurity, NULL, newLogin, psw, http, complete); mir_free(szChl); } } } } else { if (SecIsValidHandle(&hNtlm->hClientContext)) g_pSSPI->DeleteSecurityContext(&hNtlm->hClientContext); if (SecIsValidHandle(&hNtlm->hClientCredential)) g_pSSPI->FreeCredentialsHandle(&hNtlm->hClientCredential); SEC_WINNT_AUTH_IDENTITY auth; if (login != NULL && login[0] != '\0') { memset(&auth, 0, sizeof(auth)); #ifdef _UNICODE NetlibLogf(NULL, "Security login requested, user: %S pssw: %s", login, psw ? "(exist)" : "(no psw)"); #else NetlibLogf(NULL, "Security login requested, user: %s pssw: %s", login, psw ? "(exist)" : "(no psw)"); #endif const TCHAR* loginName = login; const TCHAR* domainName = _tcschr(login, '\\'); int domainLen = 0; int loginLen = lstrlen(loginName); if (domainName != NULL) { loginName = domainName + 1; loginLen = lstrlen(loginName); domainLen = domainName - login; domainName = login; } else if ((domainName = _tcschr(login, '@')) != NULL) { loginName = login; loginLen = domainName - login; domainLen = lstrlen(++domainName); } #ifdef UNICODE auth.User = (PWORD)loginName; auth.UserLength = loginLen; auth.Password = (PWORD)psw; auth.PasswordLength = lstrlen(psw); auth.Domain = (PWORD)domainName; auth.DomainLength = domainLen; auth.Flags = SEC_WINNT_AUTH_IDENTITY_UNICODE; #else auth.User = (PBYTE)loginName; auth.UserLength = loginLen; auth.Password = (PBYTE)psw; auth.PasswordLength = lstrlen(psw); auth.Domain = (PBYTE)domainName; auth.DomainLength = domainLen; auth.Flags = SEC_WINNT_AUTH_IDENTITY_ANSI; #endif hNtlm->hasDomain = domainLen != 0; } sc = g_pSSPI->AcquireCredentialsHandle(NULL, szProvider, SECPKG_CRED_OUTBOUND, NULL, hNtlm->hasDomain ? &auth : NULL, NULL, NULL, &hNtlm->hClientCredential, &tokenExpiration); if (sc != SEC_E_OK) { ReportSecError(sc, __LINE__); return NULL; } } outputBufferDescriptor.cBuffers = 1; outputBufferDescriptor.pBuffers = &outputSecurityToken; outputBufferDescriptor.ulVersion = SECBUFFER_VERSION; outputSecurityToken.BufferType = SECBUFFER_TOKEN; outputSecurityToken.cbBuffer = hNtlm->cbMaxToken; outputSecurityToken.pvBuffer = alloca(outputSecurityToken.cbBuffer); sc = g_pSSPI->InitializeSecurityContext(&hNtlm->hClientCredential, hasChallenge ? &hNtlm->hClientContext : NULL, hNtlm->szPrincipal, isGSSAPI ? ISC_REQ_MUTUAL_AUTH | ISC_REQ_STREAM : 0, 0, SECURITY_NATIVE_DREP, hasChallenge ? &inputBufferDescriptor : NULL, 0, &hNtlm->hClientContext, &outputBufferDescriptor, &contextAttributes, &tokenExpiration); complete = (sc != SEC_I_COMPLETE_AND_CONTINUE && sc != SEC_I_CONTINUE_NEEDED); if (sc == SEC_I_COMPLETE_NEEDED || sc == SEC_I_COMPLETE_AND_CONTINUE) { sc = g_pSSPI->CompleteAuthToken(&hNtlm->hClientContext, &outputBufferDescriptor); } if (sc != SEC_E_OK && sc != SEC_I_CONTINUE_NEEDED) { ReportSecError(sc, __LINE__); return NULL; } nlb64.cbDecoded = outputSecurityToken.cbBuffer; nlb64.pbDecoded = (PBYTE)outputSecurityToken.pvBuffer; } else { if (!login || !psw) return NULL; char *szLogin = mir_t2a(login); char *szPassw = mir_t2a(psw); size_t authLen = strlen(szLogin) + strlen(szPassw) + 5; char *szAuth = (char*)alloca(authLen); nlb64.cbDecoded = mir_snprintf(szAuth, authLen,"%s:%s", szLogin, szPassw); nlb64.pbDecoded=(PBYTE)szAuth; complete = true; mir_free(szPassw); mir_free(szLogin); } nlb64.cchEncoded = Netlib_GetBase64EncodedBufferSize(nlb64.cbDecoded); nlb64.pszEncoded = (char*)alloca(nlb64.cchEncoded); if (!NetlibBase64Encode(0,(LPARAM)&nlb64)) return NULL; char* result; if (http) { char* szProvider = mir_t2a(hNtlm->szProvider); nlb64.cchEncoded += (int)strlen(szProvider) + 10; result = (char*)mir_alloc(nlb64.cchEncoded); mir_snprintf(result, nlb64.cchEncoded, "%s %s", szProvider, nlb64.pszEncoded); mir_free(szProvider); } else result = mir_strdup(nlb64.pszEncoded); return result; }
int NetlibSslWrite(SslHandle *ssl, const char *buf, int num) { if (ssl == NULL) return SOCKET_ERROR; SecPkgContext_StreamSizes Sizes; SECURITY_STATUS scRet = g_pSSPI->QueryContextAttributes(&ssl->hContext, SECPKG_ATTR_STREAM_SIZES, &Sizes); if (scRet != SEC_E_OK) return scRet; PUCHAR pbDataBuffer = (PUCHAR)mir_calloc(Sizes.cbMaximumMessage + Sizes.cbHeader + Sizes.cbTrailer); PUCHAR pbMessage = pbDataBuffer + Sizes.cbHeader; DWORD sendOff = 0; while (sendOff < (DWORD)num) { DWORD cbMessage = min(Sizes.cbMaximumMessage, (DWORD)num - sendOff); memcpy(pbMessage, buf + sendOff, cbMessage); SecBuffer Buffers[4] = { 0 }; Buffers[0].pvBuffer = pbDataBuffer; Buffers[0].cbBuffer = Sizes.cbHeader; Buffers[0].BufferType = SECBUFFER_STREAM_HEADER; Buffers[1].pvBuffer = pbMessage; Buffers[1].cbBuffer = cbMessage; Buffers[1].BufferType = SECBUFFER_DATA; Buffers[2].pvBuffer = pbMessage + cbMessage; Buffers[2].cbBuffer = Sizes.cbTrailer; Buffers[2].BufferType = SECBUFFER_STREAM_TRAILER; Buffers[3].BufferType = SECBUFFER_EMPTY; SecBufferDesc Message; Message.ulVersion = SECBUFFER_VERSION; Message.cBuffers = _countof(Buffers); Message.pBuffers = Buffers; if (g_pSSPI->EncryptMessage != NULL) scRet = g_pSSPI->EncryptMessage(&ssl->hContext, 0, &Message, 0); else scRet = ((ENCRYPT_MESSAGE_FN)g_pSSPI->Reserved3)(&ssl->hContext, 0, &Message, 0); if (FAILED(scRet)) break; // Calculate encrypted packet size DWORD cbData = Buffers[0].cbBuffer + Buffers[1].cbBuffer + Buffers[2].cbBuffer; // Send the encrypted data to the server. cbData = send(ssl->s, (char*)pbDataBuffer, cbData, 0); if (cbData == SOCKET_ERROR || cbData == 0) { Netlib_Logf(NULL, "SSL failure sending data (%d)", WSAGetLastError()); scRet = SEC_E_INTERNAL_ERROR; break; } sendOff += cbMessage; } mir_free(pbDataBuffer); return scRet == SEC_E_OK ? num : SOCKET_ERROR; }
int NetlibSslRead(SslHandle *ssl, char *buf, int num, int peek) { if (ssl == NULL) return SOCKET_ERROR; if (num <= 0) return 0; if (ssl->state != sockOpen || (ssl->cbRecDataBuf != 0 && (!peek || ssl->cbRecDataBuf >= num))) return NetlibSslReadSetResult(ssl, buf, num, peek); SECURITY_STATUS scRet = SEC_E_OK; while (true) { if (0 == ssl->cbIoBuffer || scRet == SEC_E_INCOMPLETE_MESSAGE) { if (ssl->sbIoBuffer <= ssl->cbIoBuffer) { ssl->sbIoBuffer += 2048; ssl->pbIoBuffer = (PUCHAR)mir_realloc(ssl->pbIoBuffer, ssl->sbIoBuffer); } if (peek) { static const TIMEVAL tv = { 0 }; fd_set fd; FD_ZERO(&fd); FD_SET(ssl->s, &fd); DWORD cbData = select(1, &fd, NULL, NULL, &tv); if (cbData == SOCKET_ERROR) { ssl->state = sockError; return NetlibSslReadSetResult(ssl, buf, num, peek); } if (cbData == 0 && ssl->cbRecDataBuf) return NetlibSslReadSetResult(ssl, buf, num, peek); } DWORD cbData = recv(ssl->s, (char*)ssl->pbIoBuffer + ssl->cbIoBuffer, ssl->sbIoBuffer - ssl->cbIoBuffer, 0); if (cbData == SOCKET_ERROR) { Netlib_Logf(NULL, "SSL failure recieving data (%d)", WSAGetLastError()); ssl->state = sockError; return NetlibSslReadSetResult(ssl, buf, num, peek); } if (cbData == 0) { Netlib_Logf(NULL, "SSL connection gracefully closed"); if (peek && ssl->cbRecDataBuf) { ssl->state = sockClosed; return NetlibSslReadSetResult(ssl, buf, num, peek); } // Server disconnected. if (ssl->cbIoBuffer) { ssl->state = sockError; return NetlibSslReadSetResult(ssl, buf, num, peek); } return 0; } ssl->cbIoBuffer += cbData; } // Attempt to decrypt the received data. SecBuffer Buffers[4]; Buffers[0].pvBuffer = ssl->pbIoBuffer; Buffers[0].cbBuffer = ssl->cbIoBuffer; Buffers[0].BufferType = SECBUFFER_DATA; Buffers[1].BufferType = SECBUFFER_EMPTY; Buffers[2].BufferType = SECBUFFER_EMPTY; Buffers[3].BufferType = SECBUFFER_EMPTY; SecBufferDesc Message; Message.ulVersion = SECBUFFER_VERSION; Message.cBuffers = _countof(Buffers); Message.pBuffers = Buffers; if (g_pSSPI->DecryptMessage != NULL && g_pSSPI->DecryptMessage != PVOID(0x80000000)) scRet = g_pSSPI->DecryptMessage(&ssl->hContext, &Message, 0, NULL); else scRet = ((DECRYPT_MESSAGE_FN)g_pSSPI->Reserved4)(&ssl->hContext, &Message, 0, NULL); // The input buffer contains only a fragment of an // encrypted record. Loop around and read some more // data. if (scRet == SEC_E_INCOMPLETE_MESSAGE) continue; if (scRet != SEC_E_OK && scRet != SEC_I_RENEGOTIATE && scRet != SEC_I_CONTEXT_EXPIRED) { ReportSslError(scRet, __LINE__); ssl->state = sockError; return NetlibSslReadSetResult(ssl, buf, num, peek); } // Locate data and (optional) extra buffers. SecBuffer *pDataBuffer = NULL; SecBuffer *pExtraBuffer = NULL; for (int i = 1; i < _countof(Buffers); i++) { if (pDataBuffer == NULL && Buffers[i].BufferType == SECBUFFER_DATA) pDataBuffer = &Buffers[i]; if (pExtraBuffer == NULL && Buffers[i].BufferType == SECBUFFER_EXTRA) pExtraBuffer = &Buffers[i]; } // Return decrypted data. DWORD resNum = 0; if (pDataBuffer) { DWORD bytes = peek ? 0 : min((DWORD)num, pDataBuffer->cbBuffer); DWORD rbytes = pDataBuffer->cbBuffer - bytes; if (rbytes > 0) { int nbytes = ssl->cbRecDataBuf + rbytes; if (ssl->sbRecDataBuf < nbytes) { ssl->sbRecDataBuf = nbytes; ssl->pbRecDataBuf = (PUCHAR)mir_realloc(ssl->pbRecDataBuf, nbytes); } memcpy(ssl->pbRecDataBuf + ssl->cbRecDataBuf, (char*)pDataBuffer->pvBuffer + bytes, rbytes); ssl->cbRecDataBuf = nbytes; } if (peek) { resNum = bytes = min(num, ssl->cbRecDataBuf); memcpy(buf, ssl->pbRecDataBuf, bytes); } else { resNum = bytes; memcpy(buf, pDataBuffer->pvBuffer, bytes); } } // Move any "extra" data to the input buffer. if (pExtraBuffer) { memmove(ssl->pbIoBuffer, pExtraBuffer->pvBuffer, pExtraBuffer->cbBuffer); ssl->cbIoBuffer = pExtraBuffer->cbBuffer; } else ssl->cbIoBuffer = 0; if (pDataBuffer && resNum) return resNum; // Server signaled end of session if (scRet == SEC_I_CONTEXT_EXPIRED) { Netlib_Logf(NULL, "SSL Server signaled SSL Shutdown"); ssl->state = sockClosed; return NetlibSslReadSetResult(ssl, buf, num, peek); } if (scRet == SEC_I_RENEGOTIATE) { // The server wants to perform another handshake // sequence. scRet = ClientHandshakeLoop(ssl, FALSE); if (scRet != SEC_E_OK) { ssl->state = sockError; return NetlibSslReadSetResult(ssl, buf, num, peek); } } } }
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; }