// 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:
// sa_spec.h - IPv6/IPv4 IPSEC security associations
// This file collects minimal definitions that need to be exported
// from the Security Associations Database into Security Policy
// database
//



/**
 @internalComponent
*/
#ifndef __SA_SPEC_H__
#define __SA_SPEC_H__

#include <ip6_hook.h>
#include "ipaddress.h"
#include <networking/pfkeyv2.h>
#include <networking/crypto.h>	// only for TAlgorithmClass, is this really necessary?
#include "epdb.h"
#include "ipseclog.h"

// Selector applies to inbound packets
const TUint KPolicyFilter_INBOUND = 0x1;
// Selector applies to outbound packets
const TUint KPolicyFilter_OUTBOUND = 0x2;
// Selector applies to both inbound and outbound packets
const TUint KPolicyFilter_SYMMETRIC =
	KPolicyFilter_INBOUND|KPolicyFilter_OUTBOUND;

// Indicate tunnel mode for Acquire and Verify (not really a filter).
const TUint KPolicyFilter_TUNNEL = 0x4;

// Selector can be merged with another matched selector.
const TUint KPolicyFilter_MERGE = 0x8;

// Negate selector -- drop if match occurs.
const TUint KPolicyFilter_DROP = 0x10;
// Final selector, do not look for "merge" selectors after this.
const TUint KPolicyFilter_FINAL = 0x20;

/**
* Ports is present in selector.
* The value indicates whether the content is actual port (1) or a packet
* type/code (0) from ICMP, MH or other similar protocols.
*/
const TUint KTransportSelector_PORTS = 0x1;

// Object tracking in the windows environment to
// help detecting possible memory leaks.
#if __WINS__ && _DEBUG
	extern int IPSEC_OBJECT_COUNT;
	#define	IPSEC_OBJECT_INC	++IPSEC_OBJECT_COUNT
	#define	IPSEC_OBJECT_DEC	--IPSEC_OBJECT_COUNT
	#define	IPSEC_OBJECT_TRACKING 1
// ..when desperate, use the following ..
//	#define	IPSEC_OBJECT_INC	do { Log::Printf(_L("INC[%u] %d"), (TInt)this, ++IPSEC_OBJECT_COUNT); } while (0)
//	#define	IPSEC_OBJECT_DEC	do { Log::Printf(_L("DEC[%u] %d"), (TInt)this, --IPSEC_OBJECT_COUNT); } while (0)
#else
	#define	IPSEC_OBJECT_INC
	#define	IPSEC_OBJECT_DEC
	#undef	IPSEC_OBJECT_TRACKING
#endif


class CIpsecReferenceCountObject : public CBase
	/**
	* The base class for all IPSEC reference count objects.
	*
	* Many IPSEC objects are implemented as "reference counted objects"
	* which automaticly delete self, when the last reference is removed.
	*
	* The contruction of the object is counted as one reference.
	* The Open() and Close() methods are not supposed to be overridden,
	* and are not defined "virtual". This base class attempts to be
	* as light as possible
	*
	* The destructor is virtual and private. The reference counted
	* objects are never deleted from outside, only from the Close()
	* method.
	*/
	{
public:
#ifdef IPSEC_OBJECT_TRACKING
	// Non-default constructor only needed for debugging
	CIpsecReferenceCountObject() 
		{
		IPSEC_OBJECT_INC;
		}
#endif
	inline void Open();
	inline TInt IsShared() const;
	void Close();
protected:
	virtual ~CIpsecReferenceCountObject()
		{
#ifdef IPSEC_OBJECT_TRACKING
		// Non-empty destructor only needed for debugging
		IPSEC_OBJECT_DEC;
#endif
		}
	TInt iRefs;
	};

void CIpsecReferenceCountObject::Open()
	/**
	* Increment reference count.
	*
	* Records an additional reference to the object.
	*/
	{
	++iRefs;
	}

TInt CIpsecReferenceCountObject::IsShared() const
	/**
	* Return the current reference count.
	*
	* The reference count works as implicit flag to
	* test whether object has more than one reference
	* to it (is shared).
	*
	* @li = 0, only one reference exist (not shared)
	* @li > 0, other references exist (object is shared)
	*
	* @return The reference count
	*/
	{
	return iRefs;
	}

