//+--------------------------------------------------------------------------- // // 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 #include #include #include #include #include #include #include #include #include #include #include #include #include "wincrypt.h" #include #include #include #include #include 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 }; 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( IN PUNICODE_STRING DomainName, IN VOID const *pCredential, IN DWORD cbCredential, IN BOOL IsDC, OUT PUCHAR *UserPac, OUT PULONG UserPacLen, OUT PMSV1_0_PASSTHROUGH_RESPONSE * DcResponse); SECURITY_STRING SslNullString = { 0, sizeof( WCHAR ), L"" }; HANDLE SslLogonHandle = NULL; ULONG SslKerberosPackageId = 0; ULONG SslMsvPackageId = 0; TOKEN_GROUPS *SslPackageSid = 0; NTSTATUS SslInitSystemMapper(void) { NTSTATUS Status; ULONG Dummy; LSA_STRING Name; // // Get handle to Kerberos package. // Status = LsaRegisterLogonProcess( &SslPackageNameA, &SslLogonHandle, &Dummy ); if(!NT_SUCCESS(Status)) { return SP_LOG_RESULT(Status); } RtlInitString(&Name, MICROSOFT_KERBEROS_NAME_A ); Status = LsaLookupAuthenticationPackage( SslLogonHandle, &Name, &SslKerberosPackageId ); if (!NT_SUCCESS(Status)) { return SP_LOG_RESULT(Status); } // // Get handle to NTLM package. // RtlInitString(&Name, MSV1_0_PACKAGE_NAME ); Status = LsaLookupAuthenticationPackage( SslLogonHandle, &Name, &SslMsvPackageId ); if (!NT_SUCCESS(Status)) { return SP_LOG_RESULT(Status); } // // Build schannel package SID. // { SID_IDENTIFIER_AUTHORITY PackageSidAuthority = SECURITY_NT_AUTHORITY; BYTE PackageSidBuffer[ SECURITY_MAX_SID_SIZE ]; PSID PackageSid = (PSID)PackageSidBuffer; ULONG Length; RtlInitializeSid(PackageSid, &PackageSidAuthority, 2); *(RtlSubAuthoritySid(PackageSid, 0)) = SECURITY_PACKAGE_BASE_RID; *(RtlSubAuthoritySid(PackageSid, 1)) = UNISP_RPC_ID; Length = RtlLengthSid(PackageSid); SslPackageSid = LocalAlloc(LPTR, sizeof(TOKEN_GROUPS) + Length); if(SslPackageSid == NULL) { return SP_LOG_RESULT(STATUS_INSUFFICIENT_RESOURCES); } SslPackageSid->GroupCount = 1; SslPackageSid->Groups[0].Sid = (PSID)&SslPackageSid->Groups[1]; SslPackageSid->Groups[0].Attributes = SE_GROUP_MANDATORY | SE_GROUP_ENABLED_BY_DEFAULT | SE_GROUP_ENABLED; RtlCopySid(Length, SslPackageSid->Groups[0].Sid, PackageSid); } g_SslS4U2SelfInitialized = TRUE; return STATUS_SUCCESS; } PHMAPPER SslGetMapper( BOOL Ignored ) { PSSL_MAPPER_CONTEXT Context ; NT_PRODUCT_TYPE ProductType ; BOOL DC ; UNREFERENCED_PARAMETER(Ignored); 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 ) { UNREFERENCED_PARAMETER(Mapper); UNREFERENCED_PARAMETER(Reserved); DebugLog(( DEB_TRACE_MAPPER, "SslLocalGetIssuerList\n" )); return( (DWORD)GetDefaultIssuers(pIssuerList, IssuerListLength) ); } DWORD WINAPI SslLocalGetChallenge( IN PHMAPPER Mapper, IN PUCHAR AuthenticatorId, IN DWORD AuthenticatorIdLength, OUT PUCHAR Challenge, OUT DWORD * ChallengeLength ) { UNREFERENCED_PARAMETER(Mapper); UNREFERENCED_PARAMETER(AuthenticatorId); UNREFERENCED_PARAMETER(AuthenticatorIdLength); UNREFERENCED_PARAMETER(Challenge); UNREFERENCED_PARAMETER(ChallengeLength); DebugLog(( DEB_TRACE_MAPPER, "SslLocalGetChallenge\n" )); return (DWORD)SEC_E_UNSUPPORTED_FUNCTION; } //+--------------------------------------------------------------------------- // // Function: GetTokenUserSid // // Synopsis: Obtain the user SID from the specified user token // // Arguments: [hUserToken] -- User token. // [ppUserSid] -- Returned SID. // // History: 10-08-2001 jbanes Created // // Notes: // //---------------------------------------------------------------------------- BOOL GetTokenUserSid( IN HANDLE hUserToken, // token to query IN OUT PSID *ppUserSid // resultant user sid ) { BYTE FastBuffer[256]; LPBYTE SlowBuffer = NULL; PTOKEN_USER ptgUser; DWORD cbBuffer; BOOL fSuccess = FALSE; *ppUserSid = NULL; if(hUserToken == NULL) { return FALSE; } // // try querying based on a fast stack based buffer first. // ptgUser = (PTOKEN_USER)FastBuffer; cbBuffer = sizeof(FastBuffer); fSuccess = GetTokenInformation( hUserToken,// identifies access token TokenUser, // TokenUser info type ptgUser, // retrieved info buffer cbBuffer, // size of buffer passed-in &cbBuffer // required buffer size ); if(!fSuccess) { if(GetLastError() == ERROR_INSUFFICIENT_BUFFER) { // try again with the specified buffer size SlowBuffer = (LPBYTE)SPExternalAlloc(cbBuffer); if(SlowBuffer != NULL) { ptgUser = (PTOKEN_USER)SlowBuffer; fSuccess = GetTokenInformation( hUserToken,// identifies access token TokenUser, // TokenUser info type ptgUser, // retrieved info buffer cbBuffer, // size of buffer passed-in &cbBuffer // required buffer size ); } } } // // if we got the token info successfully, copy the // relevant element for the caller. // if(fSuccess) { DWORD cbSid; // reset to assume failure fSuccess = FALSE; cbSid = GetLengthSid(ptgUser->User.Sid); *ppUserSid = SPExternalAlloc( cbSid ); if(*ppUserSid != NULL) { fSuccess = CopySid(cbSid, *ppUserSid, ptgUser->User.Sid); } else { fSuccess = FALSE; } } if(!fSuccess) { if(*ppUserSid) { SPExternalFree(*ppUserSid); *ppUserSid = NULL; } } if(SlowBuffer) { SPExternalFree(SlowBuffer); } return fSuccess; } SECURITY_STATUS SslCreateTokenFromPac( PUCHAR AuthInfo, ULONG AuthInfoLength, PUNICODE_STRING Domain, PLUID NewLogonId, PHANDLE NewToken ) { NTSTATUS Status ; 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 ) ) { PSID pSid; if(!GetTokenUserSid(TokenHandle, &pSid)) { pSid = NULL; } LsaTable->AuditLogon( STATUS_SUCCESS, STATUS_SUCCESS, &PacUserName, Domain, NULL, pSid, Network, &SslTokenSource, &LogonId ); if(pSid) { SPExternalFree(pSid); } } 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"" #define CCH_ISSUER_HEADER 3 #define SUBJECT_HEADER L"" #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)) { // We found a DNS! cbName = (lstrlen(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)) { LocalFree(pszName); return STATUS_OBJECT_NAME_NOT_FOUND; } } } if(pszName) { *ppszName = pszName; } else { return STATUS_NOT_FOUND; } return STATUS_SUCCESS; } //+--------------------------------------------------------------------------- // // Function: SslTryS4U2Self // // Synopsis: Creates a user token via the Kerberos S4U2Self mechanism. // This should work even cross-forest, provided that all of the // DC's are running Whistler. Pretty cool! // // Arguments: [pChainContext] -- // [UserToken] -- // // History: 06-13-2002 jbanes Created // // Notes: // //---------------------------------------------------------------------------- NTSTATUS SslTryS4U2Self( IN PCCERT_CHAIN_CONTEXT pChainContext, OUT HANDLE *UserToken ) { NTSTATUS Status; NTSTATUS SubStatus; BOOL fMachineCert; PWSTR pszUserName = NULL; PCERT_SIMPLE_CHAIN pSimpleChain; PCCERT_CONTEXT pCert; PKERB_S4U_LOGON LogonInfo = NULL; ULONG LogonInfoSize = sizeof(KERB_S4U_LOGON); PKERB_INTERACTIVE_PROFILE Profile = NULL; ULONG ProfileSize; LUID LogonId; QUOTA_LIMITS Quotas; *UserToken = NULL; if(!g_SslS4U2SelfInitialized) { return SP_LOG_RESULT(STATUS_NOT_SUPPORTED); } // // Get the client name from the cert // pSimpleChain = pChainContext->rgpChain[0]; pCert = pSimpleChain->rgpElement[0]->pCertContext; Status = SslGetNameFromCertificate(pCert, &pszUserName, &fMachineCert); if(!NT_SUCCESS(Status)) { return Status; } if(fMachineCert) { // S4U2Self doesn't work with machine accounts. Status = STATUS_NOT_FOUND; goto cleanup; } DebugLog(( DEB_TRACE_MAPPER, "Looking for UPN name %ws\n", pszUserName )); // // Build logon info structure. // LogonInfoSize = sizeof(KERB_S4U_LOGON) + (lstrlen(pszUserName) + 1) * sizeof(WCHAR); LogonInfo = (PKERB_S4U_LOGON) LocalAlloc(LPTR, LogonInfoSize); if (NULL == LogonInfo) { Status = STATUS_NO_MEMORY; goto cleanup; } LogonInfo->MessageType = KerbS4ULogon; LogonInfo->ClientUpn.Buffer = (LPWSTR)(LogonInfo + 1); LogonInfo->ClientUpn.Length = (USHORT)(lstrlen(pszUserName)) * sizeof(WCHAR); LogonInfo->ClientUpn.MaximumLength = LogonInfo->ClientUpn.Length + sizeof(WCHAR); memcpy((PUCHAR)(LogonInfo + 1), pszUserName, LogonInfo->ClientUpn.MaximumLength); // // Attempt to log the user on. // Status = LsaLogonUser( SslLogonHandle, &SslPackageNameA, Network, SslKerberosPackageId, LogonInfo, LogonInfoSize, SslPackageSid, &SslTokenSource, (PVOID *)&Profile, &ProfileSize, &LogonId, UserToken, &Quotas, &SubStatus ); if (NT_SUCCESS(Status)) { Status = SubStatus; } if (!NT_SUCCESS(Status)) { goto cleanup; } Status = LsaISetTokenDacl(*UserToken); cleanup: if(Profile) { LsaFreeReturnBuffer(Profile); } if(LogonInfo) { LocalFree(LogonInfo); } if(pszUserName) { LocalFree(pszUserName); } return Status; } //+--------------------------------------------------------------------------- // // 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; PWSTR pszServiceName = NULL; DWORD cchServiceName; *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) { // Search for "host/foo.com". cchServiceName = lstrlenW(L"host/") + lstrlenW(pszName); SafeAllocaAllocate(pszServiceName, (cchServiceName + 1) * sizeof(WCHAR)); if(pszServiceName == NULL) { Status = STATUS_NO_MEMORY; } else { lstrcpyW(pszServiceName, L"host/"); lstrcatW(pszServiceName, pszName); RtlInitUnicodeString(&Upn, pszServiceName); DebugLog(( DEB_TRACE_MAPPER, "Looking for SPN name %ws\n", Upn.Buffer )); Status = LsaTable->GetAuthDataForUser( &Upn, SecNameSPN, 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 ); } } } else { // Search for "username@foo.com". RtlInitUnicodeString(&Upn, pszName); 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 ) { UNICODE_STRING DomainName; BOOL NameMatch; // // 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) { RtlInitUnicodeString( &DomainName, pPeriod + 1 ); SslGlobalReadLock(); NameMatch = RtlEqualUnicodeString(&DomainName, &SslGlobalDnsDomainName, TRUE); SslGlobalReleaseLock(); if(NameMatch) { ch = *(pPeriod + 1); *pPeriod = L'$'; *(pPeriod + 1) = L'\0'; RtlInitUnicodeString(&Upn, pszName); 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 ) { RtlInitUnicodeString( &DomainName, AtSign + 1 ); SslGlobalReadLock(); NameMatch = RtlEqualUnicodeString(&DomainName, &SslGlobalDnsDomainName, TRUE); SslGlobalReleaseLock(); if(NameMatch) { *AtSign = L'\0'; RtlInitUnicodeString(&Upn, pszName); 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 ) { if(fMachineCert) { RtlInitUnicodeString(&Upn, pszServiceName); DebugLog(( DEB_TRACE_MAPPER, "Cracking name %ws at GC\n", Upn.Buffer )); Status = LsaTable->CrackSingleName( DS_SERVICE_PRINCIPAL_NAME, TRUE, &Upn, NULL, DS_NT4_ACCOUNT_NAME, &Cracked, &DnsDomain, &SubStatus ); } else { RtlInitUnicodeString(&Upn, pszName); 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); } if(pszServiceName) { SafeAllocaFree(pszServiceName); } 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 = (USHORT)(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 ; 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 ) ; SafeAllocaAllocate( CompoundName.Buffer, 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 ; } } SafeAllocaFree( 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; BOOL fReferral = FALSE; *ReferencedDomain = NULL ; // // See if issuer is in cache. If so, then we know that this issuer // doesn't map to a user account. // if(SPFindIssuerInCache(pIssuer, cbIssuer)) { return STATUS_NOT_FOUND; } LogDistinguishedName(DEB_TRACE, "SslTryIssuer: %s\n", pIssuer, cbIssuer); // // Attempt to map the issuer. // 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 ) ; SafeAllocaAllocate( IssuerName.Buffer, 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; fReferral = TRUE; } if(Cracked.Buffer != NULL) { LsaTable->FreeLsaHeap( Cracked.Buffer ); } if(DnsDomain.Buffer != NULL) { LsaTable->FreeLsaHeap( DnsDomain.Buffer ); } Status = STATUS_NOT_FOUND ; } if(!fReferral) { // No mapping was found for this issuer, and no referral // either. Add this issuer to the issuer cache, so that // we don't attempt to map it again (until the cache entry // expires). SPAddIssuerToCache(pIssuer, cbIssuer); } } SafeAllocaFree(IssuerName.Buffer); } else { Status = STATUS_NO_MEMORY ; } return Status ; } //+--------------------------------------------------------------------------- // // Function: SslMapCertToUserPac // // Synopsis: Maps a certificate to a user (hopefully) and the PAC, // // Arguments: [Request] -- // [RequestLength] -- // [UserPac] -- // [UserPacLen] -- // // History: 5-11-98 RichardW Created // // Notes: // //---------------------------------------------------------------------------- NTSTATUS SslMapCertToUserPac( IN PSSL_CERT_LOGON_REQ Request, IN ULONG RequestLength, OUT PUCHAR * UserPac, OUT PULONG UserPacLen, OUT PWSTR * ReferencedDomain ) { PCCERT_CONTEXT User ; NTSTATUS Status = STATUS_LOGON_FAILURE; NTSTATUS SubStatus = STATUS_NOT_FOUND; ULONG i; *ReferencedDomain = NULL; DebugLog(( DEB_TRACE_MAPPER, "SslMapCertToUserPac called\n" )); // // Validate logon request. // if(RequestLength < sizeof(SSL_CERT_LOGON_REQ)) { return SP_LOG_RESULT(STATUS_INVALID_PARAMETER); } if(Request->Length > RequestLength) { return SP_LOG_RESULT(STATUS_INVALID_PARAMETER); } // // Extract certificate from request. // if((Request->OffsetCertificate > RequestLength) || (Request->CertLength > 0x10000) || (Request->OffsetCertificate + Request->CertLength > RequestLength)) { return SP_LOG_RESULT(STATUS_INVALID_PARAMETER); } 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) && (g_dwCertMappingMethods & SP_REG_CERTMAP_UPN_FLAG)) { 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) && (g_dwCertMappingMethods & SP_REG_CERTMAP_SUBJECT_FLAG)) { 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) && (g_dwCertMappingMethods & SP_REG_CERTMAP_ISSUER_FLAG)) { DebugLog(( DEB_TRACE_MAPPER, "Trying issuer mapping\n" )); if((Request->Flags & REQ_ISSUER_CHAIN_MAPPING) && (Request->CertCount > 0)) { if(sizeof(SSL_CERT_LOGON_REQ) + (Request->CertCount - 1) * sizeof(SSL_CERT_NAME_INFO) > RequestLength) { Status = SP_LOG_RESULT(STATUS_INVALID_PARAMETER); goto Cleanup; } // Loop through each issuer in the certificate chain. for(i = 0; i < Request->CertCount; i++) { DWORD IssuerOffset = Request->NameInfo[i].IssuerOffset; DWORD IssuerLength = Request->NameInfo[i].IssuerLength; if((IssuerOffset > RequestLength) || (IssuerLength > 0x10000) || (IssuerOffset + IssuerLength > RequestLength)) { return SP_LOG_RESULT(STATUS_INVALID_PARAMETER); } 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; if(dwMapperFlags & SCH_FLAG_NO_VALIDATION) { DebugLog((DEB_TRACE, "Skipping certificate validation.\n")); if(ppChainContext != NULL) { CERT_CHAIN_PARA ChainPara; ZeroMemory(&ChainPara, sizeof(ChainPara)); ChainPara.cbSize = sizeof(ChainPara); if(!CertGetCertificateChain( NULL, // hChainEngine pCertContext, // pCertContext NULL, // pTime pCertContext->hCertStore, // hAdditionalStore &ChainPara, // pChainPara dwCertFlags, // dwFlags NULL, // pvReserved ppChainContext)) // ppChainContext { Status = SP_LOG_RESULT(GetLastError()); return Status; } } } else { // 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(Status != STATUS_SUCCESS) { 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; if(dwMapperFlags & SCH_FLAG_NO_VALIDATION) { // Turn on UPN mapping. *pdwMethods |= REQ_UPN_MAPPING; } else { // Check to see if the certificate chain is valid for UPN mapping. Status = VerifyClientCertificate(pCertContext, dwCertFlags, dwIgnoreErrors, CERT_CHAIN_POLICY_NT_AUTH, NULL); if(Status == STATUS_SUCCESS) { // 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 DirectPac = NULL ; PUCHAR IndirectPac = NULL ; 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; UNREFERENCED_PARAMETER(pAuthority); DebugLog(( DEB_TRACE_MAPPER, "SslLocalMapCredential, context %x\n", Mapper )); if ( CredentialType != X509_ASN_CHAIN ) { return( (DWORD)SEC_E_UNKNOWN_CREDENTIALS ); } // // Validate client certificate, and obtain pointer to // entire certificate chain. // Status = MapperVerifyClientChain(pCert, Mapper->m_dwFlags, &dwMethods, &VerifyStatus, &pChainContext); if(Status != STATUS_SUCCESS) { return Status; } // // Attempt to logon via Kerberos S4U2Self. // if((dwMethods & REQ_UPN_MAPPING) && (g_dwCertMappingMethods & SP_REG_CERTMAP_S4U2SELF_FLAG)) { DebugLog(( DEB_TRACE_MAPPER, "Trying S4U2Self mapping\n" )); Status = SslTryS4U2Self(pChainContext, &Token); if ( NT_SUCCESS( Status ) ) { CertFreeCertificateChain(pChainContext); pChainContext = NULL; *phLocator = (HLOCATOR) Token ; return Status; } DebugLog(( DEB_TRACE_MAPPER, "Failed with error 0x%x\n", Status )); } // // Build the logon request. // Status = SslBuildCertLogonRequest(pChainContext, dwMethods, &pRequest, &cbRequest); CertFreeCertificateChain(pChainContext); pChainContext = NULL; if(FAILED(Status)) { return Status; } // // Attempt to find the user locally. // Status = SslMapCertToUserPac( pRequest, cbRequest, &Pac, &PacLength, &ReferencedDomain ); if(NT_SUCCESS(Status)) { // Free this PAC later using LsaTable->FreeLsaHeap. DirectPac = Pac; } 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, TRUE, &Pac, &PacLength, &Response ); if ( NT_SUCCESS( Status ) ) { // Free this later using MIDL_user_free. IndirectPac = Pac; 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 ; } } } LsaTable->FreeLsaHeap( ReferencedDomain ); } else { AccountDomain = SslDomainName ; } // // Expand the domain local groups. // if ( NT_SUCCESS( Status ) ) { Status = LsaTable->ExpandAuthDataForDomain( Pac, PacLength, NULL, &ExpandedPac, &ExpandedPacLength ); if ( NT_SUCCESS( Status ) ) { Pac = ExpandedPac ; PacLength = ExpandedPacLength ; } } // // Create the user token. // 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); } if(DirectPac) { LsaTable->FreeLsaHeap(DirectPac); } if(IndirectPac) { MIDL_user_free(IndirectPac); } 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 ; ULONG PacLength ; PUCHAR DirectPac = NULL ; PUCHAR IndirectPac = NULL ; PUCHAR ExpandedPac = NULL ; ULONG ExpandedPacLength ; PWSTR ReferencedDomain = NULL; PWSTR FirstDot ; UNICODE_STRING DomainName = { 0 }; PMSV1_0_PASSTHROUGH_RESPONSE MsvResponse = NULL ; SECPKG_CALL_INFO CallInfo; UNREFERENCED_PARAMETER(ClientRequest); UNREFERENCED_PARAMETER(ClientBufferBase); 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, SubmitBufferLen, &Pac, &PacLength, &ReferencedDomain ); DebugLog(( DEB_TRACE_MAPPER, "Local lookup returns %x\n", Status )); if(NT_SUCCESS(Status)) { // Free this PAC later using LsaTable->FreeLsaHeap. DirectPac = Pac; } if(!NT_SUCCESS(Status) && (ReferencedDomain != NULL)) { BOOL NameMatch; // // Didn't find it at this DC, but another domain appears to // have the mapping. Forward it there: // RtlInitUnicodeString( &DomainName, ReferencedDomain ); SslGlobalReadLock(); DsysAssert(!RtlEqualUnicodeString(&DomainName, &SslGlobalDnsDomainName, TRUE)); DsysAssert(!RtlEqualUnicodeString(&DomainName, &SslDomainName, TRUE)); NameMatch = (RtlEqualUnicodeString(&DomainName, &SslGlobalDnsDomainName, TRUE) || RtlEqualUnicodeString(&DomainName, &SslDomainName, TRUE)); SslGlobalReleaseLock(); if(NameMatch) { DebugLog(( DEB_TRACE_MAPPER, "GC is out of sync, bailing on this user\n" )); Status = STATUS_LOGON_FAILURE ; } else if(LsaTable->GetCallInfo(&CallInfo) && (CallInfo.Attributes & SECPKG_CALL_RECURSIVE)) { DebugLog(( DEB_ERROR, "Certificate mapper is recursing!\n" )); } else { DebugLog(( DEB_TRACE_MAPPER, "Mapping certificate at DC for domain %ws\n", ReferencedDomain )); Status = SslMapCertAtDC( &DomainName, Request, Request->Length, TRUE, &Pac, &PacLength, &MsvResponse ); if ( NT_SUCCESS( Status ) ) { // Free this later using MIDL_user_free. IndirectPac = Pac; IndirectResponse = (PSSL_CERT_LOGON_RESP) MsvResponse->ValidationData ; 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 ) ) { Pac = ExpandedPac ; PacLength = ExpandedPacLength ; } } if ( !NT_SUCCESS( Status ) ) { *ReturnBufferLength = 0; *ProtocolStatus = Status ; Status = STATUS_SUCCESS ; goto Cleanup ; } #ifdef ROGUE_DC if(DirectPac) { // We're a rogue user DC, so let's add some bogus SIDs to the PAC. // Yo ho ho. DebugLog((DEB_TRACE, "SslDoClientRequest: Calling SslInstrumentRoguePac\n")); Status = SslInstrumentRoguePac(&Pac, &PacLength); ExpandedPac = Pac; if (!NT_SUCCESS(Status)) { DebugLog((DEB_ERROR, "SslDoClientRequest: Failed SslInstrumentRoguePac 0x%x\n", Status)); goto Cleanup; } } #endif // // 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) { LsaTable->FreeReturnBuffer( MsvResponse ); } if(DirectPac) { LsaTable->FreeLsaHeap(DirectPac); } if(IndirectPac) { MIDL_user_free(IndirectPac); } 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 ; // // // // // ... // //---------------------------------------------------------------------------- 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; 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; // // // // [ padding ] // // // [ padding ] // //---------------------------------------------------------------------------- NTSTATUS WINAPI SslMapCertAtDC( IN PUNICODE_STRING DomainName, IN VOID const *pCredential, IN DWORD cbCredential, IN BOOL IsDC, OUT PUCHAR *UserPac, OUT PULONG UserPacLen, OUT PMSV1_0_PASSTHROUGH_RESPONSE * DcResponse) { NTSTATUS Status ; PMSV1_0_PASSTHROUGH_REQUEST Request ; PMSV1_0_PASSTHROUGH_RESPONSE Response = NULL; PSSL_CERT_LOGON_RESP CertResp ; DWORD Size ; DWORD RequestSize ; DWORD ResponseSize ; PUCHAR Where ; NTSTATUS SubStatus ; #if DBG DWORD CheckSize2 ; #endif PSID pTrustSid = NULL; PUCHAR Pac = NULL; ULONG PacLength; DebugLog(( DEB_TRACE_MAPPER, "Remote call to DC to do the mapping\n" )); // // Validate the input parameters. // if(cbCredential > 0x4000) { return SEC_E_ILLEGAL_MESSAGE; } // // Verify that the target DC is in the same forest. Cross-forest // certificate mapping is not supported, except via the S4U2Self // mechanism. if(IsDC) { BOOL fWithinForest = TRUE; Status = LsaIIsDomainWithinForest(DomainName, &fWithinForest, NULL, &pTrustSid, NULL, NULL, NULL); if (!NT_SUCCESS (Status)) { DebugLog((DEB_ERROR, "SslMapCertAtDC: LsaIIsDomainWithinForest failed 0x%x\n", Status)); goto cleanup; } if (!fWithinForest) { Status = SEC_E_NO_AUTHENTICATING_AUTHORITY; DebugLog((DEB_ERROR, "SslMapCertAtDC: Target DC is outside forest - fail request 0x%x\n", Status)); goto cleanup; } } // // Build the request to send to the DC. // Size = cbCredential; Size = ROUND_UP_COUNT( Size, ALIGN_DWORD ); RequestSize = DomainName->Length + SslLegacyPackageName.Length ; RequestSize = ROUND_UP_COUNT( RequestSize, ALIGN_DWORD ); #if DBG CheckSize2 = RequestSize ; #endif RequestSize += sizeof( MSV1_0_PASSTHROUGH_REQUEST ) + Size ; SafeAllocaAllocate( (PMSV1_0_PASSTHROUGH_REQUEST)Request, RequestSize ); if ( !Request ) { Status = SEC_E_INSUFFICIENT_MEMORY; goto cleanup; } 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 = SslLegacyPackageName ; Request->PackageName.Buffer = (LPWSTR) Where ; RtlCopyMemory( Where, SslLegacyPackageName.Buffer, SslLegacyPackageName.Length ); Where += SslLegacyPackageName.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 = LsaCallAuthenticationPackage( SslLogonHandle, SslMsvPackageId, Request, RequestSize, &Response, &ResponseSize, &SubStatus ); SafeAllocaFree( Request ); Request = NULL; if ( !NT_SUCCESS( Status ) ) { DebugLog(( DEB_TRACE_MAPPER, "SslMapCertAtDC returned status 0x%x\n", Status )); goto cleanup; } if ( !NT_SUCCESS( SubStatus ) ) { DebugLog(( DEB_TRACE_MAPPER, "SslMapCertAtDC returned sub-status 0x%x\n", SubStatus )); Status = SubStatus; goto cleanup; } // // Extract out the returned PAC and perform SID filtering // CertResp = (PSSL_CERT_LOGON_RESP) Response->ValidationData ; PacLength = CertResp->AuthDataLength; Pac = MIDL_user_allocate( PacLength ); if(Pac == NULL) { Status = STATUS_NO_MEMORY; goto cleanup; } memcpy(Pac, ((PUCHAR)CertResp) + CertResp->OffsetAuthData, PacLength); Status = SslCheckPacForSidFiltering( pTrustSid, &Pac, &PacLength); if ( !NT_SUCCESS( Status ) ) { DebugLog(( DEB_TRACE_MAPPER, "SslCheckPacForSidFiltering returned status 0x%x\n", Status )); goto cleanup; } // // Set output parameters. // *UserPac = Pac; *UserPacLen = PacLength; Pac = NULL; *DcResponse = Response; Response = NULL; DebugLog(( DEB_TRACE_MAPPER, "SslMapCertAtDC returned 0x%x\n", Status )); Status = STATUS_SUCCESS; cleanup: if(Pac) { MIDL_user_free(Pac); } if(pTrustSid) { MIDL_user_free(pTrustSid); } if(Response) { LsaTable->FreeReturnBuffer(Response); } return Status; } 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; UNREFERENCED_PARAMETER(ClientRequest); UNREFERENCED_PARAMETER(ClientBufferBase); DebugLog(( DEB_TRACE_MAPPER, "SslMapExternalCredential\n" )); // // Validate the input parameters. // if ( ARGUMENT_PRESENT( ProtocolReturnBuffer ) ) { *ProtocolReturnBuffer = NULL ; } if(SubmitBufferLen < sizeof(SSL_EXTERNAL_CERT_LOGON_REQ)) { Status = STATUS_INVALID_PARAMETER; goto cleanup; } Request = (PSSL_EXTERNAL_CERT_LOGON_REQ) ProtocolSubmitBuffer ; if(Request->Length != sizeof(SSL_EXTERNAL_CERT_LOGON_REQ)) { Status = STATUS_INVALID_PARAMETER; goto cleanup; } // // Attempt to map the certificate. // if(RtlGetNtProductType(&ProductType)) { DC = (ProductType == NtProductLanManNt); } else { Status = STATUS_NO_MEMORY ; goto cleanup; } 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); } DebugLog(( DEB_TRACE_MAPPER, "SslRemoteMapCredential returns 0x%x\n", Status )); 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; 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; PUCHAR Pac = NULL; ULONG PacLength; UNREFERENCED_PARAMETER(pAuthority); DebugLog(( DEB_TRACE_MAPPER, "SslRemoteMapCredential, context %x\n", Mapper )); if ( CredentialType != X509_ASN_CHAIN ) { return( (DWORD)SEC_E_UNKNOWN_CREDENTIALS ); } // // Validate client certificate, and obtain pointer to // entire certificate chain. // Status = MapperVerifyClientChain(pCert, Mapper->m_dwFlags, &dwMethods, &VerifyStatus, &pChainContext); if(Status != STATUS_SUCCESS) { return Status; } // // Attempt to logon via Kerberos S4U2Self. // if((dwMethods & REQ_UPN_MAPPING) && (g_dwCertMappingMethods & SP_REG_CERTMAP_S4U2SELF_FLAG)) { DebugLog(( DEB_TRACE_MAPPER, "Trying S4U2Self mapping\n" )); Status = SslTryS4U2Self(pChainContext, &Token); if ( NT_SUCCESS( Status ) ) { CertFreeCertificateChain(pChainContext); pChainContext = NULL; *phLocator = (HLOCATOR) Token ; return Status; } DebugLog(( DEB_TRACE_MAPPER, "Failed with error 0x%x\n", 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, FALSE, &Pac, &PacLength, &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( Pac, PacLength, &AccountDomain, &LogonId, &Token ); if ( NT_SUCCESS( Status ) ) { *phLocator = (HLOCATOR) Token ; } else { LogCertMappingFailureEvent(Status); } LsaTable->FreeReturnBuffer( Response ); MIDL_user_free(Pac); return ( Status ); } DWORD WINAPI SslLocalCloseLocator( IN PHMAPPER Mapper, IN HLOCATOR Locator ) { UNREFERENCED_PARAMETER(Mapper); DebugLog(( DEB_TRACE_MAPPER, "SslLocalCloseLocator (%p)\n", Locator )); NtClose( (HANDLE) Locator ); return( SEC_E_OK ); } DWORD WINAPI SslLocalGetAccessToken( IN PHMAPPER Mapper, IN HLOCATOR Locator, OUT HANDLE *Token ) { UNREFERENCED_PARAMETER(Mapper); DebugLog(( DEB_TRACE_MAPPER, "SslLocalGetAccessToken (%p)\n", Locator )); *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 //+------------------------------------------------------------------------- // // Function: SslDomainChangeCallback // // Synopsis: Function to be called when domain changes // // Effects: // // Arguments: // // Requires: // // Returns: // // Notes: // // //-------------------------------------------------------------------------- VOID NTAPI SslDomainChangeCallback( IN POLICY_NOTIFICATION_INFORMATION_CLASS ChangedInfoClass ) { NTSTATUS Status; PLSAPR_POLICY_INFORMATION Policy = NULL; BOOL AcquiredLock = FALSE; UNICODE_STRING TempDnsDomainName = {0}; // // We only care about domain dns information // if (ChangedInfoClass != PolicyNotifyDnsDomainInformation) { return; } DebugLog((DEB_TRACE,"SSL domain change callback\n")); // OutputDebugStringA("SSL domain change callback\n"); // // Get the new domain information // Status = I_LsaIQueryInformationPolicyTrusted( PolicyDnsDomainInformation, &Policy ); if (!NT_SUCCESS(Status)) { DebugLog((DEB_ERROR,"Failed to query domain dns information %x - not updating.\n", Status)); goto Cleanup; } // // Copy the domain name // Status = SslDuplicateString( &TempDnsDomainName, (PUNICODE_STRING) &Policy->PolicyDnsDomainInfo.DnsDomainName ); if (!NT_SUCCESS(Status)) { goto Cleanup; } // // Acquire the global lock so we can update the data // if (!SslGlobalWriteLock()) { DebugLog((DEB_ERROR,"Failed to acquire global resource. Not changing domain.\n")); goto Cleanup; } AcquiredLock = TRUE; // // Copy all the data to the global structures // SslFreeString(&SslGlobalDnsDomainName); SslGlobalDnsDomainName = TempDnsDomainName; TempDnsDomainName.Buffer = NULL; DebugLog((DEB_TRACE,"SSL DNS Domain Name changed to:%ls\n", SslGlobalDnsDomainName.Buffer)); // OutputDebugStringA("SSL DNS Domain Name changed to:"); // OutputDebugStringW(SslGlobalDnsDomainName.Buffer); // OutputDebugStringA("\n"); Cleanup: if (AcquiredLock) { SslGlobalReleaseLock(); } if (Policy != NULL) { I_LsaIFree_LSAPR_POLICY_INFORMATION( PolicyDnsDomainInformation, Policy ); } SslFreeString(&TempDnsDomainName); } //+------------------------------------------------------------------------- // // Function: SslRegisterForDomainChange // // Synopsis: Register with the LSA to be notified of domain changes // // Effects: // // Arguments: // // Requires: // // Returns: // // Notes: // // //-------------------------------------------------------------------------- NTSTATUS SslRegisterForDomainChange( VOID ) { NTSTATUS Status; Status = I_LsaIRegisterPolicyChangeNotificationCallback( SslDomainChangeCallback, PolicyNotifyDnsDomainInformation ); if (!NT_SUCCESS(Status)) { DebugLog((DEB_ERROR,"Failed to register for domain change notification: 0x%x.\n",Status)); } return(Status); } //+------------------------------------------------------------------------- // // Function: SslUnregisterForDomainChange // // Synopsis: Unregister for domain change notification // // Effects: // // Arguments: // // Requires: // // Returns: // // Notes: // // //-------------------------------------------------------------------------- VOID SslUnregisterForDomainChange( VOID ) { (VOID) I_LsaIUnregisterPolicyChangeNotificationCallback( SslDomainChangeCallback, PolicyNotifyDnsDomainInformation ); }