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".
8 // Initial Contributors:
9 // Nokia Corporation - initial contribution.
16 #include "ipseckeystreamsink.h"
17 #include <caf/streaming/keyassociation.h>
18 #include <networking/pfkeyv2.h>
22 using namespace StreamAccess;
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
27 "SECURITY_FILE_VERSION: 3\r\n[INFO]\r\n\
28 Test CAF IpSec Integration Policy\r\n\
35 local_port_specific\r\n\
36 remote_port_specific\r\n\
38 inbound local %S 255.255.255.255 local_port %d remote_port %d = { caf_sas() }\r\n\
42 // The number is taken as the maximal recommendation from OMA DRM BCAST standard (section 9.1, IPSec management)
43 const TUint KDefaultMaxSpiNumber = 3;
45 CIpSecKeyStreamSink* CIpSecKeyStreamSink::NewLC(const TInetAddr& aSrcAddr, const TInetAddr& aDstAddr)
47 CIpSecKeyStreamSink* self = new (ELeave) CIpSecKeyStreamSink(aSrcAddr, aDstAddr);
48 CleanupStack::PushL(self);
53 CIpSecKeyStreamSink::CIpSecKeyStreamSink(const TInetAddr& aSrcAddr, const TInetAddr& aDstAddr) :
54 iSourceAddr(aSrcAddr),
55 iDestinationAddr(aDstAddr),
57 iMaxSpiNumber(KDefaultMaxSpiNumber)
61 CIpSecKeyStreamSink::~CIpSecKeyStreamSink()
64 TInt submittedSaCount = iSubmittedSpiList.Count();
65 for (TInt i = 0; i < submittedSaCount; ++i)
67 TRAP_IGNORE(RemoveSaL(iSubmittedSpiList[i]));
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
81 iPolicyServer.Close();
82 iSubmittedSpiList.Close();
85 void CIpSecKeyStreamSink::ConstructL()
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."));
94 CKeyStreamSink* CIpSecKeyStreamSink::CloneLC() const
96 CIpSecKeyStreamSink *ret = CIpSecKeyStreamSink::NewLC(iSourceAddr, iDestinationAddr);
97 ret->iEncAlg = this->iEncAlg;
98 ret->iAuthAlg = this->iAuthAlg;
102 static TUint32 ConvertToNetworkOrder(TUint32 aNum)
104 const TInt KMaxTUint32CStringLen = 11;
105 TUint8 temp[ KMaxTUint32CStringLen ];
106 LittleEndian::Put32( temp, aNum );
107 return BigEndian::Get32( temp );
110 TBool CIpSecKeyStreamSink::CompareReceivedMessageExtensionsL(TPfkeyRecvMsg &aReceivedReply, TUint32 aSpi) const
112 TBool spiVerified(EFalse), destAddrVerified(EFalse);
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 )
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 )
125 const TInetAddr* addr = reinterpret_cast<const TInetAddr *>(ext.Ptr() + sizeof(struct sadb_address));
126 if (*addr == iDestinationAddr)
127 destAddrVerified = ETrue;
131 else if (extType == SADB_EXT_SA)
133 const sadb_sa* saExt = reinterpret_cast<const sadb_sa *>(ext.Ptr());
134 if (saExt->sadb_sa_spi == aSpi)
143 void CIpSecKeyStreamSink::SynchronousSendAndVerifyMessageL(TPfkeySendMsg& aMessage, TInt aMessageType, TUint32 aSpi)
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());
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
155 TPfkeyRecvMsg receivedReply;
156 iSADB.ReadRequest(receivedReply, status);
157 User::WaitForRequest(status);
158 User::LeaveIfError(status.Int());
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())
164 if (msgHeader.sadb_msg_seq != iSequenceNumber)
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)
172 isResponseToRequest = CompareReceivedMessageExtensionsL(receivedReply, aSpi);
175 ASSERT(0); // Unexpected state
177 if (!isResponseToRequest)
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)
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));
193 void CIpSecKeyStreamSink::AddAssociationL(TPfkeySendMsg& aMessage, TUint32 aSpi)
195 SynchronousSendAndVerifyMessageL(aMessage, SADB_ADD, aSpi);
197 // Take care to delete old SA-s, if needed
198 iSubmittedSpiList.AppendL(aSpi);
199 if (iSubmittedSpiList.Count() > iMaxSpiNumber)
201 RemoveSaL(iSubmittedSpiList[0]);
202 iSubmittedSpiList.Remove(0);
206 void CIpSecKeyStreamSink::ProcessNewKeyAssociationL(const CKeyAssociation& aKeyAssociation)
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);
215 DEBUG_PRINTF2(_L("IPSec key stream sink - processing new association with SPI %d."), ipsecKeyAssociation->GetSpiL());
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());
223 User::Leave(KErrArgument);
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
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);
233 sendMessage.Add( Int2Type<SADB_EXT_ADDRESS_SRC>(), iSourceAddr);
234 sendMessage.Add( Int2Type<SADB_EXT_ADDRESS_DST>(), iDestinationAddr);
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
241 TRAP_IGNORE(RemoveSaL(bigEndianSpi));
242 TInt submittedSpiCount = iSubmittedSpiList.Count();
243 if (submittedSpiCount > 0 && iSubmittedSpiList[submittedSpiCount - 1] == bigEndianSpi)
244 iSubmittedSpiList.Remove(submittedSpiCount - 1);
248 DEBUG_PRINTF2(_L("IPSec key stream sink - processed new association with SPI %d."), ipsecKeyAssociation->GetSpiL());
251 void CIpSecKeyStreamSink::RemoveSaL(TUint32 aSpi)
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);
259 void CIpSecKeyStreamSink::VerifyAssociationsNotSentL() const
261 if (iSubmittedSpiList.Count())
263 User::Leave(KErrNotSupported); // It is forbidden to change the algorithm once associations have been processed
267 const TUint32 SADB_AALG_AESCBC = 12; // temporarily here until IPSec supports AES constants
269 void CIpSecKeyStreamSink::SetEncryptionAlgorithmL(const TEncryptionAlgorithm& aEncryptionAlgorithm)
271 VerifyAssociationsNotSentL();
272 switch (aEncryptionAlgorithm)
274 case EAES_128_CBC: iEncAlg = SADB_AALG_AESCBC; break;
277 default: User::Leave(KErrNotSupported);
281 void CIpSecKeyStreamSink::SetAuthenticationAlgorithmL(const TAuthenticationAlgorithm& aAuthenticationAlgorithm)
283 VerifyAssociationsNotSentL();
284 switch (aAuthenticationAlgorithm)
286 case EHMAC_SHA1: iAuthAlg = SADB_AALG_SHA1HMAC; break;
287 case ENoAuthentication: iAuthAlg = 0; break;
288 default: User::Leave(KErrNotSupported);
292 const TUint KInet6AddrMaxBufferSize = 40;
294 void CIpSecKeyStreamSink::SetPolicyL()
296 DEBUG_PRINTF(_L("IPSec key stream sink - setting policy."));
299 TBuf<KInet6AddrMaxBufferSize> destAddrStr;
300 iDestinationAddr.Output(destAddrStr);
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);
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(),
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());
317 iPolicyServer.ActivatePolicy( iPolicyHandle(), status );
318 User::WaitForRequest(status);
319 User::LeaveIfError(status.Int());
321 CleanupStack::PopAndDestroy(policyData);
323 DEBUG_PRINTF(_L("IPSec key stream sink - policy set."));
326 static void ReadIpAddrFromStreamL(RReadStream& aStream, TInetAddr& addr)
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();
336 static void WriteIpAddrToStreamL(RWriteStream& aStream, const TInetAddr& addr)
338 TBuf<KInet6AddrMaxBufferSize> addrStr;
339 addr.Output(addrStr);
340 aStream.WriteUint8L(addrStr.Length());
341 aStream.WriteL(addrStr);
342 aStream.WriteInt32L(addr.Port());
345 void CIpSecKeyStreamSink::DoExternalizeL(RWriteStream& aStream) const
347 aStream.WriteUint16L(CKeyStreamSink::EIpSecSinkId);
348 WriteIpAddrToStreamL(aStream, iDestinationAddr);
349 WriteIpAddrToStreamL(aStream, iSourceAddr);
350 aStream.WriteUint16L(iEncAlg);
351 aStream.WriteUint16L(iAuthAlg);
354 CIpSecKeyStreamSink* CIpSecKeyStreamSink::NewLC(RReadStream& aReadStream)
357 ReadIpAddrFromStreamL(aReadStream, destAddr);
359 ReadIpAddrFromStreamL(aReadStream, srcAddr);
360 CIpSecKeyStreamSink* self = new (ELeave) CIpSecKeyStreamSink(srcAddr, destAddr);
361 CleanupStack::PushL(self);
363 self->iEncAlg = aReadStream.ReadUint16L();
364 self->iAuthAlg = aReadStream.ReadUint16L();