//+--------------------------------------------------------------------------- // // Microsoft Windows // Copyright (C) Microsoft Corporation, 1992 - 1996. // // File: ldapc.hxx // // Contents: // // History: 06-16-96 yihsins Created. // //---------------------------------------------------------------------------- #include "ldapc.hxx" #pragma hdrstop CRITICAL_SECTION g_DomainDnsCache; #define ENTER_DOMAINDNS_CRITSECT() EnterCriticalSection(&g_DomainDnsCache) #define LEAVE_DOMAINDNS_CRITSECT() LeaveCriticalSection(&g_DomainDnsCache) BOOL g_fDllsLoaded = FALSE; HANDLE g_hDllNetApi32 = NULL; HANDLE g_hDllSecur32 = NULL; extern "C" { typedef struct _WKSTA_USER_INFO_1A { LPSTR wkui1_username; LPSTR wkui1_logon_domain; LPSTR wkui1_oth_domains; LPSTR wkui1_logon_server; }WKSTA_USER_INFO_1A, *PWKSTA_USER_INFO_1A, *LPWKSTA_USER_INFO_1A; NET_API_STATUS NET_API_FUNCTION NetWkstaUserGetInfoA ( IN LPSTR reserved, IN DWORD level, OUT LPBYTE *bufptr ); } int AnsiToUnicodeString( LPSTR pAnsi, LPWSTR pUnicode, DWORD StringLength ); // // Binds to all the dll's that we need to load dynamically. // The list is // netapi32.dll // secur32.dll // // The global flag g_fDllsLoaded is updated appropriately. // void BindToDlls() { if (g_fDllsLoaded) { return; } // // Use the domaindns critical section to control access. // There is no real need to define another CS for this as this // will utmost be called once. // DWORD dwLastErr = 0; ENTER_DOMAINDNS_CRITSECT(); // // In case someones came in when we were loading the dll's. // if (g_fDllsLoaded) { LEAVE_DOMAINDNS_CRITSECT(); return; } // // Load dll's - each load lib could have set an error if it fails. // if (!(g_hDllNetApi32 = LoadLibrary(L"NETAPI32.DLL"))) { dwLastErr = GetLastError(); } g_hDllSecur32 = LoadLibrary(L"SECUR32.DLL"); // // We need to set this as the last error since one of the // loads failed. This will not work as we add more dll's // but for now should be ok. This may not even be needed // cause finally we are interested in the actual functions // not just the ability to load/unload the dll. // if (dwLastErr) { SetLastError (dwLastErr); } g_fDllsLoaded = TRUE; LEAVE_DOMAINDNS_CRITSECT(); return; } // // LoadNetApi32Function // // Args: // Function to load. // // Returns: function pointer if successfully loads the function from // NETAPI32.DLL. Returns NULL otherwise. // // PVOID LoadNetApi32Function(CHAR *function) { if (!g_fDllsLoaded) { BindToDlls(); } if (g_hDllNetApi32) { return ((PVOID) GetProcAddress((HMODULE)g_hDllNetApi32, function)); } return NULL; } // // LoadSecur32Function // // Args: // Function to load. // // Returns: function pointer if successfully loads the function from // secur32.DLL. Returns NULL otherwise. // // PVOID LoadSecur32Function(CHAR *function) { if (!g_fDllsLoaded) { BindToDlls(); } if (g_hDllSecur32) { return ((PVOID) GetProcAddress((HMODULE)g_hDllSecur32, function)); } return NULL; } // // Definition for DsGetDcName // typedef DWORD (*PF_DsGetDcName) ( IN LPCWSTR ComputerName OPTIONAL, IN LPCWSTR DomainName OPTIONAL, IN GUID *DomainGuid OPTIONAL, IN LPCWSTR SiteName OPTIONAL, IN ULONG Flags, OUT PDOMAIN_CONTROLLER_INFO *DomainControllerInfo ); // // Definition for LsaConnectUntrusted() // typedef DWORD (*PF_LsaConnectUntrusted) ( OUT PHANDLE LsaHandle ); // // For LsaCallAuthenticationPackage // typedef DWORD (*PF_LsaCallAuthenticationPackage) ( IN HANDLE LsaHandle, IN ULONG AuthenticationPackage, IN PVOID ProtocolSubmitBuffer, IN ULONG SubmitBufferLength, OUT PVOID *ProtocolReturnBuffer, OUT PULONG ReturnBufferLength, OUT PNTSTATUS ProtocolStatus ); // // For LsaDeregisterLogonProcess // typedef DWORD (*PF_LsaDeregisterLogonProcess) ( IN HANDLE LsaHandle ); // // For LsaFreeReturnBuffer // typedef DWORD (*PF_LsaFreeReturnBuffer) ( IN PVOID Buffer ); #ifdef UNICODE #define GETDCNAME_API "DsGetDcNameW" #else #define GETDCNAME_API "DsGetDcNameA" #endif // // These are same for all entry points // #define LSACONNECT_UNTRUSTED "LsaConnectUntrusted" #define LSACALL_AUTH_PACAKAGE "LsaCallAuthenticationPackage" #define LSA_DEREG_LOGON_PROC "LsaDeregisterLogonProcess" #define LSAFREE_RET_BUFFER "LsaFreeReturnBuffer" // // We will always dynamically laod the dsgetdc api so that // we can have single binary for NT4.0 and NT5.0 // DWORD DsGetDcNameWrapper( IN LPCWSTR ComputerName OPTIONAL, IN LPCWSTR DomainName OPTIONAL, IN GUID *DomainGuid OPTIONAL, IN LPCWSTR SiteName OPTIONAL, IN ULONG Flags, OUT PDOMAIN_CONTROLLER_INFO *DomainControllerInfo ) { static PF_DsGetDcName pfDsGetDcName = NULL ; static BOOL f_LoadAttempted = FALSE; // // Load the function if necessary and only once. // if (pfDsGetDcName == NULL && !f_LoadAttempted) { pfDsGetDcName = (PF_DsGetDcName) LoadNetApi32Function(GETDCNAME_API) ; f_LoadAttempted = TRUE; } if (pfDsGetDcName != NULL) { return ((*pfDsGetDcName)( ComputerName, DomainName, DomainGuid, SiteName, Flags, DomainControllerInfo ) ); } else { // // Could not load library // return (ERROR_GEN_FAILURE); } } // // Wrapper function for LsaConnectUntrusted. // DWORD LsaConnectUntrustedWrapper( OUT PHANDLE LsaHandle ) { static PF_LsaConnectUntrusted pfLsaConnectUntrusted = NULL ; static BOOL f_LoadAttempted = FALSE; // // Load the function if necessary and only once. // if (pfLsaConnectUntrusted == NULL && !f_LoadAttempted) { pfLsaConnectUntrusted = (PF_LsaConnectUntrusted) LoadSecur32Function(LSACONNECT_UNTRUSTED); f_LoadAttempted = TRUE; } if (pfLsaConnectUntrusted != NULL) { return ((*pfLsaConnectUntrusted)( LsaHandle ) ); } else { // // Could not load library // return (ERROR_GEN_FAILURE); } } // // Wrapper function for LsaCallAuthenticationPackage. // DWORD LsaCallAuthenticationPackageWrapper( IN HANDLE LsaHandle, IN ULONG AuthenticationPackage, IN PVOID ProtocolSubmitBuffer, IN ULONG SubmitBufferLength, OUT PVOID *ProtocolReturnBuffer, OUT PULONG ReturnBufferLength, OUT PNTSTATUS ProtocolStatus ) { static PF_LsaCallAuthenticationPackage pfLsaCallAuthPackage = NULL; static BOOL f_LoadAttempted = FALSE; // // Load the function if necessary and only once. // if (pfLsaCallAuthPackage == NULL && !f_LoadAttempted) { pfLsaCallAuthPackage = (PF_LsaCallAuthenticationPackage) LoadSecur32Function( LSACALL_AUTH_PACAKAGE ); f_LoadAttempted = TRUE; } if (pfLsaCallAuthPackage != NULL) { return ((*pfLsaCallAuthPackage)( LsaHandle, AuthenticationPackage, ProtocolSubmitBuffer, SubmitBufferLength, ProtocolReturnBuffer, ReturnBufferLength, ProtocolStatus ) ); } else { // // Could not load library // return (ERROR_GEN_FAILURE); } } // // Wrapper function for LsaDeregisterLogonProcess. // DWORD LsaDeregisterLogonProcessWrapper( IN HANDLE LsaHandle ) { static PF_LsaDeregisterLogonProcess pfLsaDerefLgnProc = NULL; static BOOL f_LoadAttempted = FALSE; // // Load the function if necessary and only once. // if (pfLsaDerefLgnProc == NULL && !f_LoadAttempted) { pfLsaDerefLgnProc = (PF_LsaDeregisterLogonProcess) LoadSecur32Function(LSA_DEREG_LOGON_PROC); f_LoadAttempted = TRUE; } if (pfLsaDerefLgnProc != NULL) { return ((*pfLsaDerefLgnProc)( LsaHandle ) ); } else { // // Could not load library // return (ERROR_GEN_FAILURE); } } // // Wrapper function for LsaFreeReturnBuffer. // DWORD LsaFreeReturnBufferWrapper( IN PVOID Buffer ) { static PF_LsaFreeReturnBuffer pfLsaFreeRetBuffer = NULL; static BOOL f_LoadAttempted = FALSE; // // Load the function if necessary and only once. // if (pfLsaFreeRetBuffer == NULL && !f_LoadAttempted) { pfLsaFreeRetBuffer = (PF_LsaFreeReturnBuffer) LoadSecur32Function(LSAFREE_RET_BUFFER); f_LoadAttempted = TRUE; } if (pfLsaFreeRetBuffer != NULL) { return ((*pfLsaFreeRetBuffer)( Buffer ) ); } else { // // Could not load library // return (ERROR_GEN_FAILURE); } } HANDLE g_hLsa = INVALID_HANDLE_VALUE; DWORD GetUserDomainFlatName( LPWSTR pszUserName, LPWSTR pszDomainFlatName ) { NTSTATUS dwStatus = NO_ERROR, dwSubStatus = NO_ERROR; CHAR pszDomainFlatNameA[MAX_PATH]; CHAR pszUserNameA[MAX_PATH]; PWKSTA_USER_INFO_1 pNetWkstaUserInfo = NULL; PWKSTA_USER_INFO_1A pNetWkstaUserInfoA = NULL; PWKSTA_INFO_100 pNetWkstaInfo = NULL; NEGOTIATE_CALLER_NAME_REQUEST Req; PNEGOTIATE_CALLER_NAME_RESPONSE pResp = NULL; DWORD dwSize = 0; LPWSTR pszTempUserName = NULL; PLSA_UNICODE_STRING pLsaStrUserNameTemp = NULL; PLSA_UNICODE_STRING pLsaStrDomainNameTemp = NULL; #if (defined WIN95) dwStatus = NetWkstaUserGetInfoA( NULL, 1, (LPBYTE *)&pNetWkstaUserInfoA ); if (dwStatus != NO_ERROR && dwStatus != ERROR_NO_SUCH_LOGON_SESSION) { goto error; } if (dwStatus == NO_ERROR) { AnsiToUnicodeString( pNetWkstaUserInfoA->wkui1_logon_domain, pszDomainFlatName, 0 ); AnsiToUnicodeString( pNetWkstaUserInfoA->wkui1_username, pszUserName, 0 ); } #else ENTER_DOMAINDNS_CRITSECT(); if (g_hLsa == INVALID_HANDLE_VALUE) { dwStatus = LsaConnectUntrustedWrapper(&g_hLsa); } LEAVE_DOMAINDNS_CRITSECT(); if (dwStatus == 0) { memset(&Req, 0, sizeof(Req)); Req.MessageType = NegGetCallerName; dwStatus = LsaCallAuthenticationPackageWrapper( g_hLsa, 0, &Req, sizeof(Req), (void **)&pResp, &dwSize, &dwSubStatus ); if ((dwStatus == 0) && (dwSubStatus == 0)) { dwStatus = NO_ERROR; pszTempUserName = wcschr(pResp->CallerName, L'\\'); if (!pszTempUserName) { // // Looks like there was no domain default to machine then // dwStatus = ERROR_NO_SUCH_LOGON_SESSION; } else { // // Copy over the relevant information // *pszTempUserName = L'\0'; wcscpy(pszDomainFlatName, pResp->CallerName); *pszTempUserName = L'\\'; pszTempUserName++; wcscpy(pszUserName, pszTempUserName); LsaFreeReturnBufferWrapper(pResp); } } else { if (!dwStatus) dwStatus = dwSubStatus; } } if (dwStatus != NO_ERROR) { // // Call LsaGetUserName when there is a failure with the above. // dwStatus = LsaGetUserName( &pLsaStrUserNameTemp, &pLsaStrDomainNameTemp ); if (dwStatus == NO_ERROR) { // // Unicode string may not be NULL terminated. // memcpy( pszDomainFlatName, pLsaStrDomainNameTemp->Buffer, pLsaStrDomainNameTemp->Length ); pszDomainFlatName[pLsaStrDomainNameTemp->Length / sizeof(WCHAR)] = L'\0'; memcpy( pszUserName, pLsaStrUserNameTemp->Buffer, pLsaStrUserNameTemp->Length ); pszUserName[pLsaStrUserNameTemp->Length / sizeof(WCHAR)] = L'\0'; // // Can cleanup the LsaGetUserName mem // LsaFreeMemory(pLsaStrUserNameTemp->Buffer); LsaFreeMemory(pLsaStrUserNameTemp); LsaFreeMemory(pLsaStrDomainNameTemp->Buffer); LsaFreeMemory(pLsaStrDomainNameTemp); } else if (dwStatus != ERROR_NO_SUCH_LOGON_SESSION){ goto error; } } // // Make sure this is not NT AUTHORITY // if (dwStatus == NO_ERROR && !_wcsicmp(g_szNT_Authority, pszDomainFlatName) ) { // // Force fallback to NetWkstaGetInfo as we want machine domain // dwStatus = ERROR_NO_SUCH_LOGON_SESSION; } if (dwStatus == NO_ERROR) { // // Do nothing here, we need the else clause already have data. // } #endif else { dwStatus = NetWkstaGetInfo( NULL, 100, (LPBYTE *)&pNetWkstaInfo ); if (dwStatus) { goto error; } wcscpy(pszDomainFlatName, pNetWkstaInfo->wki100_langroup); } error: if (pNetWkstaUserInfoA) { NetApiBufferFree(pNetWkstaUserInfoA); } if (pNetWkstaUserInfo) { NetApiBufferFree(pNetWkstaUserInfo); } if (pNetWkstaInfo) { NetApiBufferFree(pNetWkstaInfo); } return(dwStatus); } DWORD GetDomainDNSNameForDomain( LPWSTR pszDomainFlatName, BOOL fVerify, BOOL fWriteable, LPWSTR pszServerName, LPWSTR pszDomainDNSName ) { PDOMAIN_CONTROLLER_INFO pDomainControllerInfo = NULL; DWORD dwStatus = 0; DWORD Flags = DS_DIRECTORY_SERVICE_REQUIRED | DS_RETURN_DNS_NAME; if (fVerify) Flags |= DS_FORCE_REDISCOVERY ; if (fWriteable) Flags |= DS_WRITABLE_REQUIRED ; dwStatus = DsGetDcNameWrapper( NULL, pszDomainFlatName, NULL, NULL, Flags, &pDomainControllerInfo ) ; if (dwStatus == NO_ERROR) { wcscpy(pszServerName,pDomainControllerInfo->DomainControllerName+2); wcscpy(pszDomainDNSName,pDomainControllerInfo->DomainName); (void) NetApiBufferFree(pDomainControllerInfo) ; } return(dwStatus); } typedef struct _domaindnslist { LPWSTR pszUserName; LPWSTR pszUserDomainName; LPWSTR pszDomainDns; LPWSTR pszServer; struct _domaindnslist *pNext; } DOMAINDNSLIST, *PDOMAINDNSLIST; PDOMAINDNSLIST gpDomainDnsList = NULL; BOOL EquivalentDomains( PDOMAINDNSLIST pTemp, LPWSTR pszUserDomainName ); DWORD GetDefaultDomainName( LPWSTR szDomainDnsName, LPWSTR szServerName, BOOL fWriteable, BOOL fVerify ) { DWORD dwStatus = 0; WCHAR szUserDomainName[MAX_PATH]; WCHAR szUserName[MAX_PATH]; PDOMAINDNSLIST pTemp = NULL; PDOMAINDNSLIST pNewNode = NULL; dwStatus = GetUserDomainFlatName( szUserName, szUserDomainName ); if (dwStatus) { goto error; } // We want do a DsGetDc if the fVerify flags is specified // so we do not want to look at our list if that is the case. if (!fVerify) { ENTER_DOMAINDNS_CRITSECT(); pTemp = gpDomainDnsList; while (pTemp) { if (EquivalentDomains(pTemp, szUserDomainName)){ wcscpy(szDomainDnsName,pTemp->pszDomainDns); wcscpy(szServerName,pTemp->pszServer); LEAVE_DOMAINDNS_CRITSECT(); return(NO_ERROR); } pTemp = pTemp->pNext; } LEAVE_DOMAINDNS_CRITSECT(); } // We will hit this block if either fVerify == TRUE or if // we did not find a match in our list above. dwStatus = GetDomainDNSNameForDomain( szUserDomainName, fVerify, fWriteable, szServerName, szDomainDnsName ); if (dwStatus) { goto error; } ENTER_DOMAINDNS_CRITSECT(); pTemp = gpDomainDnsList; while (pTemp) { if (EquivalentDomains(pTemp, szUserDomainName)) { // // Found a match -looks like someone has come in before us // wcscpy(szDomainDnsName, pTemp->pszDomainDns); wcscpy(szServerName,pTemp->pszServer); LEAVE_DOMAINDNS_CRITSECT(); return(NO_ERROR); } pTemp = pTemp->pNext; } pNewNode = (PDOMAINDNSLIST)AllocADsMem(sizeof(DOMAINDNSLIST)); if (!pNewNode) { LEAVE_DOMAINDNS_CRITSECT(); return(dwStatus = (DWORD) E_OUTOFMEMORY); } pNewNode->pNext = gpDomainDnsList; pNewNode->pszUserName = AllocADsStr(szUserName); pNewNode->pszUserDomainName = AllocADsStr(szUserDomainName); pNewNode->pszDomainDns = AllocADsStr(szDomainDnsName); pNewNode->pszServer = AllocADsStr(szServerName); gpDomainDnsList = pNewNode; LEAVE_DOMAINDNS_CRITSECT(); error: return(dwStatus); } DWORD GetGCDomainName( LPWSTR pszDomainDNSName, LPWSTR pszServerName ) { PDOMAIN_CONTROLLER_INFO pDomainControllerInfo = NULL; DWORD dwStatus = 0; DWORD Flags = DS_GC_SERVER_REQUIRED | DS_RETURN_DNS_NAME; /* Flags |= DS_FORCE_REDISCOVERY ; Flags |= DS_WRITABLE_REQUIRED ; */ dwStatus = DsGetDcNameWrapper( NULL, NULL, NULL, NULL, Flags, &pDomainControllerInfo ) ; if (dwStatus == NO_ERROR) { wcscpy(pszServerName,pDomainControllerInfo->DomainControllerName+2); wcscpy(pszDomainDNSName,pDomainControllerInfo->DnsForestName); (void) NetApiBufferFree(pDomainControllerInfo) ; } return(dwStatus); } DWORD GetDefaultServer( DWORD dwPort, BOOL fVerify, LPWSTR szDomainDnsName, LPWSTR szServerName, BOOL fWriteable ) { LPWSTR pszAddresses[5]; DWORD dwStatus = NO_ERROR; if (dwPort == USE_DEFAULT_GC_PORT) { dwStatus = GetGCDomainName( szDomainDnsName, szServerName); } else { dwStatus = GetDefaultDomainName( szDomainDnsName, szServerName, fWriteable, fVerify ); } return(dwStatus); } // // Helper to see if we can use the cache for an domain DNS name // given a domain flat name. // BOOL EquivalentDomains( PDOMAINDNSLIST pTemp, LPWSTR pszUserDomainName ) { if (!pszUserDomainName || !*pszUserDomainName) { return(FALSE); } #ifdef WIN95 if (!_wcsicmp(pszUserDomainName, pTemp->pszUserDomainName)) { #else if (CompareStringW( LOCALE_SYSTEM_DEFAULT, NORM_IGNORECASE, pszUserDomainName, -1, pTemp->pszUserDomainName, -1 ) == CSTR_EQUAL ) { #endif return(TRUE); } return(FALSE); }