HRESULT CTestReader::Test1() { HRESULT hr = S_OK; StunAttribute attrib; const char* pszExpectedSoftwareAttribute = "STUN test client"; const char* pszExpectedUserName = "******"; CRefCountedBuffer spBuffer; char szStringValue[100]; const unsigned char *req = c_requestbytes; size_t requestsize = sizeof(c_requestbytes)-1; // -1 to get rid of the trailing null CStunMessageReader reader; CStunMessageReader::ReaderParseState state; // reader is expecting at least enough bytes to fill the header ChkIfA(reader.AddBytes(NULL, 0) != CStunMessageReader::HeaderNotRead, E_FAIL); ChkIfA(reader.HowManyBytesNeeded() != STUN_HEADER_SIZE, E_FAIL); state = reader.AddBytes(req, requestsize); ChkIfA(state != CStunMessageReader::BodyValidated, E_FAIL); ChkIfA(reader.HowManyBytesNeeded() != 0, E_FAIL); ChkA(reader.GetBuffer(&spBuffer)); ChkIfA(reader.GetMessageClass() != StunMsgClassRequest, E_FAIL); ChkIfA(reader.GetMessageType() != StunMsgTypeBinding, E_FAIL); ChkA(reader.GetAttributeByType(STUN_ATTRIBUTE_SOFTWARE, &attrib)); ChkIfA(attrib.attributeType != STUN_ATTRIBUTE_SOFTWARE, E_FAIL); ChkIfA(0 != ::strncmp(pszExpectedSoftwareAttribute, (const char*)(spBuffer->GetData() + attrib.offset), attrib.size), E_FAIL); ChkA(reader.GetAttributeByType(STUN_ATTRIBUTE_USERNAME, &attrib)); ChkIfA(attrib.attributeType != STUN_ATTRIBUTE_USERNAME, E_FAIL); ChkIfA(0 != ::strncmp(pszExpectedUserName, (const char*)(spBuffer->GetData() + attrib.offset), attrib.size), E_FAIL); ChkA(reader.GetStringAttributeByType(STUN_ATTRIBUTE_SOFTWARE, szStringValue, ARRAYSIZE(szStringValue))); ChkIfA(0 != ::strcmp(pszExpectedSoftwareAttribute, szStringValue), E_FAIL); ChkIfA(reader.HasFingerprintAttribute() == false, E_FAIL); ChkIfA(reader.IsFingerprintAttributeValid() == false, E_FAIL); ChkIfA(reader.HasMessageIntegrityAttribute() == false, E_FAIL); ChkA(reader.ValidateMessageIntegrityShort(c_password)); Cleanup: return hr; }
// This test validates that the construction and parsing of the message integrity attribute in a stun message works as expected // The test also validates both short term and long term credential modes with or without the presence of a fingerprint attribute HRESULT CTestIntegrity::TestMessageIntegrity(bool fWithFingerprint, bool fLongCredentials) { HRESULT hr = S_OK; const char* pszUserName = "******"; const char* pszRealm = "stunrealm"; const char* pszPassword = "******"; CStunMessageBuilder builder; CStunMessageReader reader; uint8_t *pMsg = NULL; size_t sizeMsg = 0; CStunMessageReader::ReaderParseState state; CRefCountedBuffer spBuffer; builder.AddBindingRequestHeader(); builder.AddRandomTransactionId(NULL); builder.AddUserName(pszUserName); builder.AddRealm(pszRealm); if (fLongCredentials == false) { Chk(builder.AddMessageIntegrityShortTerm(pszPassword)); } else { Chk(builder.AddMessageIntegrityLongTerm(pszUserName, pszRealm, pszPassword)); } if (fWithFingerprint) { builder.AddFingerprintAttribute(); } Chk(builder.GetResult(&spBuffer)); pMsg = spBuffer->GetData(); sizeMsg = spBuffer->GetSize(); state = reader.AddBytes(pMsg, sizeMsg); ChkIfA(state != CStunMessageReader::BodyValidated, E_FAIL); ChkIfA(reader.HasMessageIntegrityAttribute()==false, E_FAIL); if (fLongCredentials == false) { ChkA(reader.ValidateMessageIntegrityShort(pszPassword)); } else { ChkA(reader.ValidateMessageIntegrityLong(pszUserName, pszRealm, pszPassword)); } Cleanup: return hr; }
HRESULT CTestReader::TestFixedReadSizes(size_t chunksize) { HRESULT hr = S_OK; CStunMessageReader reader; CStunMessageReader::ReaderParseState prevState, state; size_t bytesread = 0; bool fRandomChunkSizing = (chunksize==0); prevState = CStunMessageReader::HeaderNotRead; state = prevState; size_t msgSize = sizeof(c_requestbytes)-1; // c_requestbytes is a string, hence the -1 while (bytesread < msgSize) { size_t remaining, toread; if (fRandomChunkSizing) { chunksize = (rand() % 17) + 1; } remaining = msgSize - bytesread; toread = (remaining > chunksize) ? chunksize : remaining; state = reader.AddBytes(&c_requestbytes[bytesread], toread); bytesread += toread; ChkIfA(state == CStunMessageReader::ParseError, E_UNEXPECTED); if ((state == CStunMessageReader::HeaderValidated) && (prevState != CStunMessageReader::HeaderValidated)) { ChkIfA(bytesread < STUN_HEADER_SIZE, E_UNEXPECTED); } if ((state == CStunMessageReader::BodyValidated) && (prevState != CStunMessageReader::BodyValidated)) { ChkIfA(prevState != CStunMessageReader::HeaderValidated, E_UNEXPECTED); ChkIfA(bytesread != msgSize, E_UNEXPECTED); } prevState = state; } ChkIfA(reader.GetState() != CStunMessageReader::BodyValidated, E_UNEXPECTED); // just validate the integrity and fingerprint, that should cover all the attributes ChkA(reader.ValidateMessageIntegrityShort(c_password)); ChkIfA(reader.IsFingerprintAttributeValid() == false, E_FAIL); Cleanup: return hr; }
// test simple authentication HRESULT CTestMessageHandler::Test3() { CStunMessageBuilder builder1, builder2, builder3; CStunMessageReader readerResponse; uint16_t errorcode = 0; HRESULT hr = S_OK; // ----------------------------------------------------------------------- // simulate an authorized user making a request with a valid password ChkA(InitBindingRequest(builder1)); builder1.AddStringAttribute(STUN_ATTRIBUTE_USERNAME, "AuthorizedUser"); builder1.AddMessageIntegrityShortTerm("password"); builder1.FixLengthField(); ChkA(SendHelper(builder1, &readerResponse, _spAuthShort)); ChkA(readerResponse.ValidateMessageIntegrityShort("password")); // ----------------------------------------------------------------------- // simulate a user with a bad password readerResponse.Reset(); InitBindingRequest(builder2); builder2.AddStringAttribute(STUN_ATTRIBUTE_USERNAME, "WrongUser"); builder2.AddMessageIntegrityShortTerm("wrongpassword"); builder2.FixLengthField(); ChkA(SendHelper(builder2, &readerResponse, _spAuthShort)) errorcode = 0; ChkA(readerResponse.GetErrorCode(&errorcode)); ChkIfA(errorcode != ::STUN_ERROR_UNAUTHORIZED, E_FAIL); // ----------------------------------------------------------------------- // simulate a client sending no credentials - we expect it to fire back with a 400/bad-request readerResponse.Reset(); ChkA(InitBindingRequest(builder3)); ChkA(SendHelper(builder3, &readerResponse, _spAuthShort)); errorcode = 0; ChkA(readerResponse.GetErrorCode(&errorcode)); ChkIfA(errorcode != ::STUN_ERROR_BADREQUEST, E_FAIL); Cleanup: return hr; }
HRESULT CStunThreadMessageHandler::ValidateAuth(CStunMessageReader& reader) { AuthAttributes authattributes; AuthResponse authresponse; HRESULT hr = S_OK; HRESULT hrRet = S_OK; if (_spAuth == NULL) { return S_OK; // nothing to do if there is no auth mechanism in place } memset(&authattributes, '\0', sizeof(authattributes)); memset(&authresponse, '\0', sizeof(authresponse)); reader.GetStringAttributeByType(STUN_ATTRIBUTE_USERNAME, authattributes.szUser, ARRAYSIZE(authattributes.szUser)); reader.GetStringAttributeByType(STUN_ATTRIBUTE_REALM, authattributes.szRealm, ARRAYSIZE(authattributes.szRealm)); reader.GetStringAttributeByType(STUN_ATTRIBUTE_NONCE, authattributes.szNonce, ARRAYSIZE(authattributes.szNonce)); reader.GetStringAttributeByType(::STUN_ATTRIBUTE_LEGACY_PASSWORD, authattributes.szLegacyPassword, ARRAYSIZE(authattributes.szLegacyPassword)); authattributes.fMessageIntegrityPresent = reader.HasMessageIntegrityAttribute(); Chk(_spAuth->DoAuthCheck(&authattributes, &authresponse)); // enforce that everything is null terminated authresponse.szNonce[ARRAYSIZE(authresponse.szNonce)-1] = 0; authresponse.szRealm[ARRAYSIZE(authresponse.szRealm)-1] = 0; authresponse.szPassword[ARRAYSIZE(authresponse.szPassword)-1] = 0; // now decide how to handle the auth if (authresponse.responseType == StaleNonce) { _error.errorcode = STUN_ERROR_STALENONCE; } else if (authresponse.responseType == Unauthorized) { _error.errorcode = STUN_ERROR_UNAUTHORIZED; } else if (authresponse.responseType == Reject) { _error.errorcode = STUN_ERROR_BADREQUEST; } else if (authresponse.responseType == Allow) { // nothing to do! } else if (authresponse.responseType == AllowConditional) { // validate the message in // if either ValidateAuth or ProcessBindingRequest set an errorcode.... if (authresponse.authCredMech == AuthCredLongTerm) { hrRet = reader.ValidateMessageIntegrityLong(authattributes.szUser, authattributes.szRealm, authresponse.szPassword); } else { hrRet = reader.ValidateMessageIntegrityShort(authresponse.szPassword); } if (SUCCEEDED(hrRet)) { _integrity.fSendWithIntegrity = true; _integrity.fUseLongTerm = (authresponse.authCredMech == AuthCredLongTerm); COMPILE_TIME_ASSERT(sizeof(_integrity.szPassword)==sizeof(authresponse.szPassword)); strcpy(_integrity.szPassword, authresponse.szPassword); strcpy(_integrity.szUser, authattributes.szUser); strcpy(_integrity.szRealm, authattributes.szRealm); } else { // bad password - so now turn this thing into a 401 _error.errorcode = STUN_ERROR_UNAUTHORIZED; } } if ((_error.errorcode == STUN_ERROR_UNAUTHORIZED) || (_error.errorcode == STUN_ERROR_STALENONCE)) { strcpy(_error.szRealm, authresponse.szRealm); strcpy(_error.szNonce, authresponse.szNonce); } Cleanup: return hr; }