class RPolicySelectorInfo
	/**
	* The policy selector information. This defines the IPSEC selector data layout,
	* as is used as basic component in the transport selectors. This is also the
	* basic information block, which is extracted from a packet (or flow).
	*/
	{
public:
	inline void FillZ();

	TUint8 iFlags;				//< Transport Selector flags (KTransportSelector_*)				
	TUint8 iProtocol;			//< IP Protocol code (0..255)
	TUint8 iReserved1;			//< Reverved for future use.(now for alignment only)
	TUint8 iReserved2;			//< Resevved for future use (now for alignment only)
	//
	TUint16 iPortRemote;		//< Remote port
	TUint16 iPortLocal;			//< Local port
	//
	RIpAddress iRemote;			//< Remote Address with scope id
	RIpAddress iLocal;			//< Local Address with scope id
	};


void RPolicySelectorInfo::FillZ()
	// Fill all fields with ZERO (including possible compiler generated padding)
	{
	TUint32 *const p = (TUint32 *)this;
	p[0] = p[1] = 0;	// iFlags, iProtocol, reserveds, iPortRemote, iPortLocal
	}


class TPolicyFilterInfo
	/**
	* The policy control and filter information.
	*
	* This defines the basic data for choosing the
	* selectors which are tested for transport selectors.
	* The filter can be based on
	*
	* - packet direction: inboud or outbound
	* - interface index
	*
	* This includes some other action controlling
	* flags, which affect the actions when the
	* selector matches.
	*
	* - merge
	* - final
	* - drop
	*
	*/
	{
public:
	TUint32 iFlags;		//< The KPolicyFilter_* flags.
	TUint32 iIndex;		//< The Interface Index.
	};

class CTransportSelector : public CIpsecReferenceCountObject
	/**
	* The Transport Selector.
	*
	* A transport selector is a list of basic selectors or'ed together.
	* The Transport Selector matches, if any of the basic selectors in
	* the list match.
	*
	* The basic transport selector is a reference counted object. The contruction
	* counts as the first reference and must be matched with corresponding
	* Close(). Record additional references with Open().
	*
	* The contruction automaticly increments the next Or'ed selector. Once
	* constructed, the selector is immutable. Aside from the reference count
	* house keeping, the content of the selector is constant.
	*/
	{
public:
	CTransportSelector(const RPolicySelectorInfo &aData, const RPolicySelectorInfo &aMask, CTransportSelector *const aOr);
	TInt Match(const RPolicySelectorInfo &aKey) const;
private:
	~CTransportSelector();
	TInt iRefs;				 				//< The reference count.
public:

	// immutable after construction
	const RPolicySelectorInfo iData;		//< The selector data (the values to check)
	const RPolicySelectorInfo iMask;		//< The selector mask (what values to check)
	CTransportSelector *const iOr;			//< Next alternative or NULL.
	};

//
// Mapping of low level types in pfkeyv2.h into more semantic names
// (This is to avoid a need to look many places in case pfkeyv2 changes)
//
typedef uint32_t TLifetimeAllocations;
typedef uint64_t TLifetimeBytes;
typedef uint64_t TLifetimeSeconds;


/**
// The default life time in seconds for larval SA's.
// Larvar SA is created by GETSPI (may also be used as a default for
// iLarvalLifetime in TSecurityAssocSpec).
*/
const TInt KLifetime_LARVAL_DEFAULT = 90;

