/*

The contents of this file are subject to the Mozilla Public License
Version 1.1 (the "License"); you may not use this file except in
compliance with the License. You may obtain a copy of the License at
http://www.mozilla.org/MPL/

Software distributed under the License is distributed on an "AS IS"
basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the
License for the specific language governing rights and limitations
under the License.

The Original Code is a portion of the Path Development API developed
by CygnaCom Solutions, Inc.

Portions created by CygnaCom Solutions, Inc. are Copyright (C) CygnaCom 
Solutions, Inc. All Rights Reserved.

Contributor(s): ______________________________________.

http://www.cygnacom.com

*/
/*------------------------------------------------------------------------*\
|  Project Name  :   Path Development API
|  File Name     :   PathDev.cpp
|  Creation Date :   May 17, 1999
|  Author        :   Matt Cooper (mcooper@cygnacom.com)
|                :   Peter Hesse (pmhesse@cygnacom.com)
|  Last Mode Date:   May 17, 1999
|  Modification History:
|--------------------------------------------------------------------------
|
|     Known Bugs:   None
|                   
|--------------------------------------------------------------------------
| Cryptographic devices and technical data regarding them are subject to
| Federal Government export controls as specified in Title 22, Code of
| Federal Regulations, Parts 121 through 128.  Cryptographic devices
| implementing the Data Encryption Standard (DES) and technical data
| regarding them must comply with these Federal regulations
|--------------------------------------------------------------------------
| Copyright (c) 1999, CygnaCom Solutions, Inc.
| All Rights Reserved.
\*------------------------------------------------------------------------*/


#if defined(_WIN32)
	#if defined(_AFXDLL)
		#include "stdafx.h"
	#else
		#include "windows.h"
	#endif
#endif

#ifdef VX /* Added by Masanori Satake. */
#include "vxWorks.h"
#include "vxdbg.h"
#endif

#include "Defines.h"
#include "utils.h"
#include "PathDev.h"
#include "CertCache.h"
#include "PathCache.h"
#include "CMAPIInterface.h"
#include "cmapi.h"

//extern int stricmp(const char *,const char *);
extern CCMAPIInterface *pCMAPIInterface;
extern CCertCache* pCertCache;
extern CPathCache* pPathCache;

//#define _DEBUG_VERBOSE

#if !defined(__cplusplus)
	#error C++ compiler required.
#endif

extern void CPL_FreeCertPath(   CertPath_struct **certPath   );

/*
typedef struct CertPath_struct
{
    Cert_struct *cert;
    Bytes_struct asn1cert;
    struct CertPath_struct *next;
} CertPath_struct;
*/


