--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/httpfilters/httpfilterauthentication/Src/HttpFilterDigestAuthentication.cpp Tue Feb 02 01:09:52 2010 +0200
@@ -0,0 +1,1096 @@
+/*
+* Copyright (c) 2003 Nokia Corporation and/or its subsidiary(-ies).
+* All rights reserved.
+* This component and the accompanying materials are made available
+* under the terms of "Eclipse Public License v1.0"
+* which accompanies this distribution, and is available
+* at the URL "http://www.eclipse.org/legal/epl-v10.html".
+*
+* Initial Contributors:
+* Nokia Corporation - initial contribution.
+*
+* Contributors:
+*
+* Description: Digest Authentication filter
+*
+*/
+
+
+
+// INCLUDE FILES
+#include <e32std.h>
+#include <e32math.h>
+#include <hash.h>
+#include <http/rhttpsession.h>
+#include <http/rhttptransaction.h>
+#include <http/rhttpheaders.h>
+#include <httpstringconstants.h>
+#include <httperr.h>
+
+// User Includes
+#include "HttpFilterDigestAuthentication.h"
+#include "HttpFilterAuthenticationLogger.h"
+
+// EXTERNAL DATA STRUCTURES
+
+// EXTERNAL FUNCTION PROTOTYPES
+
+// CONSTANTS
+
+// MACROS
+
+// LOCAL CONSTANTS AND MACROS
+
+// Length of a digest hash when represented in hex
+const TInt KHashLength = 32;
+// Length of a digest hash before converting to hex.
+const TInt KRawHashLength = 16;
+// Length of nonce-count
+const TInt KNonceCountLength = 8;
+
+_LIT8( KMd5AlgorithmStr, "MD5" );
+_LIT8( KMd5SessAlgorithmStr, "MD5-sess" );
+_LIT8( KQopAuthStr, "auth" );
+_LIT8( KQopAuthIntStr, "auth-int" );
+_LIT8( KAuthenticationInfoStr, "Authentication-Info" );
+_LIT8( KColon, ":" );
+
+// MODULE DATA STRUCTURES
+
+// LOCAL FUNCTION PROTOTYPES
+
+// FORWARD DECLARATIONS
+
+// ============================= LOCAL FUNCTIONS ===============================
+
+// -----------------------------------------------------------------------------
+// PanicDigestAuthenticationFilter
+// Panic
+// Returns: void
+// -----------------------------------------------------------------------------
+//
+void PanicDigestAuthenticationFilter( TInt aErr = 0 )
+ {
+ User::Panic( _L( "HTTP FILTER DIGEST AUTHENTICATION" ), aErr );
+ }
+
+// ============================ MEMBER FUNCTIONS ===============================
+
+// -----------------------------------------------------------------------------
+// CHttpFilterDigestAuthentication::CHttpFilterDigestAuthentication
+// C++ default constructor can NOT contain any code, that
+// might leave.
+// -----------------------------------------------------------------------------
+//
+CHttpFilterDigestAuthentication::CHttpFilterDigestAuthentication( RHTTPSession* aSession, TBool aIsProxy )
+ : CHttpFilterAuthentication( aSession, aIsProxy )
+ {
+ }
+
+// -----------------------------------------------------------------------------
+// CHttpFilterDigestAuthentication::ConstructL
+// Symbian 2nd phase constructor can leave.
+// -----------------------------------------------------------------------------
+//
+void CHttpFilterDigestAuthentication::ConstructL( RHTTPSession aSession )
+ {
+ AUTHLOGGER_ENTERFN( "ConstructL" );
+ CHttpFilterAuthentication::ConstructL( aSession );
+
+ iOpaqueStr = iStringPool.StringF( HTTP::EOpaque, RHTTPSession::GetTable() );
+ iNonceStr = iStringPool.StringF( HTTP::ENonce, RHTTPSession::GetTable() );
+ iQopStr = iStringPool.StringF( HTTP::EQop, RHTTPSession::GetTable() );
+ iStaleStr = iStringPool.StringF( HTTP::EStale, RHTTPSession::GetTable() );
+
+ iMd5Str = iStringPool.OpenFStringL( KMd5AlgorithmStr );
+ iMd5SessStr = iStringPool.OpenFStringL( KMd5SessAlgorithmStr );
+ iQopAuthStr = iStringPool.OpenFStringL( KQopAuthStr );
+ iAuthInfo = iStringPool.OpenFStringL( KAuthenticationInfoStr );
+
+ aSession.FilterCollection().AddFilterL( *this,
+ THTTPEvent::EGotResponseHeaders,
+ iAuthInfo,
+ HTTPStatus::EOk,
+ EStatusCodeHandler + 1,
+ iStringPool.StringF( HTTP::EAuthentication, RHTTPSession::GetTable() ) );
+
+ AUTHLOGGER_WRITE( "CMD5::NewL" );
+ iMD5Calculator = CMD5::NewL();
+
+ AUTHLOGGER_LEAVEFN( "ConstructL" );
+ }
+
+// Destructor
+CHttpFilterDigestAuthentication::~CHttpFilterDigestAuthentication()
+ {
+ iQopAuthStr.Close();
+ iMd5SessStr.Close();
+ iMd5Str.Close();
+ iAuthInfo.Close();
+
+ delete iMD5Calculator;
+
+ // just in case...
+ for( TInt i = 0; i < iDCredentials.Count(); ++i )
+ {
+ DRemoveCredentialsFromList( 0 );
+ }
+
+ iDCredentials.Close();
+ }
+
+// -----------------------------------------------------------------------------
+// CHttpFilterDigestAuthentication::InstallFilterL
+// Two-phased constructor. This function replaces NewL.
+// -----------------------------------------------------------------------------
+//
+CHttpFilterDigestAuthentication* CHttpFilterDigestAuthentication::InstallFilterL( TAny* aAuthenticationParams )
+ {
+ AUTHLOGGER_CREATE;
+
+ __ASSERT_DEBUG( aAuthenticationParams != NULL, PanicDigestAuthenticationFilter() );
+ TAuthenticationParams* authParams = REINTERPRET_CAST( TAuthenticationParams*, aAuthenticationParams );
+ __ASSERT_DEBUG( authParams->iSession != NULL, PanicDigestAuthenticationFilter() );
+ CHttpFilterDigestAuthentication* filter = new ( ELeave ) CHttpFilterDigestAuthentication( authParams->iSession, authParams->iIsProxy );
+ CleanupStack::PushL( filter );
+ filter->ConstructL( *( authParams->iSession ) );
+ CleanupStack::Pop( filter );
+ return filter;
+ }
+
+// -----------------------------------------------------------------------------
+// CHttpFilterDigestAuthentication::MHFRunL
+// Process a transaction event.
+// -----------------------------------------------------------------------------
+//
+void CHttpFilterDigestAuthentication::MHFRunL( RHTTPTransaction aTransaction,
+ const THTTPEvent& aEvent )
+ {
+ switch ( aEvent.iStatus )
+ {
+
+ case THTTPEvent::ESubmit:
+ {
+ DSubmitL( aTransaction );
+ break;
+ }
+
+ case THTTPEvent::EGotResponseHeaders:
+ {
+ if( aTransaction.Response().StatusCode() == HTTPStatus::EOk )
+ {
+ ParseAuthenticationInfoL( aTransaction );
+ }
+ else
+ {
+ DHandleTransactionL( aTransaction );
+ }
+ break;
+ }
+
+ default:
+ {
+ __ASSERT_DEBUG( EFalse, PanicDigestAuthenticationFilter() );
+ break;
+ }
+ }
+ }
+
+// -----------------------------------------------------------------------------
+// CHttpFilterDigestAuthentication::ParseAuthenticationInfoL
+// -----------------------------------------------------------------------------
+//
+void CHttpFilterDigestAuthentication::ParseAuthenticationInfoL( RHTTPTransaction /*aTransaction*/ )
+ {
+ // TODO: parse and store nc-value
+ }
+
+// -----------------------------------------------------------------------------
+// CHttpFilterDigestAuthentication::HandleTransactionL
+// Provides a central point for all Transaction handling.
+// -----------------------------------------------------------------------------
+//
+void CHttpFilterDigestAuthentication::DHandleTransactionL( RHTTPTransaction aTransaction )
+ {
+ AUTHLOGGER_ENTERFN( "DHandleTransactionL" );
+
+ // Find if we have credentials in the transaction's properties, and remove them from the list
+ // (but not from the properties)
+ // Find if there are WWWAuthenticate or ProxyAuthenticate headers in the request,
+ // and replace the realm property with what came from the request.
+ THTTPHdrVal headerVal;
+ TInt cred = KErrNotFound;
+ TBool doDigest = EFalse;
+ TBool stale( EFalse );
+
+ if ( aTransaction.PropertySet().Property( iRealmStr, headerVal ) )
+ {
+ if ( iIsProxy )
+ {
+ // do nothing this time
+ }
+
+ else
+ {
+ cred = DFindCredentials( headerVal, aTransaction.Request().URI() );
+ }
+ }
+
+ RStringF wwwAuthHeader = iStringPool.StringF( iHttpStringAuthenticate, RHTTPSession::GetTable() );
+ RHTTPHeaders headers( aTransaction.Response().GetHeaderCollection() );
+
+ if ( iIsProxy )
+ {
+ // do nothing this time
+ }
+
+ else
+ {
+ TInt headerPart = DFindHeaderPartToUseL( aTransaction );
+
+ if ( headerPart != KErrNotFound )
+ {
+ THTTPHdrVal authTypeParam;
+
+ if ( headers.GetField( wwwAuthHeader, headerPart, authTypeParam ) == KErrNone )
+ // we need to store realm, opaque, nonce and qop values to
+ // use them when resubmitting the request
+ {
+ THTTPHdrVal algorithmVal;
+ THTTPHdrVal nonceVal;
+ THTTPHdrVal realmVal;
+ THTTPHdrVal opaqueVal;
+ THTTPHdrVal staleVal;
+ TInt algVal( KAlgUnknown );
+ TInt authVal( KQopNone );
+
+ aTransaction.PropertySet().RemoveProperty( iNonceStr );
+ aTransaction.PropertySet().RemoveProperty( iRealmStr );
+ aTransaction.PropertySet().RemoveProperty( iOpaqueStr );
+ aTransaction.PropertySet().RemoveProperty( iQopStr );
+ aTransaction.PropertySet().RemoveProperty( iStaleStr );
+
+ if( !headers.GetParam( wwwAuthHeader,
+ iStringPool.StringF( HTTP::EAlgorithm,
+ RHTTPSession::GetTable() ),
+ algorithmVal,
+ headerPart ) )
+ {
+ if( !algorithmVal.Str().DesC().CompareF( iMd5Str.DesC() ) )
+ {
+ algVal = KAlgMd5;
+ }
+ else if( !algorithmVal.Str().DesC().CompareF( iMd5SessStr.DesC() ) )
+ {
+ algVal = KAlgMd5Sess;
+ }
+ }
+
+ authVal = CheckQop( headers, wwwAuthHeader, headerPart );
+
+ headers.GetParam( wwwAuthHeader, iNonceStr, nonceVal, headerPart );
+ headers.GetParam( wwwAuthHeader, iRealmStr, realmVal, headerPart );
+ headers.GetParam( wwwAuthHeader, iOpaqueStr, opaqueVal, headerPart );
+ headers.GetParam( wwwAuthHeader, iStaleStr, staleVal, headerPart );
+
+ // There's only MD5 and MD5-sess algorithm and QoP-Auth support
+ // Realm and Nonce values are mandatory. W/o them digest response is meaningless.
+ if( ( authVal & KQopAuth || authVal == KQopNone )
+ && nonceVal.Type() == THTTPHdrVal::KStrVal
+ && realmVal.Type() == THTTPHdrVal::KStrVal )
+ {
+ aTransaction.PropertySet().SetPropertyL( iNonceStr, nonceVal );
+ aTransaction.PropertySet().SetPropertyL( iRealmStr, realmVal );
+ // Opaque
+ if( opaqueVal.Type() == THTTPHdrVal::KStrVal )
+ {
+ aTransaction.PropertySet().SetPropertyL( iOpaqueStr, opaqueVal );
+ }
+
+ // Qop
+ aTransaction.PropertySet().SetPropertyL( iQopStr, authVal );
+ // Algorithm
+ aTransaction.PropertySet().SetPropertyL( iStringPool.StringF( HTTP::EAlgorithm,
+ RHTTPSession::GetTable() ),
+ algVal );
+
+ _LIT8( KTrue, "TRUE" );
+ if( staleVal.Type() == THTTPHdrVal::KStrVal
+ && !staleVal.Str().DesC().CompareF( KTrue ) )
+ // stale is true -> nonce expired
+ {
+ AUTHLOGGER_WRITE( "Stale found" );
+
+ __ASSERT_DEBUG( cred != KErrNotFound, PanicDigestAuthenticationFilter() );
+
+ // update nonce value
+ TDCredentials& creds = iDCredentials[cred];
+ iStringPool.String( creds.iNonce ).Close();
+ creds.iNonce = nonceVal.Str().Copy();
+
+ // indicate to engine that no uname/pwd dialog needed
+ staleVal.SetInt( 1 );
+ aTransaction.PropertySet().SetPropertyL( iStaleStr, staleVal );
+ stale = ETrue;
+ }
+
+ doDigest = ETrue;
+ }
+ }
+ }
+ }
+
+ if ( cred != KErrNotFound && !stale )
+ // if stale is true credentials were ok,
+ // only the nonce value was expired.
+ {
+ DRemoveCredentialsFromList( cred );
+ }
+
+ if( !doDigest )
+ //try with Basis
+ {
+ CHttpFilterAuthentication::HandleTransactionL( aTransaction );
+ }
+
+ AUTHLOGGER_LEAVEFN( "DHandleTransactionL" );
+ }
+
+// -----------------------------------------------------------------------------
+// CHttpFilterDigestAuthentication::SubmitL
+// -----------------------------------------------------------------------------
+//
+void CHttpFilterDigestAuthentication::DSubmitL( RHTTPTransaction aTransaction )
+ {
+ TInt cred = KErrNotFound;
+ // Get credentials from URI, only for wwwAuthenticate
+
+ // If there are credentials in the transaction properties, add them to the list
+ DGetCredentialsFromPropertiesL( aTransaction );
+
+ // just in case we always remove stale value from properties
+ aTransaction.PropertySet().RemoveProperty( iStaleStr );
+
+ if ( iIsProxy )
+ {
+ // do nothing this time
+ }
+ else
+ {
+ // If we have credentials for this URL, add them
+ cred = DFindCredentialsForURI( aTransaction.Request().URI() );
+ }
+
+ if ( cred != KErrNotFound )
+ {
+ EncodeDigestAuthL( cred, aTransaction );
+ }
+ else
+ // Do Basic
+ {
+ CHttpFilterAuthentication::SubmitL( aTransaction );
+ }
+ }
+
+// -----------------------------------------------------------------------------
+// CHttpFilterDigestAuthentication::DFindCredentials
+// Search list for matching credentials.
+// -----------------------------------------------------------------------------
+//
+TInt CHttpFilterDigestAuthentication::DFindCredentials( const RString& aRealm,
+ const TUriC8& aURI )
+ {
+ TInt count = iDCredentials.Count();
+
+ for ( TInt cred = 0; cred < count; ++cred )
+ {
+ if ( iDCredentials[ cred ].iRealm == aRealm )
+ {
+ TUriParser8 parser;
+ parser.Parse( iStringPool.StringF( iDCredentials[ cred ].iURI ).DesC() );
+
+ if ( !parser.Compare( aURI, EUriHost ) &&
+ ( ( !parser.Compare( aURI, EUriPort ) )
+ || ( !parser.IsPresent( EUriPort ) &&
+ !aURI.IsPresent( EUriPort ) ) ) )
+ {
+ AUTHLOGGER_WRITE_FORMAT( "Found cred(3): %d", cred );
+ return cred;
+ }
+ }
+ }
+
+ return KErrNotFound;
+ }
+
+// -----------------------------------------------------------------------------
+// CHttpFilterDigestAuthentication::DFindCredentialsForURI
+// Searches list for credentials that can be used with this URI
+// -----------------------------------------------------------------------------
+//
+TInt CHttpFilterDigestAuthentication::DFindCredentialsForURI( const TUriC8& aURI )
+ {
+ // Check if any of the stored URIs are the beginning of the URI
+ // we're trying now. If they are, we can immediately attempt to
+ // re-use the existing credentials.
+ _LIT8 ( KSchemeEnd, "://" );
+ TInt count = iDCredentials.Count();
+
+ for ( TInt cred = 0; cred < count; ++cred )
+ {
+ const TPtrC8& transDes = aURI.UriDes();
+ const TPtrC8& credDes =
+ iStringPool.StringF( iDCredentials[ cred ].iURI ).DesC();
+ TPtrC8 transNoSchemePtr( transDes );
+ TPtrC8 credNoSchemePtr( credDes );
+
+ if ( transNoSchemePtr.Find( KSchemeEnd() ) > 0 )
+ {
+ transNoSchemePtr.Set( transNoSchemePtr.Mid( transNoSchemePtr.Find( KSchemeEnd ) ) );
+ }
+
+ if ( credNoSchemePtr.Find( KSchemeEnd() ) > 0 )
+ {
+ credNoSchemePtr.Set( credNoSchemePtr.Mid( credNoSchemePtr.Find( KSchemeEnd ) ) );
+ }
+
+ if ( transNoSchemePtr.Length() >= credNoSchemePtr.Length() )
+ {
+ // The URI is long enough, which is a good start.
+
+ if ( transNoSchemePtr.Left( credNoSchemePtr.Length() ).Compare( credNoSchemePtr ) == 0 )
+ {
+ // The descriptors match. Things are looking good. In
+ // the interests of paranoia, if we haven't matched
+ // the entire URI, check that the character after the
+ // match is '/'.
+ if ( transNoSchemePtr.Length() == credNoSchemePtr.Length() )
+ {
+ TBuf<256> temp16;
+
+ temp16.Copy(aURI.UriDes());
+
+
+ AUTHLOGGER_WRITE_FORMAT( "Found cred(1): %d", cred );
+ AUTHLOGGER_WRITE_FORMAT8( "uri: %S", &aURI.UriDes() );
+ return cred;
+ }
+
+ else if ( transNoSchemePtr[ credNoSchemePtr.Length() ] == '/' )
+ {
+ AUTHLOGGER_WRITE_FORMAT( "Found cred(2): %d", cred );
+ AUTHLOGGER_WRITE_FORMAT8( "uri: %S", &aURI.UriDes() );
+ return cred;
+ }
+ }
+ }
+ }
+
+ AUTHLOGGER_WRITE( "cred not found" );
+
+ return KErrNotFound;
+ }
+
+// -----------------------------------------------------------------------------
+// CHttpFilterDigestAuthentication::FindHeaderPartToUseL
+// Find the Authentication header.
+// -----------------------------------------------------------------------------
+//
+TInt CHttpFilterDigestAuthentication::DFindHeaderPartToUseL( RHTTPTransaction aTransaction )
+ {
+ // While we're here, we check that all desired fields are there.
+ RStringF wwwAuthenticate = iStringPool.StringF( iHttpStringAuthenticate, RHTTPSession::GetTable() );
+ TInt lastGoodDigest = KErrNotFound;
+ RHTTPHeaders headers = aTransaction.Response().GetHeaderCollection();
+ const TInt parts = headers.FieldPartsL( wwwAuthenticate );
+
+ for ( TInt ii = 0; ii < parts; ii++ )
+ {
+ THTTPHdrVal fieldVal; // The name of the current field.
+ THTTPHdrVal hdrVal; //A scratch hdrVal
+ headers.GetField( wwwAuthenticate, ii, fieldVal );
+
+ switch ( fieldVal.StrF().Index( RHTTPSession::GetTable() ) )
+ {
+ case HTTP::EDigest:
+ {
+ if ( headers.GetParam( wwwAuthenticate, iRealmStr, hdrVal, ii ) == KErrNone )
+ {
+ lastGoodDigest = ii;
+ }
+ }
+ break;
+
+ default:
+ // We don't understand this, whatever it is. Ignore it.
+ break;
+ }
+ }
+
+ return lastGoodDigest;
+ }
+
+// -----------------------------------------------------------------------------
+// CHttpFilterDigestAuthentication::DGetCredentialsFromPropertiesL
+// Find credentials from the transaction's properties.
+// -----------------------------------------------------------------------------
+//
+void CHttpFilterDigestAuthentication::DGetCredentialsFromPropertiesL( RHTTPTransaction& aTransaction )
+ {
+ THTTPHdrVal usernameVal;
+ THTTPHdrVal passwordVal;
+ THTTPHdrVal realmVal;
+ THTTPHdrVal nonceVal;
+ THTTPHdrVal staleVal;
+ const TUriC8& uri = aTransaction.Request().URI();
+ RHTTPTransactionPropertySet propSet = aTransaction.PropertySet();
+
+ if ( propSet.Property( iUsernameStr, usernameVal ) &&
+ propSet.Property( iPasswordStr, passwordVal ) &&
+ propSet.Property( iRealmStr, realmVal ) &&
+ propSet.Property( iNonceStr, nonceVal ) &&
+ !propSet.Property( iStaleStr, staleVal ) )
+ // if stale is TRUE we don't need to update credential
+ {
+ TInt cred;
+ RStringF uriStr;
+
+ if ( iIsProxy )
+ {
+ // do nothing this time
+ }
+ else
+ {
+ TInt pushCounter( 0 );
+ if ( ( cred = DFindCredentialsForURI( uri ) ) != KErrNotFound )
+ {
+ // Remove old credentials from the list
+ DRemoveCredentialsFromList( cred );
+ }
+
+ TPtrC8 uriPathPtr( uri.UriDes() );
+
+ if ( uriPathPtr.LocateReverse( '/' ) > 0 )
+ {
+ uriPathPtr.Set( uriPathPtr.Left( uriPathPtr.LocateReverse( '/' ) ) );
+ }
+
+ uriStr = iStringPool.OpenFStringL( uriPathPtr );
+ CleanupClosePushL<RStringF>( uriStr );
+ ++pushCounter;
+
+ THTTPHdrVal opaqueVal;
+ RString opaqueStr;
+
+ if( !propSet.Property( iOpaqueStr, opaqueVal ) )
+ // this isn't a error case
+ {
+ opaqueStr = iStringPool.OpenStringL( KNullDesC8 );
+ CleanupClosePushL<RString>( opaqueStr );
+ ++pushCounter;
+ }
+ else
+ {
+ opaqueStr = opaqueVal;
+ }
+
+ THTTPHdrVal qopVal;
+
+ if( !propSet.Property( iQopStr, qopVal ) )
+ // this isn't a error case
+ {
+ qopVal.SetInt( KQopNone );
+ }
+
+
+ THTTPHdrVal algVal;
+ if( !propSet.Property( iStringPool.StringF( HTTP::EAlgorithm,
+ RHTTPSession::GetTable() ), algVal) )
+ {
+ algVal.SetInt( KAlgUnknown );
+ }
+
+ // Add credentials to the list from the transaction's properties
+ DAddCredentialsToListL( usernameVal.Str().Copy(),
+ passwordVal.Str().Copy(),
+ realmVal.Str().Copy(),
+ uriStr.Copy(),
+ opaqueStr.Copy(),
+ nonceVal.Str().Copy(),
+ qopVal.Int(),
+ algVal.Int()
+ );
+
+ // this is not the traditional way to pop and destroy elements
+ // but in this case this is the simplest one.
+ CleanupStack::PopAndDestroy( pushCounter ); // [opaqueVal,]uriStr
+ }
+ }
+ }
+
+// -----------------------------------------------------------------------------
+// CHttpFilterDigestAuthentication::DRemoveCredentialsFromList
+// Remove credentials from the list.
+// -----------------------------------------------------------------------------
+//
+void CHttpFilterDigestAuthentication::DRemoveCredentialsFromList( TInt aCredId )
+ {
+ AUTHLOGGER_WRITE_FORMAT( "DRemoveCredentialsFromList: %d", aCredId );
+
+ __ASSERT_DEBUG( aCredId >= 0 && aCredId < iDCredentials.Count(), PanicDigestAuthenticationFilter() );
+
+ TDCredentials& creds = iDCredentials[ aCredId ];
+ iStringPool.String( creds.iUser ).Close();
+ iStringPool.String( creds.iPassword ).Close();
+ iStringPool.StringF( creds.iURI ).Close();
+ iStringPool.String( creds.iRealm ).Close();
+ iStringPool.String( creds.iOpaque ).Close();
+ iStringPool.String( creds.iNonce ).Close();
+ iDCredentials.Remove( aCredId );
+ }
+
+
+// -----------------------------------------------------------------------------
+// CHttpFilterDigestAuthentication::DAddCredentialsToListL
+// Store credentials in the list.
+// -----------------------------------------------------------------------------
+//
+void CHttpFilterDigestAuthentication::DAddCredentialsToListL( RString aUsernameStr,
+ RString aPasswordStr,
+ RString aRealmStr,
+ RStringF aUriStr,
+ RString aOpaque,
+ RString aNonce,
+ TInt aQop,
+ TInt aAuthAlg )
+ {
+ TDCredentials newCred;
+ newCred.iUser = aUsernameStr;
+ newCred.iPassword = aPasswordStr;
+ newCred.iRealm = aRealmStr;
+ newCred.iURI = aUriStr;
+ newCred.iOpaque = aOpaque;
+ newCred.iNonce = aNonce;
+ newCred.iQop = aQop;
+ newCred.iAlgorithm = aAuthAlg;
+ newCred.iNc = 0;
+ User::LeaveIfError( iDCredentials.Append( newCred ) );
+ }
+
+// -----------------------------------------------------------------------------
+// CHttpFilterDigestAuthentication::EncodeDAuthL
+// Encodes digest authentication
+// -----------------------------------------------------------------------------
+//
+void CHttpFilterDigestAuthentication::EncodeDigestAuthL( TInt aCred, RHTTPTransaction aTransaction )
+ {
+ AUTHLOGGER_ENTERFN( "EncodeDigestAuthL" );
+
+ // For now, only support RFC2069 format. The digest is
+ // MD5(H(A1):nonce:H(A2))
+ TBuf8<KHashLength> hash;
+ TBuf8<KNonceCountLength> nonceCount;
+
+ TDCredentials& cred = iDCredentials[aCred];
+
+ THTTPHdrVal nonce( iStringPool.String( cred.iNonce ) );
+ THTTPHdrVal realm( iStringPool.String( cred.iRealm ) );
+ THTTPHdrVal qop;
+ THTTPHdrVal uname( iStringPool.String( cred.iUser ) );
+ THTTPHdrVal password( iStringPool.String( cred.iPassword ) );
+ THTTPHdrVal opaque( iStringPool.String( cred.iOpaque ) );
+
+ if( cred.iQop & KQopAuth )
+ // recent release supports only qop 'auth'.
+ {
+ qop.SetStrF( iQopAuthStr );
+ }
+
+ THTTPHdrVal requestUri;
+ // we get url from request and modify as made in chttpclienthandler.cpp
+ RString uriStr = RequestUriL( aTransaction );
+ CleanupClosePushL<RString>( uriStr );
+
+ requestUri.SetStr( uriStr );
+
+ HBufC8* stringToHash = NULL;
+ RStringF auth = iStringPool.StringF(HTTP::EAuth,RHTTPSession::GetTable());
+
+ TBuf8<KHashLength> cnonce;
+ if ( cred.iQop != KQopNone )
+ {
+ GenerateCNonce( cnonce );
+
+ // 7 is for colons
+ // 8 for nc-value
+ stringToHash = HBufC8::NewMaxLC( KHashLength + // H(A1)
+ 1 + // ":"
+ nonce.Str().DesC().Length() +
+ 1 + // ":"
+ 8 + // nc value
+ 1 + // ":"
+ KHashLength + // cnonce
+ 1 + // ":"
+ qop.StrF().DesC().Length() +
+ 1 + // ":"
+ KHashLength ); // H(A2)
+ TPtr8 stringMod = stringToHash->Des();
+ stringMod.Zero();
+ HA1L( cred.iAlgorithm, uname, password, realm.Str(), nonce, cnonce, stringMod );
+ stringMod.Append(':');
+ stringMod.Append(nonce.Str().DesC());
+
+ // nc value
+ stringMod.Append(':');
+ ++cred.iNc;
+ // nc value is of 8 chars length and in hexadecimal format
+ nonceCount.NumFixedWidth( cred.iNc, EHex, 8 );
+ stringMod.Append( nonceCount );
+ stringMod.Append(':');
+
+ stringMod.Append(cnonce);
+ stringMod.Append(':');
+ stringMod.Append(auth.DesC());
+ stringMod.Append(':');
+ HA2L(aTransaction.Request().Method(), requestUri.Str(), hash);
+ stringMod.Append(hash);
+ }
+ else
+ {
+ // The response is the hash of H(A1):nonce:H(A2).
+ // Crqeate a buffer for the string to convert into MD5. The
+ // length is 32 characters for each of the hashes, 2
+ // characters for the 2 colons, plus the length of the nonce
+ stringToHash = HBufC8::NewMaxLC(nonce.Str().DesC().Length() +
+ 2 * KHashLength + 2);
+ TPtr8 stringMod = stringToHash->Des();
+ stringMod.Zero();
+ HA1L( cred.iAlgorithm, uname, password, realm.Str(), nonce, cnonce, stringMod );
+ stringMod.Append(':');
+ stringMod.Append(nonce.Str().DesC());
+ stringMod.Append(':');
+ HA2L(aTransaction.Request().Method(), requestUri.Str(), hash);
+ stringMod.Append(hash);
+ }
+
+ Hash(*stringToHash, hash);
+ CleanupStack::PopAndDestroy(stringToHash);
+
+ // OK. hash now contains the digest response. Set up the header.
+ RHTTPHeaders requestHeaders(aTransaction.Request().GetHeaderCollection());
+
+ requestHeaders.RemoveField(iStringPool.StringF(HTTP::EAuthorization,RHTTPSession::GetTable()));
+
+ requestHeaders.SetFieldL(iStringPool.StringF(HTTP::EAuthorization,RHTTPSession::GetTable()),
+ THTTPHdrVal(iStringPool.StringF(HTTP::EDigest,RHTTPSession::GetTable())),
+ iStringPool.StringF(HTTP::EUsername,RHTTPSession::GetTable()),
+ uname);
+ requestHeaders.SetFieldL(iStringPool.StringF(HTTP::EAuthorization,RHTTPSession::GetTable()),
+ THTTPHdrVal(iStringPool.StringF(HTTP::EDigest,RHTTPSession::GetTable())),
+ iStringPool.StringF(HTTP::ERealm,RHTTPSession::GetTable()),
+ realm);
+ requestHeaders.SetFieldL(iStringPool.StringF(HTTP::EAuthorization,RHTTPSession::GetTable()),
+ THTTPHdrVal(iStringPool.StringF(HTTP::EDigest,RHTTPSession::GetTable())),
+ iStringPool.StringF(HTTP::ENonce,RHTTPSession::GetTable()),
+ nonce);
+ RString hashStr = iStringPool.OpenStringL(hash);
+ CleanupClosePushL<RString>(hashStr);
+
+ THTTPHdrVal hdrVal;
+ hdrVal.SetStr(hashStr);
+ requestHeaders.SetFieldL(iStringPool.StringF(HTTP::EAuthorization,RHTTPSession::GetTable()),
+ THTTPHdrVal(iStringPool.StringF(HTTP::EDigest,RHTTPSession::GetTable())),
+ iStringPool.StringF(HTTP::EResponse,RHTTPSession::GetTable()),
+ hdrVal);
+
+ requestHeaders.SetFieldL(iStringPool.StringF(HTTP::EAuthorization,RHTTPSession::GetTable()),
+ THTTPHdrVal(iStringPool.StringF(HTTP::EDigest,RHTTPSession::GetTable())),
+ iStringPool.StringF(HTTP::EUri,RHTTPSession::GetTable()),
+ requestUri);
+ CleanupStack::PopAndDestroy(&hashStr);
+ if ( cred.iQop != KQopNone )
+ {
+ // QOP
+ hdrVal.SetStrF( auth );
+ requestHeaders.SetFieldL(iStringPool.StringF(HTTP::EAuthorization,RHTTPSession::GetTable()),
+ THTTPHdrVal(iStringPool.StringF(HTTP::EDigest,RHTTPSession::GetTable())),
+ iStringPool.StringF(HTTP::EQop,RHTTPSession::GetTable()),
+ hdrVal);
+ RString cnonceString = iStringPool.OpenStringL(cnonce);
+ // CNonce
+ CleanupClosePushL<RString>( cnonceString );
+ hdrVal.SetStr(cnonceString);
+ requestHeaders.SetFieldL(iStringPool.StringF(HTTP::EAuthorization,RHTTPSession::GetTable()),
+ THTTPHdrVal(iStringPool.StringF(HTTP::EDigest,RHTTPSession::GetTable())),
+ iStringPool.StringF(HTTP::ECnonce,RHTTPSession::GetTable()),
+ hdrVal);
+ CleanupStack::PopAndDestroy(); //cnonceString
+ // Nonce-count
+ RString nonceStr = iStringPool.OpenStringL( nonceCount );
+ CleanupClosePushL<RString>( nonceStr );
+ hdrVal.SetStr( nonceStr );
+ requestHeaders.SetFieldL(iStringPool.StringF(HTTP::EAuthorization,RHTTPSession::GetTable()),
+ THTTPHdrVal(iStringPool.StringF(HTTP::EDigest,RHTTPSession::GetTable())),
+ iStringPool.StringF(HTTP::ENc,RHTTPSession::GetTable()),
+ hdrVal);
+ CleanupStack::PopAndDestroy(); // nonceStr
+ }
+
+ if( opaque.Str().DesC().Length() )
+ requestHeaders.SetFieldL(iStringPool.StringF(HTTP::EAuthorization,RHTTPSession::GetTable()),
+ THTTPHdrVal(iStringPool.StringF(HTTP::EDigest,RHTTPSession::GetTable())),
+ iStringPool.StringF(HTTP::EOpaque,RHTTPSession::GetTable()),
+ opaque );
+
+ if( cred.iAlgorithm == KAlgMd5 )
+ {
+ requestHeaders.SetFieldL(iStringPool.StringF(HTTP::EAuthorization,RHTTPSession::GetTable()),
+ THTTPHdrVal(iStringPool.StringF(HTTP::EDigest,RHTTPSession::GetTable())),
+ iStringPool.StringF(HTTP::EAlgorithm,RHTTPSession::GetTable()),
+ iMd5Str );
+ }
+ else if( cred.iAlgorithm == KAlgMd5Sess )
+ {
+ requestHeaders.SetFieldL(iStringPool.StringF(HTTP::EAuthorization,RHTTPSession::GetTable()),
+ THTTPHdrVal(iStringPool.StringF(HTTP::EDigest,RHTTPSession::GetTable())),
+ iStringPool.StringF(HTTP::EAlgorithm,RHTTPSession::GetTable()),
+ iMd5SessStr );
+ }
+
+ // set it again
+ // this may be needed if auth. fails and stale is true.
+ aTransaction.PropertySet().SetPropertyL( iRealmStr, realm );
+
+ CleanupStack::PopAndDestroy(); // requestUri
+
+ AUTHLOGGER_LEAVEFN( "EncodeDigestAuthL" );
+ }
+
+// -----------------------------------------------------------------------------
+// CHttpFilterDigestAuthentication::HA1L
+// Calculates H(A1)
+// -----------------------------------------------------------------------------
+//
+TInt CHttpFilterDigestAuthentication::HA1L(int aAlgorithm,
+ const RString& aUsername,
+ const RString& aPW,
+ const RString& aRealm,
+ const RString& aNonceValue,
+ TDesC8& aCNonce,
+ TDes8& aResult )
+ {
+ TBuf8<KHashLength> hash;
+
+ HBufC8* a1 = HBufC8::NewMaxL(aUsername.DesC().Length() +
+ aPW.DesC().Length() +
+ aRealm.DesC().Length() + 2 );
+ TPtr8 a1Mod = a1->Des();
+ a1Mod.Zero();
+ a1Mod.Append( aUsername.DesC() );
+ a1Mod.Append( KColon );
+ a1Mod.Append( aRealm.DesC() );
+ a1Mod.Append( KColon );
+ a1Mod.Append( aPW.DesC() );
+
+ Hash( *a1, hash );
+
+ delete a1;
+ a1 = NULL;
+
+ if( aAlgorithm == KAlgMd5Sess )
+ {
+ a1 = HBufC8::NewMaxL( KHashLength +
+ 1 + // ":"
+ aNonceValue.DesC().Length() +
+ 1 + // ":"
+ aCNonce.Length() );
+ TPtr8 a1Mod = a1->Des();
+ a1Mod.Zero();
+ a1Mod.Append( hash );
+ a1Mod.Append( KColon );
+ a1Mod.Append( aNonceValue.DesC() );
+ a1Mod.Append( KColon );
+ a1Mod.Append( aCNonce );
+
+ Hash(*a1, aResult);
+
+ delete a1;
+ a1 = NULL;
+ }
+ else
+ {
+ aResult.Copy( hash );
+ }
+
+ return KErrNone;
+ }
+
+
+// -----------------------------------------------------------------------------
+// CHttpFilterDigestAuthentication::HA2L
+// Calculates H(A2)
+// -----------------------------------------------------------------------------
+//
+TInt CHttpFilterDigestAuthentication::HA2L(const RStringF& aMethod,
+ const RString& aRequestUri,
+ TDes8& aResult)
+ {
+ // In the auth qop, a2 is Method:digest-uri-value
+ // Digest-uri-value. We don't support auth-int qop
+ // Allocate enough space for the method, the URI and the colon.
+ TPtrC8 requestUri = aRequestUri.DesC();
+ TPtrC8 method = aMethod.DesC();
+ HBufC8* a2 = HBufC8::NewMaxLC(requestUri.Length() + method.Length() + 1);
+ TPtr8 a2Mod = a2->Des();
+ a2Mod.Zero();
+ a2Mod.Append(method);
+ a2Mod.Append(':');
+ a2Mod.Append(requestUri);
+
+ Hash(*a2, aResult);
+
+ CleanupStack::PopAndDestroy(a2);
+
+ return KErrNone;
+ }
+
+// -----------------------------------------------------------------------------
+// CHttpFilterDigestAuthentication::Hash
+// Calculates hash value
+// -----------------------------------------------------------------------------
+//
+void CHttpFilterDigestAuthentication::Hash(const TDesC8& aMessage, TDes8& aHash)
+ {
+ // Calculate the 128 bit (16 byte) hash
+ iMD5Calculator->Reset();
+ TPtrC8 hash = iMD5Calculator->Hash( aMessage );
+ // Now print it as a 32 byte hex number
+ aHash.Zero();
+ _LIT8(formatStr, "%02x");
+ for (TInt ii = 0; ii < KRawHashLength; ii++)
+ {
+ TBuf8<2> scratch;
+ scratch.Zero();
+ scratch.Format( formatStr, hash[ii] );
+ aHash.Append( scratch );
+ }
+ }
+
+// -----------------------------------------------------------------------------
+// CHttpFilterDigestAuthentication::GenerateCNonce
+// Generate unique cnonce value.
+// -----------------------------------------------------------------------------
+//
+void CHttpFilterDigestAuthentication::GenerateCNonce(TDes8& aNonce)
+ {
+ // 'Inspired by' CObexAuthenticator
+
+ // The purpose of the client nonce is to protect against 'chosen
+ // plaintext' attacks where a hostile server tricks the client
+ // into supplying a password using a specific server nonce that
+ // allows an (as yet undiscovered) flaw in MD5 to recover the
+ // password. As such the only important thing about client nonces
+ // is that they change and aren't predictable. See section 4.9 of
+ // RFC2616
+
+ TTime time;
+ time.UniversalTime();
+ TInt64 randomNumber = Math::Rand(iSeed);
+ randomNumber <<= 32;
+ randomNumber += Math::Rand(iSeed);
+ TBuf8<33> key;
+ key.Zero();
+ key.AppendNum(time.Int64(), EHex);
+ key.Append(_L8(":"));
+ key.AppendNum(randomNumber, EHex);
+
+ Hash(key, aNonce);
+ }
+
+// -----------------------------------------------------------------------------
+// CHttpFilterDigestAuthentication::RequestUriLC
+// Returns requested URI in form that can be used in uri field.
+// This code comes from
+// Application-protocols/http/httpclient/chttpclienthandler.cpp.
+// -----------------------------------------------------------------------------
+//
+RString CHttpFilterDigestAuthentication::RequestUriL( RHTTPTransaction& aTransaction )
+ {
+ TBool useProxy( EFalse );
+ THTTPHdrVal useProxyHdr;
+ RStringF proxyUsage = iStringPool.StringF( HTTP::EProxyUsage, RHTTPSession::GetTable() );
+ RHTTPRequest request = aTransaction.Request();
+ const TUriC8& uri = request.URI();
+ RStringF scheme = iStringPool.OpenFStringL( uri.Extract( EUriScheme ) );
+ CleanupClosePushL<RStringF>( scheme );
+ CUri8* uriToUse = CUri8::NewLC( uri );
+
+ if( iSession->ConnectionInfo().Property( proxyUsage, useProxyHdr ) )
+ {
+ __ASSERT_DEBUG( useProxyHdr.Type() == THTTPHdrVal::KStrFVal,
+ PanicDigestAuthenticationFilter(-1/*THttpClientPanic::EInvalidProxySetting*/) );
+
+ // Is a proxy being used?
+ useProxy = ( useProxyHdr.StrF().Index(RHTTPSession::GetTable()) == HTTP::EUseProxy );
+ }
+
+ if( !useProxy || (useProxy && scheme.Index(RHTTPSession::GetTable()) == HTTP::EHTTPS ))
+ {
+ // Not going via a proxy - need to remove the scheme and authority parts
+ uriToUse->RemoveComponentL( EUriScheme );
+ uriToUse->RemoveComponentL( EUriHost ); // this also removes the userinfo + port
+ }
+
+ RString uriStr = iStringPool.OpenStringL( uriToUse->Uri().UriDes() );
+
+ CleanupStack::PopAndDestroy( 2 ); // schem, uriToUse
+
+ return uriStr;
+ }
+
+// -----------------------------------------------------------------------------
+// CHttpFilterDigestAuthentication::CheckQop
+//
+// -----------------------------------------------------------------------------
+//
+TInt CHttpFilterDigestAuthentication::CheckQop( RHTTPHeaders& aHeaders,
+ RStringF& aWwwAuthHeader,
+ TInt aHeaderPart )
+ {
+ THTTPHdrVal qopVal;
+ TInt retVal( KQopNone );
+
+ if( !aHeaders.GetParam( aWwwAuthHeader, iQopStr, qopVal, aHeaderPart ) )
+ {
+ TPtrC8 qopBuf( qopVal.Str().DesC() );
+ TPtrC8 qopValue;
+ TInt comma;
+
+ do
+ {
+ comma = qopBuf.Locate( ',' );
+
+ if( comma != KErrNotFound )
+ {
+ qopValue.Set( qopBuf.Left(comma) );
+ }
+ else
+ {
+ qopValue.Set( qopBuf );
+ }
+
+ if( !qopValue.CompareF(iQopAuthStr.DesC()) )
+ {
+ retVal |= KQopAuth;
+ }
+ else if( !qopValue.CompareF(KQopAuthIntStr) )
+ {
+ retVal |= KQopAuthInt;
+ }
+
+ qopBuf.Set( qopBuf.Right(qopBuf.Length()-comma-1));
+ }while( comma != KErrNotFound );
+ }
+
+ return retVal;
+ }