#ifndef _WIN32_WINNT #define _WIN32_WINNT 0x0400 #endif #include #include #include #include #include #include #include //#pragma comment(lib, "samlib.lib") #ifdef __cplusplus extern "C" { #endif NTSTATUS SamConnectWithCreds( IN PUNICODE_STRING ServerName, OUT PSAM_HANDLE ServerHandle, IN ACCESS_MASK DesiredAccess, IN POBJECT_ATTRIBUTES ObjectAttributes, IN RPC_AUTH_IDENTITY_HANDLE Creds, IN PWCHAR Spn, OUT BOOL *pfDstIsW2K ); #ifdef __cplusplus } #endif namespace { //------------------------------------------------------------------------------ // ConnectToSam Function // // Synopsis // Connects to Sam Server on specified domain controller using specified // credentials. // // Arguments // IN pszDomain - the domain name // IN pszDomainController - the domain controller to connect to // IN pszUserName - the credentials user // IN pszPassword - the credentials password // IN pszUserDomain - the credentials domain // OUT phSam - the handle to the SAM server // // Return // Win32 error code //------------------------------------------------------------------------------ DWORD __stdcall ConnectToSam ( PCWSTR pszDomain, PCWSTR pszDomainController, PCWSTR pszUserName, PCWSTR pszPassword, PCWSTR pszUserDomain, SAM_HANDLE* phSam ) { DWORD dwError = ERROR_SUCCESS; *phSam = NULL; // SAM server name UNICODE_STRING usServerName; usServerName.Length = wcslen(pszDomainController) * sizeof(WCHAR); usServerName.MaximumLength = usServerName.Length + sizeof(WCHAR); usServerName.Buffer = const_cast(pszDomainController); // object attributes OBJECT_ATTRIBUTES oa = { sizeof(OBJECT_ATTRIBUTES), NULL, NULL, 0, NULL, NULL }; // authorization identity SEC_WINNT_AUTH_IDENTITY_W swaiAuthIdentity; swaiAuthIdentity.User = const_cast(pszUserName); swaiAuthIdentity.UserLength = wcslen(pszUserName); swaiAuthIdentity.Domain = const_cast(pszUserDomain); swaiAuthIdentity.DomainLength = wcslen(pszUserDomain); swaiAuthIdentity.Password = const_cast(pszPassword); swaiAuthIdentity.PasswordLength = wcslen(pszPassword); swaiAuthIdentity.Flags = SEC_WINNT_AUTH_IDENTITY_UNICODE; // service principal name ? const WCHAR c_szA[] = L"cifs/"; const WCHAR c_szC[] = L"@"; PWSTR pszSpn = new WCHAR[wcslen(c_szA) + wcslen(pszDomainController) + wcslen(c_szC) + wcslen(pszDomain) + 1]; if (pszSpn) { wcscpy(pszSpn, c_szA); wcscat(pszSpn, pszDomainController); wcscat(pszSpn, c_szC); wcscat(pszSpn, pszDomain); } else { return ERROR_OUTOFMEMORY; } BOOL fSrcIsW2K; NTSTATUS ntsStatus = SamConnectWithCreds( &usServerName, phSam, MAXIMUM_ALLOWED, &oa, &swaiAuthIdentity, pszSpn, &fSrcIsW2K ); if (ntsStatus == RPC_NT_UNKNOWN_AUTHN_SERVICE) { // Note that following comment is from the DsAddSidHistory // implementation. // // It might be that the SrcDc is NT4 and the client is // running locally. This config is supported so try the // binding with a NULL SrcSpn to force the underlying code // to use AUTH_WINNT instead of AUTH_NEGOTIATE. ntsStatus = SamConnectWithCreds( &usServerName, phSam, MAXIMUM_ALLOWED, &oa, &swaiAuthIdentity, NULL, &fSrcIsW2K ); } delete [] pszSpn; if (ntsStatus != STATUS_SUCCESS) { // // SamConnectWithCreds sometimes returns Win32 errors instead of an // NT status. Therefore assume that if the severity is success that a // Win32 error has been returned. // if (NT_SUCCESS(ntsStatus)) { dwError = ntsStatus; } else { dwError = RtlNtStatusToDosError(ntsStatus); } } return dwError; } //------------------------------------------------------------------------------ // OpenDomain Function // // Synopsis // Opens the specified domain and returns a handle that may be used to open // objects in the domain. // // Arguments // IN hSam - SAM handle // IN pszDomain - domain to open // OUT phDomain - domain handle // // Return // Win32 error code //------------------------------------------------------------------------------ DWORD __stdcall OpenDomain(SAM_HANDLE hSam, PCWSTR pszDomain, SAM_HANDLE* phDomain) { DWORD dwError = ERROR_SUCCESS; *phDomain = NULL; // // Retrieve domain SID from SAM server. // PSID pSid; UNICODE_STRING usDomain; usDomain.Length = wcslen(pszDomain) * sizeof(WCHAR); usDomain.MaximumLength = usDomain.Length + sizeof(WCHAR); usDomain.Buffer = const_cast(pszDomain); NTSTATUS ntsStatus = SamLookupDomainInSamServer(hSam, &usDomain, &pSid); if (ntsStatus == STATUS_SUCCESS) { // // Open domain object in SAM server. // ntsStatus = SamOpenDomain(hSam, DOMAIN_LOOKUP, pSid, phDomain); if (ntsStatus != STATUS_SUCCESS) { dwError = RtlNtStatusToDosError(ntsStatus); } SamFreeMemory(pSid); } else { dwError = RtlNtStatusToDosError(ntsStatus); } return dwError; } //------------------------------------------------------------------------------ // OpenDomain Function // // Synopsis // Verifies that the principal which obtained the domain handle has domain // admin rights in the domain. // // Note that the comments and logic were borrowed from the DsAddSidHistory code. // //depot/Lab03_N/ds/ds/src/ntdsa/dra/addsid.c // // Arguments // IN hDomain - domain handle // // Return // Win32 error code //------------------------------------------------------------------------------ DWORD __stdcall VerifyDomainAdminRights(SAM_HANDLE hDomain) { DWORD dwError = ERROR_SUCCESS; // We need to verify that the credentials used to get hSam have domain // admin rights in the source domain. RichardW observes that we can // do this easily for both NT4 and NT5 cases by checking whether we // can open the domain admins object for write. On NT4, the principal // would have to be an immediate member of domain admins. On NT5 the // principal may transitively be a member of domain admins. But rather // than checking memberships per se, the ability to open domain admins // for write proves that the principal could add himself if he wanted // to, thus he/she is essentially a domain admin. I.e. The premise is // that security is set up such that only domain admins can modify the // domain admins group. If that's not the case, the customer has far // worse security issues to deal with than someone stealing a SID. // You'd think we should ask for GROUP_ALL_ACCESS. But it turns out // that in 2000.3 DELETE is not given by default to domain admins. // So we modify the access required accordingly. PraeritG has been // notified of this phenomena. SAM_HANDLE hGroup; NTSTATUS ntsStatus = SamOpenGroup(hDomain, GROUP_ALL_ACCESS & ~DELETE, DOMAIN_GROUP_RID_ADMINS, &hGroup); if (ntsStatus == STATUS_SUCCESS) { SamCloseHandle(hGroup); } else { dwError = RtlNtStatusToDosError(ntsStatus); } return dwError; } } // namespace //------------------------------------------------------------------------------ // VerifyAdminCredentials Function // // Synopsis // Verifies that the given credentials are valid in the specified domain and // that the account has domain admin rights in the domain. // // Arguments // IN pszDomain // IN pszDomainController // IN pszUserName // IN pszPassword // IN pszUserDomain // // Return // Win32 error code // ERROR_SUCCESS - credentials are valid and the account does have domain // admin rights in the domain // ERROR_ACCESS_DENIED - either the credentials are invalid or the account does // not have domain admin rights in the domain //------------------------------------------------------------------------------ DWORD __stdcall VerifyAdminCredentials ( PCWSTR pszDomain, PCWSTR pszDomainController, PCWSTR pszUserName, PCWSTR pszPassword, PCWSTR pszUserDomain ) { // // Connect to SAM server. // SAM_HANDLE hSam; DWORD dwError = ConnectToSam(pszDomain, pszDomainController, pszUserName, pszPassword, pszUserDomain, &hSam); if (dwError == ERROR_SUCCESS) { // // Open domain object on SAM server. // SAM_HANDLE hDomain; dwError = OpenDomain(hSam, pszDomain, &hDomain); if (dwError == ERROR_SUCCESS) { // // Verify credentials have administrative rights in domain. // dwError = VerifyDomainAdminRights(hDomain); SamCloseHandle(hDomain); } SamCloseHandle(hSam); } return dwError; }