/*++ Copyright (c) 1996 Microsoft Corporation Module Name: iiscmr.cxx Abstract: Classes to handle IIS client cert wildcard mappings Author: Philippe Choquier (phillich) 17-oct-1996 --*/ #include #include #include #define _CRYPT32_ #include #include #include #include #include #include #define DLL_IMPLEMENTATION #include #include "iismaprc.h" #include "mapmsg.h" #include #include #include #include typedef WINCRYPT32API BOOL (WINAPI* PFNCryptDecodeObject)( IN DWORD dwCertEncodingType, IN LPCSTR lpszStructType, IN const BYTE *pbEncoded, IN DWORD cbEncoded, IN DWORD dwFlags, OUT void *pvStructInfo, IN OUT DWORD *pcbStructInfo ); // // GLOBALS // // // definition of ASN.1 <> X.509 name conversion // MAP_ASN aMapAsn[] = { { szOID_COUNTRY_NAME, "", IDS_CMR_ASN_C }, { szOID_ORGANIZATION_NAME, "", IDS_CMR_ASN_O }, { szOID_ORGANIZATIONAL_UNIT_NAME, "", IDS_CMR_ASN_OU }, { szOID_COMMON_NAME, "", IDS_CMR_ASN_CN }, { szOID_LOCALITY_NAME, "", IDS_CMR_ASN_L }, { szOID_STATE_OR_PROVINCE_NAME, "", IDS_CMR_ASN_S }, { szOID_TITLE, "", IDS_CMR_ASN_T }, { szOID_GIVEN_NAME, "", IDS_CMR_ASN_GN }, { szOID_INITIALS, "", IDS_CMR_ASN_I }, { "1.2.840.113549.1.9.1", "", IDS_CMR_ASN_Email }, { "1.2.840.113549.1.9.8", "", IDS_CMR_ASN_Addr }, // warning: can include CR/LF } ; HINSTANCE hCapi2Lib = NULL; PFNCryptDecodeObject pfnCryptDecodeObject = NULL; char HEXTOA[] = "0123456789abcdef"; MAP_FIELD aMapField[] = { { CERT_FIELD_ISSUER, IDS_CMR_X509FLD_IS, "" }, { CERT_FIELD_SUBJECT, IDS_CMR_X509FLD_SU, "" }, { CERT_FIELD_SERIAL_NUMBER, IDS_CMR_X509FLD_SN, "" }, } ; DWORD adwFieldFlags[]={ CERT_FIELD_FLAG_CONTAINS_SUBFIELDS, CERT_FIELD_FLAG_CONTAINS_SUBFIELDS, 0, }; // // Global functions // BOOL Unserialize( LPBYTE* ppB, LPDWORD pC, LPDWORD pU ) /*++ Routine Description: Unserialize a DWORD pU is updated with DWORD from *ppB, ppB & pC are updated Arguments: ppB - ptr to addr of buffer pC - ptr to byte count in buffer pU - ptr to DWORD where to unserialize Return Value: TRUE if successful, FALSE on error --*/ { DWORD dwTemp; // added to handle 64 bit alignment problems if ( *pC >= sizeof( DWORD ) ) { dwTemp = **ppB; *pU = dwTemp; *ppB += sizeof(DWORD); *pC -= sizeof(DWORD); return TRUE; } return FALSE; } BOOL Unserialize( LPBYTE* ppB, LPDWORD pC, LPBOOL pU ) /*++ Routine Description: Unserialize a BOOL pU is updated with BOOL from *ppB, ppB & pC are updated Arguments: ppB - ptr to addr of buffer pC - ptr to byte count in buffer pU - ptr to BOOL where to unserialize Return Value: TRUE if successful, FALSE on error --*/ { BOOL bTemp; // added to handle 64 bit alignment problems if ( *pC >= sizeof( BOOL ) ) { bTemp = **ppB; *pU = bTemp; *ppB += sizeof(BOOL); *pC -= sizeof(BOOL); return TRUE; } return FALSE; } // // Extensible buffer class // BOOL CStoreXBF::Need( DWORD dwNeed ) /*++ Routine Description: Insure that CStoreXBF can store at least dwNeed bytes including bytes already stored. Arguments: dwNeed - minimum of bytes available for storage Return Value: TRUE if successful, FALSE on error --*/ { if ( dwNeed > m_cAllocBuff ) { dwNeed = ((dwNeed + m_cGrain)/m_cGrain)*m_cGrain; LPBYTE pN = (LPBYTE)LocalAlloc( LMEM_FIXED, dwNeed ); if ( pN == NULL ) { return FALSE; } if ( m_cUsedBuff ) { // expanding buffer // copy existing data into new buffer // DBG_ASSERT( m_cUsedBuff <= dwNeed ); memcpy( pN, m_pBuff, m_cUsedBuff ); } m_cAllocBuff = dwNeed; LocalFree( m_pBuff ); m_pBuff = pN; } return TRUE; } BOOL CStoreXBF::Save( HANDLE hFile ) /*++ Routine Description: Save to file Arguments: hFile - file handle Return Value: TRUE if successful, FALSE on error --*/ { DWORD dwWritten; return WriteFile( hFile, GetBuff(), GetUsed(), &dwWritten, NULL ) && dwWritten == GetUsed(); } BOOL CStoreXBF::Load( HANDLE hFile ) /*++ Routine Description: Load from file Arguments: hFile - file handle Return Value: TRUE if successful, FALSE on error --*/ { DWORD dwS = GetFileSize( hFile, NULL ); DWORD dwRead; if ( dwS != 0xffffffff && Need( dwS ) && ReadFile( hFile, GetBuff(), dwS, &dwRead, NULL ) && dwRead == dwS ) { m_cUsedBuff = dwRead; return TRUE; } m_cUsedBuff = 0; return FALSE; } BOOL Serialize( CStoreXBF* pX, DWORD dw ) /*++ Routine Description: Serialize a DWORD in CStoreXBF Arguments: pX - ptr to CStoreXBF where to add serialized DWORD dw - DWORD to serialize Return Value: TRUE if successful, FALSE on error --*/ { return pX->Append( (LPBYTE)&dw, sizeof(dw) ); } // // extensible array of LPVOID // BOOL Serialize( CStoreXBF* pX, BOOL f ) /*++ Routine Description: Serialize a BOOL in CStoreXBF Arguments: pX - ptr to CStoreXBF where to add serialized BOOL f - BOOL to serialize Return Value: TRUE if successful, FALSE on error --*/ { return pX->Append( (LPBYTE)&f, sizeof(f) ); } DWORD CPtrXBF::AddPtr( LPVOID pV ) /*++ Routine Description: Add a ptr to array Arguments: pV - ptr to be added at end of array Return Value: index ( 0-based ) in array where added or INDEX_ERROR if error --*/ { DWORD i = GetNbPtr(); if ( Append( (LPBYTE)&pV, sizeof(pV)) ) { return i; } return INDEX_ERROR; } DWORD CPtrXBF::InsertPtr( DWORD iBefore, LPVOID pV ) /*++ Routine Description: Insert a ptr to array Arguments: iBefore - index where to insert entry, or 0xffffffff if add to array pV - ptr to be inserted Return Value: index ( 0-based ) in array where added or INDEX_ERROR if error --*/ { if ( iBefore == INDEX_ERROR || iBefore >= GetNbPtr() ) { return AddPtr( pV ); } if ( AddPtr( NULL ) != INDEX_ERROR ) { // // AddPtr has taken care of expanding // moving all the pointer passed the insertion point // using memmove is thus safe // memmove( GetBuff()+(iBefore+1)*sizeof(LPVOID), GetBuff()+iBefore*sizeof(LPVOID), GetUsed()-(iBefore+1)*sizeof(LPVOID) ); SetPtr( iBefore, pV ); return iBefore; } return INDEX_ERROR; } BOOL CPtrXBF::DeletePtr( DWORD i ) /*++ Routine Description: Delete a ptr from array Arguments: i - index of ptr to delete Return Value: TRUE if success, otherwise FALSE --*/ { // // shrinking is safe // memmove( GetBuff()+i*sizeof(LPVOID), GetBuff()+(i+1)*sizeof(LPVOID), GetUsed()-(i+1)*sizeof(LPVOID) ); DecreaseUse( sizeof(LPVOID) ); return TRUE; } BOOL CPtrXBF::Unserialize( LPBYTE* ppB, LPDWORD pC, DWORD cNbEntry ) /*++ Routine Description: Unserialize a ptr array Arguments: ppB - ptr to addr of buffer to unserialize from pC - ptr to count of bytes in buffer cNbEntry - # of ptr to unserialize from buffer Return Value: TRUE if success, otherwise FALSE --*/ { Reset(); if ( *pC >= cNbEntry * sizeof(LPVOID) && Append( *ppB, cNbEntry * sizeof(LPVOID) ) ) { *ppB += cNbEntry * sizeof(LPVOID); *pC -= cNbEntry * sizeof(LPVOID); return TRUE; } return FALSE; } BOOL CPtrXBF::Serialize( CStoreXBF* pX ) /*++ Routine Description: Serialize a ptr array Arguments: pX - ptr to CStoreXBF to serialize to Return Value: TRUE if success, otherwise FALSE --*/ { return pX->Append( (LPBYTE)GetBuff(), (DWORD)GetUsed() ); } // // extensible array of DWORDS // Added to make win64 stuff work // DWORD CPtrDwordXBF::AddPtr( DWORD pV ) /*++ Routine Description: Add a ptr to array Arguments: pV - ptr to be added at end of array Return Value: index ( 0-based ) in array where added or INDEX_ERROR if error --*/ { DWORD i = GetNbPtr(); if ( Append( (LPBYTE)&pV, sizeof(pV)) ) { return i; } return INDEX_ERROR; } DWORD CPtrDwordXBF::InsertPtr( DWORD iBefore, DWORD pV ) /*++ Routine Description: Insert a ptr to array Arguments: iBefore - index where to insert entry, or 0xffffffff if add to array pV - ptr to be inserted Return Value: index ( 0-based ) in array where added or INDEX_ERROR if error --*/ { if ( iBefore == INDEX_ERROR || iBefore >= GetNbPtr() ) { return AddPtr( pV ); } if ( AddPtr( NULL ) != INDEX_ERROR ) { // // AddPtr has taken care of adding additional memory // It is safe now to shift array passed the insertion point by one // memmove( GetBuff()+(iBefore+1)*sizeof(DWORD), GetBuff()+iBefore*sizeof(DWORD), GetUsed()-(iBefore+1)*sizeof(DWORD) ); SetPtr( iBefore, pV ); return iBefore; } return INDEX_ERROR; } BOOL CPtrDwordXBF::DeletePtr( DWORD i ) /*++ Routine Description: Delete a ptr from array Arguments: i - index of ptr to delete Return Value: TRUE if success, otherwise FALSE --*/ { // // shrinking is safe, no risk of buffer overflow // memmove( GetBuff()+i*sizeof(DWORD), GetBuff()+(i+1)*sizeof(DWORD), GetUsed()-(i+1)*sizeof(DWORD) ); DecreaseUse( sizeof(LPVOID) ); return TRUE; } BOOL CPtrDwordXBF::Unserialize( LPBYTE* ppB, LPDWORD pC, DWORD cNbEntry ) /*++ Routine Description: Unserialize a ptr array Arguments: ppB - ptr to addr of buffer to unserialize from pC - ptr to count of bytes in buffer cNbEntry - # of ptr to unserialize from buffer Return Value: TRUE if success, otherwise FALSE --*/ { Reset(); if ( *pC >= cNbEntry * sizeof(DWORD)) { if (Append( *ppB, cNbEntry * sizeof(DWORD) )) { *ppB += cNbEntry * sizeof(DWORD); *pC -= cNbEntry * sizeof(DWORD); return TRUE; } } return FALSE; } BOOL CPtrDwordXBF::Serialize( CStoreXBF* pX ) /*++ Routine Description: Serialize a ptr array Arguments: pX - ptr to CStoreXBF to serialize to Return Value: TRUE if success, otherwise FALSE --*/ { return pX->Append( (LPBYTE)GetBuff(), (DWORD)GetUsed() ); } // // string storage class // BOOL CAllocString::Set( LPSTR pS ) /*++ Routine Description: Set a string content, freeing prior content if any Arguments: pS - string to copy Return Value: TRUE if successful, FALSE on error --*/ { size_t l; Reset(); if ( ( m_pStr = (LPSTR)LocalAlloc( LMEM_FIXED, l = strlen(pS)+1 ) ) != NULL ) { memcpy( m_pStr, pS, l ); return TRUE; } return FALSE; } BOOL CAllocString::Append( LPSTR pS ) /*++ Routine Description: Append a string content Arguments: pS - string to append Return Value: TRUE if successful, FALSE on error --*/ { size_t l = m_pStr ? strlen(m_pStr ) : 0; size_t nl; LPSTR pStr; if ( (pStr = (LPSTR)LocalAlloc( LMEM_FIXED, l + (nl = strlen(pS)+1 ))) != NULL ) { memcpy( pStr, m_pStr, l ); memcpy( pStr+l, pS, nl ); // // Free the old block before we blow it away. // Reset(); m_pStr = pStr; return TRUE; } return FALSE; } BOOL CAllocString::Unserialize( LPBYTE* ppb, LPDWORD pc ) /*++ Routine Description: Unserialize a string Arguments: ppB - ptr to addr of buffer to unserialize from pC - ptr to count of bytes in buffer Return Value: TRUE if success, otherwise FALSE --*/ { DWORD dwL; if ( ::Unserialize( ppb, pc, &dwL ) && (m_pStr = (LPSTR)LocalAlloc( LMEM_FIXED, dwL + 1)) != NULL ) { memcpy( m_pStr, *ppb, dwL ); m_pStr[dwL] = '\0'; *ppb += dwL; *pc -= dwL; return TRUE; } return FALSE; } BOOL CAllocString::Serialize( CStoreXBF* pX ) /*++ Routine Description: Serialize a string Arguments: pX - ptr to CStoreXBF to serialize to Return Value: TRUE if success, otherwise FALSE --*/ { LPSTR pS = m_pStr ? m_pStr : ""; return ::Serialize( pX, (DWORD)strlen(pS) ) && pX->Append( pS ); } // // binary object, contains ptr & size // BOOL CBlob::Set( LPBYTE pStr, DWORD cStr ) /*++ Routine Description: Store a buffer in a blob object buffer is copied inside blob blob is reset before copy Arguments: pStr - ptr to buffer to copy cStr - length of buffer Return Value: TRUE if success, otherwise FALSE --*/ { Reset(); return InitSet( pStr, cStr); } BOOL CBlob::InitSet( LPBYTE pStr, DWORD cStr ) /*++ Routine Description: Store a buffer in a blob object buffer is copied inside blob blob is not reset before copy, initial blob content ignored Arguments: pStr - ptr to buffer to copy cStr - length of buffer Return Value: TRUE if success, otherwise FALSE --*/ { if ( (m_pStr = (LPBYTE)LocalAlloc( LMEM_FIXED, cStr )) != NULL ) { memcpy( m_pStr, pStr, cStr ); m_cStr = cStr; return TRUE; } return FALSE; } BOOL CBlob::Unserialize( LPBYTE* ppB, LPDWORD pC ) /*++ Routine Description: Unserialize a blob Arguments: ppB - ptr to addr of buffer to unserialize from pC - ptr to count of bytes in buffer Return Value: TRUE if success, otherwise FALSE --*/ { Reset(); if ( ::Unserialize( ppB, pC, &m_cStr ) && *pC >= m_cStr && ( m_pStr = (LPBYTE)LocalAlloc( LMEM_FIXED, m_cStr ) ) != NULL ) { memcpy( m_pStr, *ppB, m_cStr ); *ppB += m_cStr; *pC -= m_cStr; } else { m_cStr = 0; return FALSE; } return TRUE; } BOOL CBlob::Serialize( CStoreXBF* pX ) /*++ Routine Description: Serialize a blob Arguments: pX - ptr to CStoreXBF to serialize to Return Value: TRUE if success, otherwise FALSE --*/ { return ::Serialize( pX, m_cStr ) && pX->Append( m_pStr, m_cStr ); } // // extensible array of strings // CStrPtrXBF::~CStrPtrXBF( ) /*++ Routine Description: CStrPtrXBF destructor Arguments: None Return Value: Nothing --*/ { DWORD iM = GetNbPtr(); UINT i; for ( i = 0 ; i < iM ; ++i ) { ((CAllocString*)GetPtrAddr(i))->Reset(); } } DWORD CStrPtrXBF::AddEntry( LPSTR pS ) /*++ Routine Description: Add a string to array string content is copied in array Arguments: pS - string to be added at end of array Return Value: index ( 0-based ) in array where added or INDEX_ERROR if error --*/ { DWORD i; if ( (i = AddPtr( NULL )) != INDEX_ERROR ) { return ((CAllocString*)GetPtrAddr(i))->Set( pS ) ? i : INDEX_ERROR; } return INDEX_ERROR; } DWORD CStrPtrXBF::InsertEntry( DWORD iBefore, LPSTR pS ) /*++ Routine Description: Insert a string in array string content is copied in array Arguments: iBefore - index where to insert entry, or 0xffffffff if add to array pS - string to be inserted in array Return Value: index ( 0-based ) in array where added or INDEX_ERROR if error --*/ { DWORD i; if ( (i = InsertPtr( iBefore, NULL )) != INDEX_ERROR ) { return ((CAllocString*)GetPtrAddr(i))->Set( pS ) ? i : INDEX_ERROR; } return INDEX_ERROR; } BOOL CStrPtrXBF::DeleteEntry( DWORD i ) /*++ Routine Description: Delete a string from array Arguments: i - index of string to delete Return Value: TRUE if success, otherwise FALSE --*/ { if ( i < GetNbPtr() ) { ((CAllocString*)GetPtrAddr(i))->Reset(); DeletePtr( i ); return TRUE; } return FALSE; } BOOL CStrPtrXBF::Unserialize( LPBYTE* ppB, LPDWORD pC, DWORD cNbEntry ) /*++ Routine Description: Unserialize a string array Arguments: ppB - ptr to addr of buffer pC - ptr to byte count in buffer cNbEntry - # of entry to unserialize Return Value: TRUE if successful, FALSE on error --*/ { UINT i; Reset(); CAllocString empty; for ( i = 0 ; i < cNbEntry ; ++i ) { if ( !Append( (LPBYTE)&empty, sizeof(empty)) || !((CAllocString*)GetPtrAddr(i))->Unserialize( ppB, pC ) ) { return FALSE; } } return TRUE; } BOOL CStrPtrXBF::Serialize( CStoreXBF* pX ) /*++ Routine Description: Serialize a string array Arguments: pX - ptr to CStoreXBF where to add serialized DWORD Return Value: TRUE if successful, FALSE on error --*/ { UINT i; for ( i = 0 ; i < GetNbEntry() ; ++i ) { if ( !((CAllocString*)GetPtrAddr(i))->Serialize( pX ) ) { return FALSE; } } return TRUE; } // // extensible array of binary object // ptr & size are stored for each entry // CBlobXBF::~CBlobXBF( ) /*++ Routine Description: CBlobXBF destructor Arguments: None Return Value: Nothing --*/ { DWORD iM = GetUsed()/sizeof(CBlob); UINT i; for ( i = 0 ; i < iM ; ++i ) { GetBlob(i)->Reset(); } } VOID CBlobXBF::Reset( ) /*++ Routine Description: Reset the blob content to NULL Arguments: None Return Value: Nothing --*/ { DWORD iM = GetUsed()/sizeof(CBlob); UINT i; for ( i = 0 ; i < iM ; ++i ) { GetBlob(i)->Reset(); } CStoreXBF::Reset(); } DWORD CBlobXBF::AddEntry( LPBYTE pS, DWORD cS ) /*++ Routine Description: Add a buffer to blob array buffer content is copied in array Arguments: pS - buffer to be added at end of array cS - length of buffer Return Value: index ( 0-based ) in array where added or INDEX_ERROR if error --*/ { DWORD i = GetNbEntry(); if ( Append( (LPBYTE)&pS, sizeof(CBlob) ) ) { return GetBlob(i)->InitSet( pS, cS ) ? i : INDEX_ERROR; } return INDEX_ERROR; } DWORD CBlobXBF::InsertEntry( DWORD iBefore, LPSTR pS, DWORD cS ) /*++ Routine Description: Insert a buffer in blob array buffer content is copied in array Arguments: iBefore - index where to insert entry, or 0xffffffff if add to array pS - buffer to be inserted in array cS - length of buffer Return Value: index ( 0-based ) in array where added or INDEX_ERROR if error --*/ { if ( iBefore == INDEX_ERROR || iBefore >= GetNbEntry() ) { return AddEntry( (LPBYTE)pS, cS ); } if ( iBefore < GetNbEntry() && Append( (LPBYTE)&pS, sizeof(CBlob) ) ) { // // Append has taken care of expanding memory // safe to memmove by one entry // memmove( GetBuff()+(iBefore+1)*sizeof(CBlob), GetBuff()+iBefore*sizeof(CBlob), GetUsed()-(iBefore+1)*sizeof(CBlob) ); return GetBlob(iBefore)->InitSet( (LPBYTE)pS, cS ) ? iBefore : INDEX_ERROR; } return INDEX_ERROR; } BOOL CBlobXBF::DeleteEntry( DWORD i ) /*++ Routine Description: Delete a entry from blob array Arguments: i - index of string to delete Return Value: TRUE if success, otherwise FALSE --*/ { if ( i < GetNbEntry() ) { GetBlob(i)->Reset(); // shrinking is safe, no risk of buffer overflow // memmove( GetBuff()+i*sizeof(CBlob), GetBuff()+(i+1)*sizeof(CBlob), GetUsed()-(i+1)*sizeof(CBlob) ); DecreaseUse( sizeof(CBlob) ); return TRUE; } return FALSE; } BOOL CBlobXBF::Unserialize( LPBYTE* ppB, LPDWORD pC, DWORD cNbEntry ) /*++ Routine Description: Unserialize a blob array Arguments: ppB - ptr to addr of buffer to unserialize from pC - ptr to count of bytes in buffer cNbEntry - # of ptr to unserialize from buffer Return Value: TRUE if success, otherwise FALSE --*/ { UINT i; Reset(); CBlob empty; for ( i = 0 ; i < cNbEntry ; ++i ) { if ( !Append( (LPBYTE)&empty, sizeof(empty) ) || !GetBlob(i)->Unserialize( ppB, pC ) ) { return FALSE; } } return TRUE; } BOOL CBlobXBF::Serialize( CStoreXBF* pX ) /*++ Routine Description: Serialize a blob array Arguments: pX - ptr to CStoreXBF where to add serialized blob Return Value: TRUE if successful, FALSE on error --*/ { UINT i; for ( i = 0 ; i < GetNbEntry() ; ++i ) { if ( !GetBlob(i)->Serialize( pX ) ) { return FALSE; } } return TRUE; } CDecodedCert::CDecodedCert( PCERT_CONTEXT pCert ) /*++ Routine Description: Constructor Store a reference to cert ( cert data is NOT copied ) Arguments: pCert - cert Return Value: Nothing --*/ { UINT i; for ( i = 0 ; i < CERT_FIELD_LAST ; ++i ) { aniFields[i] = NULL; } pCertCtx = (PCCERT_CONTEXT)pCert; } CDecodedCert::~CDecodedCert( ) /*++ Routine Description: Destructor Arguments: None Return Value: Nothing --*/ { UINT i; for ( i = 0 ; i < CERT_FIELD_LAST ; ++i ) { if ( aniFields[i] != NULL ) { LocalFree( aniFields[i] ); } } } BOOL CDecodedCert::GetIssuer( LPVOID* ppCert, LPDWORD pcCert ) /*++ Routine Description: Get the issuer portion of a cert Returns a reference : issuer is NOT copied Arguments: ppCert - updated with ptr to issuer pcCert - updated with issuer byte count Return Value: TRUE if successful, FALSE on error --*/ { if ( pCertCtx == NULL ) { return FALSE; } *ppCert = pCertCtx->pCertInfo->Issuer.pbData; *pcCert = pCertCtx->pCertInfo->Issuer.cbData; return TRUE; } PCERT_RDN_ATTR * CDecodedCert::GetSubField( CERT_FIELD_ID fi, LPSTR pszAsnName, LPDWORD pcElements ) /*++ Routine Description: Returns a cert sub-field ( e.g. Issuer.O ). There may be multiple entries for a given subfield. This functions returns an array of attribute values Arguments: fi - cert field where sub-field is located pszAsnName - ASN.1 name of sub-field inside fi pcElements - Number of elements returned in array Return Value: ptr to array of pointers to attribute blobs if success, otherwise NULL --*/ { CERT_NAME_BLOB* pBlob; DWORD cbNameInfo; PCERT_NAME_INFO pNameInfo; DWORD cRDN; DWORD cAttr; PCERT_RDN pRDN; PCERT_RDN_ATTR pAttr; PCERT_RDN_ATTR* pAttrValues = NULL; DWORD cRet = 0; DWORD cMaxRet = 0; if ( pfnCryptDecodeObject == NULL ) { SetLastError(ERROR_PROC_NOT_FOUND); return NULL; } *pcElements = 0; switch( fi ) { case CERT_FIELD_ISSUER: pBlob = &pCertCtx->pCertInfo->Issuer; break; case CERT_FIELD_SUBJECT: pBlob = &pCertCtx->pCertInfo->Subject; break; case CERT_FIELD_SERIAL_NUMBER: pAttrValues = (PCERT_RDN_ATTR*) LocalAlloc( LPTR, sizeof( PCERT_RDN_ATTR ) ); if ( pAttrValues == NULL ) { return NULL; } *pcElements = 1; // // Setup a CERT_RDN_ATTR so that GetSubField() can always return a // pointer to an array of CERT_RDN_ATTRs // SerialNumber.dwValueType = CERT_RDN_OCTET_STRING; SerialNumber.Value = pCertCtx->pCertInfo->SerialNumber; pAttrValues[ 0 ] = &SerialNumber; return pAttrValues; default: return NULL; } if ( pszAsnName == NULL ) { return NULL; } if ( (pNameInfo = aniFields[fi]) == NULL ) { if (!(*pfnCryptDecodeObject)(X509_ASN_ENCODING, (LPCSTR)X509_NAME, pBlob->pbData, pBlob->cbData, 0, NULL, &cbNameInfo)) { return NULL; } if (NULL == (pNameInfo = (PCERT_NAME_INFO)LocalAlloc(LMEM_FIXED,cbNameInfo))) { return NULL; } if (!(*pfnCryptDecodeObject)(X509_ASN_ENCODING, (LPCSTR)X509_NAME, pBlob->pbData, pBlob->cbData, 0, pNameInfo, &cbNameInfo)) { LocalFree( pNameInfo ); return NULL; } aniFields[fi] = pNameInfo; } for (cRDN = pNameInfo->cRDN, pRDN = pNameInfo->rgRDN; cRDN > 0; cRDN--, pRDN++) { for ( cAttr = pRDN->cRDNAttr, pAttr = pRDN->rgRDNAttr ; cAttr > 0 ; cAttr--, ++pAttr ) { if ( !strcmp( pAttr->pszObjId, pszAsnName ) ) { if ( ( cRet + 1 ) > cMaxRet ) { cMaxRet += 10; if ( pAttrValues ) { PCERT_RDN_ATTR* pReallocatedAttrValues = (PCERT_RDN_ATTR*) LocalReAlloc( pAttrValues, sizeof( PCERT_RDN_ATTR ) * cMaxRet, LMEM_MOVEABLE ); if ( pReallocatedAttrValues == NULL ) { LocalFree( pAttrValues ); return NULL; } pAttrValues = pReallocatedAttrValues; } else { pAttrValues = (PCERT_RDN_ATTR*) LocalAlloc( LPTR, sizeof( PCERT_RDN_ATTR ) * cMaxRet ); if ( pAttrValues == NULL ) { return NULL; } } } pAttrValues[ cRet ] = pAttr; cRet++; } } } *pcElements = cRet; return pAttrValues; } CIssuerStore::CIssuerStore( ) /*++ Routine Description: Constructor Arguments: None Return Value: Nothing --*/ { m_fValid = TRUE; } CIssuerStore::~CIssuerStore( ) /*++ Routine Description: Destructor Arguments: None Return Value: Nothing --*/ { } VOID CIssuerStore::Reset( ) /*++ Routine Description: Reset issuer list Arguments: None Return Value: None --*/ { m_pIssuerNames.Reset(); m_IssuerCerts.Reset(); m_fValid = TRUE; } BOOL CIssuerStore::Unserialize( CStoreXBF* pX ) /*++ Routine Description: Unserialize issuer list Arguments: pX - CStoreXBF to unserialize from Return Value: TRUE if success, otherwise FALSE --*/ { LPBYTE pb = pX->GetBuff(); DWORD dw = pX->GetUsed(); return Unserialize( &pb, &dw ); } BOOL CIssuerStore::Unserialize( LPBYTE* ppb, LPDWORD pc ) /*++ Routine Description: Unserialize issuer list Arguments: ppB - ptr to addr of buffer to unserialize from pC - ptr to count of bytes in buffer Return Value: TRUE if success, otherwise FALSE --*/ { DWORD dwC; Reset(); if ( ::Unserialize( ppb, pc, &dwC ) && m_pIssuerNames.Unserialize( ppb, pc, dwC ) && m_IssuerCerts.Unserialize( ppb, pc, dwC ) ) { return TRUE; } m_fValid = FALSE; return FALSE; } BOOL CIssuerStore::Serialize( CStoreXBF* pX ) /*++ Routine Description: Serialize issuer list Arguments: pX - CStoreXBF to serialize to Return Value: TRUE if success, otherwise FALSE --*/ { if ( !m_fValid ) { return FALSE; } return ::Serialize( pX, (DWORD)GetNbIssuers() ) && m_pIssuerNames.Serialize( pX ) && m_IssuerCerts.Serialize( pX ); } CCertGlobalRuleInfo::CCertGlobalRuleInfo( ) /*++ Routine Description: Constructor Arguments: None Return Value: Nothing --*/ { m_fValid = TRUE; m_fEnabled = TRUE; } CCertGlobalRuleInfo::~CCertGlobalRuleInfo( ) /*++ Routine Description: Destructor Arguments: None Return Value: Nothing --*/ { } BOOL CCertGlobalRuleInfo::Reset( ) /*++ Routine Description: Reset to default values Arguments: None Return Value: Nothing --*/ { m_Order.Reset(); m_fValid = TRUE; m_fEnabled = TRUE; return TRUE; } BOOL CCertGlobalRuleInfo::AddRuleOrder( ) /*++ Routine Description: Add a rule at end of rule order array Arguments: None Return Value: TRUE if success, otherwise FALSE --*/ { DWORD i; if ( (i = m_Order.AddPtr( NULL )) != INDEX_ERROR ) { // m_Order.SetPtr( i, (LPVOID) i ); m_Order.SetPtr( i, i ); return TRUE; } m_fValid = FALSE; return FALSE; } BOOL CCertGlobalRuleInfo::DeleteRuleById( DWORD dwId, BOOL DecrementAbove ) /*++ Routine Description: Delete a rule based on index in rules array Arguments: dwId - index in rules array DecrementAbove - flag indicating if items with a index above dwID need to be decremented. This is usually caused by the item being removed from the main array. Return Value: TRUE if success, otherwise FALSE --*/ { UINT iO; UINT iMax = m_Order.GetNbPtr(); DWORD id; for ( iO = 0 ; iO < iMax ; ++iO ) { // if it equals dwID, remove it if ( (DWORD_PTR)m_Order.GetPtr( iO ) == dwId ) { m_Order.DeletePtr( iO ); if ( DecrementAbove ) { // if we have been asked to decrement the remaining items, // need to do this in another loop here. Yuck. - Boyd iMax = m_Order.GetNbPtr(); for ( iO = 0 ; iO < iMax ; ++iO ) { // the id in question id = (DWORD)((DWORD_PTR)m_Order.GetPtr( iO )); // if it is bigger, decrement by one if ( id > dwId ) { id--; // put it back in place // m_Order.SetPtr( iO, (LPVOID) id ); //SUNDOWN ALERT m_Order.SetPtr( iO, id ); //SUNDOWN ALERT } } } // return early return TRUE; } } m_fValid = FALSE; return FALSE; } BOOL CCertGlobalRuleInfo::SerializeGlobalRuleInfo( CStoreXBF* pX ) /*++ Routine Description: Serialize mapper global information Arguments: pX - ptr to CStoreXBF to serialize to Return Value: TRUE if success, otherwise FALSE --*/ { return ::Serialize( pX, GetRuleOrderCount() ) && pX->Append( (LPBYTE)GetRuleOrderArray(), sizeof(DWORD)*GetRuleOrderCount() ) && ::Serialize( pX, m_fEnabled ); } BOOL CCertGlobalRuleInfo::UnserializeGlobalRuleInfo( LPBYTE* ppB, LPDWORD pC ) /*++ Routine Description: Unserialize mapper global info Arguments: ppB - ptr to addr of buffer to unserialize from pC - ptr to count of bytes in buffer Return Value: TRUE if success, otherwise FALSE --*/ { DWORD dwO; Reset(); return ::Unserialize( ppB, pC, &dwO ) && m_Order.Unserialize( ppB, pC, dwO ) && ::Unserialize( ppB, pC, &m_fEnabled ); } CCertMapRule::CCertMapRule( ) /*++ Routine Description: Constructor Arguments: None Return Value: Nothing --*/ { m_fEnabled = TRUE; m_fDenyAccess = FALSE; m_fMatchAllIssuers = TRUE; m_fValid = TRUE; } CCertMapRule::~CCertMapRule( ) /*++ Routine Description: Desctructor Arguments: None Return Value: Nothing --*/ { } VOID CCertMapRule::Reset( ) /*++ Routine Description: Reset to default values Arguments: None Return Value: Nothing --*/ { m_fEnabled = TRUE; m_fDenyAccess = FALSE; m_fMatchAllIssuers = TRUE; m_fValid = TRUE; m_asRuleName.Reset(); m_asAccount.Reset(); m_asPassword.Reset(); m_ElemsContent.Reset(); m_ElemsSubfield.Reset(); m_ElemsField.Reset(); m_ElemsFlags.Reset(); m_Issuers.Reset(); m_IssuersAcceptStatus.Reset(); } BOOL CCertMapRule::Unserialize( CStoreXBF* pX ) /*++ Routine Description: Unserialize a mapping rule Arguments: pX - ptr to CStoreXBF to unserialize from Return Value: TRUE if successful, FALSE on error --*/ { LPBYTE pb = pX->GetBuff(); DWORD dw = pX->GetUsed(); return Unserialize( &pb, &dw ); } BOOL CCertMapRule::Unserialize( LPBYTE* ppb, LPDWORD pc ) /*++ Routine Description: Unserialize a mapping rule Arguments: ppB - ptr to addr of buffer pC - ptr to byte count in buffer Return Value: TRUE if successful, FALSE on error --*/ { DWORD dwEl; DWORD dwIs; Reset(); if ( m_asRuleName.Unserialize( ppb, pc ) && m_asAccount.Unserialize( ppb, pc ) && m_asPassword.Unserialize( ppb, pc ) && ::Unserialize( ppb, pc, &m_fEnabled ) && ::Unserialize( ppb, pc, &m_fDenyAccess ) && ::Unserialize( ppb, pc, &dwEl ) && m_ElemsContent.Unserialize( ppb, pc, dwEl ) && m_ElemsSubfield.Unserialize( ppb, pc, dwEl ) && m_ElemsField.Unserialize( ppb, pc, dwEl ) && m_ElemsFlags.Unserialize( ppb, pc, dwEl ) && ::Unserialize( ppb, pc, &dwIs ) && m_Issuers.Unserialize( ppb, pc, dwIs ) && m_IssuersAcceptStatus.Unserialize( ppb, pc, dwIs ) && ::Unserialize( ppb, pc, &m_fMatchAllIssuers ) ) { return TRUE; } m_fValid = FALSE; return FALSE; } BOOL CCertMapRule::Serialize( CStoreXBF* pX ) /*++ Routine Description: Serialize a rule mapping in CStoreXBF Arguments: pX - ptr to CStoreXBF where to add serialized DWORD Return Value: TRUE if successful, FALSE on error --*/ { if ( m_fValid && m_asRuleName.Serialize( pX ) && m_asAccount.Serialize( pX ) && m_asPassword.Serialize( pX ) && ::Serialize( pX, m_fEnabled ) && ::Serialize( pX, m_fDenyAccess ) && ::Serialize( pX, GetRuleElemCount() ) && m_ElemsContent.Serialize( pX ) && m_ElemsSubfield.Serialize( pX ) && m_ElemsField.Serialize( pX ) && m_ElemsFlags.Serialize( pX ) && ::Serialize( pX, m_Issuers.GetNbEntry() ) && m_Issuers.Serialize( pX ) && m_IssuersAcceptStatus.Serialize( pX ) && ::Serialize( pX, m_fMatchAllIssuers ) ) { return TRUE; } return FALSE; } BOOL CCertMapRule::SetRuleAccount( LPSTR pszAcct ) /*++ Routine Description: Set NT account returned by this rule if match Arguments: pszAcct - NT account to use Return Value: TRUE if successful, FALSE on error error ERROR_INVALID_NAME if replacement name ( %subfield% ) invalid --*/ { LPSTR pR; LPSTR pD; // // Check replacement field valid // if ( ( pR = strchr( pszAcct, '%' ) ) != NULL ) { ++pR; if ( (pD = strchr( pR, '%' )) == NULL ) { SetLastError( ERROR_INVALID_NAME ); return FALSE; } *pD = '\0'; if ( !MapSubFieldToAsn1( pR ) ) { *pD = '%'; SetLastError( ERROR_INVALID_NAME ); return FALSE; } *pD = '%'; } return m_asAccount.Set( pszAcct ); } //static LPBYTE CCertMapRule::CertMapMemstr( LPBYTE pStr, UINT cStr, LPBYTE pSub, UINT cSub, BOOL fCaseInsensitive ) /*++ Routine Description: Find 1st occurence of block of memory inside buffer Arguments: pStr - buffer where to search cStr - length of pStr pSub - buffer to search for in pStr cSub - length of pSub fCaseInsensitive - TRUE is case insensitive search Return Value: Ptr to 1st occurence of pSub in pStr or NULL if not found --*/ { LPBYTE p; LPBYTE pN; if ( cSub > cStr ) { return NULL; } UINT ch = *pSub; if ( fCaseInsensitive ) { if ( cStr >= cSub && cSub ) { cStr -= cSub - 1; for ( p = pStr ; cStr ; ++p, --cStr ) { if ( !_memicmp( p, pSub, cSub ) ) { return p; } } } } else { for ( p = pStr ; ( pN = (LPBYTE)memchr( p, ch, cStr ) ) != NULL ; ) { if ( !memcmp( pN, pSub, cSub ) ) { return pN; } cStr -= (UINT)(pN - p + 1); p = pN + 1; } } return NULL; } // return ERROR_INVALID_NAME if no match BOOL CCertMapRule::Match( CDecodedCert* pC, CDecodedCert* /*pAuth*/, LPSTR pszAcct, LPSTR pszPwd, LPBOOL pfDenied ) /*++ Routine Description: Check if rule match certificate Arguments: pC - client certificate pAuth - certifying authority (not used), pszAcct - updated with NT account on success, assumed to be at longer then UNLEN+IIS_DNLEN+1 pszPwd - updated with NT password on success, assumed to be longer then PWLEN long pfDenied - updated with deny access status for this match on success TRUE if access is denied for this match, otherwise FALSE Return Value: TRUE if successful, FALSE on error Errors: ERROR_ARENA_TRASHED if rule internal state is invalid ERROR_INVALID_NAME if no match --*/ { UINT i; UINT iMax; DWORD cObjLen; DWORD cMatch = 0; BOOL fSt = FALSE; LPBYTE pMatch = NULL; INT iRet; BOOL fCaseInsensitive; PCERT_RDN_ATTR * pAttrValues; DWORD cAttrValues; DWORD cAttr; PBYTE pContent; BOOL fConverted = FALSE; PBYTE pConverted = NULL; if ( !m_fEnabled ) { return FALSE; } if ( !m_fValid ) { SetLastError( ERROR_ARENA_TRASHED ); return FALSE; } iMax = GetRuleElemCount(); for ( i = 0 ; i < iMax ; ++i ) { m_ElemsContent.GetEntry( i, &pMatch, &cMatch ); --cMatch; if( ( pAttrValues = pC->GetSubField( (CERT_FIELD_ID)((UINT_PTR)m_ElemsField.GetPtr(i)), m_ElemsSubfield.GetEntry( i ), &cAttrValues ) ) != NULL ) { fCaseInsensitive = (DWORD)((DWORD_PTR)m_ElemsFlags.GetPtr(i) & CMR_FLAGS_CASE_INSENSITIVE); for ( cAttr = 0; cAttr < cAttrValues; cAttr++ ) { fConverted = FALSE; pContent = pAttrValues[ cAttr ]->Value.pbData; cObjLen = pAttrValues[ cAttr ]->Value.cbData; if ( pAttrValues[ cAttr ]->dwValueType == CERT_RDN_UNICODE_STRING ) { cObjLen /= sizeof( WCHAR ); // // Convert UNICODE cert value to multibyte // iRet = WideCharToMultiByte( CP_ACP, 0, (WCHAR*) pContent, cObjLen, NULL, 0, NULL, NULL ); if ( !iRet ) { fSt = FALSE; break; } pConverted = (PBYTE) LocalAlloc( LPTR, iRet ); if ( pConverted == NULL ) { fSt = FALSE; break; } iRet = WideCharToMultiByte( CP_ACP, 0, (WCHAR*) pContent, cObjLen, (CHAR*) pConverted, iRet, NULL, NULL ); if ( !iRet ) { fSt = FALSE; LocalFree( pConverted ); break; } fConverted = TRUE; pContent = (PBYTE) pConverted; cObjLen = iRet; } switch ( pMatch[0] ) { case MATCH_ALL: fSt = cObjLen == cMatch && ( fCaseInsensitive ? !_memicmp( pMatch+1, pContent, cObjLen ) : !memcmp( pMatch+1, pContent, cObjLen ) ); break; case MATCH_FIRST: fSt = cObjLen >= cMatch && ( fCaseInsensitive ? !_memicmp( pMatch+1, pContent, cMatch ) : !memcmp( pMatch+1, pContent, cMatch ) ); break; case MATCH_LAST: fSt = cObjLen >= cMatch && ( fCaseInsensitive ? !_memicmp( pMatch+1, pContent+cObjLen-cMatch, cMatch ) : !memcmp( pMatch+1, pContent+cObjLen-cMatch, cMatch ) ); break; case MATCH_IN: fSt = CertMapMemstr( pContent, cObjLen, pMatch + 1, cMatch, fCaseInsensitive ) != NULL; break; default: fSt = FALSE; } if ( fConverted ) { LocalFree( pConverted ); pConverted = NULL; } if ( fSt ) { break; } } LocalFree( pAttrValues ); pAttrValues = NULL; if ( !fSt ) { break; } } else if ( cMatch ) { // non empty rule on n/a subfield : stop looking other matches break; } } // // if client cert match, check issuers // if ( i == iMax ) { fSt = TRUE; if ( fSt ) { *pfDenied = m_fDenyAccess; if ( GetRuleAccount() != NULL && GetRulePassword() != NULL ) { // // Truncate account and password // if they don't fit. // It will render the output data useless // but will prevent buffer overflow // if invalid too long accounts and pwds // happen to be stored in mappings strncpy( pszAcct, GetRuleAccount(), UNLEN + IIS_DNLEN + 1 ); pszAcct[ UNLEN + IIS_DNLEN + 1 ] = '\0'; strncpy( pszPwd, GetRulePassword(), PWLEN ); pszAcct[ PWLEN ] = '\0'; } else { SetLastError( ERROR_INVALID_PARAMETER ); fSt = FALSE; } } return fSt; } SetLastError( ERROR_INVALID_NAME ); return FALSE; } BOOL CCertMapRule::GetRuleElem( DWORD i, CERT_FIELD_ID* pfiField, LPSTR* ppContent, LPDWORD pcContent, LPSTR* ppSubField, LPDWORD pdwFlags ) /*++ Routine Description: Access a rule element Arguments: i - index ( 0-based ) of element to access pfiField - updated with CERT_FIELD_ID of this element ppContent - updated with ptr to match binary form pcContent - updated with length of match binary form ppSubField - updated with ASN.1 name of cert sub-field for match pdwFlags - updated with flags Return Value: TRUE if successful, FALSE on error --*/ { if ( !m_fValid ) { SetLastError( ERROR_ARENA_TRASHED ); return FALSE; } if ( (*ppSubField = m_ElemsSubfield.GetEntry( i )) != NULL && m_ElemsContent.GetEntry( i, (LPBYTE*)ppContent, pcContent ) ) { *pfiField = (CERT_FIELD_ID)((UINT_PTR)m_ElemsField.GetPtr( i )); if ( pdwFlags ) { *pdwFlags = (DWORD)((DWORD_PTR)m_ElemsFlags.GetPtr( i )); } return TRUE; } return FALSE; } BOOL CCertMapRule::DeleteRuleElem( DWORD i ) /*++ Routine Description: Delete a rule element Arguments: i - index ( 0-based ) of element to delete Return Value: TRUE if successful, FALSE on error --*/ { if ( !m_fValid ) { SetLastError( ERROR_ARENA_TRASHED ); return FALSE; } if ( m_ElemsContent.DeleteEntry( i ) && m_ElemsSubfield.DeleteEntry( i ) && m_ElemsField.DeletePtr( i ) && m_ElemsFlags.DeletePtr( i ) ) { return TRUE; } m_fValid = FALSE; return FALSE; } BOOL CCertMapRule::DeleteRuleElemsByField( CERT_FIELD_ID fiField ) /*++ Routine Description: Delete rule elements based on CERT_FIELD_ID Arguments: fiField - CERT_FIELD_ID of elements to delete Return Value: TRUE if successful ( even if no element deleted ), FALSE on error --*/ { UINT i; UINT iMax; if ( !m_fValid ) { SetLastError( ERROR_ARENA_TRASHED ); return FALSE; } iMax = GetRuleElemCount(); for ( i = 0 ; i < iMax ; ++i ) { if ( fiField == (CERT_FIELD_ID)((UINT_PTR)m_ElemsField.GetPtr(i) )) { if ( !DeleteRuleElem( i ) ) { m_fValid = FALSE; return FALSE; } --i; } } return TRUE; } DWORD CCertMapRule::AddRuleElem( DWORD iBefore, CERT_FIELD_ID fiField, LPSTR pszSubField, LPBYTE pContent, DWORD cContent, DWORD dwFlags ) /*++ Routine Description: Add a rule element Arguments: iBefore - index ( 0-based ) of where to insert in list, 0xffffffff to append to list fiField - CERT_FIELD_ID of this element pSubField - ASN.1 name of cert sub-field for match pContent - ptr to match binary form cContent - length of match binary form dwFlags - flags ( CMR_FLAGS_* ) Return Value: TRUE if successful, FALSE on error --*/ { if ( !m_fValid ) { SetLastError( ERROR_ARENA_TRASHED ); return FALSE; } if ( m_ElemsContent.InsertEntry( iBefore, (LPSTR)pContent, cContent ) != INDEX_ERROR && m_ElemsSubfield.InsertEntry( iBefore, pszSubField ) != INDEX_ERROR && m_ElemsField.InsertPtr( iBefore, (LPVOID)fiField ) != INDEX_ERROR && m_ElemsFlags.InsertPtr( iBefore, ULongToPtr(dwFlags) ) != INDEX_ERROR ) { return TRUE; } m_fValid = FALSE; return FALSE; } BOOL CCertMapRule::GetIssuerEntry( DWORD i, LPBOOL pfS, LPSTR* ppszI ) /*++ Routine Description: Get issuer entry from issuer list Arguments: i - index ( 0-based ) of element to delete pfS - updated with issuer accept status ppszI - updated with ptr to issuer ID Return Value: TRUE if successful ( even if no element deleted ), FALSE on error --*/ { if ( i < m_Issuers.GetNbEntry() ) { *ppszI = m_Issuers.GetEntry( i ); *pfS = (BOOL) ((DWORD_PTR)m_IssuersAcceptStatus.GetPtr( i )); return TRUE; } return FALSE; } BOOL CCertMapRule::GetIssuerEntryByName( LPSTR pszName, LPBOOL pfS ) /*++ Routine Description: Get issuer entry from issuer list Arguments: pszName - issuer ID pfS - updated with issuer accept status Return Value: TRUE if successful ( even if no element deleted ), FALSE on error --*/ { UINT i; UINT iMx = m_Issuers.GetNbEntry(); for ( i = 0 ; i < iMx ; ++i ) { if ( !strcmp( m_Issuers.GetEntry( i ), pszName ) ) { *pfS = (BOOL) ((DWORD_PTR)m_IssuersAcceptStatus.GetPtr( i )); return TRUE; } } return FALSE; } BOOL CCertMapRule::SetIssuerEntryAcceptStatus( DWORD i, BOOL fAcceptStatus ) /*++ Routine Description: Set issuer entry accept status Arguments: i - index ( 0-based ) of element to update fAcceptStatus - issuer accept status Return Value: TRUE if successful, FALSE on error --*/ { return m_IssuersAcceptStatus.SetPtr( i, ULongToPtr(fAcceptStatus) ); } BOOL CCertMapRule::AddIssuerEntry( LPSTR pszName, BOOL fAcceptStatus ) /*++ Routine Description: Add issuer entry to issuer list Arguments: pszName - issuer ID fAcceptStatus - issuer accept status Return Value: TRUE if success, otherwise FALSE --*/ { if ( m_Issuers.AddEntry( pszName ) != INDEX_ERROR && m_IssuersAcceptStatus.AddPtr( ULongToPtr((ULONG)fAcceptStatus) ) != INDEX_ERROR ) { return TRUE; } m_fValid = FALSE; return FALSE; } BOOL CCertMapRule::DeleteIssuerEntry( DWORD i ) /*++ Routine Description: Delete an issuer entry Arguments: i - index ( 0-based ) of element to delete Return Value: TRUE if successful, FALSE on error --*/ { if ( m_Issuers.DeleteEntry( i ) && m_IssuersAcceptStatus.DeletePtr( i ) ) { return TRUE; } return FALSE; } CIisRuleMapper::CIisRuleMapper( ) /*++ Routine Description: Constructor Arguments: None Return Value: Nothing --*/ { m_pRWLock = new CReaderWriterLock3(); m_fValid = ( m_pRWLock != NULL ); } CIisRuleMapper::~CIisRuleMapper( ) /*++ Routine Description: Destructor Arguments: None Return Value: Nothing --*/ { UINT i; UINT iMax = GetRuleCount(); for ( i = 0 ; i < iMax ; ++i ) { delete (CCertMapRule*)GetRule( i ); } if ( m_pRWLock != NULL ) { delete m_pRWLock; m_pRWLock = NULL; } } BOOL CIisRuleMapper::Reset( ) /*++ Routine Description: Reset to default values Arguments: None Return Value: Nothing --*/ { m_GlobalRuleInfo.Reset(); UINT i; UINT iMax = GetRuleCount(); for ( i = 0 ; i < iMax ; ++i ) { delete (CCertMapRule*)GetRule( i ); } m_Rules.Reset(); m_fValid = TRUE; return TRUE; } VOID CIisRuleMapper::WriteLockRules() { m_pRWLock->WriteLock(); } VOID CIisRuleMapper::ReadLockRules() { m_pRWLock->ReadLock(); } VOID CIisRuleMapper::WriteUnlockRules() { m_pRWLock->WriteUnlock(); } VOID CIisRuleMapper::ReadUnlockRules() { m_pRWLock->ReadUnlock(); } BOOL CIisRuleMapper::Unserialize( CStoreXBF* pX ) /*++ Routine Description: Unserialize rule mapper Arguments: pX - CStoreXBF to unserialize from Return Value: TRUE if success, otherwise FALSE --*/ { LPBYTE pb = pX->GetBuff(); DWORD dw = pX->GetUsed(); return Unserialize( &pb, &dw ); } BOOL CIisRuleMapper::Unserialize( LPBYTE* ppb, LPDWORD pc ) /*++ Routine Description: Unserialize rule mapper Arguments: ppB - ptr to addr of buffer pC - ptr to byte count in buffer Return Value: TRUE if success, otherwise FALSE --*/ { DWORD dwMx; CCertMapRule * pR; DWORD ir; UINT i; BOOL fSt = FALSE; WriteLockRules(); Reset(); if ( m_GlobalRuleInfo.UnserializeGlobalRuleInfo( ppb, pc ) && ::Unserialize( ppb, pc, &dwMx ) ) { fSt = TRUE; for ( i = 0 ; i < dwMx ; ++i ) { if ( (pR = new CCertMapRule()) == NULL || (ir = m_Rules.AddPtr( (LPVOID)pR )) == INDEX_ERROR || !pR->Unserialize( ppb, pc ) ) { fSt = FALSE; m_fValid = FALSE; if ( pR != NULL ) { delete pR; } break; } } } else { m_fValid = FALSE; } WriteUnlockRules(); return fSt; } BOOL CIisRuleMapper::Serialize( CStoreXBF* psxSer ) /*++ Routine Description: Serialize all rules Arguments: psxSer - ptr to CStoreXBF where to serialize Return Value: TRUE if successful, FALSE on error --*/ { BOOL fSt = FALSE; UINT i; DWORD dwMx; ReadLockRules(); if ( m_fValid ) { dwMx = m_Rules.GetNbPtr(); if ( m_GlobalRuleInfo.SerializeGlobalRuleInfo( psxSer ) && ::Serialize( psxSer, dwMx ) ) { fSt = TRUE; for ( i = 0 ; i < dwMx ; ++i ) { if ( !GetRule(i)->Serialize( psxSer ) ) { fSt = FALSE; break; } } } } ReadUnlockRules(); return fSt; } BOOL CIisRuleMapper::DeleteRule( DWORD dwI ) /*++ Routine Description: Delete a rule Arguments: dwI - index ( 0-based ) of rule to delete Return Value: TRUE if successful, FALSE on error --*/ { if ( dwI < GetRuleCount() ) { if ( m_Rules.DeletePtr( dwI ) && m_GlobalRuleInfo.DeleteRuleById( dwI, TRUE ) ) { return TRUE; } m_fValid = FALSE; } return FALSE; } DWORD CIisRuleMapper::AddRule( ) /*++ Routine Description: Add an empty rule Arguments: None Return Value: index of added rule or INDEX_ERROR if error --*/ { CCertMapRule *pR; DWORD i; if ( ( pR = new CCertMapRule() ) != NULL ) { if ( (i = m_Rules.AddPtr( (LPVOID)pR )) != INDEX_ERROR ) { if ( m_GlobalRuleInfo.AddRuleOrder() ) { return i; } m_fValid = FALSE; /* INTRINSA suppress = leaks */ return INDEX_ERROR; } m_fValid = FALSE; delete pR; } return INDEX_ERROR; } DWORD CIisRuleMapper::AddRule( CCertMapRule *pR ) /*++ Routine Description: Add a rule Arguments: pR - rule to add Return Value: index of added rule or INDEX_ERROR if error --*/ { DWORD i; if ( (i = m_Rules.AddPtr( (LPVOID)pR )) != INDEX_ERROR ) { if ( m_GlobalRuleInfo.AddRuleOrder() ) { return i; } } m_fValid = FALSE; return INDEX_ERROR; } BOOL CIisRuleMapper::Match( PCERT_CONTEXT pCert, PCERT_CONTEXT pAuth, LPWSTR pszAcctW, LPWSTR pszPwdW ) /*++ Routine Description: Check if rule match certificate WARNING: must be called inside lock Arguments: pCert - client cert pAuth - Certifying Authority or NULL if not recognized cbLen - ptr to DER encoded cert pszAcctW - updated with NT account on success, assumed to be at least UNLEN+IIS_DNLEN+1+1 long pszPwdW - updated with NT password on success, assumed to be at least PWLEN+1 long Return Value: TRUE if successful, FALSE on error Errors: ERROR_ARENA_TRASHED if rule internal state is invalid ERROR_INVALID_NAME if no match ERROR_ACCESS_DENIED if match and denied access --*/ { UINT iR; UINT iMax; LPDWORD pdwOrder; CDecodedCert dcCert( pCert ); CDecodedCert dcAuth( pAuth ); BOOL fSt = FALSE; BOOL fDenied; CHAR achAcct[ UNLEN + IIS_DNLEN + 1 + 1 ]; CHAR achPwd[ PWLEN + 1 ]; ReadLockRules(); if ( !IsValid() || !m_GlobalRuleInfo.IsValid() ) { SetLastError( ERROR_ARENA_TRASHED ); goto ex; } if ( !m_GlobalRuleInfo.GetRulesEnabled() ) { SetLastError( ERROR_INVALID_NAME ); goto ex; } iMax = GetRuleCount(); if ( iMax == 0 ) { SetLastError( ERROR_INVALID_NAME ); goto ex; } pdwOrder = m_GlobalRuleInfo.GetRuleOrderArray(); for ( iR = 0 ; iR < iMax ; ++iR ) { if ( ((CCertMapRule*)m_Rules.GetPtr(pdwOrder[iR]))->Match( &dcCert, &dcAuth, achAcct, achPwd, &fDenied ) ) { if ( fDenied ) { SetLastError( ERROR_ACCESS_DENIED ); fSt = FALSE; } else { fSt = TRUE; } break; } } ex: ReadUnlockRules(); if ( fSt ) { if ( !MultiByteToWideChar( CP_ACP, MB_PRECOMPOSED, achAcct, -1, pszAcctW, UNLEN+IIS_DNLEN+1+1 ) ) { return FALSE; } if ( !MultiByteToWideChar( CP_ACP, MB_PRECOMPOSED, achPwd, -1, pszPwdW, PWLEN+1 ) ) { return FALSE; } } return fSt; } CERT_FIELD_ID MapFieldToId( LPSTR pField ) /*++ Routine Description: Map field name ( "Issuer", ... ) to ID Arguments: pField - field name Return Value: CERT_FIELD_ID of field or CERT_FIELD_ERROR if error --*/ { UINT x; for ( x = 0 ; x < sizeof(aMapField)/sizeof(MAP_FIELD) ; ++x ) { if ( !_stricmp( pField, aMapField[x].pTextName ) ) { return aMapField[x].dwId; } } return CERT_FIELD_ERROR; } LPSTR MapIdToField( CERT_FIELD_ID dwId ) /*++ Routine Description: Map ID to field name ( "Issuer", ... ) Arguments: dwId - field ID Return Value: TRUE if successful, FALSE on error --*/ { UINT x; for ( x = 0 ; x < sizeof(aMapField)/sizeof(MAP_FIELD) ; ++x ) { if ( dwId == aMapField[x].dwId ) { return aMapField[x].pTextName; } } return NULL; } DWORD GetIdFlags( CERT_FIELD_ID dwId ) /*++ Routine Description: Get flags for specified ID Arguments: dwId - field ID Return Value: ID flags if success, otherwise 0xffffffff --*/ { if ( dwId < CERT_FIELD_LAST ) { return adwFieldFlags[dwId]; } return 0xffffffff; } LPSTR MapSubFieldToAsn1( LPSTR pszSubField ) /*++ Routine Description: Map field name ( "OU", ... ) to ASN.1 name Arguments: pszSubField - subfield name Return Value: ptr to ASN.1 name or NULL if error --*/ { UINT x; for ( x = 0 ; x < sizeof(aMapAsn)/sizeof(MAP_ASN) ; ++x ) { if ( !strcmp( pszSubField, aMapAsn[x].pTextName ) ) { return aMapAsn[x].pAsnName; } } return NULL; } LPSTR MapAsn1ToSubField( LPSTR pszAsn1 ) /*++ Routine Description: Map ID to field name ( "OU", ... ) Arguments: pszAsn1 - ASN.1 name Return Value: sub field name or ASN.1 name if conversion not found --*/ { UINT x; for ( x = 0 ; x < sizeof(aMapAsn)/sizeof(MAP_ASN) ; ++x ) { if ( !strcmp( pszAsn1, aMapAsn[x].pAsnName ) ) { return aMapAsn[x].pTextName; } } return pszAsn1; } LPSTR EnumerateKnownSubFields( DWORD dwIndex ) /*++ Routine Description: Get subfield name from index (0-based ) Arguments: dwIndex - enumerator ( 0-based ) Return Value: sub field name or NULL if no more subfields --*/ { if ( dwIndex < sizeof(aMapAsn)/sizeof(MAP_ASN) ) { return aMapAsn[dwIndex].pTextName; } return NULL; } VOID InitializeWildcardMapping( HANDLE hModule ) /*++ Routine Description: Initialize wildcard mapping Arguments: hModule - module handle of this DLL Return Value: Nothing --*/ { if ( ( hCapi2Lib = LoadLibrary("crypt32.dll") ) != NULL ) { pfnCryptDecodeObject = (PFNCryptDecodeObject)GetProcAddress(hCapi2Lib, "CryptDecodeObject"); } LPVOID p; UINT i; UINT l; CHAR achTmp[128]; for ( i = 0 ; i < sizeof(aMapAsn)/sizeof(MAP_ASN) ; ++ i ) { if ( (l = LoadString( (HINSTANCE)hModule, aMapAsn[i].dwResId, achTmp, sizeof(achTmp) )) == NULL || (p = LocalAlloc( LMEM_FIXED, l+1 )) == NULL ) { p = (LPVOID)""; } else { memcpy( p, achTmp, l+1 ); } aMapAsn[i].pTextName = (LPSTR)p; } for ( i = 0 ; i < sizeof(aMapField)/sizeof(MAP_FIELD) ; ++ i ) { if ( (l = LoadString( (HINSTANCE)hModule, aMapField[i].dwResId, achTmp, sizeof(achTmp) )) == NULL || (p = LocalAlloc( LMEM_FIXED, l+1 )) == NULL ) { p = (LPVOID)""; } else { memcpy( p, achTmp, l+1 ); } aMapField[i].pTextName = (LPSTR)p; } } VOID TerminateWildcardMapping( ) /*++ Routine Description: Terminate wildcard mapping Arguments: None Return Value: Nothing --*/ { UINT i; if ( hCapi2Lib != NULL ) { FreeLibrary( hCapi2Lib ); } for ( i = 0 ; i < sizeof(aMapAsn)/sizeof(MAP_ASN) ; ++ i ) { if ( aMapAsn[i].pTextName[0] ) { LocalFree( aMapAsn[i].pTextName ); } } for ( i = 0 ; i < sizeof(aMapField)/sizeof(MAP_FIELD) ; ++ i ) { if ( aMapField[i].pTextName[0] ) { LocalFree( aMapField[i].pTextName ); } } } BOOL MatchRequestToBinary( LPSTR pszReq, LPBYTE* ppbBin, LPDWORD pdwBin ) /*++ Routine Description: Convert from match request user format ( e.g. "*msft*" to internal binary format Arguments: pszReq - match in user format ppbBin - updated with ptr to alloced binary format, to be freed by calling FreeMatchConversion() pdwBin - updated with binary format length Return Value: TRUE if success, otherwise FALSE --*/ { LPSTR pReq; DWORD cReq; LPBYTE pBin; MATCH_TYPES mt; if ( !pszReq) { *ppbBin = NULL; *pdwBin = 0; return FALSE; } pReq = pszReq; cReq = (DWORD) strlen( pReq ); if ( !cReq ) { mt = MATCH_ALL; } else { if ( pReq[0] == '*' ) { if ( pReq[cReq-1] == '*' && cReq > 1 ) { mt = MATCH_IN; cReq -= 2; } else { mt = MATCH_LAST; cReq -= 1; } ++pReq; } else if ( pReq[cReq-1] == '*' ) { mt = MATCH_FIRST; cReq -= 1; } else { mt = MATCH_ALL; } } if ( (pBin = (LPBYTE)LocalAlloc( LMEM_FIXED, cReq + 1 )) == NULL ) { return FALSE; } pBin[0] = (BYTE)mt; memcpy( pBin+1, pReq, cReq ); *ppbBin = pBin; *pdwBin = cReq + 1; return TRUE; } BOOL BinaryToMatchRequest( LPBYTE pbBin, DWORD dwBin, LPSTR* ppszReq ) /*++ Routine Description: Convert from internal binary format to match request user format ( e.g. "*msft*" Arguments: pbBin - ptr to binary format, dwBin - binary format length ppszReq - updated with ptr to alloced match in user format to be freed by calling FreeMatchConversion() Return Value: TRUE if success, otherwise FALSE --*/ { BOOL fPre; BOOL fPost; UINT cMatch = dwBin + 1; LPSTR pMatch; if ( !pbBin || !dwBin ) { *ppszReq = NULL; return FALSE; } switch ( (MATCH_TYPES)(UINT)pbBin[0] ) { case MATCH_ALL: fPre = FALSE; fPost = FALSE; break; case MATCH_LAST: fPre = TRUE; fPost = FALSE; ++cMatch; break; case MATCH_FIRST: fPre = FALSE; fPost = TRUE; ++cMatch; break; case MATCH_IN: fPre = TRUE; fPost = TRUE; cMatch += 2; break; default: return FALSE; } if ( (pMatch = (LPSTR)LocalAlloc( LMEM_FIXED, cMatch )) == NULL ) { return FALSE; } *ppszReq = pMatch; if ( fPre ) { *pMatch++ = '*'; } DBG_ASSERT( cMatch >= dwBin - 1 ); memcpy( pMatch, pbBin + 1, dwBin - 1 ); pMatch += dwBin - 1; if ( fPost ) { *pMatch++ = '*'; } *pMatch = '\0'; return TRUE; } VOID FreeMatchConversion( LPVOID pvFree ) /*++ Routine Description: Free result of binary to/from user format conversion Arguments: pvFree - buffer to free Return Value: Nothing --*/ { if ( pvFree != NULL ) { LocalFree( pvFree ); } }