os/security/contentmgmt/cafstreamingsupport/source/ipsec/ipseckeystreamsink.cpp
changeset 0 bde4ae8d615e
     1.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     1.2 +++ b/os/security/contentmgmt/cafstreamingsupport/source/ipsec/ipseckeystreamsink.cpp	Fri Jun 15 03:10:57 2012 +0200
     1.3 @@ -0,0 +1,366 @@
     1.4 +// Copyright (c) 2007-2009 Nokia Corporation and/or its subsidiary(-ies).
     1.5 +// All rights reserved.
     1.6 +// This component and the accompanying materials are made available
     1.7 +// under the terms of the License "Eclipse Public License v1.0"
     1.8 +// which accompanies this distribution, and is available
     1.9 +// at the URL "http://www.eclipse.org/legal/epl-v10.html".
    1.10 +//
    1.11 +// Initial Contributors:
    1.12 +// Nokia Corporation - initial contribution.
    1.13 +//
    1.14 +// Contributors:
    1.15 +//
    1.16 +// Description:
    1.17 +//
    1.18 +
    1.19 +#include "ipseckeystreamsink.h"
    1.20 +#include <caf/streaming/keyassociation.h>
    1.21 +#include <networking/pfkeyv2.h>
    1.22 +#include <s32mem.h>
    1.23 +#include "scaflog.h"
    1.24 +
    1.25 +using namespace StreamAccess;
    1.26 +
    1.27 +// The last two rules: (inbound = {}, outbound = {}) are catch-all rules - without them, all packets
    1.28 +// which do not match the policy will get rejected
    1.29 +_LIT8( KPolicyFormat,  
    1.30 +"SECURITY_FILE_VERSION: 3\r\n[INFO]\r\n\
    1.31 +Test CAF IpSec Integration Policy\r\n\
    1.32 +[POLICY]\r\n\
    1.33 +sa caf_sas = {\r\n\
    1.34 +esp\r\n\
    1.35 +encrypt_alg %d\r\n\
    1.36 +auth_alg %d\r\n\
    1.37 +src_specific\r\n\
    1.38 +local_port_specific\r\n\
    1.39 +remote_port_specific\r\n\
    1.40 +}\r\n\
    1.41 +inbound local %S 255.255.255.255 local_port %d remote_port %d = { caf_sas() }\r\n\
    1.42 +inbound = {}\r\n\
    1.43 +outbound = {}\r\n" );
    1.44 +
    1.45 +// The number is taken as the maximal recommendation from OMA DRM BCAST standard (section 9.1, IPSec management)
    1.46 +const TUint KDefaultMaxSpiNumber = 3; 
    1.47 +
    1.48 +CIpSecKeyStreamSink* CIpSecKeyStreamSink::NewLC(const TInetAddr& aSrcAddr, const TInetAddr& aDstAddr)
    1.49 +	{
    1.50 +	CIpSecKeyStreamSink* self = new (ELeave) CIpSecKeyStreamSink(aSrcAddr, aDstAddr);
    1.51 +	CleanupStack::PushL(self);
    1.52 +	self->ConstructL();
    1.53 +	return self;
    1.54 +	}
    1.55 +
    1.56 +CIpSecKeyStreamSink::CIpSecKeyStreamSink(const TInetAddr& aSrcAddr, const TInetAddr& aDstAddr) :
    1.57 +						iSourceAddr(aSrcAddr), 
    1.58 +						iDestinationAddr(aDstAddr), 
    1.59 +						iPolicySet(EFalse),
    1.60 +						iMaxSpiNumber(KDefaultMaxSpiNumber)
    1.61 +	{		
    1.62 +	}
    1.63 +
    1.64 +CIpSecKeyStreamSink::~CIpSecKeyStreamSink()
    1.65 +	{
    1.66 +	// Remove all SA-s
    1.67 +	TInt submittedSaCount = iSubmittedSpiList.Count();
    1.68 +	for (TInt i = 0; i < submittedSaCount; ++i)
    1.69 +		{
    1.70 +		TRAP_IGNORE(RemoveSaL(iSubmittedSpiList[i]));
    1.71 +		}
    1.72 +	
    1.73 +	if (iPolicySet)
    1.74 +		{
    1.75 +		TRequestStatus status;
    1.76 +		iPolicyServer.UnloadPolicy( iPolicyHandle(), status );
    1.77 +		User::WaitForRequest(status);
    1.78 +		// Impossible to handle the error well in destructor - ignore the status code
    1.79 +		}
    1.80 +
    1.81 +	iSADB.Close();
    1.82 +	iSocketServ.Close();
    1.83 +	
    1.84 +	iPolicyServer.Close();		
    1.85 +	iSubmittedSpiList.Close();
    1.86 +	}
    1.87 +
    1.88 +void CIpSecKeyStreamSink::ConstructL()
    1.89 +	{
    1.90 +	DEBUG_PRINTF(_L("Constructing an IPSec key stream sink."));
    1.91 +	User::LeaveIfError(iSocketServ.Connect());
    1.92 +	User::LeaveIfError(iSADB.Open(iSocketServ));
    1.93 +	User::LeaveIfError(iPolicyServer.Connect());	
    1.94 +	DEBUG_PRINTF(_L("Constructed an IPSec key stream sink."));
    1.95 +	}
    1.96 +
    1.97 +CKeyStreamSink* CIpSecKeyStreamSink::CloneLC() const
    1.98 +	{
    1.99 +	CIpSecKeyStreamSink *ret = CIpSecKeyStreamSink::NewLC(iSourceAddr, iDestinationAddr);
   1.100 +  	ret->iEncAlg = this->iEncAlg;
   1.101 + 	ret->iAuthAlg = this->iAuthAlg;
   1.102 + 	return ret;
   1.103 +	}
   1.104 +
   1.105 +static TUint32 ConvertToNetworkOrder(TUint32 aNum)
   1.106 +    {
   1.107 +    const TInt KMaxTUint32CStringLen = 11;
   1.108 +    TUint8 temp[ KMaxTUint32CStringLen ];   
   1.109 +    LittleEndian::Put32( temp, aNum );
   1.110 +    return BigEndian::Get32( temp );
   1.111 +    }
   1.112 +
   1.113 +TBool CIpSecKeyStreamSink::CompareReceivedMessageExtensionsL(TPfkeyRecvMsg &aReceivedReply, TUint32 aSpi) const
   1.114 +	{
   1.115 +	TBool spiVerified(EFalse), destAddrVerified(EFalse);
   1.116 +	
   1.117 +	TPfkeyAnyExt ext;
   1.118 + 
   1.119 + 	// We verify that both the SPI matches and the destination address matches - this should exclude
   1.120 + 	// replies to unrelated messages
   1.121 +	while ( !spiVerified || !destAddrVerified )
   1.122 +         {
   1.123 +         // Both extensions should be found in the reply
   1.124 +         User::LeaveIfError(aReceivedReply.NextExtension(ext));
   1.125 +         TInt extType = ext.ExtType();
   1.126 +         if ( extType == SADB_EXT_ADDRESS_DST )
   1.127 +             {
   1.128 +             const TInetAddr* addr = reinterpret_cast<const TInetAddr *>(ext.Ptr() + sizeof(struct sadb_address));
   1.129 +    		 if (*addr == iDestinationAddr)
   1.130 +    		 	destAddrVerified = ETrue;	
   1.131 +    		 else
   1.132 +    			return EFalse;
   1.133 +             }
   1.134 +         else if (extType == SADB_EXT_SA)
   1.135 +         	{
   1.136 +    		const sadb_sa* saExt = reinterpret_cast<const sadb_sa *>(ext.Ptr());
   1.137 +    		if (saExt->sadb_sa_spi == aSpi)
   1.138 +    			spiVerified = ETrue;
   1.139 +    		else
   1.140 +    			return EFalse;
   1.141 +         	}
   1.142 +         }
   1.143 +    return ETrue;
   1.144 +	}
   1.145 +
   1.146 +void CIpSecKeyStreamSink::SynchronousSendAndVerifyMessageL(TPfkeySendMsg& aMessage, TInt aMessageType, TUint32 aSpi) 
   1.147 +	{
   1.148 +	// Wait for the message to be sent
   1.149 +	TRequestStatus status;
   1.150 +	iSADB.FinalizeAndSend(aMessage, status);
   1.151 +	User::WaitForRequest(status);
   1.152 +	User::LeaveIfError(status.Int());
   1.153 +
   1.154 +	// Receive a reply - since SADB sends replies to _all_ sockets, we need to filter out replies
   1.155 +	// which do not correspond to our own request
   1.156 +	for(;;)
   1.157 +		{
   1.158 +		TPfkeyRecvMsg receivedReply;
   1.159 +		iSADB.ReadRequest(receivedReply, status);
   1.160 +		User::WaitForRequest(status);
   1.161 +		User::LeaveIfError(status.Int());
   1.162 +		
   1.163 +		// Verify that the message is a reply to the one sent by us
   1.164 +		sadb_msg &msgHeader = receivedReply.MsgHdr();
   1.165 +		if (msgHeader.sadb_msg_pid != RProcess().Id())
   1.166 +			continue;
   1.167 +		if (msgHeader.sadb_msg_seq != iSequenceNumber)
   1.168 +			continue;
   1.169 +		// Do additional validation by checking whether the destination address and the SPI are the same as ours
   1.170 +		TBool isResponseToRequest(ETrue);
   1.171 +		switch (aMessageType)
   1.172 +			{
   1.173 +			case SADB_ADD: 
   1.174 +			case SADB_DELETE:
   1.175 +				isResponseToRequest = CompareReceivedMessageExtensionsL(receivedReply, aSpi);
   1.176 +				break;
   1.177 +			default:
   1.178 +				ASSERT(0); // Unexpected state
   1.179 +			}
   1.180 +		if (!isResponseToRequest)
   1.181 +			continue;
   1.182 +		// If the message types does not match, then the problem is internal in IPSec - it should not answer with a different message type
   1.183 +		if (msgHeader.sadb_msg_type != aMessageType)
   1.184 +			User::Leave(KErrArgument); 
   1.185 +		if (msgHeader.sadb_msg_errno != 0)
   1.186 +			{
   1.187 +			// Mimic the logic in IPSec error handling (see the Update function in key_msg.cpp)		
   1.188 +			TUint16 reservedField = (TUint16)msgHeader.sadb_msg_reserved << 8;
   1.189 +			TUint16 errnoField = msgHeader.sadb_msg_errno;
   1.190 +			User::Leave(-(reservedField + errnoField));
   1.191 +			}		
   1.192 +		break;
   1.193 +		}			
   1.194 +	}
   1.195 +	
   1.196 +void CIpSecKeyStreamSink::AddAssociationL(TPfkeySendMsg& aMessage, TUint32 aSpi) 	
   1.197 +	{
   1.198 +	SynchronousSendAndVerifyMessageL(aMessage, SADB_ADD, aSpi);
   1.199 +	
   1.200 +	// Take care to delete old SA-s, if needed
   1.201 +	iSubmittedSpiList.AppendL(aSpi); 
   1.202 +	if (iSubmittedSpiList.Count() > iMaxSpiNumber)
   1.203 +		{
   1.204 +		RemoveSaL(iSubmittedSpiList[0]);
   1.205 +		iSubmittedSpiList.Remove(0);
   1.206 +		}		
   1.207 +	}
   1.208 +	
   1.209 +void CIpSecKeyStreamSink::ProcessNewKeyAssociationL(const CKeyAssociation& aKeyAssociation) 
   1.210 +	{
   1.211 +	if (!iPolicySet)
   1.212 +		{
   1.213 +		SetPolicyL();
   1.214 +		}
   1.215 +	// No official RTTI support, using static_cast - no validation that it is indeed an IPSec association
   1.216 +	const CIpSecKeyAssociation* ipsecKeyAssociation = static_cast<const CIpSecKeyAssociation *>(&aKeyAssociation);
   1.217 +	
   1.218 +	DEBUG_PRINTF2(_L("IPSec key stream sink - processing new association with SPI %d."), ipsecKeyAssociation->GetSpiL());
   1.219 +		
   1.220 +	// We use ESP, since there is very low probability that AH will be used - it does not provide confidentiality
   1.221 +	TPfkeySendMsg sendMessage(SADB_ADD, SADB_SATYPE_ESP, ++iSequenceNumber, RProcess().Id());
   1.222 +	TUint32 bigEndianSpi(ConvertToNetworkOrder(ipsecKeyAssociation->GetSpiL()));
   1.223 +	sendMessage.Add( Int2Type<SADB_EXT_SA>(), bigEndianSpi, iAuthAlg, iEncAlg); 
   1.224 +	const HBufC8 *encryptionKey(ipsecKeyAssociation->GetEncryptionKeyL());
   1.225 +	if(!encryptionKey)
   1.226 +		User::Leave(KErrArgument);
   1.227 +	
   1.228 +	sendMessage.Add( Int2Type<SADB_EXT_KEY_ENCRYPT>(), *encryptionKey, encryptionKey->Length() * 8);
   1.229 +	if (iAuthAlg) // Authentication is optional - use it only if algorithm has been set
   1.230 +		{
   1.231 +		const HBufC8 *authenticationKey(ipsecKeyAssociation->GetAuthenticationKeyL());
   1.232 +		if (!authenticationKey)
   1.233 +			User::Leave(KErrArgument);
   1.234 +		sendMessage.Add( Int2Type<SADB_EXT_KEY_AUTH>(), *authenticationKey, authenticationKey->Length() * 8);			
   1.235 +		}
   1.236 +	sendMessage.Add( Int2Type<SADB_EXT_ADDRESS_SRC>(), iSourceAddr);
   1.237 +	sendMessage.Add( Int2Type<SADB_EXT_ADDRESS_DST>(), iDestinationAddr);
   1.238 +	
   1.239 +	TRAPD(err, AddAssociationL(sendMessage, bigEndianSpi));
   1.240 +	// If something went wrong, try to remove the SA, to keep the SADB in consistent state.
   1.241 +	// We only do our best effort, since the error might be global to the device, or it may have occured before we've added the SA
   1.242 +	if (err != KErrNone)
   1.243 +		{
   1.244 +		TRAP_IGNORE(RemoveSaL(bigEndianSpi));
   1.245 +		TInt submittedSpiCount = iSubmittedSpiList.Count();
   1.246 +		if (submittedSpiCount > 0 && iSubmittedSpiList[submittedSpiCount - 1] == bigEndianSpi)
   1.247 +			iSubmittedSpiList.Remove(submittedSpiCount - 1);
   1.248 +				
   1.249 +		User::Leave(err);
   1.250 +		}
   1.251 +	DEBUG_PRINTF2(_L("IPSec key stream sink - processed new association with SPI %d."), ipsecKeyAssociation->GetSpiL());
   1.252 +	}
   1.253 +	
   1.254 +void CIpSecKeyStreamSink::RemoveSaL(TUint32 aSpi)
   1.255 +	{	
   1.256 +	TPfkeySendMsg sendMessage(SADB_DELETE, SADB_SATYPE_ESP, ++iSequenceNumber, RProcess().Id());	
   1.257 +	sendMessage.Add( Int2Type<SADB_EXT_SA>(), aSpi, iAuthAlg, iEncAlg); 
   1.258 +	sendMessage.Add( Int2Type<SADB_EXT_ADDRESS_DST>(), iDestinationAddr);
   1.259 +	SynchronousSendAndVerifyMessageL(sendMessage, SADB_DELETE, aSpi);
   1.260 +	}
   1.261 +
   1.262 +void CIpSecKeyStreamSink::VerifyAssociationsNotSentL() const
   1.263 +	{
   1.264 +	if (iSubmittedSpiList.Count())
   1.265 +		{
   1.266 +		User::Leave(KErrNotSupported); // It is forbidden to change the algorithm once associations have been processed
   1.267 +		}	
   1.268 +	}
   1.269 +
   1.270 +const TUint32 SADB_AALG_AESCBC = 12; // temporarily here until IPSec supports AES constants
   1.271 +
   1.272 +void CIpSecKeyStreamSink::SetEncryptionAlgorithmL(const TEncryptionAlgorithm& aEncryptionAlgorithm)
   1.273 +	{
   1.274 +	VerifyAssociationsNotSentL();
   1.275 +	switch (aEncryptionAlgorithm)
   1.276 +		{
   1.277 +		case EAES_128_CBC: iEncAlg = SADB_AALG_AESCBC; break;
   1.278 +		case ENoEncryption: 
   1.279 +		case EAES_128_CTR:		
   1.280 +		default: User::Leave(KErrNotSupported);		
   1.281 +		};
   1.282 +	}
   1.283 +
   1.284 +void CIpSecKeyStreamSink::SetAuthenticationAlgorithmL(const TAuthenticationAlgorithm& aAuthenticationAlgorithm)
   1.285 +	{
   1.286 +	VerifyAssociationsNotSentL();
   1.287 +	switch (aAuthenticationAlgorithm)
   1.288 +		{
   1.289 +		case EHMAC_SHA1: iAuthAlg = SADB_AALG_SHA1HMAC; break;
   1.290 +		case ENoAuthentication:	iAuthAlg = 0; break;
   1.291 +		default: User::Leave(KErrNotSupported);
   1.292 +		};
   1.293 +	}
   1.294 +
   1.295 +const TUint KInet6AddrMaxBufferSize = 40;
   1.296 +
   1.297 +void CIpSecKeyStreamSink::SetPolicyL()
   1.298 +	{
   1.299 +	DEBUG_PRINTF(_L("IPSec key stream sink - setting policy."));
   1.300 +	ASSERT(!iPolicySet);
   1.301 +	
   1.302 +	TBuf<KInet6AddrMaxBufferSize> destAddrStr;
   1.303 +	iDestinationAddr.Output(destAddrStr);
   1.304 +	
   1.305 +	// Convert 16-bit to 8-bit. For some strange reason, the string for IP address is returned in 16-bit.
   1.306 +	TBuf8<KInet6AddrMaxBufferSize> destAddrStr8bit;
   1.307 +	destAddrStr8bit.Copy(destAddrStr);
   1.308 +	
   1.309 +	HBufC8 *policyData = HBufC8::NewLC( KPolicyFormat().Length() + 256); // Allow size for port and IP spec.
   1.310 +	TPtr8 policyDataPtr(policyData->Des());
   1.311 +	policyDataPtr.AppendFormat(KPolicyFormat, iEncAlg, iAuthAlg, &destAddrStr8bit, iDestinationAddr.Port(), 
   1.312 +							   iSourceAddr.Port());	
   1.313 +
   1.314 +	// Load the server-side policy to protect all packets to a specific port
   1.315 +	TRequestStatus status;
   1.316 +	iPolicyServer.LoadPolicy( *policyData, iPolicyHandle, status );
   1.317 +	User::WaitForRequest(status);
   1.318 +	User::LeaveIfError(status.Int());
   1.319 +	
   1.320 +	iPolicyServer.ActivatePolicy( iPolicyHandle(), status );
   1.321 +	User::WaitForRequest(status);
   1.322 +	User::LeaveIfError(status.Int());	
   1.323 +	
   1.324 +	CleanupStack::PopAndDestroy(policyData);	
   1.325 +	iPolicySet = ETrue;	
   1.326 +	DEBUG_PRINTF(_L("IPSec key stream sink - policy set."));
   1.327 +	}
   1.328 +
   1.329 +static void ReadIpAddrFromStreamL(RReadStream& aStream, TInetAddr& addr)
   1.330 +	{
   1.331 +	TBuf<KInet6AddrMaxBufferSize> addrStr;
   1.332 +	TUint8 addrStrLen = aStream.ReadUint8L();
   1.333 +	aStream.ReadL(addrStr, addrStrLen);
   1.334 +	User::LeaveIfError(addr.Input(addrStr));
   1.335 +	TInt port = aStream.ReadInt32L();
   1.336 +	addr.SetPort(port);
   1.337 +	}
   1.338 +
   1.339 +static void WriteIpAddrToStreamL(RWriteStream& aStream, const TInetAddr& addr)
   1.340 +	{
   1.341 +	TBuf<KInet6AddrMaxBufferSize> addrStr;
   1.342 +	addr.Output(addrStr);
   1.343 +	aStream.WriteUint8L(addrStr.Length());	
   1.344 +	aStream.WriteL(addrStr);
   1.345 +	aStream.WriteInt32L(addr.Port());	
   1.346 +	}
   1.347 +
   1.348 +void CIpSecKeyStreamSink::DoExternalizeL(RWriteStream& aStream) const
   1.349 +	{
   1.350 +	aStream.WriteUint16L(CKeyStreamSink::EIpSecSinkId);
   1.351 +	WriteIpAddrToStreamL(aStream, iDestinationAddr);
   1.352 +	WriteIpAddrToStreamL(aStream, iSourceAddr);
   1.353 +	aStream.WriteUint16L(iEncAlg);
   1.354 +	aStream.WriteUint16L(iAuthAlg);
   1.355 +	}
   1.356 +	
   1.357 +CIpSecKeyStreamSink* CIpSecKeyStreamSink::NewLC(RReadStream& aReadStream)
   1.358 +	{
   1.359 +	TInetAddr destAddr;
   1.360 +	ReadIpAddrFromStreamL(aReadStream, destAddr);
   1.361 +	TInetAddr srcAddr;
   1.362 +	ReadIpAddrFromStreamL(aReadStream, srcAddr);	
   1.363 +	CIpSecKeyStreamSink* self = new (ELeave) CIpSecKeyStreamSink(srcAddr, destAddr);
   1.364 +	CleanupStack::PushL(self);
   1.365 +	self->ConstructL();
   1.366 +	self->iEncAlg = aReadStream.ReadUint16L();
   1.367 +	self->iAuthAlg = aReadStream.ReadUint16L();
   1.368 +	return self;
   1.369 +	}