class CIdentity : public CIpsecReferenceCountObject
	/**
	* A container for the Identity string.
	*/
	{
public:

	// Create and construct a new Identity block
	static CIdentity *NewL(const TDesC &aIdentity);
	static CIdentity *NewL(const TDesC8 &aIdentity, TUint16 aType);
	inline TUint16 Type() const	{return iType; }
	inline TInt Match(const CIdentity &aOther) const;
private:
	// Construct and destruct are private, triggered internally.
	CIdentity(TUint32 aLength) : iTypeLength(aLength) {};
	~CIdentity() {}

	TUint16 iType;						//< Type of the identity string (PREFIX, FQDN, USERFQDN)

	// //
	// *WARNING* *WARNING* *WARNING*
	// What now follows, is the TLitC8 structure.
	// The extra space is allocated only, if iTypeLength
	// is non-zero.
	// //
	// Why this TLitC8 "hack" instead of traditional
	// C construct with a "length" member and "fake buf[1]"?
	//
	// As far as layout, this is exactly the same. The TLitC8
	// "hack" only forces a Symbian specific layout. When a
	// descriptor is needed, it doesn't need to be constructed,
	// it's already existing and just returning a reference
	// to iTypeLength as TLitC8 is sufficient.
	// //
	const TUint iTypeLength;			//< TypeLength of TLitC8
public:
	inline const TDesC8 &Identity() const
		/**
		* Return the Identity string.
		*
		* @return The identity
		*/
		{
		return ((TLitC8<1> &)iTypeLength)();
		}
	};
	
TInt CIdentity::Match(const CIdentity &aOther) const
	/*
	* Return ETrue, if identities match.
	*
	* @param aOther The other identity
	* @return result of comparison.
	*/
	{
	return Identity() == aOther.Identity();
	}


// TLifetime, a help structure

class TLifetime
	{
public:
	TLifetime(const struct sadb_lifetime &aLifetime);
	static void Freeze(TTime &aTime, const TTime &aNow);
	TLifetime();
	// For current, these will count items used so far. For Hard and
	// Soft these will contain the limit values for the current
	// counts.
	// study: present unspecified limit with 0 or max value?
	TLifetimeAllocations iAllocations;	// Connections limit
	TLifetimeBytes iBytes;				// Transmitted bytes limit
	//
	// For Current, these will record the creation and first use times.
	// For Hard and Soft, these will record the expiration times (e.g.
	// simple comparison with the current time can be used to test for
	// expiration, and for returning CURRENT values to application, use
	// the SecondsFrom method with current.
	//
	TTime iAddtime;						// Lifetime limit from creation
	TTime iUsetime;						// Lifetime limit from first use
	};


class TSecurityAssocSpec
	/**
	* Security Association template.
	*
	* The TSecurityAssocSpec is a template for a Security Association.
	* This information and the information extracted from the packet is used
	* to locate a matching Security Association.
	*/
	{
public:
	TUint8 iType;				//< Security Association type (AH or ESP)
	TUint8 iAalg;				//< Authentication algorithm number
	TUint8 iEalg;				//< Encryption algorithm number
	TUint8 iReplayWindowLength;	//< Use Replay Window
	TUint iPfs:1;				//< "Perfect Forward Secresy" (PFS)

	/**
	* The SA is local address specicic.
	*
	* When set, the SA's is bound to a specific local
	* address. If not set, the SA can be used with any of
	* the currently valid own addresses.
	*
	* Note: The member name "iMatchSrc" is misleading.
	*/
	TUint iMatchSrc:1;

	// MatchProxy retained for backward compatibility
	TUint iMatchProxy:1;		//< (PFP) (deprecated) incoming == iMatchRemote, outgoing == iMatchLocal

	// The PFP (Populate From Packet) flags
	TUint iMatchLocal:1;		//< (PFP) Specific to local address of the packet
	TUint iMatchRemote:1;		//< (PFP) Specific to remote address of the packet
	TUint iMatchProtocol:1;		//< (PFP) Specific to protocol of the packet
	TUint iMatchLocalPort:1;	//< (PFP) Specific to local port of the packet
	TUint iMatchRemotePort:1;	//< (PFP) Specific to remtoe port of the packet

	// Identity references
	CIdentity *iIdentityLocal;	//< The local Identity
	CIdentity *iIdentityRemote;	//< The remote Identity

	// Limits for key lengths (for ACQUIRE only)
	TUint16 iMinAuthBits, iMaxAuthBits;			//< Required length of the authentication key
	TUint16 iMinEncryptBits, iMaxEncryptBits;	//< Required length of the encryption key

	/**
	* Max time for the Key Managers to handle ACQUIRE request.
	*
	* iLarvalLifetime specifies the maximum time to wait, after
	* an ACQUIRE request originating from this template is sent
	* to the key manager(s). This time should be long enough to
	* allow key manager to complete the negotiation for an
	* association.
	*
	* If not specified (=0), the default is #KLifetime_LARVAL_DEFAULT
	*/
	TUint iLarvalLifetime;

	// Required lifetimes
	struct sadb_lifetime iHard;	//< Hard Lifetime requirement (copied into ACQUIRE)
	struct sadb_lifetime iSoft;	//< Soft Lifetime requirement (copied into ACQUIRE)
	};