long CPLI_DevelopPathFromCache(long session, LPLOCAL_SESSION_INFO pLocalInfo, LPCPL_SESSION_INFO sessionInfo, CertPath_struct **certPath )
{
	LPCERTCACHE_ENTRY_STRUCT pCertEntry = NULL;
	LPPATH_ENTRY_STRUCT pPathStruct = NULL;
	CertPath_struct *pTail = NULL;
	CPathRec *pRec = NULL;
	Cert_struct *pCert = NULL;
	int atRoot = 0;
	long rv = 0;

	do
	{

		if(pLocalInfo->pPartialPath)
		{
			// Do not try cache again
			pLocalInfo->usePathCache = 0;

			pCert = pLocalInfo->pPartialPath->cert;
			if( pCert )
			{
				rv = pCertCache->GetCert(pCert->subject,pCert->issuer,&(pCert->serial_num),pLocalInfo->mask,sessionInfo,&pCertEntry);
				if( rv )
					break;
			}
			else
			{
				break;
			}
		}
		else if( 0 == pCertCache->GetNextCert(pLocalInfo->subjectDN,pLocalInfo->mask,sessionInfo,&pCertEntry ) )
		{
			// Nothing to do here...
		}
		else
		{
			break;
		}

		pCert = pCertEntry->pCert;

		pLocalInfo->pHead = NULL;

		do
		{
			// have a cert?
			if( NULL == pCert )
				break;

			// correct subject?
			if( !CPLI_IsSameDN(pCert->subject, pLocalInfo->subjectDN) )
				break;

			pRec = pPathCache->FindEntry(pCert);
			if( NULL == pRec )
			{
				// No need to proceed with this cert
				break;
			}

			if( !CPLI_IsValidTargetCert(pLocalInfo,sessionInfo,pCert) )
			{
				break;
			}

			// else target cert passes

			*certPath = new CertPath_struct;
			if( NULL == *certPath )
				return CPL_ALLOC_FAILURE;
				
			CPLI_CopyCertToPathStruct(pCertEntry,*certPath);
			(*certPath)->next = NULL;
			pTail = *certPath;
			pLocalInfo->pHead = pTail;

			if( pLocalInfo->pPartialPath )
			{
				pCertEntry->flag |= pLocalInfo->mask;
			}

			// at trust root?
			if( CPLI_IsTrustedDN(sessionInfo->rootDN,pCertEntry->pCert->issuer) )
			{
				atRoot = 1;
				break;
			}

			// add issuer certs while exist in path cache and not to trust root
			do
			{

				pPathStruct = pRec->get_entry_pointer();

				if( NULL == pPathStruct )
				{
					#if defined(_DEBUG)
                                        #ifdef _WIN32 /* Added by Masanori Satake. */
						afxDump << "ERROR: CPLI_DevelopPathFromCache: pPathStruct == NULL!\n";
                                        #else /* _UNIX or VX */
						PRINTERR("ERROR: CPLI_DevelopPathFromCache: pPathStruct == NULL!\n");
                                        #endif /* #ifdef _WIN32 */
					#endif
					break;
				}

				rv = pCertCache->GetCert(pPathStruct->subjectIssuerDN,pPathStruct->issuerCADN,&(pPathStruct->issuerSerial),pLocalInfo->mask,sessionInfo,&pCertEntry);

				if( NULL == pCertEntry || 0 != rv )
				{

					#if defined(_DEBUG)
					if( 0 != rv && CERTCACHE_NO_MORE_CERTS != rv )
                                                #ifdef _WIN32 /* Added by Masanori Satake. */
						afxDump << "ERROR: CPLI_DevelopPathFromCache: pCertCache->GetCert(...) ret'd " << rv << "\n";
                                                #else /* _UNIX or VX */
					        {
						  PRINTERR("ERROR: CPLI_DevelopPathFromCache: pCertCache->GetCert(...) ret'd ");
						  PRINTERR(rv);
						  PRINTERR("\n");
						}
                                                #endif /* #ifdef _WIN32 */
					#endif
					// no cert found or err
					break;
				}

				rv = CPLI_AddCertToTail(pLocalInfo, sessionInfo, pTail, pCertEntry );
				if( 0 != rv )
				{
					// cert not good for this path, get next subject cert
					break;
				}

				if( pLocalInfo->pPartialPath )
				{
					pCertEntry->flag |= pLocalInfo->mask;
				}

				// at trust root?
				if( CPLI_IsTrustedDN(sessionInfo->rootDN,pCertEntry->pCert->issuer) )
				{
					atRoot = 1;
					break;
				}

				pRec = pPathCache->FindEntry(pPathStruct->issuerCADN,&(pPathStruct->issuerSerial));

			}
			while( NULL != pRec  );
			
			if( atRoot )
				break;

			if( !pLocalInfo->pPartialPath )
			{
				CPL_FreeCertPath(certPath);
			}

			pTail = NULL;

		}
		while(FALSE);

		if( atRoot || pLocalInfo->pPartialPath )
		{
			break;
		}

	}
	while(TRUE);

	pLocalInfo->usePathCache = 0;

	if( !atRoot )
	{
		// clear all checked flags
		pCertCache->ClearAllFlags(pLocalInfo->mask);
		// Start over from scratch!
		CPL_FreeCertPath(certPath);
		return CPLI_DevelopPath( session,  pLocalInfo, sessionInfo, certPath );
	}
	else
	{
		return 0;
	}
}


