windows-nt/Source/XPSP1/NT/com/rpc/runtime/mtrt/principal.cxx
2020-09-26 16:20:57 +08:00

1430 lines
40 KiB
C++
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/*++
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 <precomp.hxx>
#include <rpcssl.h>
#include <cryptimp.hxx>
#include <CharConv.hxx>
#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:
<Context> is a CryptoAPI 2.0 context
<PrincipalName> 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:
<Context> is a CryptoAPI 2.0 context
<PrincipalName> is a principal name with no prefix
It can be in several forms:
- complete and explicit, e.g.
"\<CA>\<Name>"
"\<CA>\<SA1>\<SA2>\<Name>"
- wildcard in the subject-name position:
"\<CA>\*"
- no CA specified
"name" - This is equivalent to "\*\<CA>"
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 "\<CA>\<Name>". 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;
}