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.
 
 
 
 
 
 

305 lines
8.6 KiB

///////////////////////////////////////////////////////////////////////////////
//
// Copyright (c) Microsoft Corp. All rights reserved.
//
// FILE
//
// namemapper.cpp
//
// SYNOPSIS
//
// This file defines the class NameMapper.
//
///////////////////////////////////////////////////////////////////////////////
#include "ias.h"
#include "iaslsa.h"
#include "samutil.h"
#include "namemapper.h"
#include "cracker.h"
#include "identityhelper.h"
#include "iastlutl.h"
#include "ntdsapip.h"
NameCracker NameMapper::cracker;
IdentityHelper NameMapper::identityHelper;
STDMETHODIMP NameMapper::Initialize() const throw()
{
DWORD error = IASLsaInitialize();
if (error) { return HRESULT_FROM_WIN32(error); }
return identityHelper.initialize();
}
STDMETHODIMP NameMapper::Shutdown() const throw()
{
IASLsaUninitialize();
return S_OK;
}
IASREQUESTSTATUS NameMapper::onSyncRequest(IRequest* pRequest) throw ()
{
// This is function scope, so it can be freed in the catch block.
PDS_NAME_RESULTW result = NULL;
wchar_t identity[254];
wchar_t* pIdentity = identity;
HRESULT hr = S_OK;
try
{
size_t identitySize = sizeof(identity);
IASRequest request(pRequest);
bool identityRetrieved = identityHelper.getIdentity(request,
pIdentity,
identitySize);
if (!identityRetrieved)
{
// allocate a big enough buffer and use it
pIdentity = new wchar_t[identitySize];
identityRetrieved = identityHelper.getIdentity(request,
pIdentity,
identitySize);
if (!identityRetrieved)
{
IASTraceString("Error: no user identity found");
_com_issue_error(IAS_PROXY_MALFORMED_RESPONSE);
}
}
// Allocate an attribute to hold the NT4 Account Name.
IASAttribute nt4Name(true);
nt4Name->dwId = IAS_ATTRIBUTE_NT4_ACCOUNT_NAME;
DS_NAME_FORMAT formatOffered = DS_UNKNOWN_NAME;
// If it already contains a backslash
// then use as is.
PWCHAR delim = wcschr(identity, L'\\');
if (delim)
{
if (IASGetRole() == IAS_ROLE_STANDALONE ||
IASGetProductType() == IAS_PRODUCT_WORKSTATION)
{
// Strip out the domain.
*delim = L'\0';
// Make sure this is a local user.
if (!IASIsDomainLocal(identity))
{
IASTraceString("Non-local users are not allowed -- rejecting.");
_com_issue_error(IAS_LOCAL_USERS_ONLY);
}
// Restore the delimiter.
*delim = L'\\';
}
IASTraceString("Username is already an NT4 account name.");
nt4Name.setString(identity);
}
else if (isCrackable(identity, formatOffered) &&
(IASGetRole() != IAS_ROLE_STANDALONE))
{
// identity seems to be crackable and IAS is not a standalone machine.
// (either a domain member or a domain controller)
mapName(identity, nt4Name, formatOffered, 0);
}
else
{
// Assume no domain was specified and use the default domain
IASTraceString("Prepending default domain.");
nt4Name->Value.String.pszWide = prependDefaultDomain(identity);
nt4Name->Value.String.pszAnsi = NULL;
nt4Name->Value.itType = IASTYPE_STRING;
}
// Convert the domain name to uppercase.
delim = wcschr(nt4Name->Value.String.pszWide, L'\\');
*delim = L'\0';
_wcsupr(nt4Name->Value.String.pszWide);
*delim = L'\\';
nt4Name.store(request);
// For now, we'll use this as the FQDN as well.
IASStoreFQUserName(
request,
DS_NT4_ACCOUNT_NAME,
nt4Name->Value.String.pszWide
);
IASTracePrintf("SAM-Account-Name is \"%S\".",
nt4Name->Value.String.pszWide);
}
catch (const _com_error& ce)
{
IASTraceExcept();
hr = ce.Error();
}
if (pIdentity != identity)
{
delete[] pIdentity;
}
if (result)
{
cracker.freeNameResult(result);
}
if ( FAILED(hr) || ((hr != S_OK) && (hr < 0x0000ffff)) )
{
// IAS reason code: the reason code will be used
// or error code: will map to internal error
return IASProcessFailure(pRequest, hr);
}
else
{
// S_OK
return IAS_REQUEST_STATUS_HANDLED;
}
}
PWSTR NameMapper::prependDefaultDomain(PCWSTR username)
{
IASTraceString("NameMapper::prependDefaultDomain");
_ASSERT(username != NULL);
// Figure out how long everything is.
PCWSTR domain = IASGetDefaultDomain();
ULONG domainLen = wcslen(domain);
ULONG usernameLen = wcslen(username) + 1;
// Allocate the needed memory.
ULONG needed = domainLen + usernameLen + 1;
PWSTR retval = (PWSTR)CoTaskMemAlloc(needed * sizeof(WCHAR));
if (!retval) { _com_issue_error(E_OUTOFMEMORY); }
// Set up the cursor used for packing the strings.
PWSTR dst = retval;
// Copy in the domain name.
memcpy(dst, domain, domainLen * sizeof(WCHAR));
dst += domainLen;
// Add the delimiter.
*dst++ = L'\\';
// Copy in the username.
// Note: usernameLen includes the null-terminator.
memcpy(dst, username, usernameLen * sizeof(WCHAR));
return retval;
}
//////////
// Determines whether an identity can be cracked through DsCrackNames and which
// name format should be offered if it is crackable.
//////////
bool NameMapper::isCrackable(
const wchar_t* szIdentity,
DS_NAME_FORMAT& format
) const throw ()
{
format = DS_UNKNOWN_NAME;
if (wcschr(szIdentity, L'@') != 0)
{
if (allowAltSecId)
{
format = static_cast<DS_NAME_FORMAT>(
DS_USER_PRINCIPAL_NAME_AND_ALTSECID
);
}
return true;
}
return (wcschr(szIdentity, L'=') != 0) || // DS_FQDN_1779_NAME
(wcschr(szIdentity, L'/') != 0); // DS_CANONICAL_NAME
}
void NameMapper::mapName(
const wchar_t* identity,
IASAttribute& nt4Name,
DS_NAME_FORMAT formatOffered,
const wchar_t* suffix
)
{
_ASSERT(identity != NULL);
_ASSERT(nt4Name != NULL);
_ASSERT(*nt4Name != NULL);
PDS_NAME_RESULTW result = NULL;
HRESULT hr = S_OK;
do
{
// call cracker
DWORD dwErr = cracker.crackNames(
DS_NAME_FLAG_EVAL_AT_DC,
formatOffered,
DS_NT4_ACCOUNT_NAME,
identity,
suffix,
&result
);
if (dwErr != NO_ERROR)
{
IASTraceFailure("DsCrackNames", dwErr);
hr = IAS_GLOBAL_CATALOG_UNAVAILABLE;
break;
}
if (result->rItems->status == DS_NAME_NO_ERROR)
{
IASTraceString("Successfully cracked username.");
// DsCrackNames returned an NT4 Account Name, so use it.
nt4Name.setString(result->rItems->pName);
}
else
{
// GC could not crack the name
if (formatOffered != DS_SID_OR_SID_HISTORY_NAME)
{
// Not using SID: try to append the default domain to the identity
IASTraceString("Global Catalog could not crack username; "
"prepending default domain.");
// If it can't be cracked we'll assume that it's a flat
// username with some weird characters.
nt4Name->Value.String.pszWide = prependDefaultDomain(identity);
nt4Name->Value.String.pszAnsi = NULL;
nt4Name->Value.itType = IASTYPE_STRING;
}
else
{
// using SID. nothing else can be done.
IASTracePrintf("Global Catalog could not crack username. Error %x",
result->rItems->status);
hr = IAS_NO_SUCH_USER;
}
}
}
while(false);
cracker.freeNameResult(result);
if ( FAILED(hr) || ((hr != S_OK) && (hr < 0x0000ffff)))
{
_com_issue_error(hr);
}
}