long CPLI_DevelopPath(long session, LPLOCAL_SESSION_INFO pLocalInfo, LPCPL_SESSION_INFO sessionInfo, CertPath_struct **certPath )
{
	LPCERTCACHE_ENTRY_STRUCT pCertEntry = NULL;
	CertPath_struct *pTail = NULL;
	Cert_struct *pCert = NULL;
	long rv = 0;

	if( NULL == *certPath )
	{
		do
		{
			rv = pCertCache->GetNextCert(pLocalInfo->subjectDN,pLocalInfo->mask,sessionInfo,&pCertEntry );
			if( NULL == pCertEntry || 0 != rv )
				return rv;

			pCert = pCertEntry->pCert;

			if( CPLI_IsValidTargetCert(pLocalInfo,sessionInfo,pCert) )
			{
				*certPath = new CertPath_struct;
				if( NULL == *certPath )
					return CPL_ALLOC_FAILURE;
					
				CPLI_CopyCertToPathStruct(pCertEntry,*certPath);
				(*certPath)->next = NULL;
				pTail = *certPath;
				pLocalInfo->pHead = *certPath;

				// at trust root?
				if( CPLI_IsTrustedDN(sessionInfo->rootDN,pTail->cert->issuer) )
				{
					return 0;
				}

				rv = CPLI_DevelopPath( session, pLocalInfo, sessionInfo, certPath );
				if( 0 == rv )
					return 0;

				CPL_FreeCertPath(certPath);
				pLocalInfo->pHead = NULL;
				pTail = NULL;

			}

		}
		while( NULL != pCertEntry );

		return rv;
	}


	pTail = *certPath;
	
	if( pTail->next )
	{
		pTail = pTail->next;
		if( pTail->next )
		{
			rv = CPLI_DevelopPath( session, pLocalInfo, sessionInfo, &pTail );
			if( 0 == rv )
				return 0;
		}

		CPL_FreeCertPath(&pTail);
		(*certPath)->next = NULL;

	}

	do
	{
		pTail = *certPath;

		if( CPLI_IsIssuerCertAlreadyInPath(pLocalInfo, pTail->cert->issuer) )
		{
			#if defined(_DEBUG)
                                #ifdef _WIN32 /* Added by Masanori Satake. */
				afxDump << "***Duplicate issuer found and discarded from path\n";
                                #else /* _UNIX or VX */
				PRINTERR("***Duplicate issuer found and discarded from path\n");
                                #endif /* #ifdef _WIN32 */
			#endif
			return CPL_DUPLICATE_ISSUER;
		}


		rv = pCertCache->GetNextCert(pTail->cert->issuer,pLocalInfo->mask,sessionInfo,&pCertEntry );
		if( NULL == pCertEntry || 0 != rv )
		{
			if( pLocalInfo->pHead == pTail )
			{
				// path dev failed for this target cert, set path to NULL and
				// try next target cert........
				CPL_FreeCertPath(&pTail);
				pLocalInfo->pHead = NULL;
				*certPath = NULL;
				return CPLI_GET_NEXT_TARGET_CERT;
				//rv = CPLI_DevelopPath( session, pLocalInfo, sessionInfo, certPath );
			}

			return rv;
		}

		if( 0 == CPLI_AddCertToTail(pLocalInfo,sessionInfo,pTail,pCertEntry) )
		{
			pTail = pTail->next;
			pTail->next = NULL;

			// at trust root?
			if( CPLI_IsTrustedDN(sessionInfo->rootDN,pTail->cert->issuer) )
			{
				return 0;
			}

			rv = CPLI_DevelopPath( session, pLocalInfo, sessionInfo, &pTail );
			if( 0 == rv )
				return 0;

			CPL_FreeCertPath(&pTail);
			pTail = NULL;
			(*certPath)->next = NULL;
		}

	}
	while( NULL != pCertEntry );



	return 0;
}

