/*++ Copyright (C) Microsoft Corporation, 1998 - 1999 Module Name: Abstract: Functions to manipulate SSL-tyle principal names and certificates Author: Jeff Roberts Revisions: Jeff Roberts (jroberts) 1-20-1998 created the file --*/ #include #include #include #include #define INITIAL_NAME_LENGTH 100 // The prefix lengths do not include the NULL char. Actual strings are one // character longer. This is generally OK, since these strings are prefixes and // the NULL char gets overwritten. #define MSSTD_PREFIX_LENGTH 6 const RPC_CHAR MSSTD_PREFIX[] = RPC_T("msstd:"); #define FULLPATH_PREFIX_LENGTH 8 const RPC_CHAR FULLPATH_PREFIX[] = RPC_T("fullsic:"); //------------------------------------------------------------------------ DWORD AddComponentName( RPC_CHAR * * pBuffer, unsigned long * pBufferLength, unsigned long * pCursor, CERT_NAME_BLOB * Name ); DWORD RpcCertMatchPrincipalName( PCCERT_CONTEXT Context, RPC_CHAR PrincipalName[] ); DWORD MatchFullPathPrincipalName( PCCERT_CONTEXT Context, RPC_CHAR EncodedPrincipalName[] ); DWORD CompareRdnElement( PCCERT_CONTEXT Context, DWORD dwGetNameStringType, void *pvGetNameStringTypePara, RPC_CHAR PrincipalName[], BOOL CaseSensitive ); DWORD MarkPrincipalNameComponents( RPC_CHAR * PrincipalName, unsigned * pCount ); PCCERT_CONTEXT FindMatchingCertificate( HCERTSTORE Store, RPC_CHAR * SubjectName, RPC_CHAR * IssuerName ); DWORD MatchMsPrincipalName( PCCERT_CONTEXT Context, RPC_CHAR EncodedPrincipalName[] ); DWORD DecodeEscapedString( RPC_CHAR * Source, RPC_CHAR * Destination ); DWORD FindIssuerContext( PCCERT_CONTEXT * pContext, HCERTSTORE * pStore, BOOL * pfFreeStore ); RPC_CHAR * EndOfRfc1779Name( RPC_CHAR * Name ); unsigned Int4StrLen( unsigned long * String ); //------------------------------------------------------------------------ RPC_CHAR * __cdecl RpcpStringReverse ( RPC_CHAR * string ) { RPC_CHAR *start = string; RPC_CHAR *left = string; RPC_CHAR ch; while (*string++) /* find end of string */ ; string -= sizeof(RPC_CHAR); while (left < string) { ch = *left; *left++ = *string; *string-- = ch; } return(start); } DWORD RpcCertMatchPrincipalName( PCCERT_CONTEXT Context, RPC_CHAR PrincipalName[] ) /*++ Routine Description: This routine Arguments: is a CryptoAPI 2.0 context is a principal name prefixed with either "msstd:" or "fullsic:" Return Value: 0 if successful, otherwise an error --*/ { DWORD Status = 0; InitializeIfNecessary(); if (!LoadCrypt32Imports()) { return GetLastError(); } if (0 == RpcpStringNCompare(PrincipalName, MSSTD_PREFIX, MSSTD_PREFIX_LENGTH)) { Status = MatchMsPrincipalName(Context, PrincipalName + MSSTD_PREFIX_LENGTH); } else if (0 == RpcpStringNCompare(PrincipalName, FULLPATH_PREFIX, FULLPATH_PREFIX_LENGTH)) { Status = MatchFullPathPrincipalName(Context, PrincipalName + FULLPATH_PREFIX_LENGTH); } else { ASSERT( 0 && "RPC: principal name is in incorrect format" ); Status = ERROR_INVALID_PARAMETER; } return Status; } DWORD MatchMsPrincipalName( PCCERT_CONTEXT Context, RPC_CHAR EncodedPrincipalName[] ) { PCERT_NAME_INFO Subject = 0; DWORD Status = 0; RPC_CHAR * PrincipalName = 0; // // It's time to decode the principal name. // The encoding is just a matter of extra backslashes when the // principal name contains our metacharacters. Decoding will // always make the string smaller. // PrincipalName = (RPC_CHAR *) _alloca(sizeof(RPC_CHAR) * RpcpStringLength(EncodedPrincipalName)); Status = DecodeEscapedString(EncodedPrincipalName, PrincipalName); if (Status) { RpcpErrorAddRecord(EEInfoGCSecurityProvider, Status, EEInfoDLMatchMsPrincipalName10); return Status; } if (RpcpCharacter((const RPC_SCHAR *) PrincipalName, '@')) { // Compare the principal name to the certificate subject's // email-address RDN attribute. return CompareRdnElement(Context, CERT_NAME_EMAIL_TYPE, NULL, PrincipalName, TRUE); } else { // Compare the principal name to the certificate subject's // common-name RDN attribute. return CompareRdnElement(Context, CERT_NAME_ATTR_TYPE, szOID_COMMON_NAME, PrincipalName, FALSE); } ASSERT( 0 ); } DWORD CompareRdnElement( PCCERT_CONTEXT Context, DWORD dwGetNameStringType, void *pvGetNameStringTypePara, RPC_CHAR PrincipalName[], BOOL CaseSensitive ) { // // Decode the certificate's Subject field so we can see its attributes. // DWORD Status = 0; RPC_CHAR * pPrincipalName; DWORD NameSize = CertGetNameStringW(Context, dwGetNameStringType, 0, // dwFlags pvGetNameStringTypePara, // pvTypePara NULL, 0); if (NameSize > 1) { pPrincipalName = (RPC_CHAR *) _alloca( sizeof(wchar_t) * NameSize ); NameSize = CertGetNameStringW(Context, dwGetNameStringType, 0, pvGetNameStringTypePara, pPrincipalName, NameSize); ASSERT(NameSize > 1); } else { RpcpErrorAddRecord(EEInfoGCSecurityProvider, ERROR_ACCESS_DENIED, EEInfoDLCompareRdnElement10, GetLastError()); return ERROR_ACCESS_DENIED; } // // Now compare the cetificate info to the principal name. // if (CaseSensitive) { if (0 != RpcpStringSCompare(PrincipalName, pPrincipalName)) { return ERROR_ACCESS_DENIED; } } else { if (0 != RpcpStringCompareInt(PrincipalName, pPrincipalName)) { return ERROR_ACCESS_DENIED; } } return 0; } DWORD DecodeEscapedString( RPC_CHAR * Source, RPC_CHAR * Destination ) { for (;;) { if (*Source == '\\') { if(Source[1] != '<' && Source[1] != '*') { Source++; } if (!*Source) { return ERROR_INVALID_PARAMETER; } *Destination = *Source; } else { *Destination = *Source; if (!*Source) { return 0; } } ++Source; ++Destination; } } DWORD MatchFullPathPrincipalName( PCCERT_CONTEXT Context, RPC_CHAR EncodedPrincipalName[] ) /*++ Routine Description: Arguments: is a CryptoAPI 2.0 context is a principal name with no prefix It can be in several forms: - complete and explicit, e.g. "\\" "\\\\" - wildcard in the subject-name position: "\\*" - no CA specified "name" - This is equivalent to "\*\" Return Value: 0 if all components were successfully validated ERROR_NO_TOP_LEVEL_AUTHORITY if the top cert isn't in the CA store Some other error if something failed. --*/ { DWORD Status = 0; DWORD ReturnedStatus = 0; HCERTSTORE Store = Context->hCertStore; unsigned PrincipalNameLength; RPC_CHAR * CopyOfPrincipalName = 0; if (!*EncodedPrincipalName) { return ERROR_INVALID_PARAMETER; } PrincipalNameLength = RpcpStringLength(EncodedPrincipalName); // // If the name doesn't begin with a backslash, then treat it as "\*\subjectname". // if (EncodedPrincipalName[0] != '\\') { CopyOfPrincipalName = (RPC_CHAR *) _alloca( (5 + PrincipalNameLength + 2) * sizeof(RPC_CHAR)); RpcpStringCopy(CopyOfPrincipalName, RPC_CONST_STRING("\\*\\<")); RpcpStringCat(CopyOfPrincipalName, EncodedPrincipalName); RpcpStringCat(CopyOfPrincipalName, RPC_CONST_STRING(">")); return MatchFullPathPrincipalName( Context, CopyOfPrincipalName ); } // // Form is "\\". More precisely, the name looks like a // file system path, where each element of the path // is either // "*" // or // '<' plus an RFC1779 name plus '>' // unsigned idx; unsigned ComponentCount; RPC_CHAR ** Components = 0; RPC_CHAR * Cursor; CopyOfPrincipalName = (RPC_CHAR *) _alloca( (PrincipalNameLength + 2) * sizeof(RPC_CHAR)); RpcpStringCopy(CopyOfPrincipalName, EncodedPrincipalName); // // Sizing pass. Count the number of path elements and separate them by '\0'. // Status = MarkPrincipalNameComponents( CopyOfPrincipalName, &ComponentCount ); if (Status) { RpcpErrorAddRecord(EEInfoGCSecurityProvider, Status, EEInfoDLMatchFullPathPrincipalName10); return Status; } // // Init the component array. The 0'th element of the array points to // the last component of the principal name (i.e., the client's subject name). // Components = (RPC_CHAR **) _alloca( sizeof(RPC_CHAR *) * ComponentCount ); Cursor = CopyOfPrincipalName; idx = ComponentCount-1; do { while (!*Cursor) { ++Cursor; } ASSERT( Cursor < CopyOfPrincipalName+PrincipalNameLength ); Components[idx] = Cursor; Cursor += RpcpStringLength( Cursor ); } while ( idx-- > 0 ); // // Verify the principal name of all certs except the top authority one. // PCCERT_CONTEXT NewContext = 0; for (idx=0; idx < ComponentCount-1; ++idx) { if (NewContext) { CertFreeCertificateContext( NewContext ); } NewContext = FindMatchingCertificate( Store, Components[idx], Components[idx+1] ); if (!NewContext) { Status = GetLastError(); if (Status == ERROR_NOT_ENOUGH_MEMORY) { ReturnedStatus = ERROR_NOT_ENOUGH_MEMORY; } else { ReturnedStatus = ERROR_ACCESS_DENIED; } RpcpErrorAddRecord(EEInfoGCSecurityProvider, ReturnedStatus, EEInfoDLMatchFullPathPrincipalName20, Status); return ReturnedStatus; } } // // Look for the top-level authority certificate in the CA and ROOT stores, // matching the last remaining principal name component. // HCERTSTORE CaStore; HCERTSTORE RootStore; CaStore = CertOpenStore( CERT_STORE_PROV_SYSTEM_W, 0, 0, CERT_SYSTEM_STORE_CURRENT_USER, RPC_CONST_STRING("CA") ); if (!CaStore) { Status = GetLastError(); RpcpErrorAddRecord(EEInfoGCSecurityProvider, Status, EEInfoDLMatchFullPathPrincipalName30); CertFreeCertificateContext( NewContext ); return Status; } RootStore = CertOpenStore( CERT_STORE_PROV_SYSTEM_W, 0, 0, CERT_SYSTEM_STORE_CURRENT_USER, RPC_CONST_STRING("ROOT") ); if (!RootStore) { Status = GetLastError(); RpcpErrorAddRecord(EEInfoGCSecurityProvider, Status, EEInfoDLMatchFullPathPrincipalName40); CertCloseStore( CaStore, 0 ); CertFreeCertificateContext( NewContext ); return Status; } // // Use Context instead of Components[] for the issuer name // so that the self-signed cert that we look for // is always the one that matches the Context we have. // Otherwise we'd be wrong about strings with a wildcard as top-level authority. // PCCERT_CONTEXT Parent = 0; while (1) { Parent = CertFindCertificateInStore( RootStore, X509_ASN_ENCODING, 0, // no flags CERT_FIND_SUBJECT_NAME, // exact match &NewContext->pCertInfo->Issuer, Parent ); if (!Parent) { Parent = CertFindCertificateInStore( CaStore, X509_ASN_ENCODING, 0, // no flags CERT_FIND_SUBJECT_NAME, // exact match &NewContext->pCertInfo->Issuer, Parent ); } if (!Parent) { goto finish; } if (CertCompareCertificateName( X509_ASN_ENCODING, & Parent->pCertInfo->Issuer, &Context->pCertInfo->Issuer )) { goto finish; } } finish: CertCloseStore( CaStore, 0 ); CertCloseStore( RootStore, 0 ); CertFreeCertificateContext( NewContext ); if (!Parent) { Status = GetLastError(); if (Status == ERROR_NOT_ENOUGH_MEMORY) { ReturnedStatus = ERROR_NOT_ENOUGH_MEMORY; } else { ReturnedStatus = SEC_E_NO_AUTHENTICATING_AUTHORITY; } RpcpErrorAddRecord(EEInfoGCSecurityProvider, ReturnedStatus, EEInfoDLMatchFullPathPrincipalName50, Status); return ReturnedStatus; } CertFreeCertificateContext( Parent ); return 0; } PCCERT_CONTEXT FindMatchingCertificate( HCERTSTORE Store, RPC_CHAR * SubjectName, RPC_CHAR * IssuerName ) { PCCERT_CONTEXT Context = 0; CERT_NAME_BLOB * IssuerBlob = 0; CERT_NAME_BLOB * SubjectBlob = 0; ASSERT( IssuerName[0] != '*' || SubjectName[0] != '*' ); // // Make an issuer blob, if necessary. // if (IssuerName[0] != '*') { unsigned long BlobSize = INITIAL_NAME_LENGTH; IssuerBlob = (CERT_NAME_BLOB *) _alloca( sizeof(CERT_NAME_BLOB)+INITIAL_NAME_LENGTH ); IssuerBlob->cbData = INITIAL_NAME_LENGTH; IssuerBlob->pbData = (unsigned char *) (IssuerBlob+1); if (! CertStrToNameT( X509_ASN_ENCODING, IssuerName, 0, // no format restrictions NULL, // reserved, MBZ IssuerBlob->pbData, &IssuerBlob->cbData, NULL // would point to first invalid character )) { BlobSize = IssuerBlob->cbData; IssuerBlob = (CERT_NAME_BLOB *) _alloca( sizeof(CERT_NAME_BLOB)+BlobSize ); IssuerBlob->cbData = BlobSize; IssuerBlob->pbData = (unsigned char *) (IssuerBlob+1); if (! CertStrToNameT( X509_ASN_ENCODING, IssuerName, 0, // no format restrictions NULL, // reserved, MBZ IssuerBlob->pbData, &IssuerBlob->cbData, NULL // would point to first invalid character )) { return 0; } } } // // Make a subject blob, if necessary. // if (SubjectName[0] != '*') { unsigned long BlobSize = INITIAL_NAME_LENGTH; SubjectBlob = (CERT_NAME_BLOB *) _alloca( sizeof(CERT_NAME_BLOB)+INITIAL_NAME_LENGTH ); SubjectBlob->cbData = INITIAL_NAME_LENGTH; SubjectBlob->pbData = (unsigned char *) (SubjectBlob+1); if (! CertStrToNameT( X509_ASN_ENCODING, SubjectName, 0, // no format restrictions NULL, // reserved, MBZ SubjectBlob->pbData, &SubjectBlob->cbData, NULL // would point to first invalid character )) { BlobSize = SubjectBlob->cbData; SubjectBlob = (CERT_NAME_BLOB *) _alloca( sizeof(CERT_NAME_BLOB)+BlobSize ); SubjectBlob->cbData = BlobSize; SubjectBlob->pbData = (unsigned char *) (SubjectBlob+1); if (! CertStrToNameT( X509_ASN_ENCODING, SubjectName, 0, // no format restrictions NULL, // reserved, MBZ SubjectBlob->pbData, &SubjectBlob->cbData, NULL // would point to first invalid character )) { return 0; } } } // // Search for a certificate. // if (SubjectName[0] == '*') { // // Search by issuer only. // Context = CertFindCertificateInStore( Store, X509_ASN_ENCODING, 0, // no flags CERT_FIND_ISSUER_NAME, // exact match IssuerBlob, NULL // previous context in search ); return Context; } else if (IssuerName[0] == '*') { // // Search by subject only. // Context = CertFindCertificateInStore( Store, X509_ASN_ENCODING, 0, // no flags CERT_FIND_SUBJECT_NAME, // exact match SubjectBlob, NULL // previous context in search ); return Context; } else { // // Search by both. The primary search is by subject, // on the theory that most subject names are distinct and // most issuer names are not. // while (1) { Context = CertFindCertificateInStore( Store, X509_ASN_ENCODING, 0, // no flags CERT_FIND_SUBJECT_NAME, // exact match SubjectBlob, NULL // previous context in search ); if (!Context) { return Context; } if (CertCompareCertificateName( X509_ASN_ENCODING, &Context->pCertInfo->Issuer, IssuerBlob )) { return Context; } } } ASSERT( 0 && "should never get here" ); } DWORD MarkPrincipalNameComponents( RPC_CHAR * PrincipalName, unsigned * pCount ) { RPC_CHAR * Cursor; unsigned ComponentCount; ComponentCount = 0; Cursor = PrincipalName; do { ++ComponentCount; if ( *Cursor != '\\' ) { return ERROR_INVALID_PARAMETER; } *Cursor = '\0'; ++Cursor; if (*Cursor == '*') { ++Cursor; } else if (*Cursor == '<') { *Cursor = '\0'; ++Cursor; Cursor = EndOfRfc1779Name( Cursor ); if (*Cursor != '>') { return ERROR_INVALID_PARAMETER; } *Cursor = '\0'; ++Cursor; } else { return ERROR_INVALID_PARAMETER; } } while ( *Cursor ); *pCount = ComponentCount; return 0; } RPC_CHAR * EndOfRfc1779Name( RPC_CHAR * Name ) { unsigned Quotes = 0; for ( ; *Name; ++Name) { if (*Name == '>') { if (0 == (Quotes % 2)) { return Name; } } else if (*Name == '\\') { ++Name; } else if (*Name == '"') { ++Quotes; } } return Name; } RPC_STATUS RpcCertGeneratePrincipalName( PCCERT_CONTEXT Context, DWORD Flags, RPC_CHAR ** pBuffer ) { THREAD *Thread; DWORD Status = 0; InitializeIfNecessary(); Thread = ThreadSelf(); if (Thread) { RpcpPurgeEEInfoFromThreadIfNecessary(Thread); } if (!LoadCrypt32Imports()) { return GetLastError(); } if (Flags & RPC_C_FULL_CERT_CHAIN) { RPC_CHAR * Buffer; unsigned long Cursor = 0; unsigned long BufferLength = INITIAL_NAME_LENGTH; HCERTSTORE Store = Context->hCertStore; BOOL fFreeStore = FALSE; Buffer = new RPC_CHAR[BufferLength]; if (!Buffer) { return ERROR_NOT_ENOUGH_MEMORY; } PCCERT_CONTEXT Node = Context; do { Status = AddComponentName( &Buffer, &BufferLength, &Cursor, &Node->pCertInfo->Subject ); if (Status) { RpcpErrorAddRecord(EEInfoGCSecurityProvider, Status, EEInfoDLRpcCertGeneratePrincipalName10); return Status; } // // Load the next certificate. // PCCERT_CONTEXT OldNode = Node; if (CertCompareCertificateName( X509_ASN_ENCODING, &Node->pCertInfo->Subject, &Node->pCertInfo->Issuer )) { break; } Node = CertFindCertificateInStore( Store, X509_ASN_ENCODING, 0, // no flags CERT_FIND_SUBJECT_NAME, // exact match &OldNode->pCertInfo->Issuer, NULL // previous context in search ); if (!Node) { // // Take the top-level CA name from the current node's issuer field. // Status = AddComponentName( &Buffer, &BufferLength, &Cursor, &OldNode->pCertInfo->Issuer ); if (Status) { RpcpErrorAddRecord(EEInfoGCSecurityProvider, Status, EEInfoDLRpcCertGeneratePrincipalName20); CertFreeCertificateContext( OldNode ); return Status; } } if (OldNode != Context) { CertFreeCertificateContext( OldNode ); } } while ( Node ); if (Cursor+FULLPATH_PREFIX_LENGTH+1 > BufferLength) { DWORD LongerBufferLength = BufferLength+FULLPATH_PREFIX_LENGTH+1; RPC_CHAR * LongerBuffer = new RPC_CHAR[LongerBufferLength]; if (!LongerBuffer) { delete Buffer; return ERROR_NOT_ENOUGH_MEMORY; } RpcpStringNCopy( LongerBuffer, Buffer, BufferLength ); delete Buffer; Buffer = LongerBuffer; BufferLength = LongerBufferLength; } RpcpStringCopy( Buffer+Cursor,FULLPATH_PREFIX ); RpcpStringReverse( Buffer+Cursor ); RpcpStringReverse( Buffer ); *pBuffer = Buffer; return 0; } else { RPC_CHAR * outputName; DWORD NameSize = CertGetNameStringW(Context, CERT_NAME_EMAIL_TYPE, 0, // dwFlags NULL, // pvTypePara NULL, 0); if (NameSize > 1) { outputName = new RPC_CHAR[ NameSize + MSSTD_PREFIX_LENGTH ]; if (!outputName) { return ERROR_NOT_ENOUGH_MEMORY; } RpcpStringCopy(outputName, MSSTD_PREFIX); NameSize = CertGetNameStringW(Context, CERT_NAME_EMAIL_TYPE, 0, NULL, outputName + MSSTD_PREFIX_LENGTH, NameSize); ASSERT(NameSize > 1); } else { NameSize = CertGetNameStringW(Context, CERT_NAME_ATTR_TYPE, 0, szOID_COMMON_NAME, NULL, 0); if (NameSize > 1) { outputName = new RPC_CHAR[ NameSize + MSSTD_PREFIX_LENGTH ]; if (!outputName) { return ERROR_NOT_ENOUGH_MEMORY; } RpcpStringCopy(outputName, MSSTD_PREFIX); NameSize = CertGetNameString(Context, CERT_NAME_ATTR_TYPE, 0, szOID_COMMON_NAME, outputName + MSSTD_PREFIX_LENGTH, NameSize); ASSERT(NameSize > 1); } else { RpcpErrorAddRecord(EEInfoGCSecurityProvider, ERROR_INVALID_PARAMETER, EEInfoDLRpcCertGeneratePrincipalName30, GetLastError()); return ERROR_INVALID_PARAMETER; } } *pBuffer = outputName; return 0; } ASSERT( 0 && "never reach here" ); return ERROR_INVALID_PARAMETER; } DWORD AddComponentName( RPC_CHAR * * pBuffer, unsigned long * pBufferLength, unsigned long * pCursor, CERT_NAME_BLOB * Name ) { (*pBuffer)[(*pCursor)++] = '>'; DWORD Length = *pBufferLength - *pCursor; Length = CertNameToStrT( X509_ASN_ENCODING, Name, CERT_X500_NAME_STR, *pBuffer + *pCursor, Length ); if (Length >= *pBufferLength - *pCursor) { DWORD LongerBufferLength = 2 * *pBufferLength + Length; RPC_CHAR * LongerBuffer = new RPC_CHAR[LongerBufferLength]; if (!LongerBuffer) { delete *pBuffer; return ERROR_NOT_ENOUGH_MEMORY; } RpcpStringNCopy( LongerBuffer, *pBuffer, *pBufferLength ); delete *pBuffer; *pBuffer = LongerBuffer; *pBufferLength = LongerBufferLength; Length = *pBufferLength - *pCursor; Length = CertNameToStrT( X509_ASN_ENCODING, Name, CERT_X500_NAME_STR, *pBuffer + *pCursor, Length ); } RpcpStringReverse(*pBuffer + *pCursor); *pCursor += Length-1; // write over the '\0' (*pBuffer)[(*pCursor)++] = '<'; (*pBuffer)[(*pCursor)++] = '\\'; return 0; } unsigned Int4StrLen( unsigned long * String ) { unsigned long * Cursor = String; while (*Cursor) { ++Cursor; } return (unsigned) (Cursor - String); } HMODULE Crypt32Handle = 0; struct CRYPT32_FUNCTION_TABLE CFT; BOOL LoadCrypt32Imports() { if (0 == Crypt32Handle) { RequestGlobalMutex(); if (Crypt32Handle) { goto Cleanup; } Crypt32Handle = LoadLibrary(RPC_CONST_SSTRING("crypt32.dll")); if (!Crypt32Handle) { goto Cleanup; } FARPROC * ppProc = (FARPROC *) &CFT; *ppProc++ = GetProcAddress(Crypt32Handle, "CertOpenStore"); *ppProc++ = GetProcAddress(Crypt32Handle, "CertCloseStore"); *ppProc++ = GetProcAddress(Crypt32Handle, "CertFindCertificateInStore"); *ppProc++ = GetProcAddress(Crypt32Handle, "CertFreeCertificateContext"); *ppProc++ = GetProcAddress(Crypt32Handle, "CertCompareCertificateName"); #ifdef UNICODE *ppProc++ = GetProcAddress(Crypt32Handle, "CertStrToNameW"); *ppProc++ = GetProcAddress(Crypt32Handle, "CertNameToStrW"); #else *ppProc++ = GetProcAddress(Crypt32Handle, "CertStrToNameA"); *ppProc++ = GetProcAddress(Crypt32Handle, "CertNameToStrA"); #endif *ppProc++ = GetProcAddress(Crypt32Handle, "CertFindRDNAttr"); *ppProc++ = GetProcAddress(Crypt32Handle, "CryptDecodeObject"); #if MANUAL_CERT_CHECK *ppProc++ = GetProcAddress(Crypt32Handle, "CertVerifyCertificateChainPolicy"); *ppProc++ = GetProcAddress(Crypt32Handle, "CertGetCertificateChain"); *ppProc++ = GetProcAddress(Crypt32Handle, "CertFreeCertificateChain"); #endif *ppProc++ = GetProcAddress(Crypt32Handle, "CertGetNameStringW"); ppProc = (FARPROC *) &CFT; for (int i = 0; i < sizeof(CRYPT32_FUNCTION_TABLE)/sizeof(FARPROC); i++) { if (*ppProc++ == 0) { FreeLibrary(Crypt32Handle); Crypt32Handle = 0; goto Cleanup; } } ClearGlobalMutex(); } return TRUE; Cleanup: ClearGlobalMutex(); return FALSE; } #if MANUAL_CERT_CHECK DWORD RpcCertVerifyContext( IN PCERT_CONTEXT Context, IN DWORD CapabilityFlags ) { DWORD s = 0; HCERTSTORE CaStore = 0; PCCERT_CHAIN_CONTEXT Chain = 0; // // Windows 2000 checked only the CA store, so we should check it also. // CaStore = CertOpenStore( CERT_STORE_PROV_SYSTEM_W, 0, 0, CERT_SYSTEM_STORE_CURRENT_USER, RPC_CONST_STRING("CA") ); if (!CaStore) { s = GetLastError(); RpcpErrorAddRecord(EEInfoGCSecurityProvider, s, EEInfoDLRpcCertVerifyContext10); goto Cleanup; } // // Build a certificate chain from the single certificate we have. // CERT_CHAIN_PARA ChainParameters; ChainParameters.cbSize = sizeof(CERT_CHAIN_PARA); ChainParameters.RequestedUsage.dwType = USAGE_MATCH_TYPE_AND; ChainParameters.RequestedUsage.Usage.cUsageIdentifier = 0; if (!CertGetCertificateChain( NULL, // use default chain engine Context, NULL, // match against current time CaStore, // aditional store to search &ChainParameters, 0, // no special flags 0, // reserved, MBZ &Chain )) { s = GetLastError(); RpcpErrorAddRecord(EEInfoGCSecurityProvider, s, EEInfoDLRpcCertVerifyContext20); goto Cleanup; } // // Verify the chain. // CERT_CHAIN_POLICY_PARA PolicyParameters; CERT_CHAIN_POLICY_STATUS PolicyStatus; PolicyParameters.cbSize = sizeof(CERT_CHAIN_POLICY_PARA); PolicyParameters.dwFlags = 0; PolicyParameters.pvExtraPolicyPara = 0; if (CapabilityFlags & RPC_C_QOS_CAPABILITIES_ANY_AUTHORITY) { PolicyParameters.dwFlags |= CERT_CHAIN_POLICY_ALLOW_UNKNOWN_CA_FLAG; } PolicyStatus.cbSize = sizeof(CERT_CHAIN_POLICY_STATUS); if (!CertVerifyCertificateChainPolicy( CERT_CHAIN_POLICY_BASE, Chain, &PolicyParameters, &PolicyStatus)) { s = GetLastError(); RpcpErrorAddRecord(EEInfoGCSecurityProvider, s, EEInfoDLRpcCertVerifyContext30); goto Cleanup; } if (PolicyStatus.dwError) { s = ERROR_ACCESS_DENIED; RpcpErrorAddRecord(EEInfoGCSecurityProvider, s, EEInfoDLRpcCertVerifyContext40, GetLastError()); goto Cleanup; } // // Certificate verification succeeded. // s = 0; Cleanup: if (Chain) { CertFreeCertificateChain( Chain ); } if (CaStore) { CertCloseStore( CaStore, 0 ); } return s; } #endif RPC_STATUS ValidateSchannelPrincipalName( IN RPC_CHAR * EncodedName ) // // Does a quick syntactic check of an SSL principal name. // The name should not be modified in any way. // // It should begin either with "msstd:" or "fullsic:". // If msstd, then any non-enpty name will do. // If fullsic, it should be a series of RFC1179 names, each surrounded // by angle brackets, and separated by backslashes. We check this by cloning // the string and marking the components. // { #define MAX_SSL_SPN_LENGTH 8000 if (0 == RpcpStringNCompare(EncodedName, MSSTD_PREFIX, MSSTD_PREFIX_LENGTH)) { if (EncodedName[MSSTD_PREFIX_LENGTH] == 0) { return ERROR_INVALID_PARAMETER; } } else if (0 == RpcpStringNCompare(EncodedName, FULLPATH_PREFIX, FULLPATH_PREFIX_LENGTH)) { RPC_STATUS Status; size_t Length; unsigned ComponentCount; RPC_CHAR * PrincipalName; RPC_CHAR * EncodedPrincipalName; Length = RpcpStringLength( EncodedName ); if (Length > MAX_SSL_SPN_LENGTH) { return ERROR_INVALID_PARAMETER; } PrincipalName = (RPC_CHAR *) _alloca(sizeof(RPC_CHAR) * Length); Status = DecodeEscapedString(EncodedName, PrincipalName); if (Status) { return Status; } Status = MarkPrincipalNameComponents( PrincipalName + FULLPATH_PREFIX_LENGTH, &ComponentCount ); if (Status) { return Status; } if (ComponentCount < 1) { return ERROR_INVALID_PARAMETER; } } else { return ERROR_INVALID_PARAMETER; } return RPC_S_OK; } RPC_STATUS I_RpcTransCertMatchPrincipalName( PCCERT_CONTEXT Context, RPC_CHAR PrincipalName[] ) /*++ Routine Description: Verifies that the passed in context match the passed in principal name and frees the certificate. Arguments: Context - certificate context. PrincipalName - a principal name prefixed with either "msstd:" or "fullsic:" Return Value: RPC_S_OK or RPC_S_ACCESS_DENIED --*/ { RPC_STATUS RpcStatus; RpcStatus = RpcCertMatchPrincipalName(Context, PrincipalName); if (RpcStatus == ERROR_OUTOFMEMORY) RpcStatus = RPC_S_OUT_OF_MEMORY; else if (RpcStatus != RPC_S_OK) RpcStatus = RPC_S_ACCESS_DENIED; CertFreeCertificateContext(Context); return RpcStatus; }