|
|
//+---------------------------------------------------------------------------
//
// Microsoft Windows
// Copyright (C) Microsoft Corporation, 1992 - 1995.
//
// File: mapper.c
//
// Contents: Implements the DS Mapping Layer
//
// Classes:
//
// Functions:
//
// History: 10-15-96 RichardW Created
//
// Notes: The code here has two forks. One, the direct path, for when
// the DLL is running on a DC, and the second, for when we're
// running elsewhere and remoting through the generic channel
// to the DC.
//
//----------------------------------------------------------------------------
#include "sslp.h"
#include <crypt.h>
#include <lmcons.h>
#include <ntsam.h>
#include <samrpc.h>
#include <samisrv.h>
#include <lsarpc.h>
#include <lsaisrv.h>
#include <dnsapi.h>
#include <certmap.h>
#include <align.h>
#include <ntmsv1_0.h>
#include <ntdsapi.h>
#include <ntdsapip.h>
#include "mapsam.h"
#include "wincrypt.h"
#include <msaudite.h>
LONG WINAPI SslLocalRefMapper( PHMAPPER Mapper);
LONG WINAPI SslLocalDerefMapper( PHMAPPER Mapper);
DWORD WINAPI SslLocalGetIssuerList( IN PHMAPPER Mapper, IN PVOID Reserved, OUT PBYTE pIssuerList, OUT PDWORD IssuerListLength);
DWORD WINAPI SslLocalGetChallenge( IN PHMAPPER Mapper, IN PUCHAR AuthenticatorId, IN DWORD AuthenticatorIdLength, OUT PUCHAR Challenge, OUT DWORD * ChallengeLength);
DWORD WINAPI SslLocalMapCredential( IN PHMAPPER Mapper, IN DWORD CredentialType, VOID const *pCredential, VOID const *pAuthority, OUT HLOCATOR * phLocator);
DWORD WINAPI SslRemoteMapCredential( IN PHMAPPER Mapper, IN DWORD CredentialType, VOID const *pCredential, VOID const *pAuthority, OUT HLOCATOR * phLocator);
DWORD WINAPI SslLocalCloseLocator( IN PHMAPPER Mapper, IN HLOCATOR Locator);
DWORD WINAPI SslLocalGetAccessToken( IN PHMAPPER Mapper, IN HLOCATOR Locator, OUT HANDLE *Token);
MAPPER_VTABLE SslLocalTable = { SslLocalRefMapper, SslLocalDerefMapper, SslLocalGetIssuerList, SslLocalGetChallenge, SslLocalMapCredential, SslLocalGetAccessToken, SslLocalCloseLocator };
MAPPER_VTABLE SslRemoteTable = {SslLocalRefMapper, SslLocalDerefMapper, SslLocalGetIssuerList, SslLocalGetChallenge, SslRemoteMapCredential, SslLocalGetAccessToken, SslLocalCloseLocator };
MAPPER_VTABLE SslUserModeTable = { SslLocalRefMapper, SslLocalDerefMapper, NULL, NULL, NULL, SslLocalGetAccessToken, SslLocalCloseLocator };
typedef struct _SSL_MAPPER_CONTEXT { HMAPPER Mapper ; LONG Ref ;
} SSL_MAPPER_CONTEXT, * PSSL_MAPPER_CONTEXT ;
NTSTATUS WINAPI SslBuildCertLogonRequest( PCCERT_CHAIN_CONTEXT pChainContext, DWORD dwMethods, PSSL_CERT_LOGON_REQ *ppRequest, PDWORD pcbRequest);
NTSTATUS WINAPI SslMapCertAtDC( PUNICODE_STRING DomainName, VOID const *pCredential, DWORD cbCredential, PMSV1_0_PASSTHROUGH_RESPONSE * DcResponse );
PWSTR SslDnsDomainName ; SECURITY_STRING SslNullString = { 0, sizeof( WCHAR ), L"" };
PHMAPPER SslGetMapper( BOOL Ignored ) { PSSL_MAPPER_CONTEXT Context ; NTSTATUS Status ; NT_PRODUCT_TYPE ProductType ; BOOL DC ;
if ( RtlGetNtProductType( &ProductType ) ) { DC = (ProductType == NtProductLanManNt ); } else { return NULL ; }
Context = (PSSL_MAPPER_CONTEXT) SPExternalAlloc( sizeof( SSL_MAPPER_CONTEXT ) );
if ( Context ) { Context->Mapper.m_dwMapperVersion = MAPPER_INTERFACE_VER ; Context->Mapper.m_dwFlags = SCH_FLAG_SYSTEM_MAPPER ; Context->Mapper.m_Reserved1 = NULL ;
Context->Ref = 0;
if ( DC ) { Context->Mapper.m_vtable = &SslLocalTable ; } else { Context->Mapper.m_vtable = &SslRemoteTable ; }
return &Context->Mapper ; } else { return NULL ; } }
LONG WINAPI SslLocalRefMapper( PHMAPPER Mapper ) { PSSL_MAPPER_CONTEXT Context ;
Context = (PSSL_MAPPER_CONTEXT) Mapper ;
if ( Context == NULL ) { return( -1 ); }
DebugLog(( DEB_TRACE_MAPPER, "Ref of Context %x\n", Mapper ));
return( InterlockedIncrement( &Context->Ref ) );
}
LONG WINAPI SslLocalDerefMapper( PHMAPPER Mapper ) { PSSL_MAPPER_CONTEXT Context ; DWORD RefCount;
Context = (PSSL_MAPPER_CONTEXT) Mapper ;
if ( Context == NULL ) { return( -1 ); }
DebugLog(( DEB_TRACE_MAPPER, "Deref of Context %x\n", Mapper ));
RefCount = InterlockedDecrement( &Context->Ref );
if(RefCount == 0) { SPExternalFree(Context); }
return RefCount; }
DWORD WINAPI SslLocalGetIssuerList( IN PHMAPPER Mapper, IN PVOID Reserved, OUT PBYTE pIssuerList, OUT PDWORD IssuerListLength ) { DebugLog(( DEB_TRACE_MAPPER, "GetIssuerList, context %x\n", Mapper ));
return( GetDefaultIssuers(pIssuerList, IssuerListLength) ); }
DWORD WINAPI SslLocalGetChallenge( IN PHMAPPER Mapper, IN PUCHAR AuthenticatorId, IN DWORD AuthenticatorIdLength, OUT PUCHAR Challenge, OUT DWORD * ChallengeLength ) { DebugLog(( DEB_TRACE_MAPPER, "GetChallenge, context %x\n", Mapper ));
return SEC_E_UNSUPPORTED_FUNCTION; }
SECURITY_STATUS SslCreateTokenFromPac( PUCHAR AuthInfo, ULONG AuthInfoLength, PUNICODE_STRING Domain, PLUID NewLogonId, PHANDLE NewToken ) { NTSTATUS Status ; PVOID TokenInfo ; LSA_TOKEN_INFORMATION_TYPE TokenInfoType ; PLSA_TOKEN_INFORMATION_V1 TokenV1 ; PLSA_TOKEN_INFORMATION_NULL TokenNull ; LUID LogonId ; HANDLE TokenHandle ; NTSTATUS SubStatus ; SECURITY_STRING PacUserName ;
//
// Get the marshalled blob into a more useful form:
//
PacUserName.Buffer = NULL ;
Status = LsaTable->ConvertAuthDataToToken( AuthInfo, AuthInfoLength, SecurityImpersonation, &SslTokenSource, Network, Domain, &TokenHandle, &LogonId, &PacUserName, &SubStatus );
if ( NT_SUCCESS( Status ) ) { LsaTable->AuditLogon( STATUS_SUCCESS, STATUS_SUCCESS, &PacUserName, Domain, NULL, NULL, Network, &SslTokenSource, &LogonId ); } else { LsaTable->AuditLogon( Status, Status, &SslNullString, &SslNullString, NULL, NULL, Network, &SslTokenSource, &LogonId );
}
if ( !NT_SUCCESS( Status ) ) { return Status ; }
*NewToken = TokenHandle ; *NewLogonId = LogonId ;
if ( PacUserName.Buffer ) { LsaTable->FreeLsaHeap( PacUserName.Buffer ); }
return Status ; }
#define ISSUER_HEADER L"<I>"
#define CCH_ISSUER_HEADER 3
#define SUBJECT_HEADER L"<S>"
#define CCH_SUBJECT_HEADER 3
//+---------------------------------------------------------------------------
//
// Function: SslGetNameFromCertificate
//
// Synopsis: Extracts the UPN name from the certificate
//
// Arguments: [pCert] --
// [ppszName] --
// [pfMachineCert] --
//
// History: 8-8-2000 jbanes Created
//
// Notes:
//
//----------------------------------------------------------------------------
NTSTATUS SslGetNameFromCertificate( PCCERT_CONTEXT pCert, PWSTR * ppszName, BOOL * pfMachineCert) { ULONG ExtensionIndex; PWSTR pszName; DWORD cbName;
*pfMachineCert = FALSE;
//
// See if cert has UPN in AltSubjectName->otherName
//
pszName = NULL;
for(ExtensionIndex = 0; ExtensionIndex < pCert->pCertInfo->cExtension; ExtensionIndex++) { if(strcmp(pCert->pCertInfo->rgExtension[ExtensionIndex].pszObjId, szOID_SUBJECT_ALT_NAME2) == 0) { PCERT_ALT_NAME_INFO AltName = NULL; DWORD AltNameStructSize = 0; ULONG CertAltNameIndex = 0; if(CryptDecodeObjectEx(pCert->dwCertEncodingType, X509_ALTERNATE_NAME, pCert->pCertInfo->rgExtension[ExtensionIndex].Value.pbData, pCert->pCertInfo->rgExtension[ExtensionIndex].Value.cbData, CRYPT_DECODE_ALLOC_FLAG, NULL, (PVOID)&AltName, &AltNameStructSize)) {
for(CertAltNameIndex = 0; CertAltNameIndex < AltName->cAltEntry; CertAltNameIndex++) { PCERT_ALT_NAME_ENTRY AltNameEntry = &AltName->rgAltEntry[CertAltNameIndex];
if((CERT_ALT_NAME_OTHER_NAME == AltNameEntry->dwAltNameChoice) && (NULL != AltNameEntry->pOtherName) && (0 == strcmp(szOID_NT_PRINCIPAL_NAME, AltNameEntry->pOtherName->pszObjId))) { PCERT_NAME_VALUE PrincipalNameBlob = NULL; DWORD PrincipalNameBlobSize = 0;
// We found a UPN!
if(CryptDecodeObjectEx(pCert->dwCertEncodingType, X509_UNICODE_ANY_STRING, AltNameEntry->pOtherName->Value.pbData, AltNameEntry->pOtherName->Value.cbData, CRYPT_DECODE_ALLOC_FLAG, NULL, (PVOID)&PrincipalNameBlob, &PrincipalNameBlobSize)) { pszName = LocalAlloc(LPTR, PrincipalNameBlob->Value.cbData + sizeof(WCHAR)); if(pszName != NULL) { CopyMemory(pszName, PrincipalNameBlob->Value.pbData, PrincipalNameBlob->Value.cbData); }
LocalFree(PrincipalNameBlob); PrincipalNameBlob = NULL;
if(pszName == NULL) { LocalFree(AltName); return STATUS_NO_MEMORY; } } if(pszName) { break; } } } LocalFree(AltName); AltName = NULL;
if(pszName) { break; } } } }
//
// See if cert has DNS in AltSubjectName->pwszDNSName
//
if(pszName == NULL) { for(ExtensionIndex = 0; ExtensionIndex < pCert->pCertInfo->cExtension; ExtensionIndex++) { if(strcmp(pCert->pCertInfo->rgExtension[ExtensionIndex].pszObjId, szOID_SUBJECT_ALT_NAME2) == 0) { PCERT_ALT_NAME_INFO AltName = NULL; DWORD AltNameStructSize = 0; ULONG CertAltNameIndex = 0; if(CryptDecodeObjectEx(pCert->dwCertEncodingType, X509_ALTERNATE_NAME, pCert->pCertInfo->rgExtension[ExtensionIndex].Value.pbData, pCert->pCertInfo->rgExtension[ExtensionIndex].Value.cbData, CRYPT_DECODE_ALLOC_FLAG, NULL, (PVOID)&AltName, &AltNameStructSize)) {
for(CertAltNameIndex = 0; CertAltNameIndex < AltName->cAltEntry; CertAltNameIndex++) { PCERT_ALT_NAME_ENTRY AltNameEntry = &AltName->rgAltEntry[CertAltNameIndex];
if((CERT_ALT_NAME_DNS_NAME == AltNameEntry->dwAltNameChoice) && (NULL != AltNameEntry->pwszDNSName)) { PCERT_NAME_VALUE PrincipalNameBlob = NULL; DWORD PrincipalNameBlobSize = 0;
// We found a DNS!
cbName = (wcslen(AltNameEntry->pwszDNSName) + 1) * sizeof(WCHAR);
pszName = LocalAlloc(LPTR, cbName); if(pszName == NULL) { LocalFree(AltName); return STATUS_NO_MEMORY; }
CopyMemory(pszName, AltNameEntry->pwszDNSName, cbName); *pfMachineCert = TRUE; break; } } LocalFree(AltName); AltName = NULL;
if(pszName) { break; } } } } }
//
// There was no UPN in the AltSubjectName, so look for
// one in the Subject Name, in case this is a B3 compatability
// cert.
//
if(pszName == NULL) { ULONG Length;
Length = CertGetNameString( pCert, CERT_NAME_ATTR_TYPE, 0, szOID_COMMON_NAME, NULL, 0 );
if(Length) { pszName = LocalAlloc(LPTR, Length * sizeof(WCHAR));
if(!pszName) { return STATUS_NO_MEMORY ; }
if ( !CertGetNameStringW(pCert, CERT_NAME_ATTR_TYPE, 0, szOID_COMMON_NAME, pszName, Length)) { return GetLastError(); } } }
if(pszName) { *ppszName = pszName; } else { return STATUS_NOT_FOUND; }
return STATUS_SUCCESS; }
//+---------------------------------------------------------------------------
//
// Function: SslTryUpn
//
// Synopsis: Tries to find the user by UPN encoded in Cert
//
// Arguments: [User] --
// [AuthData] --
// [AuthDataLen] --
//
// History: 5-11-98 RichardW Created
//
// Notes:
//
//----------------------------------------------------------------------------
NTSTATUS SslTryUpn( PCCERT_CONTEXT User, PUCHAR * AuthData, PULONG AuthDataLen, PWSTR * ReferencedDomain ) { NTSTATUS Status ; UNICODE_STRING Upn = {0, 0, NULL}; UNICODE_STRING Cracked = {0}; UNICODE_STRING DnsDomain = {0}; UNICODE_STRING FlatName = { 0 }; ULONG SubStatus ; BOOL fMachineCert; PWSTR pszName = NULL;
*ReferencedDomain = NULL ;
//
// Get the client name from the cert
//
Status = SslGetNameFromCertificate(User, &pszName, &fMachineCert);
if(!NT_SUCCESS(Status)) { return Status; }
//
// now, try and find this guy:
//
if(fMachineCert) { Status = STATUS_NOT_FOUND; } else { // Search for "[email protected]".
Upn.Buffer = pszName; Upn.Length = wcslen(Upn.Buffer) * sizeof(WCHAR); Upn.MaximumLength = Upn.Length + sizeof(WCHAR);
DebugLog(( DEB_TRACE_MAPPER, "Looking for UPN name %ws\n", Upn.Buffer ));
Status = LsaTable->GetAuthDataForUser( &Upn, SecNameFlat, NULL, AuthData, AuthDataLen, &FlatName );
if ( FlatName.Length ) { LsaTable->AuditAccountLogon( SE_AUDITID_ACCOUNT_MAPPED, (BOOLEAN) NT_SUCCESS( Status ), &SslPackageName, &Upn, &FlatName, Status );
LsaTable->FreeLsaHeap( FlatName.Buffer ); } }
if ( Status == STATUS_NOT_FOUND ) { //
// Do the hacky check of seeing if this is our own domain, and
// if so, try opening the user as a flat, SAM name.
//
if(fMachineCert) { PWSTR pPeriod; WCHAR ch; pPeriod = wcschr( pszName, L'.' );
if(pPeriod) { if ( DnsNameCompare_W( (pPeriod+1), SslDnsDomainName ) ) { ch = *(pPeriod + 1);
*pPeriod = L'$'; *(pPeriod + 1) = L'\0';
Upn.Buffer = pszName; Upn.Length = wcslen( Upn.Buffer ) * sizeof( WCHAR ); Upn.MaximumLength = Upn.Length + sizeof(WCHAR);
DebugLog(( DEB_TRACE_MAPPER, "Looking for machine name %ws\n", Upn.Buffer ));
// Search for "computer$".
Status = LsaTable->GetAuthDataForUser( &Upn, SecNameSamCompatible, NULL, AuthData, AuthDataLen, NULL );
*pPeriod = L'.'; *(pPeriod + 1) = ch; } } } else { PWSTR AtSign; AtSign = wcschr( pszName, L'@' );
if ( AtSign ) { if ( DnsNameCompare_W( (AtSign+1), SslDnsDomainName ) ) { *AtSign = L'\0';
Upn.Buffer = pszName; Upn.Length = wcslen( Upn.Buffer ) * sizeof( WCHAR ); Upn.MaximumLength = Upn.Length + sizeof(WCHAR);
DebugLog(( DEB_TRACE_MAPPER, "Looking for user name %ws\n", Upn.Buffer ));
// Search for "username".
Status = LsaTable->GetAuthDataForUser( &Upn, SecNameSamCompatible, NULL, AuthData, AuthDataLen, NULL );
*AtSign = L'@'; } } }
if (Status == STATUS_NOT_FOUND ) { Upn.Buffer = pszName; Upn.Length = wcslen(Upn.Buffer) * sizeof(WCHAR); Upn.MaximumLength = Upn.Length + sizeof(WCHAR);
DebugLog(( DEB_TRACE_MAPPER, "Cracking name %ws at GC\n", Upn.Buffer ));
Status = LsaTable->CrackSingleName( DS_USER_PRINCIPAL_NAME, TRUE, &Upn, NULL, DS_NT4_ACCOUNT_NAME, &Cracked, &DnsDomain, &SubStatus );
if ( NT_SUCCESS( Status ) ) { if ( SubStatus == 0 ) { *ReferencedDomain = DnsDomain.Buffer ; DnsDomain.Buffer = NULL; }
if(Cracked.Buffer != NULL) { LsaTable->FreeLsaHeap( Cracked.Buffer ); } if(DnsDomain.Buffer != NULL) { LsaTable->FreeLsaHeap( DnsDomain.Buffer ); }
Status = STATUS_NOT_FOUND ; }
}
}
if(pszName) { LocalFree(pszName); }
return Status ; }
void ConvertNameString(UNICODE_STRING *Name) { PWSTR Comma1, Comma2;
//
// Scan through the name, converting "\r\n" to ",". This should be
// done by the CertNameToStr APIs, but that won't happen for a while.
//
Comma1 = Comma2 = Name->Buffer ; while ( *Comma2 ) { *Comma1 = *Comma2 ;
if ( *Comma2 == L'\r' ) { if ( *(Comma2 + 1) == L'\n' ) { *Comma1 = L','; Comma2++ ; } }
Comma1++; Comma2++; }
*Comma1 = L'\0';
Name->Length = wcslen( Name->Buffer ) * sizeof( WCHAR ); }
//+---------------------------------------------------------------------------
//
// Function: SslTryCompoundName
//
// Synopsis: Tries to find the user by concatenating the issuer and subject
// names, and looking for an AlternateSecurityId.
//
// Arguments: [User] --
// [AuthData] --
// [AuthDataLen] --
//
// History: 5-11-98 RichardW Created
//
// Notes:
//
//----------------------------------------------------------------------------
NTSTATUS SslTryCompoundName( PCCERT_CONTEXT User, PUCHAR * AuthData, PULONG AuthDataLen, PWSTR * ReferencedDomain ) { UNICODE_STRING CompoundName ; ULONG Length ; ULONG IssuerLength ; NTSTATUS Status ; PWSTR Current ; PWSTR Comma1, Comma2 ; UNICODE_STRING Cracked = {0}; UNICODE_STRING DnsDomain = {0}; UNICODE_STRING FlatName = { 0 }; ULONG SubStatus ; const DWORD dwNameToStrFlags = CERT_X500_NAME_STR | CERT_NAME_STR_NO_PLUS_FLAG | CERT_NAME_STR_CRLF_FLAG;
*ReferencedDomain = NULL ;
IssuerLength = CertNameToStr( User->dwCertEncodingType, &User->pCertInfo->Issuer, dwNameToStrFlags, NULL, 0 );
Length = CertNameToStr( User->dwCertEncodingType, &User->pCertInfo->Subject, dwNameToStrFlags, NULL, 0 );
if ( ( IssuerLength == 0 ) || ( Length == 0 ) ) { return STATUS_NO_MEMORY ; }
CompoundName.MaximumLength = (USHORT) (Length + IssuerLength + CCH_ISSUER_HEADER + CCH_SUBJECT_HEADER) * sizeof( WCHAR ) ;
CompoundName.Buffer = LocalAlloc( LMEM_FIXED, CompoundName.MaximumLength );
if ( CompoundName.Buffer ) { wcscpy( CompoundName.Buffer, ISSUER_HEADER ); Current = CompoundName.Buffer + CCH_ISSUER_HEADER ;
IssuerLength = CertNameToStrW( User->dwCertEncodingType, &User->pCertInfo->Issuer, dwNameToStrFlags, Current, IssuerLength );
Current += IssuerLength - 1 ; wcscpy( Current, SUBJECT_HEADER ); Current += CCH_SUBJECT_HEADER ;
Length = CertNameToStrW( User->dwCertEncodingType, &User->pCertInfo->Subject, dwNameToStrFlags, Current, Length );
ConvertNameString(&CompoundName);
Status = LsaTable->GetAuthDataForUser( &CompoundName, SecNameAlternateId, &SslNamePrefix, AuthData, AuthDataLen, &FlatName );
if ( FlatName.Length ) { LsaTable->AuditAccountLogon( SE_AUDITID_ACCOUNT_MAPPED, (BOOLEAN) NT_SUCCESS( Status ), &SslPackageName, &CompoundName, &FlatName, Status );
LsaTable->FreeLsaHeap( FlatName.Buffer ); }
if ( Status == STATUS_NOT_FOUND ) { Status = LsaTable->CrackSingleName( DS_ALT_SECURITY_IDENTITIES_NAME, TRUE, &CompoundName, &SslNamePrefix, DS_NT4_ACCOUNT_NAME, &Cracked, &DnsDomain, &SubStatus );
if ( NT_SUCCESS( Status ) ) { if ( SubStatus == 0 ) { *ReferencedDomain = DnsDomain.Buffer ; DnsDomain.Buffer = NULL; }
if(Cracked.Buffer != NULL) { LsaTable->FreeLsaHeap( Cracked.Buffer ); } if(DnsDomain.Buffer != NULL) { LsaTable->FreeLsaHeap( DnsDomain.Buffer ); }
Status = STATUS_NOT_FOUND ; }
}
LocalFree( CompoundName.Buffer ); } else { Status = STATUS_NO_MEMORY ; }
return Status ;
}
//+---------------------------------------------------------------------------
//
// Function: SslTryIssuer
//
// Synopsis: Tries to find a user that has an issuer mapped to it.
//
// Arguments: [User] --
// [AuthData] --
// [AuthDataLen] --
//
// History: 5-11-98 RichardW Created
//
// Notes:
//
//----------------------------------------------------------------------------
NTSTATUS SslTryIssuer( PBYTE pIssuer, DWORD cbIssuer, PUCHAR * AuthData, PULONG AuthDataLen, PWSTR * ReferencedDomain ) { UNICODE_STRING IssuerName ; ULONG IssuerLength ; NTSTATUS Status ; UNICODE_STRING Cracked = { 0 }; UNICODE_STRING DnsDomain = { 0 }; UNICODE_STRING FlatName = { 0 }; ULONG SubStatus ; const DWORD dwNameToStrFlags = CERT_X500_NAME_STR | CERT_NAME_STR_NO_PLUS_FLAG | CERT_NAME_STR_CRLF_FLAG; CERT_NAME_BLOB Issuer;
*ReferencedDomain = NULL ;
Issuer.pbData = pIssuer; Issuer.cbData = cbIssuer;
IssuerLength = CertNameToStr( CRYPT_ASN_ENCODING, &Issuer, dwNameToStrFlags, NULL, 0 );
if ( IssuerLength == 0 ) { return STATUS_NO_MEMORY ; }
IssuerName.MaximumLength = (USHORT)(CCH_ISSUER_HEADER + IssuerLength) * sizeof( WCHAR ) ;
IssuerName.Buffer = LocalAlloc( LMEM_FIXED, IssuerName.MaximumLength );
if ( IssuerName.Buffer ) { wcscpy( IssuerName.Buffer, ISSUER_HEADER );
IssuerLength = CertNameToStrW(CRYPT_ASN_ENCODING, &Issuer, dwNameToStrFlags, IssuerName.Buffer + CCH_ISSUER_HEADER, IssuerLength );
ConvertNameString(&IssuerName);
Status = LsaTable->GetAuthDataForUser( &IssuerName, SecNameAlternateId, &SslNamePrefix, AuthData, AuthDataLen, &FlatName );
if ( FlatName.Length ) { LsaTable->AuditAccountLogon( SE_AUDITID_ACCOUNT_MAPPED, (BOOLEAN) NT_SUCCESS( Status ), &SslPackageName, &IssuerName, &FlatName, Status );
LsaTable->FreeLsaHeap( FlatName.Buffer ); }
if ( Status == STATUS_NOT_FOUND ) { Status = LsaTable->CrackSingleName( DS_ALT_SECURITY_IDENTITIES_NAME, TRUE, &IssuerName, &SslNamePrefix, DS_NT4_ACCOUNT_NAME, &Cracked, &DnsDomain, &SubStatus );
if ( NT_SUCCESS( Status ) ) { if ( SubStatus == 0 ) { *ReferencedDomain = DnsDomain.Buffer ; DnsDomain.Buffer = NULL; }
if(Cracked.Buffer != NULL) { LsaTable->FreeLsaHeap( Cracked.Buffer ); } if(DnsDomain.Buffer != NULL) { LsaTable->FreeLsaHeap( DnsDomain.Buffer ); }
Status = STATUS_NOT_FOUND ; }
}
LocalFree(IssuerName.Buffer); } else { Status = STATUS_NO_MEMORY ; }
return Status ;
}
//+---------------------------------------------------------------------------
//
// Function: SslMapCertToUserPac
//
// Synopsis: Maps a certificate to a user (hopefully) and the PAC,
//
// Arguments: [Request] --
// [UserPac] --
// [UserPacLen] --
//
// History: 5-11-98 RichardW Created
//
// Notes:
//
//----------------------------------------------------------------------------
NTSTATUS SslMapCertToUserPac( IN PSSL_CERT_LOGON_REQ Request, OUT PUCHAR * UserPac, OUT PULONG UserPacLen, OUT PWSTR * ReferencedDomain ) { PCCERT_CONTEXT User ; NTSTATUS Status = STATUS_LOGON_FAILURE; NTSTATUS SubStatus = STATUS_NOT_FOUND; UNICODE_STRING UserName ; HANDLE UserHandle ; ULONG i;
*ReferencedDomain = NULL;
DebugLog(( DEB_TRACE_MAPPER, "SslMapCertToUserPac called\n" ));
User = CertCreateCertificateContext( X509_ASN_ENCODING, (PBYTE)Request + Request->OffsetCertificate, Request->CertLength );
if ( !User ) { Status = STATUS_NO_MEMORY ;
goto Cleanup ; }
//
// First, try the UPN
//
if(Request->Flags & REQ_UPN_MAPPING) { DebugLog(( DEB_TRACE_MAPPER, "Trying UPN mapping\n" ));
Status = SslTryUpn( User, UserPac, UserPacLen, ReferencedDomain );
if ( NT_SUCCESS( Status ) || ( *ReferencedDomain ) ) { goto Cleanup; }
DebugLog(( DEB_TRACE_MAPPER, "Failed with error 0x%x\n", Status )); }
//
// Swing and a miss. Try the constructed issuer+subject name
//
if(Request->Flags & REQ_SUBJECT_MAPPING) { DebugLog(( DEB_TRACE_MAPPER, "Trying Subject mapping\n" ));
Status = SslTryCompoundName( User, UserPac, UserPacLen, ReferencedDomain );
if ( NT_SUCCESS( Status ) || ( *ReferencedDomain ) ) { goto Cleanup; }
DebugLog(( DEB_TRACE_MAPPER, "Failed with error 0x%x\n", Status ));
// Return error code from issuer+subject name mapping
// in preference to the error code from many-to-one mapping.
SubStatus = Status; }
//
// Strike two. Try the issuer for a many-to-one mapping:
//
if(Request->Flags & REQ_ISSUER_MAPPING) { DebugLog(( DEB_TRACE_MAPPER, "Trying issuer mapping\n" ));
if(Request->Flags & REQ_ISSUER_CHAIN_MAPPING) { // Loop through each issuer in the certificate chain.
for(i = 0; i < Request->CertCount; i++) { Status = SslTryIssuer( (PBYTE)Request + Request->NameInfo[i].IssuerOffset, Request->NameInfo[i].IssuerLength, UserPac, UserPacLen, ReferencedDomain );
if ( NT_SUCCESS( Status ) || ( *ReferencedDomain ) ) { goto Cleanup; } } } else { // Extract the issuer name from the certificate and try
// to map it.
Status = SslTryIssuer( User->pCertInfo->Issuer.pbData, User->pCertInfo->Issuer.cbData, UserPac, UserPacLen, ReferencedDomain );
if ( NT_SUCCESS( Status ) || ( *ReferencedDomain ) ) { goto Cleanup; } }
DebugLog(( DEB_TRACE_MAPPER, "Failed with error 0x%x\n", Status )); }
//
// Certificate mapping failed. Decide what error code to return.
//
if(Status == STATUS_OBJECT_NAME_COLLISION || SubStatus == STATUS_OBJECT_NAME_COLLISION) { Status = SEC_E_MULTIPLE_ACCOUNTS; } else if(Status != STATUS_NO_MEMORY) { Status = STATUS_LOGON_FAILURE ; }
Cleanup:
if ( User ) { CertFreeCertificateContext( User ); }
DebugLog(( DEB_TRACE_MAPPER, "SslMapCertToUserPac returned 0x%x\n", Status ));
return Status ; }
DWORD WINAPI MapperVerifyClientChain( PCCERT_CONTEXT pCertContext, DWORD dwMapperFlags, DWORD * pdwMethods, NTSTATUS * pVerifyStatus, PCCERT_CHAIN_CONTEXT *ppChainContext) // optional
{ DWORD dwCertFlags = 0; DWORD dwIgnoreErrors = 0; NTSTATUS Status;
*pdwMethods = 0; *pVerifyStatus = STATUS_SUCCESS;
DebugLog(( DEB_TRACE_MAPPER, "Checking to see if cert is verified.\n" ));
if(dwMapperFlags & SCH_FLAG_REVCHECK_END_CERT) dwCertFlags |= CERT_CHAIN_REVOCATION_CHECK_END_CERT; if(dwMapperFlags & SCH_FLAG_REVCHECK_CHAIN) dwCertFlags |= CERT_CHAIN_REVOCATION_CHECK_CHAIN; if(dwMapperFlags & SCH_FLAG_REVCHECK_CHAIN_EXCLUDE_ROOT) dwCertFlags |= CERT_CHAIN_REVOCATION_CHECK_CHAIN_EXCLUDE_ROOT; if(dwMapperFlags & SCH_FLAG_IGNORE_NO_REVOCATION_CHECK) dwIgnoreErrors |= CRED_FLAG_IGNORE_NO_REVOCATION_CHECK; if(dwMapperFlags & SCH_FLAG_IGNORE_REVOCATION_OFFLINE) dwIgnoreErrors |= CRED_FLAG_IGNORE_REVOCATION_OFFLINE;
// Check to see if the certificate chain is properly signed all the way
// up and that we trust the issuer of the root certificate.
Status = VerifyClientCertificate(pCertContext, dwCertFlags, dwIgnoreErrors, CERT_CHAIN_POLICY_SSL, ppChainContext); if(!NT_SUCCESS(Status)) { DebugLog((DEB_WARN, "Client certificate failed to verify with SSL policy (0x%x)\n", Status)); LogBogusClientCertEvent(pCertContext, Status); return Status; }
// Turn on Subject and Issuer mapping.
*pdwMethods |= REQ_SUBJECT_MAPPING | REQ_ISSUER_MAPPING;
// Check to see if the certificate chain is valid for UPN mapping.
Status = VerifyClientCertificate(pCertContext, dwCertFlags, dwIgnoreErrors, CERT_CHAIN_POLICY_NT_AUTH, NULL); if(NT_SUCCESS(Status)) { // Turn on UPN mapping.
*pdwMethods |= REQ_UPN_MAPPING; } else { DebugLog((DEB_WARN, "Client certificate failed to verify with NT_AUTH policy (0x%x)\n", Status)); LogFastMappingFailureEvent(pCertContext, Status); *pVerifyStatus = Status; }
DebugLog((DEB_TRACE, "Client certificate verified with methods: 0x%x\n", *pdwMethods));
return SEC_E_OK; }
DWORD WINAPI SslLocalMapCredential( IN PHMAPPER Mapper, IN DWORD CredentialType, VOID const *pCredential, VOID const *pAuthority, OUT HLOCATOR * phLocator ) { PCCERT_CONTEXT pCert = (PCERT_CONTEXT)pCredential; PMSV1_0_PASSTHROUGH_RESPONSE Response = NULL ; PSSL_CERT_LOGON_REQ pRequest = NULL; PSSL_CERT_LOGON_RESP CertResp ; DWORD cbRequest; PUCHAR Pac = NULL ; ULONG PacLength ; PUCHAR ExpandedPac = NULL ; ULONG ExpandedPacLength ; NTSTATUS Status ; NTSTATUS VerifyStatus ; HANDLE Token ; LUID LogonId ; DWORD dwMethods ; PWSTR ReferencedDomain ; UNICODE_STRING DomainName ; UNICODE_STRING AccountDomain = { 0 }; PCCERT_CHAIN_CONTEXT pChainContext = NULL;
DebugLog(( DEB_TRACE_MAPPER, "SslLocalMapCredential, context %x\n", Mapper ));
if ( CredentialType != X509_ASN_CHAIN ) { return( SEC_E_UNKNOWN_CREDENTIALS ); }
//
// Validate client certificate, and obtain pointer to
// entire certificate chain.
//
Status = MapperVerifyClientChain(pCert, Mapper->m_dwFlags, &dwMethods, &VerifyStatus, &pChainContext); if(FAILED(Status)) { return Status; }
//
// Build the logon request.
//
Status = SslBuildCertLogonRequest(pChainContext, dwMethods, &pRequest, &cbRequest);
CertFreeCertificateChain(pChainContext); pChainContext = NULL;
if(FAILED(Status)) { return Status; }
Status = SslMapCertToUserPac( pRequest, &Pac, &PacLength, &ReferencedDomain );
if ( !NT_SUCCESS( Status ) && ( ReferencedDomain != NULL ) ) { //
// Didn't find it at this DC, but another domain appears to
// have the mapping. Forward it there:
//
RtlInitUnicodeString( &DomainName, ReferencedDomain );
Status = SslMapCertAtDC( &DomainName, pRequest, pRequest->Length, &Response );
if ( NT_SUCCESS( Status ) ) { CertResp = (PSSL_CERT_LOGON_RESP) Response->ValidationData ; Pac = (((PUCHAR) CertResp) + CertResp->OffsetAuthData); PacLength = CertResp->AuthDataLength ;
//
// older servers (pre 2010 or so) won't return the full structure,
// so we need to examine it carefully.
if ( CertResp->Length - CertResp->AuthDataLength <= sizeof( SSL_CERT_LOGON_RESP )) { AccountDomain = SslDomainName ; } else { if ( CertResp->DomainLength < 65536 ) { AccountDomain.Length = (USHORT) CertResp->DomainLength ; AccountDomain.MaximumLength = AccountDomain.Length ; AccountDomain.Buffer = (PWSTR) (((PUCHAR) CertResp) + CertResp->OffsetDomain ); } else { AccountDomain = SslDomainName ; } } }
LsaTable->FreeLsaHeap( ReferencedDomain );
} else { AccountDomain = SslDomainName ; }
if ( NT_SUCCESS( Status ) ) { Status = LsaTable->ExpandAuthDataForDomain( Pac, PacLength, NULL, &ExpandedPac, &ExpandedPacLength );
if ( NT_SUCCESS( Status ) ) { //
// Since we're about to replace the PAC
// pointer, determine if we got it indirectly
// (in the response from a remote server)
// or directly from this DC
//
if ( Response == NULL ) { //
// This is a direct one. Free the old
// copy
//
LsaTable->FreeLsaHeap( Pac ); }
Pac = ExpandedPac ; PacLength = ExpandedPacLength ;
if ( Response == NULL ) { //
// If we don't have a response, then the pac
// will get freed. If it is out of the response
// then leave it set, and it will be caught in
// the cleanup.
//
ExpandedPac = NULL ; } } }
if ( NT_SUCCESS( Status ) ) {
VerifyStatus = STATUS_SUCCESS;
Status = SslCreateTokenFromPac( Pac, PacLength, &AccountDomain, &LogonId, &Token );
if ( NT_SUCCESS( Status ) ) { *phLocator = (HLOCATOR) Token ; }
}
if(pRequest) { LocalFree(pRequest); }
if ( Response ) { LsaTable->FreeReturnBuffer( Response ); } else { LsaTable->FreeLsaHeap( Pac ); }
if ( ExpandedPac ) { LsaTable->FreeLsaHeap( ExpandedPac ); }
if(!NT_SUCCESS(Status)) { DebugLog((DEB_WARN, "Certificate mapping failed (0x%x)\n", Status));
LogCertMappingFailureEvent(Status);
if(!NT_SUCCESS(VerifyStatus)) { // Return certificate validation error code, unless the mapper
// error has already been mapped to a proper sspi error code.
if(HRESULT_FACILITY(Status) != FACILITY_SECURITY) { Status = VerifyStatus; } } }
return ( Status ); }
NTSTATUS NTAPI SslDoClientRequest( IN PLSA_CLIENT_REQUEST ClientRequest, IN PVOID ProtocolSubmitBuffer, IN PVOID ClientBufferBase, IN ULONG SubmitBufferLen, OUT PVOID * ProtocolReturnBuffer, OUT PULONG ReturnBufferLength, OUT PNTSTATUS ProtocolStatus ) { NTSTATUS Status ; PSSL_CERT_LOGON_REQ Request ; PSSL_CERT_LOGON_RESP Response, IndirectResponse ; PUCHAR Pac = NULL ; PUCHAR ExpandedPac = NULL ; ULONG PacLength ; ULONG ExpandedPacLength ; PWSTR ReferencedDomain = NULL; PWSTR FirstDot ; UNICODE_STRING DomainName = { 0 }; PMSV1_0_PASSTHROUGH_RESPONSE MsvResponse = NULL ;
DebugLog(( DEB_TRACE_MAPPER, "Handling request to do mapping\n" ));
if ( ARGUMENT_PRESENT( ProtocolReturnBuffer ) ) { *ProtocolReturnBuffer = NULL ; }
//
// Attempt to map the certificate locally.
//
Request = (PSSL_CERT_LOGON_REQ) ProtocolSubmitBuffer ;
Status = SslMapCertToUserPac( Request, &Pac, &PacLength, &ReferencedDomain );
DebugLog(( DEB_TRACE_MAPPER, "Local lookup returns %x\n", Status ));
if ( !NT_SUCCESS( Status ) && ( ReferencedDomain != NULL ) ) { //
// Didn't find it at this DC, but another domain appears to
// have the mapping. Forward it there:
//
RtlInitUnicodeString( &DomainName, ReferencedDomain );
DsysAssert( !DnsNameCompare_W( ReferencedDomain, SslDnsDomainName ) ); DsysAssert( !RtlEqualUnicodeString( &DomainName, &SslDomainName, TRUE ) );
if ( DnsNameCompare_W( ReferencedDomain, SslDnsDomainName ) || RtlEqualUnicodeString( &DomainName, &SslDomainName, TRUE ) ) { DebugLog(( DEB_TRACE_MAPPER, "GC is out of sync, bailing on this user\n" )); Status = STATUS_LOGON_FAILURE ;
} else { DebugLog(( DEB_TRACE_MAPPER, "Mapping certificate at DC for domain %ws\n", ReferencedDomain ));
Status = SslMapCertAtDC( &DomainName, Request, Request->Length, &MsvResponse );
if ( NT_SUCCESS( Status ) ) { IndirectResponse = (PSSL_CERT_LOGON_RESP) MsvResponse->ValidationData ;
Pac = (((PUCHAR) IndirectResponse) + IndirectResponse->OffsetAuthData);
PacLength = IndirectResponse->AuthDataLength ;
FirstDot = wcschr( ReferencedDomain, L'.' );
if ( FirstDot ) { *FirstDot = L'\0'; RtlInitUnicodeString( &DomainName, ReferencedDomain ); }
if ( IndirectResponse->Length - IndirectResponse->AuthDataLength <= sizeof( SSL_CERT_LOGON_RESP )) { //
// use the first token from the referenced domain
//
NOTHING ;
} else { if ( IndirectResponse->DomainLength < 65536 ) { DomainName.Length = (USHORT) IndirectResponse->DomainLength ; DomainName.MaximumLength = DomainName.Length ; DomainName.Buffer = (PWSTR) (((PUCHAR) IndirectResponse) + IndirectResponse->OffsetDomain ); } else { NOTHING ; } } }
}
} else { DomainName = SslDomainName ; }
if ( NT_SUCCESS( Status ) ) { //
// expand resource groups
//
Status = LsaTable->ExpandAuthDataForDomain( Pac, PacLength, NULL, &ExpandedPac, &ExpandedPacLength );
if ( NT_SUCCESS( Status ) ) { //
// Careful manipulation of pointers to handle
// the free cases in the cleanup. This is
// better explained up one function where
// the expand call is also made.
//
if ( MsvResponse == NULL ) { LsaTable->FreeLsaHeap( Pac ); }
Pac = ExpandedPac ; PacLength = ExpandedPacLength ;
if ( MsvResponse == NULL ) { ExpandedPac = NULL ; } } }
if ( !NT_SUCCESS( Status ) ) { *ReturnBufferLength = 0; *ProtocolStatus = Status ;
Status = STATUS_SUCCESS ;
goto Cleanup ;
}
//
// Construct the response blob:
//
Response = VirtualAlloc( NULL, sizeof( SSL_CERT_LOGON_RESP ) + PacLength + DomainName.Length, MEM_COMMIT, PAGE_READWRITE );
if ( Response ) { Response->MessageType = SSL_LOOKUP_CERT_MESSAGE; Response->Length = sizeof( SSL_CERT_LOGON_RESP ) + PacLength + DomainName.Length ;
Response->OffsetAuthData = sizeof( SSL_CERT_LOGON_RESP ); Response->AuthDataLength = PacLength ;
RtlCopyMemory( ( Response + 1 ), Pac, PacLength );
Response->OffsetDomain = sizeof( SSL_CERT_LOGON_RESP ) + PacLength ; Response->DomainLength = DomainName.Length ;
RtlCopyMemory( (PUCHAR) Response + Response->OffsetDomain, DomainName.Buffer, DomainName.Length );
*ProtocolReturnBuffer = Response ; *ReturnBufferLength = Response->Length ; *ProtocolStatus = STATUS_SUCCESS ;
Status = STATUS_SUCCESS ;
} else { Status = STATUS_NO_MEMORY ; }
Cleanup:
if ( MsvResponse == NULL ) { LsaTable->FreeLsaHeap( Pac ); } else { LsaTable->FreeReturnBuffer( MsvResponse ); }
if ( ExpandedPac ) { LsaTable->FreeLsaHeap( ExpandedPac ); }
if ( ReferencedDomain ) { LsaTable->FreeLsaHeap( ReferencedDomain ); }
return Status ;
}
//+---------------------------------------------------------------------------
//
// Function: SslBuildCertLogonRequest
//
// Synopsis: Builds a certificate logon request to send to the server.
//
// Arguments: [pChainContext] --
// [dwMethods] --
// [ppRequest] --
// [pcbRequest] --
//
// History: 2-26-2001 Jbanes Created
//
// Notes: The certificate data that this function builds
// looks something like this:
//
// typedef struct _SSL_CERT_LOGON_REQ {
// ULONG MessageType ;
// ULONG Length ;
// ULONG OffsetCertficate ;
// ULONG CertLength ;
// ULONG Flags;
// ULONG CertCount;
// SSL_CERT_NAME_INFO NameInfo[1];
// } SSL_CERT_LOGON_REQ, * PSSL_CERT_LOGON_REQ ;
//
// <client certificate>
// <issuer #1 name>
// <issuer #2 name>
// ...
//
//----------------------------------------------------------------------------
NTSTATUS WINAPI SslBuildCertLogonRequest( PCCERT_CHAIN_CONTEXT pChainContext, DWORD dwMethods, PSSL_CERT_LOGON_REQ *ppRequest, PDWORD pcbRequest) { PCERT_SIMPLE_CHAIN pSimpleChain; PCCERT_CONTEXT pCert; PCCERT_CONTEXT pCurrentCert; PSSL_CERT_LOGON_REQ pCertReq = NULL; DWORD Size; DWORD Offset; DWORD CertCount; NTSTATUS Status; ULONG i;
//
// Compute the request size.
//
pSimpleChain = pChainContext->rgpChain[0];
pCert = pSimpleChain->rgpElement[0]->pCertContext;
Size = sizeof(SSL_CERT_LOGON_REQ) + pCert->cbCertEncoded;
CertCount = 0;
for(i = 0; i < pSimpleChain->cElement; i++) { pCurrentCert = pSimpleChain->rgpElement[i]->pCertContext;
if(i > 0) { // Verify that this is not a root certificate.
if(CertCompareCertificateName(pCurrentCert->dwCertEncodingType, &pCurrentCert->pCertInfo->Issuer, &pCurrentCert->pCertInfo->Subject)) { break; }
Size += sizeof(SSL_CERT_NAME_INFO); }
Size += pCurrentCert->pCertInfo->Issuer.cbData; CertCount++; }
Size = ROUND_UP_COUNT( Size, ALIGN_DWORD );
//
// Build the request.
//
pCertReq = (PSSL_CERT_LOGON_REQ)LocalAlloc(LPTR, Size);
if ( !pCertReq ) { return SEC_E_INSUFFICIENT_MEMORY ; }
Offset = sizeof(SSL_CERT_LOGON_REQ) + (CertCount - 1) * sizeof(SSL_CERT_NAME_INFO);
pCertReq->MessageType = SSL_LOOKUP_CERT_MESSAGE; pCertReq->Length = Size; pCertReq->OffsetCertificate = Offset; pCertReq->CertLength = pCert->cbCertEncoded; pCertReq->Flags = dwMethods | REQ_ISSUER_CHAIN_MAPPING;
RtlCopyMemory((PBYTE)pCertReq + Offset, pCert->pbCertEncoded, pCert->cbCertEncoded); Offset += pCert->cbCertEncoded;
pCertReq->CertCount = CertCount;
for(i = 0; i < CertCount; i++) { pCurrentCert = pSimpleChain->rgpElement[i]->pCertContext;
pCertReq->NameInfo[i].IssuerOffset = Offset; pCertReq->NameInfo[i].IssuerLength = pCurrentCert->pCertInfo->Issuer.cbData;
RtlCopyMemory((PBYTE)pCertReq + Offset, pCurrentCert->pCertInfo->Issuer.pbData, pCurrentCert->pCertInfo->Issuer.cbData); Offset += pCurrentCert->pCertInfo->Issuer.cbData; }
Offset = ROUND_UP_COUNT( Offset, ALIGN_DWORD );
#if DBG
DsysAssert(Offset == Size); #endif
//
// Return completed request.
//
*ppRequest = pCertReq; *pcbRequest = Size;
return STATUS_SUCCESS; }
//+---------------------------------------------------------------------------
//
// Function: SslMapCertAtDC
//
// Synopsis: Maps a certificate to a user (hopefully) and the PAC,
//
// Arguments: [DomainName] --
// [pCredential] --
// [cbCredential] --
// [DcResponse] --
//
// History: 5-11-1998 RichardW Created
// 2-26-2001 Jbanes Added certificate chaining support.
//
// Notes: The request that gets sent to the DC looks something
// like this:
//
// typedef struct _MSV1_0_PASSTHROUGH_REQUEST {
// MSV1_0_PROTOCOL_MESSAGE_TYPE MessageType;
// UNICODE_STRING DomainName;
// UNICODE_STRING PackageName;
// ULONG DataLength;
// PUCHAR LogonData;
// ULONG Pad ;
// } MSV1_0_PASSTHROUGH_REQUEST, *PMSV1_0_PASSTHROUGH_REQUEST;
//
// <domain name>
// <package name>
// [ padding ]
//
// <credential>
// [ padding ]
//
//----------------------------------------------------------------------------
NTSTATUS WINAPI SslMapCertAtDC( PUNICODE_STRING DomainName, VOID const *pCredential, DWORD cbCredential, PMSV1_0_PASSTHROUGH_RESPONSE * DcResponse ) { PUCHAR Pac ; ULONG PacLength ; NTSTATUS Status ; PMSV1_0_PASSTHROUGH_REQUEST Request ; PMSV1_0_PASSTHROUGH_RESPONSE Response ; DWORD Size ; DWORD RequestSize ; DWORD ResponseSize ; PUCHAR Where ; NTSTATUS SubStatus ; #if DBG
DWORD CheckSize2 ; #endif
DebugLog(( DEB_TRACE_MAPPER, "Remote call to DC to do the mapping\n" ));
// Reality check size of certificate.
if(cbCredential > 0x4000) { return SEC_E_ILLEGAL_MESSAGE; }
Size = cbCredential;
Size = ROUND_UP_COUNT( Size, ALIGN_DWORD );
RequestSize = DomainName->Length + SslPackageName.Length ;
RequestSize = ROUND_UP_COUNT( RequestSize, ALIGN_DWORD );
#if DBG
CheckSize2 = RequestSize ; #endif
RequestSize += sizeof( MSV1_0_PASSTHROUGH_REQUEST ) + Size ;
Request = (PMSV1_0_PASSTHROUGH_REQUEST) LocalAlloc( LMEM_FIXED, RequestSize );
if ( !Request ) { return SEC_E_INSUFFICIENT_MEMORY ; }
Where = (PUCHAR) (Request + 1);
Request->MessageType = MsV1_0GenericPassthrough ; Request->DomainName = *DomainName ; Request->DomainName.Buffer = (LPWSTR) Where ;
RtlCopyMemory( Where, DomainName->Buffer, DomainName->Length );
Where += DomainName->Length ;
Request->PackageName = SslPackageName ; Request->PackageName.Buffer = (LPWSTR) Where ; RtlCopyMemory( Where, SslPackageName.Buffer, SslPackageName.Length );
Where += SslPackageName.Length ;
Where = ROUND_UP_POINTER( Where, ALIGN_DWORD );
#if DBG
DsysAssert( (((PUCHAR) Request) + CheckSize2 + sizeof( MSV1_0_PASSTHROUGH_REQUEST ) ) == (PUCHAR) Where ); #endif
Request->LogonData = Where ; Request->DataLength = Size ;
RtlCopyMemory( Request->LogonData, pCredential, cbCredential );
//
// Now, call through to our DC:
//
Status = LsaTable->CallPackage( &SslMsvName, Request, RequestSize, &Response, &ResponseSize, &SubStatus );
LocalFree( Request );
if ( !NT_SUCCESS( Status ) ) { DebugLog(( DEB_TRACE_MAPPER, "SslMapCertAtDC returned status 0x%x\n", Status )); return Status ; }
if ( !NT_SUCCESS( SubStatus ) ) { DebugLog(( DEB_TRACE_MAPPER, "SslMapCertAtDC returned sub-status 0x%x\n", SubStatus )); return SubStatus ; }
*DcResponse = Response ;
DebugLog(( DEB_TRACE_MAPPER, "SslMapCertAtDC returned 0x%x\n", STATUS_SUCCESS ));
return STATUS_SUCCESS ;
}
NTSTATUS NTAPI SslMapExternalCredential( IN PLSA_CLIENT_REQUEST ClientRequest, IN PVOID ProtocolSubmitBuffer, IN PVOID ClientBufferBase, IN ULONG SubmitBufferLen, OUT PVOID * ProtocolReturnBuffer, OUT PULONG ReturnBufferLength, OUT PNTSTATUS ProtocolStatus ) { PSSL_EXTERNAL_CERT_LOGON_REQ Request; PSSL_EXTERNAL_CERT_LOGON_RESP Response; NT_PRODUCT_TYPE ProductType; BOOL DC; HMAPPER Mapper; NTSTATUS Status; HANDLE hUserToken = NULL;
DebugLog(( DEB_TRACE_MAPPER, "SslMapExternalCredential\n" ));
//
// Validate the input parameters.
//
if ( ARGUMENT_PRESENT( ProtocolReturnBuffer ) ) { *ProtocolReturnBuffer = NULL ; }
Request = (PSSL_EXTERNAL_CERT_LOGON_REQ) ProtocolSubmitBuffer ;
if(Request->Length != sizeof(SSL_EXTERNAL_CERT_LOGON_REQ)) { return STATUS_INVALID_PARAMETER; }
//
// Attempt to map the certificate.
//
if(RtlGetNtProductType(&ProductType)) { DC = (ProductType == NtProductLanManNt); } else { return STATUS_NO_MEMORY ; }
memset(&Mapper, 0, sizeof(Mapper));
Mapper.m_dwFlags = SCH_FLAG_SYSTEM_MAPPER | Request->Flags;
if(DC) { Status = SslLocalMapCredential( &Mapper, Request->CredentialType, Request->Credential, NULL, (PHLOCATOR)&hUserToken); } else { Status = SslRemoteMapCredential(&Mapper, Request->CredentialType, Request->Credential, NULL, (PHLOCATOR)&hUserToken); }
if(!NT_SUCCESS(Status)) { *ReturnBufferLength = 0; *ProtocolStatus = Status; Status = STATUS_SUCCESS; goto cleanup; }
//
// Build the response.
//
Response = VirtualAlloc( NULL, sizeof(SSL_EXTERNAL_CERT_LOGON_RESP), MEM_COMMIT, PAGE_READWRITE);
if ( Response ) { Response->MessageType = SSL_LOOKUP_EXTERNAL_CERT_MESSAGE; Response->Length = sizeof(SSL_EXTERNAL_CERT_LOGON_RESP); Response->UserToken = hUserToken; Response->Flags = 0;
*ProtocolReturnBuffer = Response; *ReturnBufferLength = Response->Length; *ProtocolStatus = STATUS_SUCCESS; hUserToken = NULL;
Status = STATUS_SUCCESS; } else { Status = STATUS_NO_MEMORY; }
cleanup:
if(hUserToken) { CloseHandle(hUserToken); }
return Status; }
DWORD WINAPI SslRemoteMapCredential( IN PHMAPPER Mapper, IN DWORD CredentialType, VOID const *pCredential, VOID const *pAuthority, OUT HLOCATOR * phLocator ) { PCCERT_CONTEXT pCert = (PCERT_CONTEXT)pCredential; PUCHAR Pac ; ULONG PacLength ; NTSTATUS Status ; NTSTATUS VerifyStatus ; HANDLE Token ; LUID LogonId = { 0 }; PMSV1_0_PASSTHROUGH_RESPONSE Response ; PSSL_CERT_LOGON_RESP CertResp ; UNICODE_STRING AccountDomain ; DWORD dwMethods; PCCERT_CHAIN_CONTEXT pChainContext = NULL; PSSL_CERT_LOGON_REQ pRequest = NULL; DWORD cbRequest;
DebugLog(( DEB_TRACE_MAPPER, "SslRemoteMapCredential, context %x\n", Mapper ));
if ( CredentialType != X509_ASN_CHAIN ) { return( SEC_E_UNKNOWN_CREDENTIALS ); }
//
// Validate client certificate, and obtain pointer to
// entire certificate chain.
//
Status = MapperVerifyClientChain(pCert, Mapper->m_dwFlags, &dwMethods, &VerifyStatus, &pChainContext); if(FAILED(Status)) { return Status; }
//
// Build the logon request.
//
Status = SslBuildCertLogonRequest(pChainContext, dwMethods, &pRequest, &cbRequest);
CertFreeCertificateChain(pChainContext); pChainContext = NULL;
if(FAILED(Status)) { return Status; }
//
// Send the request to the DC.
//
Status = SslMapCertAtDC( &SslDomainName, pRequest, cbRequest, &Response );
LocalFree(pRequest); pRequest = NULL;
if ( !NT_SUCCESS( Status ) ) { LsaTable->AuditLogon( Status, VerifyStatus, &SslNullString, &SslNullString, NULL, NULL, Network, &SslTokenSource, &LogonId );
LogCertMappingFailureEvent(Status);
if(!NT_SUCCESS(VerifyStatus)) { // Return certificate validation error code, unless the mapper
// error has already been mapped to a proper sspi error code.
if(HRESULT_FACILITY(Status) != FACILITY_SECURITY) { Status = VerifyStatus; } }
return Status ; }
//
// Ok, we got mapping data. Try to use it:
//
CertResp = (PSSL_CERT_LOGON_RESP) Response->ValidationData ;
//
// older servers (pre 2010 or so) won't return the full structure,
// so we need to examine it carefully.
if ( CertResp->Length - CertResp->AuthDataLength <= sizeof( SSL_CERT_LOGON_RESP )) { AccountDomain = SslDomainName ; } else { if ( CertResp->DomainLength < 65536 ) { AccountDomain.Length = (USHORT) CertResp->DomainLength ; AccountDomain.MaximumLength = AccountDomain.Length ; AccountDomain.Buffer = (PWSTR) (((PUCHAR) CertResp) + CertResp->OffsetDomain ); } else { AccountDomain = SslDomainName ; } }
Status = SslCreateTokenFromPac( (((PUCHAR) CertResp) + CertResp->OffsetAuthData), CertResp->AuthDataLength, &AccountDomain, &LogonId, &Token );
if ( NT_SUCCESS( Status ) ) { *phLocator = (HLOCATOR) Token ; } else { LogCertMappingFailureEvent(Status); }
LsaTable->FreeReturnBuffer( Response );
return ( Status );
}
DWORD WINAPI SslLocalCloseLocator( IN PHMAPPER Mapper, IN HLOCATOR Locator ) { DebugLog(( DEB_TRACE_MAPPER, "CloseLocator, context %x\n", Mapper ));
NtClose( (HANDLE) Locator );
return( SEC_E_OK ); }
DWORD WINAPI SslLocalGetAccessToken( IN PHMAPPER Mapper, IN HLOCATOR Locator, OUT HANDLE *Token ) { DebugLog(( DEB_TRACE_MAPPER, "GetAccessToken, context %x\n", Mapper ));
*Token = (HANDLE) Locator ;
return( SEC_E_OK ); }
BOOL SslRelocateToken( IN HLOCATOR Locator, OUT HLOCATOR * NewLocator) { NTSTATUS Status ;
Status = LsaTable->DuplicateHandle( (HANDLE) Locator, (PHANDLE) NewLocator );
if ( NT_SUCCESS( Status ) ) { return( TRUE ); }
return( FALSE );
}
#if 0
DWORD TestExternalMapper( PCCERT_CONTEXT pCertContext) { NTSTATUS Status; NTSTATUS AuthPackageStatus; SSL_EXTERNAL_CERT_LOGON_REQ Request; PSSL_EXTERNAL_CERT_LOGON_RESP pResponse; ULONG ResponseLength; UNICODE_STRING PackageName;
//
// Build request.
//
memset(&Request, 0, sizeof(SSL_EXTERNAL_CERT_LOGON_REQ));
Request.MessageType = SSL_LOOKUP_EXTERNAL_CERT_MESSAGE; Request.Length = sizeof(SSL_EXTERNAL_CERT_LOGON_REQ); Request.CredentialType = X509_ASN_CHAIN; Request.Credential = (PVOID)pCertContext;
//
// Call security package (must make call as local system).
//
RtlInitUnicodeString(&PackageName, L"Schannel");
Status = LsaICallPackage( &PackageName, &Request, Request.Length, (PVOID *)&pResponse, &ResponseLength, &AuthPackageStatus );
if(!NT_SUCCESS(Status)) { return Status; }
if(NT_SUCCESS(AuthPackageStatus)) { //
// Mapping was successful.
//
}
LsaIFreeReturnBuffer( pResponse );
return ERROR_SUCCESS; } #endif
|