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