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 + }