// Copyright (c) 2005-2009 Nokia Corporation and/or its subsidiary(-ies).
// All rights reserved.
// This component and the accompanying materials are made available
// under the terms of "Eclipse Public License v1.0"
// which accompanies this distribution, and is available
// at the URL "http://www.eclipse.org/legal/epl-v10.html".
//
// Initial Contributors:
// Nokia Corporation - initial contribution.
//
// Contributors:
//
// Description:
// spdb.cpp - IPv6/IPv4 IPsec Security Policy Database (SPD)
// Implementation of the IPsec Security Policy Database (SPD)
//
/**
@file spdb.cpp
*/
#include "epdb.h"
#include "spdb.h"
#include <networking/pfkeyv2.h>
#include "sa_spec.h"
#include <networking/ipsecerr.h>
#ifdef SYMBIAN_IPSEC_VOIP_SUPPORT
#include <e32std.h>
#endif // SYMBIAN_IPSEC_VOIP_SUPPORT
#ifndef OLD_SELECTOR_ORDERING
/** @deprecated
* The old syntax allowed total mixing of "filter" and "selector"
* keywords. The new syntax requires the filter keywords (outbound,
* inbound, merge, final, etc.) to precede any real selectors.
* For backward compatibility, allow old deprecated ordering, if
* OLD_SELECTOR_ORDERING is defined.
*/
#define OLD_SELECTOR_ORDERING 1
#endif
/**
* Static keyword macro.
*
* Store all keywords as "plain ascii" (narrow, not UNICODE)
*/
#define KEYWORD(s) {(sizeof(s)-1), (const TText8 *)s}
/**
* Keyword number macro.
*
* Generate enum name for the keyword 's'.
*/
#define KEYENUM(s) E_ ## s
#ifdef SYMBIAN_IPSEC_VOIP_SUPPORT
// CPropList
CPropList::CPropList(TInt aGranularity) :
CArrayFixFlat<CSecurityProposalSpec *>(aGranularity)
{}
CPropList* CPropList::NewL(TInt aGranularity)
{
CPropList* self = new (ELeave) CPropList(aGranularity);
self->Construct(aGranularity);
return self;
}
void CPropList::Construct(TInt /* aGranularity */)
{}
CPropList::CPropList(CPropList* aSAList) :
CArrayFixFlat<CSecurityProposalSpec *>(aSAList->Count())
{}
CPropList* CPropList::NewL(CPropList* aSAList)
{
CPropList* self = new (ELeave) CPropList(aSAList);
CleanupStack::PushL(self);
self->ConstructL(aSAList);
CleanupStack::Pop();
return self;
}
void CPropList::ConstructL(CPropList* aSAList)
{
TInt count(aSAList->Count());
}
#endif //SYMBIAN_IPSEC_VOIP_SUPPORT
class TKeyword
/**
* The keyword definition.
*/
{
public:
TInt iLength; //< Length of the keyword
const TText8 *iWord; //< The keyword
};
static TInt Lookup(const TKeyword *const aList, const TInt aLength, const TDesC &aToken)
/**
* Lookup a keyword from list.
*
* @param aList The list of keyword
* @param aLength The number of keywords in the list
* @param aToken The keyword to be looked for
*
* @return
* The index of the matched keyword, or aLength, if none matches.
*/
{
const TInt token_length = aToken.Length();
for (TInt i = 0; i < aLength; i++)
{
const TKeyword &key = aList[i];
if (key.iLength == token_length)
{
// Use own matching loop (instead of Compare), because keywords
// are stored as 8-bit strings (to make them compact).
for (TInt k = token_length;;)
{
if (--k < 0)
return i; // Keyword matched!
if (key.iWord[k] != aToken[k])
break; // No Match!
}
}
}
return aLength;
}
typedef enum
/**
* Tokens of the IPsec policy syntax.
*/
{
token_string, //< Any string of non-white space and non-token characters.
token_question, //< Question mark: '?'
token_equal, //< Equal sign: '='
token_comma, //< Comma: ','
token_brace_left, //< Left brace: '{'
token_brace_right, //< Right brace: '}'
token_par_left, //< Left parens: '('
token_par_right, //< Right parens: ')'
token_eof //< End of policy string.
} token_type;
class TParser : public TLex
/**
* Parser of the policy definition.
*/
{
public:
TParser(CSecurityPolicy *aSP, const TDesC &aPolicy, REndPoints &aEp);
void ParseL(TUint aStartOffset);
private:
void ParseEndPointL();
void SetAddressOrEndPointL(RIpAddress &aAddr, TInt aMask, TInt aError);
void ParseAddressL(RIpAddress &aAddr, TInt aMask, TInt aError);
void ParseAddressAndMaskL(RIpAddress &aAddr, RIpAddress& aMask);
void ParseSecurityBundleL(RPolicyActions &aBundle, CTransportSelector *aTS);
#ifdef SYMBIAN_IPSEC_VOIP_SUPPORT
void ParseAssociationParametersL(CPolicySpec *aSpec);
#else
void ParseAssociationParametersL(TSecurityAssocSpec &aSpec);
#endif //SYMBIAN_IPSEC_VOIP_SUPPORT
void ParseSelectorL(CPolicySelector *&aPs);
token_type TransportSelectorL(CTransportSelector *& aTS);
void ParseAssociationL();
TAlgorithmMap *ParseAlgorithmReferenceL(TInt anInsert);
TInt ParseAlgorithmMappingL(TAlgorithmClass aClass);
token_type NextToken();
void SkipSpaceAndMark();
#ifdef SYMBIAN_IPSEC_VOIP_SUPPORT
token_type CheckProposalCloseAndMoreProposals(TInt &aPropBraces);
CSecurityProposalSpec* CreateProposalL(CPropList& aPropList);
#endif //SYMBIAN_IPSEC_VOIP_SUPPORT
TPtrC iToken; //< The current token.
CSecurityPolicy *iSp; //< The result of the parsing operation, The new policy
REndPoints &iEp; //< The End Point collection to use for the named endpoints.
};
#ifdef SYMBIAN_IPSEC_VOIP_SUPPORT
CSecurityProposalSpec* TParser::CreateProposalL(CPropList& aPropList)
{
CSecurityProposalSpec* prop = new(ELeave) CSecurityProposalSpec;
prop->iType = SADB_SATYPE_UNSPEC;
aPropList.AppendL(prop);
return prop;
}
token_type TParser::CheckProposalCloseAndMoreProposals(TInt &aPropBraces)
{
token_type val;
val=NextToken();
if (val == token_brace_right )
{
if (--aPropBraces != -1)
return NextToken();
}
return val;
}
#endif // SYMBIAN_IPSEC_VOIP_SUPPORT
void TParser::SkipSpaceAndMark()
/**
* Skip white space and mark.
*
* Skip white space and comments. The '#' character
* starts a comment that continues to the end of the line.
* Syntactically, a comment is white space (and terminates
* token string.
*
* Set the mark on first character that is not white space.
*/
{
TInt comment = 0;
while (!Eos())
{
const TChar ch = Get();
if (ch == '\n')
comment = 0;
else if (comment || ch == '#')
comment = 1;
else if (!ch.IsSpace())
{
UnGet();
break;
}
}
Mark();
}
token_type TParser::NextToken()
/**
* Parse over the next token.
*
* Skip over white space and return the next token. The
* parsing point is after the returned token. Set the
* iToken to reference the returned token as a string.
*
* @return The token type
*/
{
token_type val;
SkipSpaceAndMark();
if (Eos())
val = token_eof;
else
{
TChar ch = Get();
if (ch == '{')
val = token_brace_left;
else if (ch == '}')
val = token_brace_right;
else if (ch == '(')
val = token_par_left;
else if (ch == ')')
val = token_par_right;
else if (ch == '=')
val = token_equal;
else if (ch == ',')
val = token_comma;
else if (ch == '?')
val = token_question;
else
{
val = token_string;
while (!Eos())
{
ch = Peek();
if (ch == '{' || ch == '}' ||
ch == '(' || ch == ')' ||
ch == '=' || ch == '#' ||
ch == '?' || ch.IsSpace())
break;
Inc();
}
}
}
iToken.Set(MarkedToken());
SkipSpaceAndMark();
return val;
}
TAlgorithmMap *TParser::ParseAlgorithmReferenceL(TInt aInsert)
/**
* Parse algorithm reference
* @code [ library '.' ] algorithm
* @endcode
*
* @param aInsert Create TAlgorithMap, if not found
* @return The algorithm map.
*/
{
_LIT(K_null, "null");
if (NextToken() == token_string)
{
TInt dot = iToken.Locate('.');
const TPtrC lib = dot < 0 ? (TPtrC&)KNullDesC() : iToken.Left(dot);
TPtrC alg = dot < 0 ? iToken : iToken.Right(iToken.Length()-dot-1);
if (alg.Compare(K_null) == 0)
alg.Set(0,0); // 'null' is a special algorithm name!
TAlgorithmMap *map = iSp->FindAlg(lib, alg);
if (map == NULL && aInsert)
map = iSp->NewAlgL(lib, alg);
return map;
}
// Ás this is syntax error, should probably report something..
return NULL;
}
TInt TParser::ParseAlgorithmMappingL(TAlgorithmClass aClass)
/**
* Parse algorithm mapping
* @code ParseAlgorithmReferenceL '(' id ',' bits [',' dummy]* ')'
* @endcode
*
* The library reference is followed by a list of numbers in
* parentheses. The kernel side only needs one or two (for digest)
* and rest are ignored (the other numbers may be used by the
* key management or configuration tools).
*
* @param aClass Algorithm type (cipher or digest).
* @return KErrNone if no errors.
*/
{
TAlgorithmMap *map = ParseAlgorithmReferenceL(1);
if (map && NextToken() == token_par_left)
{
TInt i, n;
map->iClass = aClass;
for (i = 0;;++i)
{
const TInt error = Val(n);
if (error != KErrNone)
return error;
//
// Only the first two integers are used by the
// kernel IPSEC, pass other numbers silently.
//
if (i == 0)
map->iId = n;
else if (i == 1)
map->iBits = n;
#ifdef SYMBIAN_IPSEC_VOIP_SUPPORT
if (map->iId == SADB_AALG_AES_XCBC_MAC)
{
map->iClass = EAlgorithmClass_Mac;
}
#endif // SYMBIAN_IPSEC_VOIP_SUPPORT
const TInt tok = NextToken();
if (tok == token_par_right)
break;
else if (tok != token_comma)
return KErrGeneral;
}
//
// At least one number (id) must be present
//
if (i == 0)
return KErrGeneral;
}
return KErrNone;
}
void TParser::ParseEndPointL()
/**
* Parse Named End Point
* @code name '=' '{' [ '?' ] ip-address '}'
* @endcode
*/
{
if (NextToken() != token_string)
User::Leave(EIpsec_PolicySpecName); // Endpoint name expeted!
// Allocate a slot for the new association specification
const TPtrC name(iToken);
if (NextToken() != token_equal ||
NextToken() != token_brace_left)
User::Leave(EIpsec_PolicySyntaxError);
TInt opt = 0;
token_type val = NextToken();
if (val == token_question)
{
opt = 1;
val = NextToken();
}
TIpAddress addr;
if (val == token_brace_right)
addr.SetAddressNone();
else
{
if (val != token_string || addr.SetAddress(iToken) != KErrNone)
User::Leave(EIpsec_PolicyIpAddressExpected);
if (NextToken() != token_brace_right)
User::Leave(EIpsec_PolicyCloseBraceExpected);
}
// Add RIpAddress handle to iEndPoints to keep the EP around
// as long as this policy is loaded (if EP is attached to some
// other objects, like SA, its life extends beyond the policy
// life).
const TInt index = iSp->iEndPoints.Count();
User::LeaveIfError(iSp->iEndPoints.Append(RIpAddress()));
User::LeaveIfError(iSp->iEndPoints[index].Open(iEp, name, addr, opt));
}
#ifdef SYMBIAN_IPSEC_VOIP_SUPPORT
void TParser::ParseAssociationParametersL(CPolicySpec *aPolicySpec)
#else
void TParser::ParseAssociationParametersL(TSecurityAssocSpec &aSpec)
#endif // SYMBIAN_IPSEC_VOIP_SUPPORT
/**
* Parse a sequence of association parameters
* @code [ 'ah' | 'esp' | 'encrypt_alg' number | 'auth_alg' number |
* 'identity' string | 'identity_remote' string | 'identity_local' string |
* 'pfs' |
* 'protocol_specific' | 'local_port_specific' | 'remote_port_specific' |
* 'proxy_specific' | 'local_specific' | 'remote_specific' |
* 'src_specific' | 'replay_win_len' number |
* 'min_auth_bits' number | 'max_auth_bits' number |
* 'min_encrypt_bits' number | 'max_encrypt_bits' number |
* 'hard_lifetime_allocations' number | 'hard_lifetime_bytes' number |
* 'hard_lifetime_addtime' number | 'hard_lifetime_usetime' number |
* 'soft_lifetime_allocations' number | 'soft_lifetime_bytes' number |
* 'soft_lifetime_addtime' number | 'soft_lifetime_usetime' number | 'proposal' ]+ '}'
* @endcode
*
* Some parameters are just plain keyword, some have additional numeric
* or string arguments. The parameter keywords can be in any order, but
* each keyword can only appear once. At least one 'ah' or 'esp' must be
* present.
*
* @param aSpec The specificantion to fill.
*/
{
static const TKeyword list[] =
{
KEYWORD("ah"),
KEYWORD("esp"),
KEYWORD("encrypt_alg"),
KEYWORD("auth_alg"),
KEYWORD("identity"),
KEYWORD("identity_remote"),
KEYWORD("identity_local"),
KEYWORD("pfs"),
KEYWORD("protocol_specific"),
KEYWORD("local_port_specific"),
KEYWORD("remote_port_specific"),
KEYWORD("proxy_specific"), // -- kept for backward compatibility
KEYWORD("local_specific"),
KEYWORD("remote_specific"),
KEYWORD("src_specific"),
KEYWORD("replay_win_len"),
KEYWORD("min_auth_bits"),
KEYWORD("max_auth_bits"),
KEYWORD("min_encrypt_bits"),
KEYWORD("max_encrypt_bits"),
KEYWORD("hard_lifetime_allocations"),
KEYWORD("hard_lifetime_bytes"),
KEYWORD("hard_lifetime_addtime"),
KEYWORD("hard_lifetime_usetime"),
KEYWORD("soft_lifetime_allocations"),
KEYWORD("soft_lifetime_bytes"),
KEYWORD("soft_lifetime_addtime"),
KEYWORD("soft_lifetime_usetime")
#ifdef SYMBIAN_IPSEC_VOIP_SUPPORT
,KEYWORD("proposal")
#endif //SYMBIAN_IPSEC_VOIP_SUPPORT
};
enum // Order must be exactly the same as above
{
KEYENUM(ah),
KEYENUM(esp),
KEYENUM(encrypt_alg),
KEYENUM(auth_alg),
KEYENUM(identity),
KEYENUM(identity_remote),
KEYENUM(identity_local),
KEYENUM(pfs),
KEYENUM(protocol_specific),
KEYENUM(local_port_specific),
KEYENUM(remote_port_specific),
KEYENUM(proxy_specific),
KEYENUM(local_specific),
KEYENUM(remote_specific),
KEYENUM(src_specific),
KEYENUM(replay_win_len),
KEYENUM(min_auth_bits),
KEYENUM(max_auth_bits),
KEYENUM(min_encrypt_bits),
KEYENUM(max_encrypt_bits),
KEYENUM(hard_lifetime_allocations),
KEYENUM(hard_lifetime_bytes),
KEYENUM(hard_lifetime_addtime),
KEYENUM(hard_lifetime_usetime),
KEYENUM(soft_lifetime_allocations),
KEYENUM(soft_lifetime_bytes),
KEYENUM(soft_lifetime_addtime),
KEYENUM(soft_lifetime_usetime),
#ifdef SYMBIAN_IPSEC_VOIP_SUPPORT
KEYENUM(proposal),
#endif //SYMBIAN_IPSEC_VOIP_SUPPORT
KEYENUM(max_parameters)
};
TInt sa_type_defined = 0;
TInt error = KErrNone;
token_type val;
#ifdef SYMBIAN_IPSEC_VOIP_SUPPORT
val = NextToken();
TInt propasalsDefined=0;
CSecurityProposalSpec* prop = NULL;
while (val == token_string)
#else
while ((val = NextToken()) == token_string)
#endif //SYMBIAN_IPSEC_VOIP_SUPPORT
{
switch (Lookup(list, E_max_parameters, iToken))
{
#ifdef SYMBIAN_IPSEC_VOIP_SUPPORT
case E_proposal:
propasalsDefined++;
prop = CreateProposalL (*aPolicySpec->iPropList);
if ((val=NextToken()) != token_brace_left)
{
User::Leave(EIpsec_PolicySyntaxError);
}
break;
#endif //SYMBIAN_IPSEC_VOIP_SUPPORT
case E_ah:
sa_type_defined++;
#ifdef SYMBIAN_IPSEC_VOIP_SUPPORT
aPolicySpec->iSpec.iType = SADB_SATYPE_AH;
if (prop) prop->iType = SADB_SATYPE_AH;
#else
aSpec.iType = SADB_SATYPE_AH;
#endif //SYMBIAN_IPSEC_VOIP_SUPPORT
break;
case E_esp:
sa_type_defined++;
#ifdef SYMBIAN_IPSEC_VOIP_SUPPORT
aPolicySpec->iSpec.iType = SADB_SATYPE_ESP;
if (prop) prop->iType = SADB_SATYPE_ESP;
#else
aSpec.iType = SADB_SATYPE_ESP;
#endif //SYMBIAN_IPSEC_VOIP_SUPPORT
break;
case E_encrypt_alg:
#ifdef SYMBIAN_IPSEC_VOIP_SUPPORT
if (prop && (error = Val(prop->iEalg, EDecimal)) == KErrNone &&
iSp->FindAlg(EAlgorithmClass_Cipher, prop->iEalg) == NULL)
User::Leave(EIpsec_PolicyUnknownEncrypt);
if(prop)
aPolicySpec->iSpec.iEalg = prop->iEalg;
#else
if ((error = Val(aSpec.iEalg, EDecimal)) == KErrNone &&
iSp->FindAlg(EAlgorithmClass_Cipher, aSpec.iEalg) == NULL)
User::Leave(EIpsec_PolicyUnknownEncrypt);
#endif //SYMBIAN_IPSEC_VOIP_SUPPORT
break;
case E_auth_alg:
#ifdef SYMBIAN_IPSEC_VOIP_SUPPORT
if (prop && (error = Val(prop->iAalg, EDecimal)) == KErrNone &&
(iSp->FindAlg(EAlgorithmClass_Digest, prop->iAalg) == NULL)
&& (iSp->FindAlg(EAlgorithmClass_Mac, prop->iAalg) == NULL) )
User::Leave(EIpsec_PolicyUnknownAuth);
if(prop)
aPolicySpec->iSpec.iAalg = prop->iAalg ;
#else
if ((error = Val(aSpec.iAalg, EDecimal)) == KErrNone &&
iSp->FindAlg(EAlgorithmClass_Digest, aSpec.iAalg) == NULL)
User::Leave(EIpsec_PolicyUnknownAuth);
#endif //SYMBIAN_IPSEC_VOIP_SUPPORT
break;
case E_identity: // temp. backwards compatibility
case E_identity_remote:
#ifdef SYMBIAN_IPSEC_VOIP_SUPPORT
if (aPolicySpec->iSpec.iIdentityRemote)
User::Leave(EIpsec_PolicyIdentityDefined);
(void)NextToken(); // Advance iToken to the identity value
aPolicySpec->iSpec.iIdentityRemote = CIdentity::NewL(iToken);
#else
if (aSpec.iIdentityRemote)
User::Leave(EIpsec_PolicyIdentityDefined);
(void)NextToken(); // Advance iToken to the identity value
aSpec.iIdentityRemote = CIdentity::NewL(iToken);
#endif //SYMBIAN_IPSEC_VOIP_SUPPORT
break;
case E_identity_local:
#ifdef SYMBIAN_IPSEC_VOIP_SUPPORT
if (aPolicySpec->iSpec.iIdentityLocal)
User::Leave(EIpsec_PolicyIdentityDefined);
(void)NextToken(); // Advance iToken to the identity value
aPolicySpec->iSpec.iIdentityLocal = CIdentity::NewL(iToken);
#else
if (aSpec.iIdentityLocal)
User::Leave(EIpsec_PolicyIdentityDefined);
(void)NextToken(); // Advance iToken to the identity value
aSpec.iIdentityLocal = CIdentity::NewL(iToken);
#endif //SYMBIAN_IPSEC_VOIP_SUPPORT
break;
case E_pfs:
#ifdef SYMBIAN_IPSEC_VOIP_SUPPORT
aPolicySpec->iSpec.iPfs = 1;
#else
aSpec.iPfs = 1;
#endif //SYMBIAN_IPSEC_VOIP_SUPPORT
break;
case E_protocol_specific:
#ifdef SYMBIAN_IPSEC_VOIP_SUPPORT
aPolicySpec->iSpec.iMatchProtocol = 1;
#else
aSpec.iMatchProtocol = 1;
#endif //SYMBIAN_IPSEC_VOIP_SUPPORT
break;
case E_local_port_specific:
#ifdef SYMBIAN_IPSEC_VOIP_SUPPORT
aPolicySpec->iSpec.iMatchLocalPort = 1;
#else
aSpec.iMatchLocalPort = 1;
#endif //SYMBIAN_IPSEC_VOIP_SUPPORT
break;
case E_remote_port_specific:
#ifdef SYMBIAN_IPSEC_VOIP_SUPPORT
aPolicySpec->iSpec.iMatchRemotePort = 1;
#else
aSpec.iMatchRemotePort = 1;
#endif //SYMBIAN_IPSEC_VOIP_SUPPORT
break;
case E_proxy_specific:
// ** should be deprecated -- msa
#ifdef SYMBIAN_IPSEC_VOIP_SUPPORT
aPolicySpec->iSpec.iMatchProxy = 1;
#else
aSpec.iMatchProxy = 1;
#endif //SYMBIAN_IPSEC_VOIP_SUPPORT
break;
case E_local_specific:
#ifdef SYMBIAN_IPSEC_VOIP_SUPPORT
aPolicySpec->iSpec.iMatchLocal = 1;
#else
aSpec.iMatchLocal = 1;
#endif //SYMBIAN_IPSEC_VOIP_SUPPORT
break;
case E_remote_specific:
#ifdef SYMBIAN_IPSEC_VOIP_SUPPORT
aPolicySpec->iSpec.iMatchRemote = 1;
#else
aSpec.iMatchRemote = 1;
#endif //SYMBIAN_IPSEC_VOIP_SUPPORT
break;
case E_src_specific:
#ifdef SYMBIAN_IPSEC_VOIP_SUPPORT
aPolicySpec->iSpec.iMatchSrc = 1;
#else
aSpec.iMatchSrc = 1;
#endif //SYMBIAN_IPSEC_VOIP_SUPPORT
break;
case E_replay_win_len:
#ifdef SYMBIAN_IPSEC_VOIP_SUPPORT
error = Val(aPolicySpec->iSpec.iReplayWindowLength, EDecimal);
#else
error = Val(aSpec.iReplayWindowLength, EDecimal);
#endif //SYMBIAN_IPSEC_VOIP_SUPPORT
break;
case E_min_auth_bits:
#ifdef SYMBIAN_IPSEC_VOIP_SUPPORT
error = Val(aPolicySpec->iSpec.iMinAuthBits, EDecimal);
#else
error = Val(aSpec.iMinAuthBits, EDecimal);
#endif //SYMBIAN_IPSEC_VOIP_SUPPORT
break;
case E_max_auth_bits:
#ifdef SYMBIAN_IPSEC_VOIP_SUPPORT
if (prop)
{
error = Val(prop->iMaxAuthBits, EDecimal);
}
#else
error = Val(aSpec.iMaxAuthBits, EDecimal);
#endif //SYMBIAN_IPSEC_VOIP_SUPPORT
break;
case E_min_encrypt_bits:
#ifdef SYMBIAN_IPSEC_VOIP_SUPPORT
if (prop)
{
error = Val(prop->iMinEncryptBits, EDecimal);
}
#else
error = Val(aSpec.iMinEncryptBits, EDecimal);
#endif //SYMBIAN_IPSEC_VOIP_SUPPORT
break;
case E_max_encrypt_bits:
#ifdef SYMBIAN_IPSEC_VOIP_SUPPORT
if (prop)
{
error = Val(prop->iMaxEncryptBits, EDecimal);
}
#else
error = Val(aSpec.iMaxEncryptBits, EDecimal);
#endif //SYMBIAN_IPSEC_VOIP_SUPPORT
break;
case E_hard_lifetime_allocations:
#ifdef SYMBIAN_IPSEC_VOIP_SUPPORT
if (prop)
{
error = Val(prop->iHard.sadb_lifetime_allocations , EDecimal);
}
#else
error = Val(aSpec.iHard.sadb_lifetime_allocations, EDecimal);
#endif //SYMBIAN_IPSEC_VOIP_SUPPORT
break;
case E_hard_lifetime_bytes:
#ifdef SYMBIAN_IPSEC_VOIP_SUPPORT
if (prop)
{
error = Val(prop->iHard.sadb_lifetime_bytes , EDecimal);
}
#else
error = Val(aSpec.iHard.sadb_lifetime_bytes, EDecimal);
#endif //SYMBIAN_IPSEC_VOIP_SUPPORT
break;
case E_hard_lifetime_addtime:
#ifdef SYMBIAN_IPSEC_VOIP_SUPPORT
if (prop)
{
error = Val(prop->iHard.sadb_lifetime_addtime , EDecimal);
}
#else
error = Val(aSpec.iHard.sadb_lifetime_addtime, EDecimal);
#endif //SYMBIAN_IPSEC_VOIP_SUPPORT
break;
case E_hard_lifetime_usetime:
#ifdef SYMBIAN_IPSEC_VOIP_SUPPORT
if (prop)
{
error = Val(prop->iHard.sadb_lifetime_usetime , EDecimal);
}
#else
error = Val(aSpec.iHard.sadb_lifetime_usetime, EDecimal);
#endif //SYMBIAN_IPSEC_VOIP_SUPPORT
break;
case E_soft_lifetime_allocations:
#ifdef SYMBIAN_IPSEC_VOIP_SUPPORT
if (prop)
{
error = Val(prop->iSoft.sadb_lifetime_allocations , EDecimal);
}
#else
error = Val(aSpec.iSoft.sadb_lifetime_allocations, EDecimal);
#endif //SYMBIAN_IPSEC_VOIP_SUPPORT
break;
case E_soft_lifetime_bytes:
#ifdef SYMBIAN_IPSEC_VOIP_SUPPORT
if (prop)
{
error = Val(prop->iSoft.sadb_lifetime_bytes , EDecimal);
}
#else
error = Val(aSpec.iSoft.sadb_lifetime_bytes, EDecimal);
#endif //SYMBIAN_IPSEC_VOIP_SUPPORT
break;
case E_soft_lifetime_addtime:
#ifdef SYMBIAN_IPSEC_VOIP_SUPPORT
if (prop)
{
error = Val(prop->iSoft.sadb_lifetime_addtime , EDecimal);
}
#else
error = Val(aSpec.iSoft.sadb_lifetime_addtime, EDecimal);
#endif //SYMBIAN_IPSEC_VOIP_SUPPORT
break;
case E_soft_lifetime_usetime:
#ifdef SYMBIAN_IPSEC_VOIP_SUPPORT
if (prop)
{
error = Val(prop->iSoft.sadb_lifetime_usetime , EDecimal);
}
#else
error = Val(aSpec.iSoft.sadb_lifetime_usetime, EDecimal);
#endif //SYMBIAN_IPSEC_VOIP_SUPPORT
break;
default:
User::Leave(EIpsec_PolicyUnknownSpec);
}
#ifdef SYMBIAN_IPSEC_VOIP_SUPPORT
val = CheckProposalCloseAndMoreProposals(propasalsDefined);
#endif //SYMBIAN_IPSEC_VOIP_SUPPORT
if (error != KErrNone)
User::Leave(EIpsec_PolicyNumberExpected);
}
if (val != token_brace_right)
User::Leave(EIpsec_PolicyCloseBraceExpected);
else if (sa_type_defined < 1)
User::Leave(EIpsec_PolicyNoType);
#ifdef SYMBIAN_IPSEC_VOIP_SUPPORT
else if ((aPolicySpec->iSpec.iType == SADB_SATYPE_AH) && !aPolicySpec->iSpec.iAalg)
User::Leave(EIpsec_PolicyNoAuthAlgorithm);
else if ((aPolicySpec->iSpec.iType == SADB_SATYPE_ESP) && !aPolicySpec->iSpec.iEalg)
User::Leave(EIpsec_PolicyNoEncryptAlgorithm);
#else
else if (sa_type_defined > 1)
User::Leave(EIpsec_PolicyTooManyTypes);
else if ((aSpec.iType == SADB_SATYPE_AH) && !aSpec.iAalg)
User::Leave(EIpsec_PolicyNoAuthAlgorithm);
else if ((aSpec.iType == SADB_SATYPE_ESP) && !aSpec.iEalg)
User::Leave(EIpsec_PolicyNoEncryptAlgorithm);
#endif //SYMBIAN_IPSEC_VOIP_SUPPORT
}
void TParser::ParseAssociationL()
/**
* Parse security association
* @code name '=' '{' ParseAssociationParametersL
* @endcode
*/
{
if (NextToken() != token_string)
User::Leave(EIpsec_PolicySpecName);
// Allocate a slot for the new association specification
CPolicySpec *const ps = new (ELeave) CPolicySpec();
if (iSp->iSpecs.Append(ps) != KErrNone)
{
ps->Close();
User::Leave(KErrNoMemory);
}
ps->iName = HBufC::NewMaxL(iToken.Length());
*ps->iName = iToken;
if (NextToken() != token_equal ||
NextToken() != token_brace_left)
User::Leave(EIpsec_PolicySyntaxError);
#ifdef SYMBIAN_IPSEC_VOIP_SUPPORT
ParseAssociationParametersL(ps);
#else
ParseAssociationParametersL(ps->iSpec);
#endif // SYMBIAN_IPSEC_VOIP_SUPPORT
}
void TParser::ParseSecurityBundleL(RPolicyActions &aActions, CTransportSelector *aTS)
/**
* Parse security actions
* @code [ sa-name '(' [ address ] ')' ]* '}'
* @endcode
*
* Parse a (possibly empty) list of references to security specifications. This will
* be the bundle of security actions for a selector.
*
* @retval aActions The colleted actions
* @param aTS The traffic selector.
*/
{
_LIT(K_tunnel, "tunnel");
token_type val;
TUint opt = 0;
for (;;)
{
val = NextToken();
//
// Experimental addition, allow optional bundle items
// by prefixing them with '?'...
//
if (opt == 0 && val == token_question)
{
opt = 1;
continue;
}
else if (val != token_string)
break;
// A temporary(?) special kluge: if the keyword is 'tunnel'
// assume this is a plain tunnel specification, without any
// relation to the IPSEC. if nobody defined a "tunnel" sa
// specification. (should probably disallow 'tunnel' as SA
// spec name, to avoid confusion.. )
//
CPolicySpec *spec = iSp->FindSpec(iToken);
if (spec == NULL && iToken.Compare(K_tunnel) != 0)
User::Leave(EIpsec_PolicySpecNotFound);
if (NextToken() != token_par_left)
User::Leave(EIpsec_PolicyLeftParen);
CPolicyAction *action = new (ELeave) CPolicyAction;
if (aActions.Append(action) != KErrNone)
{
action->Close();
User::Leave(KErrNoMemory);
}
if ((action->iSpec = spec) != NULL)
spec->Open();
// Record the current selector into each action (this is to make it
// easier to generate the TS list into Acquire message).
if ((action->iTS = aTS) != NULL)
aTS->Open();
action->iOptional = opt;
if ((val = NextToken()) == token_string)
{
SetAddressOrEndPointL(action->iTunnel, 0, EIpsec_PolicyInvalidIpAddress);
action->iIsTunnel = 1; // Flag a tunnel.
val = NextToken();
}
if (val != token_par_right)
User::Leave(EIpsec_PolicyRightParen);
opt = 0; // Optional only affects single item at time.
}
if (val != token_brace_right)
User::Leave(EIpsec_PolicyCloseBraceExpected);
}
void TParser::SetAddressOrEndPointL(RIpAddress &aAddr, TInt aMask, TInt aError)
/**
* Initiliaze address from current token.
*
* The token can contain direct address literal or a name of an end point.
* If a name is used, it must exist.
*
* @retval aAddr The address to initialize.
* @param aMask Non-zero, if address is used as mask.
* @param aError The error to use if end point does not exist (leave)
*/
{
TIpAddress addr;
if (addr.SetAddress(iToken, aMask) == KErrNone)
{
// plain address
User::LeaveIfError(aAddr.Open(iEp, addr));
}
else if (aAddr.Open(iEp, iToken) != KErrNone)
{
// Named endpoint not found
User::Leave(aError);
}
}
void TParser::ParseAddressL(RIpAddress &aAddr, TInt aMask, TInt aError)
/**
* Parse next token as address.
* @code address
* @endcode
*
* @retval aAddr The address to initialize
* @param aMask Non-zero, if address is used as mask.
* @param aError The error to use, if error
*/
{
if (NextToken() != token_string)
{
User::Leave(aError);
}
else
{
SetAddressOrEndPointL(aAddr, aMask, aError);
}
}
void TParser::ParseAddressAndMaskL(RIpAddress &aAddr, RIpAddress& aMask)
/**
* Parse two tokens as addres and address mask
* @code ParseAddressL ParseAddressL
* @endcode
*
* @param aAddr The address to initialize
* @param aMask The mask to initialize
*/
{
ParseAddressL(aAddr, 0, EIpsec_PolicyIpAddressExpected);
ParseAddressL(aMask, 1, EIpsec_PolicyIpMaskExpected);
}
token_type TParser::TransportSelectorL(CTransportSelector *&aTs)
/**
* Parse one transport selector.
* @code [
* 'remote' ParseAddressAndMaskL | 'local' ParseAddressAndMaskL |
* 'user_id' | 'protocol' number |
* 'local_port' number | 'remote_port' number |
* 'icmp_type' number | 'type' number | 'icmp_code' number ]*
* @endcode
*/
{
static const TKeyword list[] =
{
KEYWORD("remote"),
KEYWORD("local"),
KEYWORD("user_id"),
KEYWORD("protocol"),
KEYWORD("local_port"),
KEYWORD("remote_port"),
KEYWORD("icmp_type"),
KEYWORD("icmp_code"),
KEYWORD("type"),
};
enum // Order must be exactly the same as above
{
KEYENUM(remote),
KEYENUM(local),
KEYENUM(user_id),
KEYENUM(protocol),
KEYENUM(local_port),
KEYENUM(remote_port),
KEYENUM(icmp_type),
KEYENUM(icmp_code),
KEYENUM(type), // synonym for "icmp_type" (use for MH and similar)
KEYENUM(max_parameters)
};
token_type val;
//
// By default, a new selector will match everything (mask is all zero)
//
RPolicySelectorInfo data;
RPolicySelectorInfo mask;
data.FillZ();
mask.FillZ();
#if OLD_SELECTOR_ORDERING
// If selector already exists, continue filling it.
if (aTs != NULL)
{
data = aTs->iData;
mask = aTs->iMask;
aTs->Close();
aTs = NULL;
}
#endif
do
{
TUint8 tmp8;
switch (Lookup(list, E_max_parameters, iToken))
{
case E_remote:
ParseAddressAndMaskL(data.iRemote, mask.iRemote);
break;
case E_local:
ParseAddressAndMaskL(data.iLocal, mask.iLocal);
break;
case E_user_id:
break; // Needs to be examined, TIdentity? -- msa
case E_protocol:
mask.iProtocol = 0xFF;
User::LeaveIfError(Val(data.iProtocol, EDecimal));
break;
case E_local_port:
if (mask.iPortLocal)
User::Leave(EIpsec_PolicySyntaxError); // Already defined (or type/code)
mask.iPortLocal = 0xFFFF;
User::LeaveIfError(Val(data.iPortLocal, EDecimal));
mask.iFlags |= KTransportSelector_PORTS;
data.iFlags |= KTransportSelector_PORTS;
break;
case E_remote_port:
if (mask.iPortRemote)
User::Leave(EIpsec_PolicySyntaxError); // Already defined
mask.iPortRemote = ~0;
User::LeaveIfError(Val(data.iPortRemote, EDecimal));
mask.iFlags |= KTransportSelector_PORTS;
data.iFlags |= KTransportSelector_PORTS;
break;
case E_icmp_type:
case E_type:
if ((mask.iPortLocal | mask.iPortRemote) & 0xFF00)
User::Leave(EIpsec_PolicySyntaxError); // Already defined (or port used)
mask.iPortLocal |= 0xFF00;
User::LeaveIfError(Val(tmp8, EDecimal));
data.iPortLocal |= tmp8 << 8;
// For Type/Code remote port selector is always same as local port value
mask.iPortRemote = mask.iPortLocal;
data.iPortRemote = data.iPortLocal;
mask.iFlags |= KTransportSelector_PORTS;
data.iFlags &= ~KTransportSelector_PORTS;
break;
case E_icmp_code:
if ((mask.iPortLocal | mask.iPortRemote) & 0x00FF)
User::Leave(EIpsec_PolicySyntaxError); // Already defined (or port used)
mask.iPortLocal |= 0x00FF;
User::LeaveIfError(Val(tmp8, EDecimal));
data.iPortLocal |= tmp8;
// For Type/Code remote port selector is always same as local port value
mask.iPortRemote = mask.iPortLocal;
data.iPortRemote = data.iPortLocal;
mask.iFlags |= KTransportSelector_PORTS;
data.iFlags &= ~KTransportSelector_PORTS;
break;
default:
#if OLD_SELECTOR_ORDERING
val = token_string;
goto wrapup;
#else
User::Leave(EIpsec_PolicyUnknownSelector);
#endif
}
}
while
((val = NextToken()) == token_string);
#if OLD_SELECTOR_ORDERING
wrapup:
#endif
aTs = new (ELeave) CTransportSelector(data, mask, NULL);
return val;
}
void TParser::ParseSelectorL(CPolicySelector *&aPs)
/**
* Parse a policy selector
* @code [ 'final' | 'merge' | 'outbound' | 'inbound' | 'if' interface ]*
* TransportSelectorL '=' ( '{' ParseSecurityBundleL | 'drop' )
* @endcode
*/
{
static const TKeyword list[] =
{
KEYWORD("final"),
KEYWORD("merge"),
KEYWORD("outbound"),
KEYWORD("inbound"),
KEYWORD("if"),
};
enum // Order must be exactly the same as above
{
KEYENUM(final),
KEYENUM(merge),
KEYENUM(outbound),
KEYENUM(inbound),
KEYENUM(if),
KEYENUM(max_parameters)
};
_LIT(K_drop, "drop");
token_type val;
//
aPs = new (ELeave) CPolicySelector();
//
// ...by default the "merge" must be off to match.
//
aPs->iFilterMask = KPolicyFilter_MERGE;
do
{
#if OLD_SELECTOR_ORDERING
check_again:
#endif
switch (Lookup(list, E_max_parameters, iToken))
{
case E_final:
if (aPs->iFilterData & KPolicyFilter_FINAL)
User::Leave(EIpsec_PolicySyntaxError); // Duplicate "final" keyword.
// No need to set "mask", this is a flag only.
aPs->iFilterData |= KPolicyFilter_FINAL;
break;
case E_merge:
if ((aPs->iFilterMask & KPolicyFilter_MERGE) == 0)
User::Leave(EIpsec_PolicySyntaxError); // Duplicate "merge" keyword.
aPs->iFilterMask &= ~KPolicyFilter_MERGE; // allow this selector to be merged.
break;
case E_outbound:
if (aPs->iFilterData & KPolicyFilter_SYMMETRIC)
User::Leave(EIpsec_PolicyInboundOutbound);
aPs->iFilterData |= KPolicyFilter_OUTBOUND;
aPs->iFilterMask |= KPolicyFilter_OUTBOUND;
break;
case E_inbound:
if (aPs->iFilterData & KPolicyFilter_SYMMETRIC)
User::Leave(EIpsec_PolicyInboundOutbound);
aPs->iFilterData |= KPolicyFilter_INBOUND;
aPs->iFilterMask |= KPolicyFilter_INBOUND;
break;
case E_if:
if (aPs->iInterface || NextToken() != token_string)
User::Leave(EIpsec_PolicySyntaxError); // <-- need own error code?
aPs->iInterface = iSp->LookupInterfaceL(iToken);
break;
default:
val = TransportSelectorL(aPs->iTS);
#if OLD_SELECTOR_ORDERING
if (Lookup(list, E_max_parameters, iToken) < E_max_parameters)
goto check_again;
#endif
goto wrapup;
}
}
while
((val = NextToken()) == token_string);
wrapup:
if (val != token_equal)
User::Leave(EIpsec_PolicySyntaxError);
if (NextToken() == token_brace_left)
ParseSecurityBundleL(aPs->iActions, aPs->iTS);
else if (iToken.Compare(K_drop) == 0)
aPs->iFilterData |= KPolicyFilter_DROP;
else
User::Leave(EIpsec_PolicySyntaxError);
}
#ifdef __VC32__
#pragma warning(disable : 4097) // typedef-name used as synonym for class-name
#endif
TParser::TParser(CSecurityPolicy *aSp, const TDesC &aPolicy, REndPoints &aEp) :
TLex(aPolicy), iSp(aSp), iEp(aEp)
{
}
void TParser::ParseL(TUint aStartOffset)
/**
* Parse complete security policy
* @code (
* 'sa' ParseAssociationL |
* 'encrypt' ParseAlgorithmMappingL |
* 'auth' ParseAlgorithmMappingL |
* 'ep' ParseEndPointL )*
* @endcode
*
* @param aStartOffset Starting offset of the actual policy
* @leave error on syntax or other error.
*/
{
static const TKeyword list[] =
{
KEYWORD("sa"),
KEYWORD("encrypt"),
KEYWORD("auth"),
KEYWORD("ep"),
};
enum
{
KEYENUM(sa),
KEYENUM(encrypt),
KEYENUM(auth),
KEYENUM(ep),
KEYENUM(max_list)
};
CPolicySelector **last = &iSp->iSelectors;
Inc(aStartOffset);
while (NextToken() == token_string)
{
switch (Lookup(list, E_max_list, iToken))
{
case E_sa:
ParseAssociationL();
break;
case E_encrypt:
ParseAlgorithmMappingL(EAlgorithmClass_Cipher);
break;
case E_auth:
ParseAlgorithmMappingL(EAlgorithmClass_Digest);
break;
case E_ep:
ParseEndPointL();
break;
default:
ParseSelectorL(*last);
if (*last)
last = &(*last)->iNext;
break;
}
}
if (!Eos())
// Parsing didn't detect error, but not all parsed!
User::Leave(EIpsec_PolicySyntaxError);
}
CSecurityPolicy::CSecurityPolicy()
/**
* Constructor.
*/
{
IPSEC_OBJECT_INC;
}
TInt CSecurityPolicy::SetPolicy(CSecurityPolicy * &aPolicy, const TDesC &aNewPolicy, TUint &aOffset, REndPoints &aEp)
/**
* Construct a new binary policy and replace a the current with it.
*
* @retval aPolicy Holder of the current binary policy
* @param aNewPolicy The new policy definition (text string)
* @retval aOffset The initial start, and final point in parsing
* @param aEp The End point collection to store the named endpoints
*
* @return KErrNone on success, and some < 0 error code otherwise.
*/
{
delete aPolicy;
aPolicy = new CSecurityPolicy();
if (aPolicy == NULL)
return KErrNoMemory;
TParser parser(aPolicy, aNewPolicy, aEp);
aPolicy->iAlgorithms = new CAlgorithmList;
if (aPolicy->iAlgorithms == NULL)
return KErrNoMemory;
TRAPD(result, parser.ParseL(aOffset); );
aOffset = parser.MarkedOffset();
return result;
}
CSecurityPolicy::~CSecurityPolicy()
/**
* Descructor.
*/
{
// release association descriptions/templates
for (TInt i = iSpecs.Count(); --i >= 0; )
{
CPolicySpec *const s = iSpecs[i];
if (s)
s->Close();
}
iSpecs.Close();
// release End Points assocated with this policy
for (TInt j = iEndPoints.Count(); --j >= 0; )
{
iEndPoints[j].Close();
}
iEndPoints.Close();
// release the policy selectors
CPolicySelector *ps;
while ((ps = iSelectors) != NULL)
{
iSelectors = ps->iNext;
delete ps;
}
// release the named interface entries
while (iInterfaces)
{
CSelectorInterface *tmp = iInterfaces;
iInterfaces = tmp->iNext;
delete tmp;
}
// ..and algorithms table
delete iAlgorithms;
IPSEC_OBJECT_DEC;
}
CPolicySelector::CPolicySelector()
/**
* Constructor
*/
{
IPSEC_OBJECT_INC;
}
CPolicySelector::~CPolicySelector()
/**
* Descructor.
*/
{
// Release policy actions
for (TInt i = iActions.Count(); --i >= 0; )
{
CPolicyAction *const action = iActions[i];
if (action)
action->Close();
}
iActions.Close();
// Release transport selector(s)
if (iTS)
iTS->Close();
IPSEC_OBJECT_DEC;
}
//
CSelectorInterface::CSelectorInterface()
/**
* Constructor
*/
{
IPSEC_OBJECT_INC;
}
CSelectorInterface::~CSelectorInterface()
/**
* Descructor.
*/
{
delete iName;
IPSEC_OBJECT_DEC;
}
const CSelectorInterface *CSecurityPolicy::LookupInterfaceL(const TDesC &aName)
/**
* Locate interface by name from policy.
*
* Return an existing interface object, if found and otherwise,
* create and initialize new object.
*
* @param aName The interface name
* @return The interface
* @leave KErrNoMemory, if allocation fails.
*/
{
CSelectorInterface *si;
//
// See if interface already exists
//
for (si = iInterfaces; si != NULL; si = si->iNext)
if (aName.Compare(*si->iName) == 0)
return si; // Already exists, reuse
//
// First time occurrence of this interface name, create a new entry
//
si = new (ELeave) CSelectorInterface();
si->iNext = iInterfaces;
iInterfaces = si;
si->iName = HBufC::NewMaxL(aName.Length());
*si->iName = aName;
return si;
}
CPolicySpec *CSecurityPolicy::FindSpec(const TDesC &aName) const
/**
* Locate security association specification by name.
*
* @param aName
* @return The specification or NULL, if not found
*
*/
{
for (TInt i = iSpecs.Count(); i > 0; )
{
CPolicySpec *const s = iSpecs[--i];
if (aName.Compare(*s->iName) == 0)
return s;
}
return NULL;
}
//
CPolicyAction::~CPolicyAction()
/**
* Desctructor.
*/
{
if (iSpec)
iSpec->Close();
if (iTS)
iTS->Close();
}
//
CPolicySpec::CPolicySpec()
{
#ifdef SYMBIAN_IPSEC_VOIP_SUPPORT
iPropList = CPropList::NewL(1);
#endif // SYMBIAN_IPSEC_VOIP_SUPPORT
}
CPolicySpec::~CPolicySpec()
/**
* Destructor.
*/
{
delete iName;
//
// Identity blocks are reference counted, there
// may still be SA's referring to them
//
if (iSpec.iIdentityLocal)
iSpec.iIdentityLocal->Close();
if (iSpec.iIdentityRemote)
iSpec.iIdentityRemote->Close();
}