// Cwmsoapauth2.cpp : Implementation of Cwmsoapauth2 #include "stdafx.h" #include "wmsoapauth2.h" #include "..\soap_source\wmmd5.h" #include "..\soap_source\bytearrayconv.h" #include "..\soap_source\hexcode.h" #include "..\soap_source\soap.h" #include #include #include "..\wmsoapmsg\wmsoapmsg_i.c" #include "..\wmsoapstruct\wmsoapstruct.h" #include "..\wmsoapstruct\wmsoapstruct_i.c" // Cwmsoapauth2 static wchar_t *g_pwszHeaderNS = L"http://soap-authentication.org/digest/2001/10/"; bool Cwmsoapauth2::Authenticate(Iwmsoapmsg2 *pIwmsoapmsg) { if (ValidateRealmUser(pIwmsoapmsg) == false) { return false; } // check nonce aging if (CheckNonceAging() == false) { _AtlModule.LogEvent("Expired Server Nonce in request"); m_strAuthStatus = "Unauthenticated.ExpiredNonce"; return false; } // is response correct? unsigned char Expected[MD5_HASH_SIZE]; CalcDigest(Expected); if (memcmp(Expected, m_Auth, MD5_HASH_SIZE) != 0) { _AtlModule.LogEvent("Invalid Client response in request"); m_strAuthStatus = "Unauthenticated.InvalidResponse"; return false; } m_strAuthStatus = "Authenticated"; return true; } bool Cwmsoapauth2::Response(Iwmsoapmsg2 *pIwmsoapmsg) { // fill up name array BSTR bstrNames[4]; bstrNames[0] = SysAllocString(L"Status"); bstrNames[1] = SysAllocString(L"Nonce"); bstrNames[2] = SysAllocString(L"ClientNonce"); bstrNames[3] = SysAllocString(L"ServerAuth"); // instantiate struct object Iwmsoapstruct *pIwmsoapstructOut; HRESULT res = CoCreateInstance( CLSID_wmsoapstruct, NULL, CLSCTX_SERVER, IID_Iwmsoapstruct, (void **) &pIwmsoapstructOut); if (FAILED(res)) { return false; } VARIANT_BOOL bResult; _variant_t varTemp; for (int j = 0; j < 4; j++) { switch (j) { case 0: { // Status varTemp = m_strAuthStatus.c_str(); pIwmsoapstructOut->SetMember(bstrNames[j], varTemp, &bResult); } break; case 1: { EnterCriticalSection(&_AtlModule.m_CSNonces); // update map - delete entry whose key is old sn map::iterator iM; if ((iM = _AtlModule.m_mapNonces.find(m_strSN)) != _AtlModule.m_mapNonces.end()) { delete (*iM).second; _AtlModule.m_mapNonces.erase(iM); } // New server nonce WMNonce *pNonce = new WMNonce; NewNonce(pNonce); // convert to hex encoded string m_strSN.erase(); WMHexCode hexCode; hexCode.Encode(pNonce->Nonce, MD5_HASH_SIZE, m_strSN); // set struct member varTemp = m_strSN.c_str(); pIwmsoapstructOut->SetMember(bstrNames[j], varTemp, &bResult); // update map - insert entry whose key is new sn _AtlModule.m_mapNonces[m_strSN] = pNonce; LeaveCriticalSection(&_AtlModule.m_CSNonces); } break; case 2: { if (m_bCNFound == true) { // Client nonce varTemp = m_strCN.c_str(); pIwmsoapstructOut->SetMember(bstrNames[j], varTemp, &bResult); } } break; case 3: { if (m_bCNFound == true) { // Server auth token - calculated using the new sn unsigned char ServerAuth[MD5_HASH_SIZE]; CalcDigest(ServerAuth); SAFEARRAY *pArray = ByteArrayFromBuffer(ServerAuth, MD5_HASH_SIZE); varTemp.parray = pArray; varTemp.vt = VT_ARRAY | VT_UI1; pIwmsoapstructOut->SetMember(bstrNames[j], varTemp, &bResult); } } break; } } // now get IDispatch pointer IDispatch *pIDispatchOut; res = pIwmsoapstructOut->QueryInterface(IID_IDispatch, (void **)&pIDispatchOut); if (FAILED(res)) { for (int j = 0; j < 4; j++) { SysFreeString(bstrNames[j]); } pIwmsoapstructOut->Release(); return false; } // release pIwmsoapstructOut->Release(); // set up variant _variant_t varContent; varContent.vt = VT_DISPATCH; varContent.pdispVal = pIDispatchOut; // add 'NextChallenge' header element BSTR bstrNamespaceURI = SysAllocString(g_pwszHeaderNS); BSTR bstrLocalName = SysAllocString(L"NextChallenge"); VARIANT_BOOL bMustUnderstand = VARIANT_TRUE; BSTR bstrActorURI = SysAllocString(L""); // Is this a SOAP Fault message? pIwmsoapmsg->IsSOAPFault(&bResult); if (bResult == VARIANT_FALSE) { pIwmsoapmsg->AddHeaderElem( bstrNamespaceURI, bstrLocalName, varContent, bMustUnderstand, bstrActorURI, &bResult); } else { pIwmsoapmsg->AddFaultHeaderElem(bstrNamespaceURI, bstrLocalName, varContent, bMustUnderstand, bstrActorURI, &bResult); } SysFreeString(bstrNamespaceURI); SysFreeString(bstrLocalName); SysFreeString(bstrActorURI); // done with names for (j = 0; j < 4; j++) { SysFreeString(bstrNames[j]); } if (bResult == VARIANT_FALSE) { return false; } return true; } bool Cwmsoapauth2::Challenge(Iwmsoapmsg2 *pIwmsoapmsg) { // fill up name array BSTR bstrNames[3]; bstrNames[0] = SysAllocString(L"Status"); bstrNames[1] = SysAllocString(L"Nonce"); bstrNames[2] = SysAllocString(L"Realm"); // instantiate struct object Iwmsoapstruct *pIwmsoapstructOut; HRESULT res = CoCreateInstance( CLSID_wmsoapstruct, NULL, CLSCTX_SERVER, IID_Iwmsoapstruct, (void **) &pIwmsoapstructOut); if (FAILED(res)) { return false; } VARIANT_BOOL bResult; _variant_t varTemp; for (int j = 0; j < 3; j++) { switch (j) { case 0: { // Status varTemp = m_strAuthStatus.c_str(); pIwmsoapstructOut->SetMember(bstrNames[j], varTemp, &bResult); } break; case 1: { EnterCriticalSection(&_AtlModule.m_CSNonces); // update map - delete entry whose key is old sn map::iterator iM; if ((iM = _AtlModule.m_mapNonces.find(m_strSN)) != _AtlModule.m_mapNonces.end()) { delete (*iM).second; _AtlModule.m_mapNonces.erase(iM); } // New Server nonce WMNonce *pNonce = new WMNonce; NewNonce(pNonce); // convert to string m_strSN.erase(); WMHexCode hexCode; hexCode.Encode(pNonce->Nonce, MD5_HASH_SIZE, m_strSN); // set struct member varTemp = m_strSN.c_str(); pIwmsoapstructOut->SetMember(bstrNames[j], varTemp, &bResult); // update map - insert entry whose key is new sn _AtlModule.m_mapNonces[m_strSN] = pNonce; LeaveCriticalSection(&_AtlModule.m_CSNonces); } break; case 2: { // Realm name BSTR bstrService; pIwmsoapmsg->GetServiceName(&bstrService); // see if service name is in map USES_CONVERSION; map::iterator iM; if ((iM = _AtlModule.m_mapServices.find(OLE2A(bstrService))) == _AtlModule.m_mapServices.end()) { // not in map... use default m_strRealm = _AtlModule.m_strDefaultRealm; } else { // found it m_strRealm = (*iM).second; } SysFreeString(bstrService); varTemp = m_strRealm.c_str(); pIwmsoapstructOut->SetMember(bstrNames[j], varTemp, &bResult); } break; } } // now get IDispatch pointer IDispatch *pIDispatchOut; res = pIwmsoapstructOut->QueryInterface(IID_IDispatch, (void **)&pIDispatchOut); if (FAILED(res)) { for (int j = 0; j < 3; j++) { SysFreeString(bstrNames[j]); } pIwmsoapstructOut->Release(); return false; } // release pIwmsoapstructOut->Release(); // set up variant _variant_t varContent; varContent.vt = VT_DISPATCH; varContent.pdispVal = pIDispatchOut; // add 'AuthSCF' header element BSTR bstrNamespaceURI = SysAllocString(g_pwszHeaderNS); BSTR bstrLocalName = SysAllocString(L"Challenge"); VARIANT_BOOL bMustUnderstand = VARIANT_TRUE; BSTR bstrActorURI = SysAllocString(L""); pIwmsoapmsg->AddFaultHeaderElem(bstrNamespaceURI, bstrLocalName, varContent, bMustUnderstand, bstrActorURI, &bResult); SysFreeString(bstrNamespaceURI); SysFreeString(bstrLocalName); SysFreeString(bstrActorURI); // done with names for (j = 0; j < 3; j++) { SysFreeString(bstrNames[j]); } if (bResult == VARIANT_FALSE) { return false; } return true; } bool Cwmsoapauth2::InitChallenge(Iwmsoapmsg2 *pIwmsoapmsg) { if (ValidateRealmUser(pIwmsoapmsg) == false) { return false; } // fill up name array BSTR bstrNames[4]; bstrNames[0] = SysAllocString(L"Status"); bstrNames[1] = SysAllocString(L"Nonce"); bstrNames[2] = SysAllocString(L"ClientNonce"); bstrNames[3] = SysAllocString(L"ServerAuth"); // instantiate struct object Iwmsoapstruct *pIwmsoapstructOut; HRESULT res = CoCreateInstance( CLSID_wmsoapstruct, NULL, CLSCTX_SERVER, IID_Iwmsoapstruct, (void **) &pIwmsoapstructOut); if (FAILED(res)) { return false; } VARIANT_BOOL bResult; _variant_t varTemp; for (int j = 0; j < 4; j++) { switch (j) { case 0: { // Status varTemp = m_strAuthStatus.c_str(); pIwmsoapstructOut->SetMember(bstrNames[j], varTemp, &bResult); } break; case 1: { // New server nonce WMNonce *pNonce = new WMNonce; NewNonce(pNonce); // convert to hex encoded string m_strSN.erase(); WMHexCode hexCode; hexCode.Encode(pNonce->Nonce, MD5_HASH_SIZE, m_strSN); // set struct member varTemp = m_strSN.c_str(); pIwmsoapstructOut->SetMember(bstrNames[j], varTemp, &bResult); // update map - insert entry whose key is new sn EnterCriticalSection(&_AtlModule.m_CSNonces); _AtlModule.m_mapNonces[m_strSN] = pNonce; LeaveCriticalSection(&_AtlModule.m_CSNonces); } break; case 2: { if (m_bCNFound == true) { // Client nonce varTemp = m_strCN.c_str(); pIwmsoapstructOut->SetMember(bstrNames[j], varTemp, &bResult); } } break; case 3: { if (m_bCNFound == true) { // Server auth token - calculated using the new sn unsigned char ServerAuth[MD5_HASH_SIZE]; CalcDigest(ServerAuth); SAFEARRAY *pArray = ByteArrayFromBuffer(ServerAuth, MD5_HASH_SIZE); varTemp.parray = pArray; varTemp.vt = VT_ARRAY | VT_UI1; pIwmsoapstructOut->SetMember(bstrNames[j], varTemp, &bResult); } } break; } } // now get IDispatch pointer IDispatch *pIDispatchOut; res = pIwmsoapstructOut->QueryInterface(IID_IDispatch, (void **)&pIDispatchOut); if (FAILED(res)) { for (int j = 0; j < 4; j++) { SysFreeString(bstrNames[j]); } pIwmsoapstructOut->Release(); return false; } // release pIwmsoapstructOut->Release(); // set up variant _variant_t varContent; varContent.vt = VT_DISPATCH; varContent.pdispVal = pIDispatchOut; // add 'NextChallenge' header element BSTR bstrNamespaceURI = SysAllocString(g_pwszHeaderNS); BSTR bstrLocalName = SysAllocString(L"NextChallenge"); VARIANT_BOOL bMustUnderstand = VARIANT_TRUE; BSTR bstrActorURI = SysAllocString(L""); // this a SOAP Fault message pIwmsoapmsg->AddFaultHeaderElem(bstrNamespaceURI, bstrLocalName, varContent, bMustUnderstand, bstrActorURI, &bResult); SysFreeString(bstrNamespaceURI); SysFreeString(bstrLocalName); SysFreeString(bstrActorURI); // done with names for (j = 0; j < 4; j++) { SysFreeString(bstrNames[j]); } if (bResult == VARIANT_FALSE) { return false; } return true; } bool Cwmsoapauth2::ValidateRealmUser(Iwmsoapmsg2 *pIwmsoapmsg) { // check if realm name is valid BSTR bstrService; pIwmsoapmsg->GetServiceName(&bstrService); string strTemp; // see if service name is in map USES_CONVERSION; map::iterator iM; if ((iM = _AtlModule.m_mapServices.find(OLE2A(bstrService))) == _AtlModule.m_mapServices.end()) { // not in map... use default strTemp = _AtlModule.m_strDefaultRealm; } else { // found it strTemp = (*iM).second; } SysFreeString(bstrService); if (m_strRealm != strTemp) { _AtlModule.LogEvent("Invalid Realm name in request"); m_strAuthStatus = "Unauthenticated.InvalidRealm"; return false; } // check if user id is valid m_strAccount = m_strRealm; m_strAccount += ":"; m_strAccount += m_strUserID; if (_AtlModule.m_mapUsers.find(m_strAccount) == _AtlModule.m_mapUsers.end()) { _AtlModule.LogEvent("Invalid User ID in request"); m_strAuthStatus = "Unauthenticated.InvalidUser"; return false; } // all is ok return true; } bool Cwmsoapauth2::NewNonce(WMNonce *pNonce) { // first the time pNonce->Time = (long)time(NULL); // now the nonce GUID g; CoCreateGuid(&g); WMMD5 md5; md5.Update((unsigned char *)&g, sizeof(GUID)); md5.Finalize((unsigned long *)&pNonce->Nonce[0]); return true; } bool Cwmsoapauth2::CalcDigest(void *pBuffer) { // calculate proper response // if client nonce given = MD5( MD5(uid:realm:pwd):SN:CN ) // else = MD5( MD5(uid:realm:pwd):SN ) string strTemp1 = m_strUserID; strTemp1 += ":"; strTemp1 += m_strRealm; strTemp1 += ":"; strTemp1 += _AtlModule.m_mapUsers[m_strAccount]; string strTemp2; WMMD5 md5; md5.Update((unsigned char *)strTemp1.c_str(), strTemp1.length()); md5.FinalizeHexEncoded(strTemp2); strTemp1 = strTemp2; strTemp1 += ":"; // server nonce string strTemp1 += m_strSN; if (m_bCNFound == true) { // incorporate client nonce strTemp1 += ":"; strTemp1 += m_strCN; } md5.Update((unsigned char *)strTemp1.c_str(), strTemp1.length()); md5.Finalize((unsigned long *)pBuffer); return true; } bool Cwmsoapauth2::CheckNonceAging() { EnterCriticalSection(&_AtlModule.m_CSNonces); map::iterator iM; if ((iM = _AtlModule.m_mapNonces.find(m_strSN)) == _AtlModule.m_mapNonces.end()) { LeaveCriticalSection(&_AtlModule.m_CSNonces); return false; } WMNonce *pNonce = (*iM).second; if (pNonce == NULL) { LeaveCriticalSection(&_AtlModule.m_CSNonces); return false; } time_t t = pNonce->Time; LeaveCriticalSection(&_AtlModule.m_CSNonces); // stale if nonce life is exceeded if ((time(NULL) - t) > _AtlModule.m_nNonceLife) { return false; } return true; } STDMETHODIMP Cwmsoapauth2::OnProcessHeader(VARIANT SOAPMsgInterface, VARIANT_BOOL *pResult) { // get interface ptr IDispatch *pIDispatch = SOAPMsgInterface.pdispVal; Iwmsoapmsg2 *pIwmsoapmsg; HRESULT hRes = pIDispatch->QueryInterface(IID_Iwmsoapmsg2, (void **)&pIwmsoapmsg); if (hRes != S_OK) { return E_FAIL; } VARIANT_BOOL bResult; pIwmsoapmsg->IsRequestPhase(&bResult); if (bResult == VARIANT_FALSE) { // this is response phase *pResult = VARIANT_TRUE; if (m_bAuthSucceeded == true) { // post response header entry *pResult = Response(pIwmsoapmsg) == true ? VARIANT_TRUE : VARIANT_FALSE; } pIwmsoapmsg->Release(); return S_OK; } // request phase... assume the worst m_bAuthSucceeded = false; m_bAuthFound = false; m_bUserIDFound = false; m_bRealmFound = false; m_bCNFound = false; m_bSNFound = false; m_strAuthStatus = "Unauthenticated.NoCredentials"; *pResult = VARIANT_FALSE; _variant_t varContent; bool bClientAuthFound = false; bool bInitChallengeFound = false; // is the 'ClientAuth' header entry present? BSTR bstrNamespaceURI = SysAllocString(g_pwszHeaderNS); BSTR bstrLocalName = SysAllocString(L"ClientAuth"); hRes = pIwmsoapmsg->GetHeaderElem(bstrNamespaceURI, bstrLocalName, &varContent, &bResult); SysFreeString(bstrLocalName); if (hRes == S_OK && bResult == VARIANT_TRUE && (varContent.vt & VT_TYPEMASK) == VT_DISPATCH) { bClientAuthFound = true; } else { // is the 'InitChallenge' header entry present? bstrLocalName = SysAllocString(L"InitChallenge"); hRes = pIwmsoapmsg->GetHeaderElem(bstrNamespaceURI, bstrLocalName, &varContent, &bResult); SysFreeString(bstrLocalName); if (hRes == S_OK && bResult == VARIANT_TRUE && (varContent.vt & VT_TYPEMASK) == VT_DISPATCH) { bInitChallengeFound = true; } } SysFreeString(bstrNamespaceURI); // extract info from any header entry found if (bClientAuthFound == true) { // fill up name array BSTR bstrNames[5]; bstrNames[0] = SysAllocString(L"Nonce"); bstrNames[1] = SysAllocString(L"Auth"); bstrNames[2] = SysAllocString(L"UserID"); bstrNames[3] = SysAllocString(L"Realm"); bstrNames[4] = SysAllocString(L"ClientNonce"); IDispatch *pIDispatch = varContent.pdispVal; Iwmsoapstruct *pIwmsoapstruct; HRESULT res = pIDispatch->QueryInterface(IID_Iwmsoapstruct, (void **)&pIwmsoapstruct); if (FAILED(res)) { for (int i = 0; i < 5; i++) { SysFreeString(bstrNames[i]); } return S_OK; } USES_CONVERSION; _variant_t varTemp; VARIANT_BOOL bResult; // now iterate over struct members for (int j = 0; j < 5; j++) { // get member pIwmsoapstruct->GetMember(bstrNames[j], &varTemp, &bResult); switch (j) { case 0: // Server Nonce if (bResult == VARIANT_TRUE) { m_strSN = OLE2A(varTemp.bstrVal); m_bSNFound = true; } break; case 1: // Auth token if (bResult == VARIANT_TRUE) { void *pBuffer; unsigned long lSize; if (BufferFromByteArray(varTemp.parray, &pBuffer, &lSize) == true) { memmove(m_Auth, pBuffer, MD5_HASH_SIZE); free(pBuffer); m_bAuthFound = true; } } break; case 2: // User ID if (bResult == VARIANT_TRUE) { m_strUserID = OLE2A(varTemp.bstrVal); m_bUserIDFound = true; } break; case 3: // Realm if (bResult == VARIANT_TRUE) { m_strRealm = OLE2A(varTemp.bstrVal); m_bRealmFound = true; } break; case 4: // Client Nonce if (bResult == VARIANT_TRUE) { m_strCN = OLE2A(varTemp.bstrVal); m_bCNFound = true; } break; } varTemp.Clear(); } // release interface ptr pIwmsoapstruct->Release(); // done with names for (j = 0; j < 5; j++) { SysFreeString(bstrNames[j]); } } else if (bInitChallengeFound == true) { // fill up name array BSTR bstrNames[3]; bstrNames[0] = SysAllocString(L"UserID"); bstrNames[1] = SysAllocString(L"Realm"); bstrNames[2] = SysAllocString(L"ClientNonce"); IDispatch *pIDispatch = varContent.pdispVal; Iwmsoapstruct *pIwmsoapstruct; HRESULT res = pIDispatch->QueryInterface(IID_Iwmsoapstruct, (void **)&pIwmsoapstruct); if (FAILED(res)) { for (int i = 0; i < 3; i++) { SysFreeString(bstrNames[i]); } return S_OK; } USES_CONVERSION; _variant_t varTemp; VARIANT_BOOL bResult; // now iterate over struct members for (int j = 0; j < 5; j++) { // get member pIwmsoapstruct->GetMember(bstrNames[j], &varTemp, &bResult); switch (j) { case 0: // User ID if (bResult == VARIANT_TRUE) { m_strUserID = OLE2A(varTemp.bstrVal); m_bUserIDFound = true; } break; case 1: // Realm if (bResult == VARIANT_TRUE) { m_strRealm = OLE2A(varTemp.bstrVal); m_bRealmFound = true; } break; case 2: // Client Nonce if (bResult == VARIANT_TRUE) { m_strCN = OLE2A(varTemp.bstrVal); m_bCNFound = true; } break; } varTemp.Clear(); } // release interface ptr pIwmsoapstruct->Release(); // done with names for (j = 0; j < 3; j++) { SysFreeString(bstrNames[j]); } } varContent.Clear(); // now process *pResult = VARIANT_TRUE; if (bClientAuthFound == true && m_bAuthFound == true && m_bUserIDFound == true && m_bRealmFound == true && m_bSNFound == true) { // 'ClientAuth' header entry found if (Authenticate(pIwmsoapmsg) == true) { // success... done for now m_bAuthSucceeded = true; pIwmsoapmsg->Release(); return S_OK; } else { // authentication failed... issue challenge Challenge(pIwmsoapmsg); } } else if ( bInitChallengeFound == true && m_bUserIDFound == true && m_bRealmFound == true) { // 'InitChallenge' header entry found if (InitChallenge(pIwmsoapmsg) == false) { // realm name or user id was invalid... issue challenge Challenge(pIwmsoapmsg); } } else { // no header entry found... issue challenge Challenge(pIwmsoapmsg); } long lSOAPVersion; pIwmsoapmsg->GetSOAPVersion(&lSOAPVersion); BSTR bstrFaultcode, bstrFaultcodeNS; BSTR bstrFaultstring = SysAllocString(L"Authentication failed: missing, malformed, or invalid credentials."); if (lSOAPVersion == SOAP_VERSION_1_1) { bstrFaultcode = SysAllocString(L"Client"); bstrFaultcodeNS = SysAllocString(g_pwszSoapEnvelopeNS); pIwmsoapmsg->SetSOAPFault(bstrFaultcode, bstrFaultcodeNS, bstrFaultstring, VARIANT_FALSE, &bResult); } else { bstrFaultcode = SysAllocString(L"Sender"); bstrFaultcodeNS = SysAllocString(g_pwszSoap12EnvelopeNS); BSTR bstrSubcode = SysAllocString(L""); BSTR bstrSubcodeNS = SysAllocString(L""); BSTR bstrLangId = SysAllocString(L"en"); pIwmsoapmsg->SetSOAP12Fault(bstrFaultcode, bstrFaultcodeNS, bstrSubcode, bstrSubcodeNS, bstrFaultstring, bstrLangId, &bResult); SysFreeString(bstrSubcode); SysFreeString(bstrSubcodeNS); SysFreeString(bstrLangId); } SysFreeString(bstrFaultcode); SysFreeString(bstrFaultcodeNS); SysFreeString(bstrFaultstring); pIwmsoapmsg->Release(); return S_OK; } STDMETHODIMP Cwmsoapauth2::OnTestMUHeaderBlock(BSTR bstrNamespace, BSTR bstrLocalName, VARIANT_BOOL *pResult) { *pResult = VARIANT_FALSE; if (wcscmp(bstrNamespace, g_pwszHeaderNS) == 0) { if (wcscmp(bstrLocalName, L"ClientAuth") == 0 || wcscmp(bstrLocalName, L"InitChallenge") == 0) { *pResult = VARIANT_TRUE; return S_OK; } } return S_OK; }