class TAlgorithmMap
	/**
	* Map symbolic algorithm name with algorithm number.
	*
	* The symbolic name of the algorithm identifies the implementation. That is, the name
	* given here is matched against the names advertised by the installed cryptograpchic
	* libaries. Each installed cryptographic library provides a list of supported algorithm
	* as names. This name is local concept.
	*
	* The algorithm number is the externally visible identification, which
	* is used by the key management negotiations (in IKE etc), and appears in PFKEY messages.
	*/
	{
public:
	TAlgorithmMap(TAlgorithmClass aClass, TInt anId, TInt aBits, const TDesC &aLibrary, const TDesC &aAlgorithm);
	TAlgorithmClass iClass;		//< Algorithm Class (digest, cipher, ...)
	TInt iId;					//< IPsec algorithm id number
	TInt iBits;					//< Actual # of bits to be used (digest only)

	/**
	* Name of the library instance.
	* If iLibrary is empty,
	* then the first matching algorithm from any of the
	* installed libraries is used.
	*/
	TProtocolName iLibrary;
	/**
	* Name of the algorithm.
	* If iAlgorithm is empty, then
	* this map entry describes a NULL algorithm. No libraries
	* are searched.
	*/
	TAlgorithmName iAlgorithm;
	};

class CAlgorithmList : public CArrayFixFlat<TAlgorithmMap>
	/**
	* List of potentially available algorithtms.
	*
	* CAlgorithmList is an array of TAlgorithmMap entries and provides methods
	* for finding a specific map and adding new mapping. The use of this information
	* is documented in more detail in connection of the Cryptographic library.
	*
	* The algorithm list defines all potentially available algorithms. The actual
	* set of available algorithms depends on currently installed crypto libraries.
	*/
	{
public:
	CAlgorithmList();
	void AddL(TAlgorithmClass aClass, TInt anId, TInt aBits, const TDesC &aLibrary, const TDesC &anAlg);
	TAlgorithmMap *Lookup(TAlgorithmClass aClass, TInt anAlg) const;
	TAlgorithmMap *Lookup(const TDesC &aLibrary, const TDesC &anAlg) const;
	};



class CSecurityAssoc;
class REndPoints;
#ifdef SYMBIAN_IPSEC_VOIP_SUPPORT
class CPropList;
#endif //SYMBIAN_IPSEC_VOIP_SUPPORT