// Checks for the issuer of the cert in pCertEntry in the existing path (pLocalInfo->pHead)
long CPLI_IsIssuerCertAlreadyInPath(LPLOCAL_SESSION_INFO pLocalInfo, char *issuerDN )
{
	if( NULL == pLocalInfo || NULL == issuerDN )
	{
		#if defined(_DEBUG)
			if( !pLocalInfo )
                             #ifdef _WIN32 /* Added by Masanori Satake. */
				afxDump << "ERROR: CPLI_IsIssuerCertAlreadyInPath: pLocalInfo is NULL!\n";
                             #else /* _UNIX or VX */
                                PRINTERR("ERROR: CPLI_IsIssuerCertAlreadyInPath: pLocalInfo is NULL!\n");
                             #endif /* #ifdef _WIN32 */
			if( !issuerDN )
                             #ifdef _WIN32 /* Added by Masanori Satake. */
				afxDump << "ERROR: CPLI_IsIssuerCertAlreadyInPath: issuerDN is NULL!\n";
                             #else /* _UNIX or VX */
                                PRINTERR("ERROR: CPLI_IsIssuerCertAlreadyInPath: issuerDN is NULL!\n");
                             #endif /* #ifdef _WIN32 */
		#endif
		return 1;
	}

	if( NULL == pLocalInfo->pHead )
	{
		return 0;
	}

	CertPath_struct *pPath = pLocalInfo->pHead;

	while( pPath )
	{
		if( CPLI_IsSameDN(pPath->cert->subject,issuerDN) )
		{
			#if defined(_DEBUG)
                                #ifdef _WIN32 /* Added by Masanori Satake. */
				afxDump << "CPLI_IsIssuerCertAlreadyInPath: Issuer " << issuerDN << " already exists in path.\n";
                                #else /* _UNIX or VX */
				PRINTERR("CPLI_IsIssuerCertAlreadyInPath: Issuer ");
				PRINTERR(issuerDN);
				PRINTERR(" already exists in path.\n");
                                #endif /* #ifdef _WIN32 */
			#endif
			return 1;
		}

		pPath = pPath->next;
	}


	return 0;

}

