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.
 
 
 
 
 
 

489 lines
12 KiB

///////////////////////////////////////////////////////////////////////////////
//
// Copyright (c) Microsoft Corp. All rights reserved.
//
// FILE
//
// Cracker.cpp
//
// SYNOPSIS
//
// This file defines the class NameCracker.
//
// MODIFICATION HISTORY
//
// 04/13/1998 Original version.
// 08/10/1998 Remove NT4 support.
// 08/21/1998 Removed initialization/shutdown routines.
//
///////////////////////////////////////////////////////////////////////////////
#include "ias.h"
#include "cracker.h"
#include "iaslsa.h"
#include "iasutil.h"
#include "ntdsapip.h"
#include <sddl.h>
#include <new>
#include <strsafe.h>
// Passport authority for SIDs
#ifndef SECURITY_PASSPORT_AUTHORITY
#define SECURITY_PASSPORT_AUTHORITY {0,0,0,0,0,10}
#endif
///////////////////////////////////////////////////////////////////////////////
//
// FUNCTION
//
// DsCrackNameAutoChaseW
//
// DESCRIPTION
//
// Extension to DsCrackNames that automatically chases cross-forest
// referrals using default credentials.
//
///////////////////////////////////////////////////////////////////////////////
DWORD
WINAPI
DsCrackNameAutoChaseW(
HANDLE hDS,
DS_NAME_FLAGS flags,
DS_NAME_FORMAT formatOffered,
DS_NAME_FORMAT formatDesired,
PCWSTR name,
PDS_NAME_RESULTW* ppResult,
BOOL* pChased
)
{
DWORD error;
if (pChased == NULL)
{
return ERROR_INVALID_PARAMETER;
}
*pChased = FALSE;
flags = (DS_NAME_FLAGS)(flags | DS_NAME_FLAG_TRUST_REFERRAL);
error = DsCrackNamesW(
hDS,
flags,
formatOffered,
formatDesired,
1,
&name,
ppResult
);
while ((error == NO_ERROR) &&
((*ppResult)->rItems->status == DS_NAME_ERROR_TRUST_REFERRAL))
{
*pChased = TRUE;
HANDLE hDsForeign;
error = DsBindW(NULL, (*ppResult)->rItems->pDomain, &hDsForeign);
DsFreeNameResultW(*ppResult);
*ppResult = NULL;
if (error == NO_ERROR)
{
error = DsCrackNamesW(
hDsForeign,
flags,
formatOffered,
formatDesired,
1,
&name,
ppResult
);
DsUnBindW(&hDsForeign);
}
}
// Win2K Global Catalogs do not support DS_USER_PRINCIPAL_NAME_AND_ALTSECID
// and will simply return DS_NAME_ERROR_NOT_FOUND every time. Thus, we can't
// tell the difference between an invalid name and a downlevel GC, so try
// again as a plain ol' UPN.
if ((formatOffered == DS_USER_PRINCIPAL_NAME_AND_ALTSECID) &&
(error == NO_ERROR) &&
((*ppResult)->rItems->status == DS_NAME_ERROR_NOT_FOUND))
{
DsFreeNameResultW(*ppResult);
*ppResult = 0;
return DsCrackNameAutoChaseW(
hDS,
flags,
DS_USER_PRINCIPAL_NAME,
formatDesired,
name,
ppResult,
pChased
);
}
return error;
}
///////////////////////////////////////////////////////////////////////////////
//
// CLASS
//
// DsHandle
//
// DESCRIPTION
//
// This class represents a reference counted NTDS handle.
//
///////////////////////////////////////////////////////////////////////////////
class DsHandle
: public NonCopyable
{
public:
HANDLE get() const throw ()
{ return subject; }
operator HANDLE() const throw ()
{ return subject; }
protected:
friend class NameCracker;
// Constructor and destructor are protected since only NameCracker is
// allowed to open new handles.
DsHandle(HANDLE h) throw ()
: refCount(1), subject(h)
{ }
~DsHandle() throw ()
{
if (subject) { DsUnBindW(&subject); }
}
void AddRef() throw ()
{
InterlockedIncrement(&refCount);
}
void Release() throw ()
{
if (!InterlockedDecrement(&refCount)) { delete this; }
}
LONG refCount; // reference count.
HANDLE subject; // HANDLE being ref counted.
};
NameCracker::NameCracker() throw ()
: gc(NULL)
{ }
NameCracker::~NameCracker() throw ()
{
if (gc) { gc->Release(); }
}
DWORD NameCracker::crackNames(
DS_NAME_FLAGS flags,
DS_NAME_FORMAT formatOffered,
DS_NAME_FORMAT formatDesired,
PCWSTR name,
PCWSTR upnSuffix,
PDS_NAME_RESULTW *ppResult
) throw ()
{
wchar_t* upnString = 0;
DWORD errorCode = NO_ERROR;
DS_NAME_FORMAT newFormatOffered = formatOffered;
// if this is a SID
if (formatOffered == DS_SID_OR_SID_HISTORY_NAME)
{
// can return NO_ERROR AND upnString = 0 if the SID is not a Passport SID
errorCode = processSID(name, upnSuffix, newFormatOffered, &upnString);
}
// Get a handle to the GC.
if (errorCode == NO_ERROR)
{
DsHandle* hDS1;
errorCode = getGC(&hDS1);
if (errorCode == NO_ERROR)
{
// Try to crack the names.
BOOL chased;
errorCode = DsCrackNameAutoChaseW(
*hDS1,
flags,
newFormatOffered,
formatDesired,
(upnString != 0)? upnString : name,
ppResult,
&chased
);
if (errorCode != NO_ERROR && !chased)
{
// We failed, so disable the current handle ...
disable(hDS1);
// ... and try to get a new one.
DsHandle* hDS2;
errorCode = getGC(&hDS2);
if (errorCode == NO_ERROR)
{
// Give it one more try with the new handle.
errorCode = DsCrackNameAutoChaseW(
*hDS2,
flags,
formatOffered,
formatDesired,
name,
ppResult,
&chased
);
if (errorCode != NO_ERROR && !chased)
{
// No luck so disable the handle.
disable(hDS2);
}
hDS2->Release();
}
}
hDS1->Release();
}
}
delete[] upnString;
return errorCode;
}
void NameCracker::disable(DsHandle* h) throw ()
{
_serialize
// If it doesn't match our cached handle, then someone else
// has already disabled it.
if (h == gc && gc != NULL)
{
gc->Release();
gc = NULL;
}
}
DWORD NameCracker::getGC(DsHandle** h) throw ()
{
_ASSERT(h != NULL);
*h = NULL;
_serialize
// Do we already have a cached handle?
if (!gc)
{
// Bind to a GC.
HANDLE hGC;
DWORD err = DsBindWithCredA(NULL, NULL, NULL, &hGC);
if (err != NO_ERROR)
{
return err;
}
// Allocate a new DsHandle object to wrap the NTDS handle.
gc = new (std::nothrow) DsHandle(hGC);
if (!gc)
{
DsUnBindW(&hGC);
return ERROR_NOT_ENOUGH_MEMORY;
}
}
// AddRef the handle and return to caller.
(*h = gc)->AddRef();
return NO_ERROR;
}
//
// NameCracker::processSID
//
// Transforms a Passport SID into a UPN
// or does nothing if the SID is not a passport SID
//
// can return NO_ERROR AND upnString = 0 if the SID is not a Passport SID
//
DWORD NameCracker::processSID(
PCWSTR name,
PCWSTR upnSuffix,
DS_NAME_FORMAT& newFormatOffered,
wchar_t** ppUpnString) throw()
{
LARGE_INTEGER puid;
if (!convertSid2Puid(name, puid))
{
// Not a passport SID. nothing to do, return NO_ERROR.
return NO_ERROR;
}
DWORD errorCode = NO_ERROR;
// The munged Passport SID is stored in altSecurityIdentities.
newFormatOffered = static_cast<DS_NAME_FORMAT>(
DS_USER_PRINCIPAL_NAME_AND_ALTSECID
);
wchar_t* dnsDomain = 0;
// 16 = hex string representation of PUID
// 1 for "@" and 1 for '\0'
// example of string: [email protected]
DWORD upnStringCch = 16 + 1 + 1;
if (upnSuffix == 0)
{
// No suffix, use the dns domain name
DWORD domainSize = 0;
IASGetDnsDomainName(0, &domainSize);
// update the size
upnStringCch += domainSize;
dnsDomain = new (std::nothrow) wchar_t [domainSize + 1];
// no need to check the result: the IASGetDnsDomainName will return
// ERROR_INSUFFICIENT_BUFFER if the pointer is null
errorCode = IASGetDnsDomainName(dnsDomain, &domainSize);
if (errorCode != NO_ERROR)
{
// delete[] will work fine even if dnsDomain is NULL
delete[] dnsDomain;
return errorCode;
}
}
else
{
// upn suffix was provided. Update the size
upnStringCch += wcslen(upnSuffix);
}
// Allocate the string to store the full UPN
*ppUpnString = new (std::nothrow) wchar_t [upnStringCch];
if (*ppUpnString == 0)
{
errorCode = ERROR_INSUFFICIENT_BUFFER;
}
else
{
// This is a passport Sid: convert it
errorCode = convertPuid2String(
puid,
*ppUpnString,
upnStringCch,
(upnSuffix!=0)? upnSuffix:dnsDomain
);
}
// dnsDomain is not used anymore
delete[] dnsDomain;
return errorCode;
}
//
// Function: convertSid2Puid
//
// Synopsis:
// passport generated sid to puid
//
// Effects:
//
// Arguments:
// PSid [in] sid to convert
// PUID* [out] corresponding puid
//
// Returns: invalid_param if not passport generated puid or null
//
// Notes:
// SID: S-1-10-D0-D1-...-Dn-X-R where
// n == 0 for passport
// D0[31:16] == 0
// D0[15:0] == PUID[63:48]
// X[31:0] == PUID[47:16]
// R[31:16] == PUID[15:0]
// R[15:0] == 0 (reserved)
// R[10] == 1 (so that R > 1024)
//
bool NameCracker::convertSid2Puid(PCWSTR sidString, LARGE_INTEGER& puid) throw()
{
_ASSERT(sidString != 0);
PSID pSid = 0;
if (!ConvertStringSidToSidW(sidString, &pSid))
{
return false;
}
bool isPuid;
// Check that this is a Passport SID
SID_IDENTIFIER_AUTHORITY PassportIA = SECURITY_PASSPORT_AUTHORITY;
if (memcmp(GetSidIdentifierAuthority(pSid),
&PassportIA,
sizeof(SID_IDENTIFIER_AUTHORITY)) ||
*GetSidSubAuthorityCount(pSid) != 3)
{
isPuid = false;
}
else
{
// domain portion of the puid
puid.HighPart = *GetSidSubAuthority(pSid, 0) << 16;
puid.HighPart |= *GetSidSubAuthority(pSid, 1) >> 16;
puid.LowPart = *GetSidSubAuthority(pSid, 1) << 16;
puid.LowPart |= *GetSidSubAuthority(pSid, 2) >> 16;
// No need to check the 1st and 3rd subauth for the bits that
// are not part of the PUID
isPuid = true;
}
LocalFree(pSid);
return isPuid;
}
//
// converts a PUID into a string.
//
DWORD NameCracker::convertPuid2String(
const LARGE_INTEGER& puid,
wchar_t* upnString,
DWORD upnStringCch,
const wchar_t* suffix
) throw()
{
HRESULT hr = StringCchPrintfW(
upnString,
upnStringCch,
L"%08X%08X@%s",
puid.HighPart,
puid.LowPart,
suffix
);
if (FAILED(hr))
{
return HRESULT_CODE(hr);
}
else
{
return NO_ERROR;
}
}