class MAssociationManager
	/**
	* The Security Association Database (SDB) interface as seen by Security Policy.
	*/
	{
public:
	virtual void Open() = 0;
	virtual void Close() = 0;

	/**
	* Acquire a new Security Association.
	*
	* SECPOL calls this when it needs a Security Association for a flow.
	*
	* If a matching Security Association already exists, it is returned.
	* Otherwise this generates a PFKEY ACQUIRE message for each registered
	* key management application and the function returns without SA.
	*
	* When not found, this creates a "larval egg SA" that will match any
	* future request with same parameters. This prevents generating multiple
	* ACQUIRE messages for the same security association.
	*
	* @retval aSa	located SA
	* @param aSpec	SA requirements
	* @param aPropList The list of (possibly multiple) proposals pertaining to the aSa
	* @param aTS	The traffic selector.
	* @param aSrc	the source address (of SA)
	* @param aDst	the destination address (of SA)
	* @param aInfo	the selector information
	* @param aTunnel True, when association is used in tunnel mode.
	* @returns
	* @li	KErrNone        SA found and returned.
	* @li	KRequestPending SA found (or created), but in LARVAL state
	* @li	KErrDied        the new larval SA expired (some weird problem)
	* @li	KErrNotFound    creating SA failed (parameters error? memory?)
	*/
	virtual TInt Acquire(
		CSecurityAssoc * &aSa,
		const TSecurityAssocSpec &aSpec,
#ifdef SYMBIAN_IPSEC_VOIP_SUPPORT
		const CPropList *aPropList,
#endif //SYMBIAN_IPSEC_VOIP_SUPPORT
		const CTransportSelector *aTS,
		const RIpAddress &aSrc,
		const RIpAddress &aDst,
		const RPolicySelectorInfo &aInfo,
		TBool aTunnel) = 0;

	/**
	* Verify Security Association.
	*
	* SECPOL calls this to verify that the applied SA matches the
	* policy specification.
	*
	* @param aSa	the SA to be verified
	* @param aSpec	the required SA features
	* @param aSrc	the source address from the packet
	* @param aDst	the destination address of the packet
	* @param aInfo	the selector information
	*
	* @returns
	*	@li	KErrNone, when all is OK
	*	@li	error < 0, when something doesn't match
	*/
	virtual TInt Verify(
		const CSecurityAssoc *aSa,
		const TSecurityAssocSpec &aSpec,
#ifdef SYMBIAN_IPSEC_VOIP_SUPPORT
		const CPropList *aPropList,
#endif // SYMBIAN_IPSEC_VOIP_SUPPORT	
		const RIpAddress &aSrc,
		const RIpAddress &aDst,
		const RPolicySelectorInfo &aInfo) = 0;
		
	
	/**
	* Outgoing packet transformation for IPSEC
	*
	* SECPOL calls this once for each packet for each IPsec required
	* IPsec transform (ESP or AH).
	*
	* First, applies the tunnel transform, if present.
	* After this, applies the IPSEC transform specified by the Security
	* Association if present.
	* Having a NULL SA parameter allows this to be used as a plain
	* tunnel wrapper.
	*
	* @param aSa		The SA to be applied to the packet (or NULL)
	* @param aPacket	The outgoing packet
	* @param aInfo		The info block associated with the packet
	* @param aTunnel	The outer tunnel destination (request tunneling if specified)
	*
	* @returns
	* @li KErrNone, if transformation successfully done
	* @li KErrGeneral, otherwise (some error condition occurred)
	*/
	virtual TInt ApplyL(
		CSecurityAssoc* aSa,
		RMBufSendPacket &aPacket,
		RMBufSendInfo &aInfo,
		const TIpAddress &aTunnel) = 0;

	/**
	* Incoming packet transformation for IPSEC (one layer).
	*
	* SECPOL calls this for each incoming packet when the next
	* procotol indicates IPsec header (AH or ESP).
	*
	* Decode IPSEC layer from the received packet and return the
	* the applied Security Association and the optional tunnel
	* address.
	*
	* @retval aSa		Returns the SA that was used by this transformation (if any)
	* @param aPacket	The incoming packet
	* @param aInfo		The info block associated with the packet
	* @param aProtocol	The protocol (either AH or ESP, and maybe UDP for NAT traversal)
	* @retval aTunnel	Returns outer source address, if detunneling was done. Otherwise
	*					just unspecified address.
	* @returns
	* @li	< 0, transform failed with error
	* @li	>= 0, transform succesfull, the next protocol id after unwrap.
	*
	* @exception leave	transform failed with an error
	*
	* If the input aProtocol is not ESP, AH or IP-in-IP, this function does
	* nothing and just returns the aProtocol and packet unchanged!!
	*/
	virtual TInt ApplyL(
		CSecurityAssoc* &aSa,
		RMBufRecvPacket &aPacket,
		RMBufRecvInfo &aInfo,
		TInt aProtocol,
		TIpAddress &aTunnel) = 0;

	/**
	* Returns the maximum overhead caused by this SA/Tunnel combination
	* for an outbound packet.
	*
	* @param aSa		the association (can be null)
	* @param aTunnel	request IPSEC tunneling, if address is specified
	*
	* @return	the header overhead caused by the transformation
	*/
	virtual TInt Overhead(const CSecurityAssoc *const aSa, const TIpAddress &aTunnel) const = 0;

	/**
	* Unconditionally remove all references to the SA and destroy the object,
	*
	* The Security Association must be deleted by this function, the ~CSecurityAssociation()
	* destructor must not be invoked from outside this function.
	*
	* Remove the association from the hash table (iHash) and terminate
	* the pending timer (if any).
	*
	* @param aSa	The SA (NULL also allowed for NOP)
	*/
	virtual void Delete(CSecurityAssoc *aSa) = 0;

	/**
	* Activate a timeout callback for SA.
	*
	* CSecurityAssociation calls this to set a timer for self when the SA
	* has time based lifetime. Unless cancelled, timeout expiration calls the
	* CSecurityAssoc::TimerExpired after aDelta seconds has passed.
	*
	* @param aSa The affected SA
	* @param aDelta The timeout
	*/
	virtual void TimerOn(CSecurityAssoc &aSa, TInt aDelta) = 0;

	/**
	* Generate Expired message.
	* Called by CSecurityAssocition, when it detects that lifetime has expired (hard)
	* or is about to expired (soft). Generate an Expired message and deliver it to all
	* interested parties.
	*
	* @param aSa	The association
	* @param aType	Expiration type (SADB_EXT_LIFETIME_SOFT, SADB_EXT_LIFETIME_HARD)
	* @param aLifetime Expired lifetime
	*/
	virtual void Expired(const CSecurityAssoc &aSa, TInt aType, const TLifetime &aLifetime) = 0;

	/**
	* Deliver Algorithm map from policy to Security Association Database (SAD).
	*
	* If successful, the SAD takes ownership of the table and sets aList to NULL.
	* If not succesful, the ownership of the table remains
	* with the caller (aList is not changed).
	*
	* @param aList	The algorithm List.
	*/
	virtual void SetAlgorithms(CAlgorithmList*& aList) = 0;

	/**
	* Find an SA matching the parameters.
	*
	* If SA cannot be located with the given destination address, the search
	* is repeated with no destination address.
	*
	* This function exists ONLY for locating the INCOMING SA for a packet,
	* which has AH or ESP header..
	*
	* @param aType		the Association Type (AH or ESP)
	* @param aSPI		the SPI number
	* @param aDst		the destination address (never NONE)
	* @returns
	* @li non NULL,   pointer to CSecurityAssociation, if found
	* @li NULL,       if the requested association does not exist
	*/
	virtual CSecurityAssoc *Lookup(TUint8 aType, TUint32 aSPI, const TIpAddress &aDst) const = 0;

	/**
	* Return the named EndPoint collection.
	*
	* Security Associtiations can be bound to named end points. The same EP's can be
	* referenced in the security policy. To allow this, the Security Policy (SPD) and
	* Security Association Databases (SAD) must use a shared "name space" for the
	* end points.
	*
	* The end point collection is owned by SAD, and the SPD needs to find a reference
	* to the same instance using this function.
	*
	* @return The end point collection.
	*/
	virtual REndPoints &EndPointCollection() = 0;
	};


class RSecurityAssociation;

/**
* Security Association callback.
*/
typedef void (*SecurityAssociationCallback)(RSecurityAssociation &aAssoc);

class RSecurityAssociation : public RCircularList
	/**
	* Security Association handle.
	*
	* The handle contains a reference to a security association. The circular
	* list links together handles, which reference the same association. The
	* "head" of the list is in the security association.
	*
	* The callback function is called when the state of the association changes
	* in any way.
	*/
	{
	friend class CSecurityAssoc;
	friend class CProtocolKey;
public:

	TInt Status() const;
	void Init(SecurityAssociationCallback aCallback);
	void None();
	void Reset(CSecurityAssoc *aSa);

	inline CSecurityAssoc *Association() const
		{
		return iAssociation;
		}
	inline TInt ReadErr()const { return iErrorVal; }
	inline void SetError(TInt aError) { iErrorVal=aError; }
private:
	CSecurityAssoc *iAssociation;			//< The security association.
	SecurityAssociationCallback iCallback;	//< The callback function.
	TInt iErrorVal;				            // Stores the error value returned by KMD server
	};


#endif
