os/security/contentmgmt/cafstreamingsupport/source/ipsec/ipseckeystreamsink.cpp
author sl
Tue, 10 Jun 2014 14:32:02 +0200
changeset 1 260cb5ec6c19
permissions -rw-r--r--
Update contrib.
     1 // Copyright (c) 2007-2009 Nokia Corporation and/or its subsidiary(-ies).
     2 // All rights reserved.
     3 // This component and the accompanying materials are made available
     4 // under the terms of the License "Eclipse Public License v1.0"
     5 // which accompanies this distribution, and is available
     6 // at the URL "http://www.eclipse.org/legal/epl-v10.html".
     7 //
     8 // Initial Contributors:
     9 // Nokia Corporation - initial contribution.
    10 //
    11 // Contributors:
    12 //
    13 // Description:
    14 //
    15 
    16 #include "ipseckeystreamsink.h"
    17 #include <caf/streaming/keyassociation.h>
    18 #include <networking/pfkeyv2.h>
    19 #include <s32mem.h>
    20 #include "scaflog.h"
    21 
    22 using namespace StreamAccess;
    23 
    24 // The last two rules: (inbound = {}, outbound = {}) are catch-all rules - without them, all packets
    25 // which do not match the policy will get rejected
    26 _LIT8( KPolicyFormat,  
    27 "SECURITY_FILE_VERSION: 3\r\n[INFO]\r\n\
    28 Test CAF IpSec Integration Policy\r\n\
    29 [POLICY]\r\n\
    30 sa caf_sas = {\r\n\
    31 esp\r\n\
    32 encrypt_alg %d\r\n\
    33 auth_alg %d\r\n\
    34 src_specific\r\n\
    35 local_port_specific\r\n\
    36 remote_port_specific\r\n\
    37 }\r\n\
    38 inbound local %S 255.255.255.255 local_port %d remote_port %d = { caf_sas() }\r\n\
    39 inbound = {}\r\n\
    40 outbound = {}\r\n" );
    41 
    42 // The number is taken as the maximal recommendation from OMA DRM BCAST standard (section 9.1, IPSec management)
    43 const TUint KDefaultMaxSpiNumber = 3; 
    44 
    45 CIpSecKeyStreamSink* CIpSecKeyStreamSink::NewLC(const TInetAddr& aSrcAddr, const TInetAddr& aDstAddr)
    46 	{
    47 	CIpSecKeyStreamSink* self = new (ELeave) CIpSecKeyStreamSink(aSrcAddr, aDstAddr);
    48 	CleanupStack::PushL(self);
    49 	self->ConstructL();
    50 	return self;
    51 	}
    52 
    53 CIpSecKeyStreamSink::CIpSecKeyStreamSink(const TInetAddr& aSrcAddr, const TInetAddr& aDstAddr) :
    54 						iSourceAddr(aSrcAddr), 
    55 						iDestinationAddr(aDstAddr), 
    56 						iPolicySet(EFalse),
    57 						iMaxSpiNumber(KDefaultMaxSpiNumber)
    58 	{		
    59 	}
    60 
    61 CIpSecKeyStreamSink::~CIpSecKeyStreamSink()
    62 	{
    63 	// Remove all SA-s
    64 	TInt submittedSaCount = iSubmittedSpiList.Count();
    65 	for (TInt i = 0; i < submittedSaCount; ++i)
    66 		{
    67 		TRAP_IGNORE(RemoveSaL(iSubmittedSpiList[i]));
    68 		}
    69 	
    70 	if (iPolicySet)
    71 		{
    72 		TRequestStatus status;
    73 		iPolicyServer.UnloadPolicy( iPolicyHandle(), status );
    74 		User::WaitForRequest(status);
    75 		// Impossible to handle the error well in destructor - ignore the status code
    76 		}
    77 
    78 	iSADB.Close();
    79 	iSocketServ.Close();
    80 	
    81 	iPolicyServer.Close();		
    82 	iSubmittedSpiList.Close();
    83 	}
    84 
    85 void CIpSecKeyStreamSink::ConstructL()
    86 	{
    87 	DEBUG_PRINTF(_L("Constructing an IPSec key stream sink."));
    88 	User::LeaveIfError(iSocketServ.Connect());
    89 	User::LeaveIfError(iSADB.Open(iSocketServ));
    90 	User::LeaveIfError(iPolicyServer.Connect());	
    91 	DEBUG_PRINTF(_L("Constructed an IPSec key stream sink."));
    92 	}
    93 
    94 CKeyStreamSink* CIpSecKeyStreamSink::CloneLC() const
    95 	{
    96 	CIpSecKeyStreamSink *ret = CIpSecKeyStreamSink::NewLC(iSourceAddr, iDestinationAddr);
    97   	ret->iEncAlg = this->iEncAlg;
    98  	ret->iAuthAlg = this->iAuthAlg;
    99  	return ret;
   100 	}
   101 
   102 static TUint32 ConvertToNetworkOrder(TUint32 aNum)
   103     {
   104     const TInt KMaxTUint32CStringLen = 11;
   105     TUint8 temp[ KMaxTUint32CStringLen ];   
   106     LittleEndian::Put32( temp, aNum );
   107     return BigEndian::Get32( temp );
   108     }
   109 
   110 TBool CIpSecKeyStreamSink::CompareReceivedMessageExtensionsL(TPfkeyRecvMsg &aReceivedReply, TUint32 aSpi) const
   111 	{
   112 	TBool spiVerified(EFalse), destAddrVerified(EFalse);
   113 	
   114 	TPfkeyAnyExt ext;
   115  
   116  	// We verify that both the SPI matches and the destination address matches - this should exclude
   117  	// replies to unrelated messages
   118 	while ( !spiVerified || !destAddrVerified )
   119          {
   120          // Both extensions should be found in the reply
   121          User::LeaveIfError(aReceivedReply.NextExtension(ext));
   122          TInt extType = ext.ExtType();
   123          if ( extType == SADB_EXT_ADDRESS_DST )
   124              {
   125              const TInetAddr* addr = reinterpret_cast<const TInetAddr *>(ext.Ptr() + sizeof(struct sadb_address));
   126     		 if (*addr == iDestinationAddr)
   127     		 	destAddrVerified = ETrue;	
   128     		 else
   129     			return EFalse;
   130              }
   131          else if (extType == SADB_EXT_SA)
   132          	{
   133     		const sadb_sa* saExt = reinterpret_cast<const sadb_sa *>(ext.Ptr());
   134     		if (saExt->sadb_sa_spi == aSpi)
   135     			spiVerified = ETrue;
   136     		else
   137     			return EFalse;
   138          	}
   139          }
   140     return ETrue;
   141 	}
   142 
   143 void CIpSecKeyStreamSink::SynchronousSendAndVerifyMessageL(TPfkeySendMsg& aMessage, TInt aMessageType, TUint32 aSpi) 
   144 	{
   145 	// Wait for the message to be sent
   146 	TRequestStatus status;
   147 	iSADB.FinalizeAndSend(aMessage, status);
   148 	User::WaitForRequest(status);
   149 	User::LeaveIfError(status.Int());
   150 
   151 	// Receive a reply - since SADB sends replies to _all_ sockets, we need to filter out replies
   152 	// which do not correspond to our own request
   153 	for(;;)
   154 		{
   155 		TPfkeyRecvMsg receivedReply;
   156 		iSADB.ReadRequest(receivedReply, status);
   157 		User::WaitForRequest(status);
   158 		User::LeaveIfError(status.Int());
   159 		
   160 		// Verify that the message is a reply to the one sent by us
   161 		sadb_msg &msgHeader = receivedReply.MsgHdr();
   162 		if (msgHeader.sadb_msg_pid != RProcess().Id())
   163 			continue;
   164 		if (msgHeader.sadb_msg_seq != iSequenceNumber)
   165 			continue;
   166 		// Do additional validation by checking whether the destination address and the SPI are the same as ours
   167 		TBool isResponseToRequest(ETrue);
   168 		switch (aMessageType)
   169 			{
   170 			case SADB_ADD: 
   171 			case SADB_DELETE:
   172 				isResponseToRequest = CompareReceivedMessageExtensionsL(receivedReply, aSpi);
   173 				break;
   174 			default:
   175 				ASSERT(0); // Unexpected state
   176 			}
   177 		if (!isResponseToRequest)
   178 			continue;
   179 		// If the message types does not match, then the problem is internal in IPSec - it should not answer with a different message type
   180 		if (msgHeader.sadb_msg_type != aMessageType)
   181 			User::Leave(KErrArgument); 
   182 		if (msgHeader.sadb_msg_errno != 0)
   183 			{
   184 			// Mimic the logic in IPSec error handling (see the Update function in key_msg.cpp)		
   185 			TUint16 reservedField = (TUint16)msgHeader.sadb_msg_reserved << 8;
   186 			TUint16 errnoField = msgHeader.sadb_msg_errno;
   187 			User::Leave(-(reservedField + errnoField));
   188 			}		
   189 		break;
   190 		}			
   191 	}
   192 	
   193 void CIpSecKeyStreamSink::AddAssociationL(TPfkeySendMsg& aMessage, TUint32 aSpi) 	
   194 	{
   195 	SynchronousSendAndVerifyMessageL(aMessage, SADB_ADD, aSpi);
   196 	
   197 	// Take care to delete old SA-s, if needed
   198 	iSubmittedSpiList.AppendL(aSpi); 
   199 	if (iSubmittedSpiList.Count() > iMaxSpiNumber)
   200 		{
   201 		RemoveSaL(iSubmittedSpiList[0]);
   202 		iSubmittedSpiList.Remove(0);
   203 		}		
   204 	}
   205 	
   206 void CIpSecKeyStreamSink::ProcessNewKeyAssociationL(const CKeyAssociation& aKeyAssociation) 
   207 	{
   208 	if (!iPolicySet)
   209 		{
   210 		SetPolicyL();
   211 		}
   212 	// No official RTTI support, using static_cast - no validation that it is indeed an IPSec association
   213 	const CIpSecKeyAssociation* ipsecKeyAssociation = static_cast<const CIpSecKeyAssociation *>(&aKeyAssociation);
   214 	
   215 	DEBUG_PRINTF2(_L("IPSec key stream sink - processing new association with SPI %d."), ipsecKeyAssociation->GetSpiL());
   216 		
   217 	// We use ESP, since there is very low probability that AH will be used - it does not provide confidentiality
   218 	TPfkeySendMsg sendMessage(SADB_ADD, SADB_SATYPE_ESP, ++iSequenceNumber, RProcess().Id());
   219 	TUint32 bigEndianSpi(ConvertToNetworkOrder(ipsecKeyAssociation->GetSpiL()));
   220 	sendMessage.Add( Int2Type<SADB_EXT_SA>(), bigEndianSpi, iAuthAlg, iEncAlg); 
   221 	const HBufC8 *encryptionKey(ipsecKeyAssociation->GetEncryptionKeyL());
   222 	if(!encryptionKey)
   223 		User::Leave(KErrArgument);
   224 	
   225 	sendMessage.Add( Int2Type<SADB_EXT_KEY_ENCRYPT>(), *encryptionKey, encryptionKey->Length() * 8);
   226 	if (iAuthAlg) // Authentication is optional - use it only if algorithm has been set
   227 		{
   228 		const HBufC8 *authenticationKey(ipsecKeyAssociation->GetAuthenticationKeyL());
   229 		if (!authenticationKey)
   230 			User::Leave(KErrArgument);
   231 		sendMessage.Add( Int2Type<SADB_EXT_KEY_AUTH>(), *authenticationKey, authenticationKey->Length() * 8);			
   232 		}
   233 	sendMessage.Add( Int2Type<SADB_EXT_ADDRESS_SRC>(), iSourceAddr);
   234 	sendMessage.Add( Int2Type<SADB_EXT_ADDRESS_DST>(), iDestinationAddr);
   235 	
   236 	TRAPD(err, AddAssociationL(sendMessage, bigEndianSpi));
   237 	// If something went wrong, try to remove the SA, to keep the SADB in consistent state.
   238 	// 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
   239 	if (err != KErrNone)
   240 		{
   241 		TRAP_IGNORE(RemoveSaL(bigEndianSpi));
   242 		TInt submittedSpiCount = iSubmittedSpiList.Count();
   243 		if (submittedSpiCount > 0 && iSubmittedSpiList[submittedSpiCount - 1] == bigEndianSpi)
   244 			iSubmittedSpiList.Remove(submittedSpiCount - 1);
   245 				
   246 		User::Leave(err);
   247 		}
   248 	DEBUG_PRINTF2(_L("IPSec key stream sink - processed new association with SPI %d."), ipsecKeyAssociation->GetSpiL());
   249 	}
   250 	
   251 void CIpSecKeyStreamSink::RemoveSaL(TUint32 aSpi)
   252 	{	
   253 	TPfkeySendMsg sendMessage(SADB_DELETE, SADB_SATYPE_ESP, ++iSequenceNumber, RProcess().Id());	
   254 	sendMessage.Add( Int2Type<SADB_EXT_SA>(), aSpi, iAuthAlg, iEncAlg); 
   255 	sendMessage.Add( Int2Type<SADB_EXT_ADDRESS_DST>(), iDestinationAddr);
   256 	SynchronousSendAndVerifyMessageL(sendMessage, SADB_DELETE, aSpi);
   257 	}
   258 
   259 void CIpSecKeyStreamSink::VerifyAssociationsNotSentL() const
   260 	{
   261 	if (iSubmittedSpiList.Count())
   262 		{
   263 		User::Leave(KErrNotSupported); // It is forbidden to change the algorithm once associations have been processed
   264 		}	
   265 	}
   266 
   267 const TUint32 SADB_AALG_AESCBC = 12; // temporarily here until IPSec supports AES constants
   268 
   269 void CIpSecKeyStreamSink::SetEncryptionAlgorithmL(const TEncryptionAlgorithm& aEncryptionAlgorithm)
   270 	{
   271 	VerifyAssociationsNotSentL();
   272 	switch (aEncryptionAlgorithm)
   273 		{
   274 		case EAES_128_CBC: iEncAlg = SADB_AALG_AESCBC; break;
   275 		case ENoEncryption: 
   276 		case EAES_128_CTR:		
   277 		default: User::Leave(KErrNotSupported);		
   278 		};
   279 	}
   280 
   281 void CIpSecKeyStreamSink::SetAuthenticationAlgorithmL(const TAuthenticationAlgorithm& aAuthenticationAlgorithm)
   282 	{
   283 	VerifyAssociationsNotSentL();
   284 	switch (aAuthenticationAlgorithm)
   285 		{
   286 		case EHMAC_SHA1: iAuthAlg = SADB_AALG_SHA1HMAC; break;
   287 		case ENoAuthentication:	iAuthAlg = 0; break;
   288 		default: User::Leave(KErrNotSupported);
   289 		};
   290 	}
   291 
   292 const TUint KInet6AddrMaxBufferSize = 40;
   293 
   294 void CIpSecKeyStreamSink::SetPolicyL()
   295 	{
   296 	DEBUG_PRINTF(_L("IPSec key stream sink - setting policy."));
   297 	ASSERT(!iPolicySet);
   298 	
   299 	TBuf<KInet6AddrMaxBufferSize> destAddrStr;
   300 	iDestinationAddr.Output(destAddrStr);
   301 	
   302 	// Convert 16-bit to 8-bit. For some strange reason, the string for IP address is returned in 16-bit.
   303 	TBuf8<KInet6AddrMaxBufferSize> destAddrStr8bit;
   304 	destAddrStr8bit.Copy(destAddrStr);
   305 	
   306 	HBufC8 *policyData = HBufC8::NewLC( KPolicyFormat().Length() + 256); // Allow size for port and IP spec.
   307 	TPtr8 policyDataPtr(policyData->Des());
   308 	policyDataPtr.AppendFormat(KPolicyFormat, iEncAlg, iAuthAlg, &destAddrStr8bit, iDestinationAddr.Port(), 
   309 							   iSourceAddr.Port());	
   310 
   311 	// Load the server-side policy to protect all packets to a specific port
   312 	TRequestStatus status;
   313 	iPolicyServer.LoadPolicy( *policyData, iPolicyHandle, status );
   314 	User::WaitForRequest(status);
   315 	User::LeaveIfError(status.Int());
   316 	
   317 	iPolicyServer.ActivatePolicy( iPolicyHandle(), status );
   318 	User::WaitForRequest(status);
   319 	User::LeaveIfError(status.Int());	
   320 	
   321 	CleanupStack::PopAndDestroy(policyData);	
   322 	iPolicySet = ETrue;	
   323 	DEBUG_PRINTF(_L("IPSec key stream sink - policy set."));
   324 	}
   325 
   326 static void ReadIpAddrFromStreamL(RReadStream& aStream, TInetAddr& addr)
   327 	{
   328 	TBuf<KInet6AddrMaxBufferSize> addrStr;
   329 	TUint8 addrStrLen = aStream.ReadUint8L();
   330 	aStream.ReadL(addrStr, addrStrLen);
   331 	User::LeaveIfError(addr.Input(addrStr));
   332 	TInt port = aStream.ReadInt32L();
   333 	addr.SetPort(port);
   334 	}
   335 
   336 static void WriteIpAddrToStreamL(RWriteStream& aStream, const TInetAddr& addr)
   337 	{
   338 	TBuf<KInet6AddrMaxBufferSize> addrStr;
   339 	addr.Output(addrStr);
   340 	aStream.WriteUint8L(addrStr.Length());	
   341 	aStream.WriteL(addrStr);
   342 	aStream.WriteInt32L(addr.Port());	
   343 	}
   344 
   345 void CIpSecKeyStreamSink::DoExternalizeL(RWriteStream& aStream) const
   346 	{
   347 	aStream.WriteUint16L(CKeyStreamSink::EIpSecSinkId);
   348 	WriteIpAddrToStreamL(aStream, iDestinationAddr);
   349 	WriteIpAddrToStreamL(aStream, iSourceAddr);
   350 	aStream.WriteUint16L(iEncAlg);
   351 	aStream.WriteUint16L(iAuthAlg);
   352 	}
   353 	
   354 CIpSecKeyStreamSink* CIpSecKeyStreamSink::NewLC(RReadStream& aReadStream)
   355 	{
   356 	TInetAddr destAddr;
   357 	ReadIpAddrFromStreamL(aReadStream, destAddr);
   358 	TInetAddr srcAddr;
   359 	ReadIpAddrFromStreamL(aReadStream, srcAddr);	
   360 	CIpSecKeyStreamSink* self = new (ELeave) CIpSecKeyStreamSink(srcAddr, destAddr);
   361 	CleanupStack::PushL(self);
   362 	self->ConstructL();
   363 	self->iEncAlg = aReadStream.ReadUint16L();
   364 	self->iAuthAlg = aReadStream.ReadUint16L();
   365 	return self;
   366 	}