Leaked source code of windows server 2003
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

348 lines
9.5 KiB

#ifndef _WIN32_WINNT
#define _WIN32_WINNT 0x0400
#endif
#include <nt.h>
#include <ntrtl.h>
#include <nturtl.h>
#include <rpc.h>
#include <ntsam.h>
#include <String.h>
#include <ntdsapi.h>
//#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<PWSTR>(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<PWSTR>(pszUserName);
swaiAuthIdentity.UserLength = wcslen(pszUserName);
swaiAuthIdentity.Domain = const_cast<PWSTR>(pszUserDomain);
swaiAuthIdentity.DomainLength = wcslen(pszUserDomain);
swaiAuthIdentity.Password = const_cast<PWSTR>(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<PWSTR>(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;
}