/* * 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; }
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); } }
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; }
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; }