long CPLI_AddCertToTail(LPLOCAL_SESSION_INFO pLocalInfo, LPCPL_SESSION_INFO sessionInfo, CertPath_struct *certPath, LPCERTCACHE_ENTRY_STRUCT pCertEntry )
{
	if( NULL == pCertEntry || NULL == certPath )
	{
		#if defined(_DEBUG)
                        #if defined(_WIN32) /* Added by Masanori Satake. */
			afxDump << "ERROR: CPLI_AddCertToTail(...) - Missing cert or path param!\n";
                        #else /* _UNIX or VX */
			PRINTERR("ERROR: CPLI_AddCertToTail(...) - Missing cert or path param!\n");
                        #endif /* #ifdef _WIN32*/

			#if defined(_WIN32)
				ASSERT(FALSE);
			#endif
		#endif
		return CPL_PARAM_MISSING;
	}

	// Default return value is failure
	long rv = CPL_INVALID_ISSUER;

	// Set pCert to the cert we want to add to the path - we will test to insure that
	// pCert is indeed the issuer of the current last cert in the list and that pCert meets
	// all the user supplied criteria.
	Cert_struct *pCert = pCertEntry->pCert;
	if( !pCert )
	{
		#if defined(_DEBUG)
                        #ifdef _WIN32 /* Added by Masanori Satake. */
			afxDump << "ERROR: CPLI_AddCertToTail(...) - Missing decoded cert!\n";
                        #else /* _UNIX or VX */
			PRINTERR("ERROR: CPLI_AddCertToTail(...) - Missing decoded cert!\n");
                        #endif /* #ifdef _WIN32 */

			#if defined(_WIN32)
				ASSERT(FALSE);
			#endif
		#endif
		return CPL_PARAM_MISSING;
	}

	CertPath_struct *pTail = NULL;

	pTail = pLocalInfo->pHead;
	if( !pTail )
	{
		// The target cert must already be added to the list before calling this
		return CPL_PARAM_MISSING;
	}

	// issuer loop check
	while( pTail )
	{
		if( CPLI_IsSameDN(pCert->issuer,pTail->cert->issuer) )
		{
//			#if defined(_DEBUG) && defined(_WIN32)
//				ASSERT(FALSE);
//			#endif
			if( CPLI_IsSameBytesStruct(&(pCert->serial_num),&(pTail->cert->serial_num)))
			{
				return -1; // loop found 
			}
		}
		pTail = pTail->next;
	}

	// Set pTail to last cert in list 
	pTail = certPath;
	while( pTail->next )
	{
		pTail = pTail->next;
	}

	if( !pTail->cert )
	{
		#if defined(_DEBUG)
                        #ifdef _WIN32 /* Added by Masanori Satake. */
			afxDump << "ERROR: CPLI_AddCertToTail(...) - Missing decoded cert!\n";
                        #else /* _UNIX or VX */
			PRINTERR("ERROR: CPLI_AddCertToTail(...) - Missing decoded cert!\n");
                        #endif /* #ifdef _WIN32 */

			#if defined(_WIN32)
				ASSERT(FALSE);
			#endif
		#endif
		return CPL_PARAM_MISSING;
	}

	#if defined(_DEBUG) && defined(_DEBUG_VERBOSE)
                #ifdef _WIN32 /* Added by Masanori Satake. */
		afxDump << "CPLI_AddCertToTail( <subject>" << pCert->subject << "\n\t\t    <issuer>" << pCert->issuer << "\n" ;
                #else /* _UNIX or VX */
		PRINTERR("CPLI_AddCertToTail( <subject>");
		PRINTERR(pCert->subject);
		PRINTERR("\n\t\t    <issuer>");
		PRINTERR(pCert->issuer);
		PRINTERR("\n");
                #endif /* #ifdef _WIN32 */
	#endif

	//  Test pTail & pCert relationship - pTail should be issued by pCert 

	do
	{
	//  typedef struct Auth_key_struct
	//{
	//    Bytes_struct *id;               /* Key identifier (optional) */
	//    Gen_names_struct *issuer;       /* Certificate issuer (optional) */
	//    Bytes_struct *serial_num;       /* Serial number of issuer's cert */
	//} Auth_key_struct;                  /*  (present or absent with issuer name) */

		// authority skid == subject skid?

		// The serial number and issuer DN are both required if the key 
		// identifier is not used.   If the key identifier field is used 
		// then both the serial number and issuer DN fields must be absent.
		if( pTail->cert->exts && pTail->cert->exts->authKeyID)
		{
			Auth_key_struct *pAKID = (Auth_key_struct*)(pTail->cert->exts->authKeyID->value);

			/*
			// XXX123 - MAC - ignoring serial for now.  Bridge CA issued certs would have diff serial nums
			// pAKID is the akid of pTail (subject)
			if( pAKID && pAKID->serial_num )
			{
				// we already know the dns match..  ( subject->akid->sn, issuer->sn )
				if( !CPLI_IsSameBytesStruct(pAKID->serial_num,&pCert->serial_num) )
				{
					// subjects AKID serial # does not match this certs serial
					#if defined(_DEBUG) && defined(_DEBUG_VERBOSE)
						afxDump << "\tSubject AKID serial # does not match this certs serial.\n";
					#endif
					rv = CPL_WRONG_ISSUER;
					break;
				}
			}
			*/

			if( pCert->exts && pCert->exts->subjKeyID && pAKID && pAKID->id )
			{
				// pSKID is the skid of issuer cert
				Bytes_struct *pSKID = (Bytes_struct*)(pCert->exts->subjKeyID->value);
				if( !CPLI_IsSameBytesStruct(pAKID->id, pSKID) )
				{

					// key IDs do not match
					#if defined(_DEBUG) && defined(_DEBUG_VERBOSE)
						char *tmpbuf = new char[256];
						CPLI_btoa((char*)pAKID->id->data, tmpbuf, pAKID->id->num );
                                                #ifdef _WIN32 /* Added by Masanori Satake. */
						afxDump << "\tThe AKID key ID for " << pTail->cert->subject << " by " << pTail->cert->issuer << 
							"\n\tis: " << tmpbuf;
                                                #else /* _UNIX or VX */
						PRINTERR("\tThe AKID key ID for ");
						PRINTERR(pTail->cert->subject);
						PRINTERR(" by ");
						PRINTERR(pTail->cert->issuer);
						PRINTERR("\n\tis: ");
						PRINTERR(tmpbuf);
                                                #endif /* #ifdef _WIN32 */

						CPLI_btoa((char*)pSKID->data, tmpbuf, pSKID->num );
                                                #ifdef _WIN32 /* Added by Masanori Satake. */
						afxDump << "\n\tdoes not match the SKID for " << pCert->subject << " by " << pCert->issuer << 
								".\n\tThe SKID is: " << tmpbuf << "\n";
                                                #else /* _UNIX or VX */
						PRINTERR("\n\tdoes not match the SKID for ");
						PRINTERR(pCert->subject);
						PRINTERR(" by ");
						PRINTERR(pCert->issuer);
						PRINTERR(".\n\tThe SKID is: ");
						PRINTERR(tmpbuf);
						PRINTERR("\n");
                                                #endif /* #ifdef _WIN32 */

						delete []tmpbuf;

					#endif
					rv = CPL_WRONG_ISSUER;
					break;
				}
			}
		}

		// cert sign ? - note - always treating as critical if present
		if( pCert->exts && pCert->exts->keyUsage )
		{
			Extn_struct* pUseExt = pCert->exts->keyUsage;

			// XXX123 - changed to NOT CHECK IF NOT CRITICAL!
			if(  pUseExt->critical && pUseExt->value )
			{
				if( 0 == ( CM_KEY_CERT_SIGN & (*(short*)(pUseExt->value)))  )
				{
					#if defined(_DEBUG) && defined(_DEBUG_VERBOSE)
                                                #ifdef _WIN32 /* Added by Masanori Satake. */
						afxDump << "\tThis cert has critical key usage and no cert-sign.\n";
                                                #else /* _UNIX or VX */
						PRINTERR("\tThis cert has critical key usage and no cert-sign.\n");
                                                #endif /* #ifdef _WIN32 */
					#endif
					break;
				}
			}
		}
		
		// allowed algs ?
		if( sessionInfo->acceptableAlgOIDArray )
		{
			if( !CPLI_IsStringInStringArray(pCert->pub_key.oid,sessionInfo->acceptableAlgOIDArray) )
			{
				#if defined(_DEBUG) && defined(_DEBUG_VERBOSE)
                                        #ifdef _WIN32 /* Added by Masanori Satake. */
					afxDump << "\tThis cert's public key alg is not in the acceptable alg list.\n";
                                        #else /* _UNIX or VX */
					PRINTERR("\tThis cert's public key alg is not in the acceptable alg list.\n");
                                        #endif /* #ifdef _WIN32 */
				#endif
				break;
			}

			if( pCert->sig.alg )
			{
				if( !CPLI_IsStringInStringArray(pCert->sig.alg,sessionInfo->acceptableAlgOIDArray) )
				{
					#if defined(_DEBUG) && defined(_DEBUG_VERBOSE)
                                                #ifdef _WIN32 /* Added by Masanori Satake. */
						afxDump << "\tThis cert's signature alg is not in the acceptable alg list.\n";
                                                #else /* _UNIX or VX */
						PRINTERR("\tThis cert's signature alg is not in the acceptable alg list.\n");
                                                #endif /* #ifdef _WIN32 */
					#endif
					break;
				}
			}
			else if( pCert->signature )
			{
				if( !CPLI_IsStringInStringArray(pCert->signature,sessionInfo->acceptableAlgOIDArray) )
				{
					#if defined(_DEBUG) && defined(_DEBUG_VERBOSE)
                                                #ifdef _WIN32 /* Added by Masanori Satake. */
						afxDump << "\tThis cert's signature alg is not in the acceptable alg list.\n";
                                                #else /* _UNIX or VX */
						PRINTERR("\tThis cert's signature alg is not in the acceptable alg list.\n");
                                                #endif /* #ifdef _WIN32 */
					#endif
					break;
				}
			}
			else
			{
				break;
			}
		}

//typedef struct Policy_struct
//{
//    CM_OID policy_id;				/* Certificate policy OID */
//    Qualifier_struct *qualifiers;   /* Pointer to first policy qualifier */
//    struct Policy_struct *next;
//} Policy_struct;
//

//typedef struct Basic_cons_struct
//{
//    CM_BOOL cA_flag;				/* Indicates whether this is a CA cert */
//    short max_path;                 /* Path length constraint (Optional)    */
//} Basic_cons_struct;                /*    Set to CM_NOT_PRESENT if not used */
//

		// check basic constraints
		if( pCert->exts && pCert->exts->basicCons )
		{
			if( pCert->exts->basicCons->critical )
			{
				Basic_cons_struct* pCons = (Basic_cons_struct*)(pCert->exts->basicCons->value);
				if( !pCons->cA_flag )
				{
					#if defined(_DEBUG) && defined(_DEBUG_VERBOSE)
                                                #ifdef _WIN32 /* Added by Masanori Satake. */
						afxDump << "\tThis cert's basic constraints is critical and CA == FALSE.\n";
                                                #else /* _UNIX or VX */
						PRINTERR("\tThis cert's basic constraints is critical and CA == FALSE.\n");
                                                #endif /* #ifdef _WIN32 */
					#endif
					break;
				}
			}
		}

		// cert policies exist?
		if( CPLI_REQUIRE_EXPLICIT_POLICY == pLocalInfo->requireExplicitPolicy )
		{

			if( !(pCert->exts && pCert->exts->certPolicies) )
			{
				#if defined(_DEBUG) && defined(_DEBUG_VERBOSE)
                                        #ifdef _WIN32 /* Added by Masanori Satake. */
					afxDump << "\tRequire Explicit Policy == TRUE and this cert has no policies.\n";
                                        #else /* _UNIX or VX */
					PRINTERR("\tRequire Explicit Policy == TRUE and this cert has no policies.\n");
                                        #endif /* #ifdef _WIN32 */
				#endif
				break;
			}

			// 0 means inhibit policy mapping, so at least one pol from 
			// the acceptable set must be in the cert
			if( CPLI_INHIBIT_POLICY_MAPPING == pLocalInfo->inhibitPolicyMapping && 
				sessionInfo->acceptablePolicySetArray &&
				sessionInfo->acceptablePolicySetArray[0] )
			{
				// Get ptr to cert policies extension
				Policy_struct* pPols = (Policy_struct*)(pCert->exts->certPolicies->value);

				if( NULL != pPols )
				{
					int tmprv = 0;
					char **pIntersection = NULL;
					int hasPolicy = 0;

					// Get intersection of cert policies and acceptable policies
					tmprv = CPLI_IntersectPolicyArrayAndPolicySet( sessionInfo->acceptablePolicySetArray, pPols, &pIntersection );
					if( 0 != tmprv )
					{	// Very bad
						rv = tmprv;
						break;
					}

					if(  NULL != pIntersection[0] )
					{
						hasPolicy = 1;
					}

					if( NULL != pIntersection )
						CPLI_FreeStringArray(&pIntersection);
				
					if( !hasPolicy )
					{
						#if defined(_DEBUG) && defined(_DEBUG_VERBOSE)
                                                        #ifdef _WIN32 /* Added by Masanori Satake. */
							afxDump << "\tReq Explicit && Inhibit Mapping == TRUE and this cert's policies do not intersect the allowable set.\n";
                                                        #else /* _UNIX or VX */
							PRINTERR("\tReq Explicit && Inhibit Mapping == TRUE and this cert's policies do not intersect the allowable set.\n");
                                                        #endif /* #ifdef _WIN32 */
						#endif
						break;
					}
				}
				else
				{
					#if defined(_DEBUG) && defined(_DEBUG_VERBOSE)
                                                #ifdef _WIN32 /* Added by Masanori Satake. */
						afxDump << "\tReq Explicit && Inhibit Mapping == TRUE and this cert has no policies.\n";
                                                #else /* _UNIX or VX */
						PRINTERR("\tReq Explicit && Inhibit Mapping == TRUE and this cert has no policies.\n");
                                                #endif /* #ifdef _WIN32 */
					#endif
					break;
				}
			}

		}
		
		// cert is valid for this position..

		// allocate / append
		pTail->next = new CertPath_struct;
		if( NULL == pTail->next )
			return CPL_ALLOC_FAILURE;

		pTail->next->next = NULL;

		// copy data
		rv = CPLI_CopyCertToPathStruct(pCertEntry,pTail->next);
		if( rv )
			break;


		// at root?
		if( CPLI_IsTrustedDN(sessionInfo->rootDN,pTail->next->cert->issuer) )
		{
			// validate policy info
			if( !CPLI_IsValidPolicySetChain( sessionInfo->acceptablePolicySetArray, pLocalInfo->pHead, pLocalInfo->inhibitPolicyMapping, pLocalInfo->requireExplicitPolicy ) )
			{
				#if defined(_DEBUG) && defined(_DEBUG_VERBOSE)
                                        #ifdef _WIN32 /* Added by Masanori Satake. */
					afxDump << "\tRoot Cert found, but CPLI_IsValidPolicySetChain failed.\n";
                                        #else /* _UNIX or VX */
					PRINTERR("\tRoot Cert found, but CPLI_IsValidPolicySetChain failed.\n");
                                        #endif /* #ifdef _WIN32 */
				#endif
				// oops, maybe next time..
				CPL_FreeCertPath(&(pTail->next));
				pTail->next = NULL;
				rv = CPL_INVALID_ISSUER;
			}
			#if defined(_DEBUG) && defined(_DEBUG_VERBOSE)
			else
			{
                                #ifdef _WIN32 /* Added by Masanori Satake. */
				afxDump << "\tRoot Cert added to tail of path.\n";
                                #else /* _UNIX or VX */
				PRINTERR("\tRoot Cert added to tail of path.\n");
                                #endif /* #ifdef _WIN32 */
			}
			#endif
		}
		#if defined(_DEBUG) && defined(_DEBUG_VERBOSE)
		else
		{
                        #ifdef _WIN32 /* Added by Masanori Satake. */
			afxDump << "\tCert added to tail of path.\n";
                        #else /* _UNIX or VX */
			PRINTERR("\tCert added to tail of path.\n");
                        #endif /* #ifdef _WIN32 */
		}
		#endif

	}
	while(FALSE);

	return rv;
}

long CPLI_CopyCertToPathStruct( LPCERTCACHE_ENTRY_STRUCT pCertEntry, CertPath_struct *pPathStruct )
{
	if( !pCertEntry || !pPathStruct )
		return CPL_PARAM_MISSING;

	if( !pCertEntry->binCert.data )
		return CPL_PARAM_MISSING;

	long rv = 0;

	rv = pCMAPIInterface->DecodeCert(pCertEntry->binCert.data,&(pPathStruct->cert));
	if( 0 != rv || NULL == pPathStruct->cert )
		return CPL_DECODE_CERT_FAIL;

	#if defined(_DEBUG) && defined(_WIN32)
		rv = CPLI_CopyBytesStruct(&(pCertEntry->binCert),&(pPathStruct->asn1cert));
		ASSERT( 0 == rv );
	#else
		CPLI_CopyBytesStruct(&(pCertEntry->binCert),&(pPathStruct->asn1cert));
	#endif

	return 0;
}

#ifdef _DEBUG_VERBOSE
#undef _DEBUG_VERBOSE
#endif
