windows-nt/Source/XPSP1/NT/net/ias/providers/ntuser/names/ntsamnames.cpp
2020-09-26 16:20:57 +08:00

372 lines
11 KiB
C++

///////////////////////////////////////////////////////////////////////////////
//
// Copyright (c) 1998, Microsoft Corp. All rights reserved.
//
// FILE
//
// NTSamNames.cpp
//
// SYNOPSIS
//
// This file defines the class NTSamNames.
//
// MODIFICATION HISTORY
//
// 04/13/1998 Original version.
// 04/25/1998 Reject non-local users under WKS or stand-alone SRV.
// 07/22/1998 Don't abort non-local users.
// 08/21/1998 Trace/error improvements.
// 09/08/1998 Added realm stripping.
// 09/10/1998 Remove localOnly flag.
// 09/16/1998 Apply all realm stripping rules.
// 10/22/1998 Allow more name types.
// 01/20/1999 Add support for DNIS/ANI/Guest.
// 02/18/1999 Move registry values.
// 03/19/1999 Add new realms support & overrideUsername flag.
// 01/25/2000 Leave stripped username on the heap.
// 02/15/2000 Remove realms support.
//
///////////////////////////////////////////////////////////////////////////////
#include <ias.h>
#include <iaslsa.h>
#include <memory>
#include <sdoias.h>
#include <samutil.h>
#include <ntsamnames.h>
#include <varvec.h>
/////////
// Registry keys and values.
/////////
const WCHAR PARAMETERS_KEY[] =
L"SYSTEM\\CurrentControlSet\\Services\\RemoteAccess\\Policy";
const WCHAR IDENTITY_ATTR_VALUE[] = L"User Identity Attribute";
const WCHAR DEFAULT_IDENTITY_VALUE[] = L"Default User Identity";
const WCHAR OVERRIDE_USERNAME_VALUE[] = L"Override User-Name";
//////////
// Determines whether an identity can be cracked through DsCrackNames.
//////////
inline BOOL isCrackable(PCWSTR szIdentity) throw ()
{
return wcschr(szIdentity, L'@') || // DS_USER_PRINCIPAL_NAME
wcschr(szIdentity, L'=') || // DS_FQDN_1779_NAME
wcschr(szIdentity, L'/') ; // DS_CANONICAL_NAME
}
STDMETHODIMP NTSamNames::Initialize()
{
// Initialize the LSA API.
DWORD error = IASLsaInitialize();
if (error) { return HRESULT_FROM_WIN32(error); }
LONG status;
DWORD cbData, type;
// Open the Parameters registry key.
HKEY hKey;
status = RegOpenKeyW(
HKEY_LOCAL_MACHINE,
PARAMETERS_KEY,
&hKey
);
if (status == NO_ERROR)
{
// Query the Identity Attribute.
DWORD cbData = sizeof(DWORD);
status = RegQueryValueExW(
hKey,
IDENTITY_ATTR_VALUE,
NULL,
&type,
(LPBYTE)&identityAttr,
&cbData
);
// If we didn't successfully read a DWORD, set to default.
if (status != NO_ERROR || type != REG_DWORD || cbData != sizeof(DWORD))
{
identityAttr = RADIUS_ATTRIBUTE_USER_NAME;
}
// Query the Override User-Name flag.
cbData = sizeof(DWORD);
status = RegQueryValueExW(
hKey,
OVERRIDE_USERNAME_VALUE,
NULL,
&type,
(LPBYTE)&overrideUsername,
&cbData
);
// If we didn't successfully read a DWORD, set to default.
if (status != NO_ERROR || type != REG_DWORD || cbData != sizeof(DWORD))
{
overrideUsername = FALSE;
}
// Query the length of the Default Identity.
defaultLength = 0;
status = RegQueryValueExW(
hKey,
DEFAULT_IDENTITY_VALUE,
NULL,
&type,
NULL,
&defaultLength
);
if (status == NO_ERROR &&
type == REG_SZ &&
defaultLength > 2 * sizeof(WCHAR))
{
// Allocate memory to hold the Default Identity.
defaultIdentity = new (std::nothrow)
WCHAR[defaultLength / sizeof(WCHAR)];
if (defaultIdentity)
{
// Query the value of the Default Identity.
status = RegQueryValueExW(
hKey,
DEFAULT_IDENTITY_VALUE,
NULL,
&type,
(LPBYTE)defaultIdentity,
&defaultLength
);
if (status != NO_ERROR ||
type != REG_SZ ||
defaultLength < 2 * sizeof(WCHAR))
{
delete[] defaultIdentity;
defaultIdentity = NULL;
defaultLength = 0;
}
}
}
RegCloseKey(hKey);
}
IASTracePrintf("User identity attribute: %lu", identityAttr);
IASTracePrintf("Override User-Name: %s",
overrideUsername ? "TRUE" : "FALSE");
IASTracePrintf("Default user identity: %S",
(defaultIdentity ? defaultIdentity : L"<Guest>"));
// Small optimization.
if (identityAttr == RADIUS_ATTRIBUTE_USER_NAME) { overrideUsername = TRUE; }
return S_OK;
}
STDMETHODIMP NTSamNames::Shutdown()
{
IASLsaUninitialize();
delete[] defaultIdentity;
defaultIdentity = NULL;
defaultLength = 0;
return S_OK;
}
IASREQUESTSTATUS NTSamNames::onSyncRequest(IRequest* pRequest) throw ()
{
// I had to stick this at function scope, so it can be freed in
// the catch block.
PDS_NAME_RESULTW result = NULL;
try
{
IASRequest request(pRequest);
WCHAR name[DNLEN + UNLEN + 2], *identity;
// Get the identity attribute.
IASAttribute attr;
if (overrideUsername ||
!attr.load(request, RADIUS_ATTRIBUTE_USER_NAME, IASTYPE_OCTET_STRING))
{
attr.load(request, identityAttr, IASTYPE_OCTET_STRING);
}
if (attr != NULL && attr->Value.OctetString.dwLength != 0)
{
// Convert it to a UNICODE string.
identity = IAS_OCT2WIDE(attr->Value.OctetString);
IASTracePrintf(
"NT-SAM Names handler received request with user identity %S.",
identity
);
}
else
{
// No identity attribute.
if (defaultIdentity)
{
// Use the default identity if set.
identity = defaultIdentity;
}
else
{
// Otherwise use the guest account for the default domain.
IASGetGuestAccountName(identity = name);
}
IASTracePrintf(
"NT-SAM Names handler using default user identity %S.",
identity
);
}
// Allocate an attribute to hold the NT4 Account Name.
IASAttribute nt4Name(true);
nt4Name->dwId = IAS_ATTRIBUTE_NT4_ACCOUNT_NAME;
// (1) 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.");
return IASProcessFailure(request, IAS_LOCAL_USERS_ONLY);
}
// Restore the delimiter.
*delim = L'\\';
}
IASTraceString("Username is already an NT4 account name.");
nt4Name.setString(identity);
}
// (2) If we're stand-alone, then we don't support DsCrackNames.
// (3) If it doesn't look crackable, then don't waste the network I/O.
else if (IASGetRole() == IAS_ROLE_STANDALONE || !isCrackable(identity))
{
IASTraceString("Prepending default domain.");
nt4Name->Value.String.pszWide = prependDefaultDomain(identity);
nt4Name->Value.String.pszAnsi = NULL;
nt4Name->Value.itType = IASTYPE_STRING;
}
else
{
// (4) Call DsCrackNames.
DWORD dwErr = cracker.crackNames(
DS_NAME_FLAG_EVAL_AT_DC,
DS_UNKNOWN_NAME,
DS_NT4_ACCOUNT_NAME,
identity,
&result
);
if (dwErr != NO_ERROR)
{
IASTraceFailure("DsCrackNames", dwErr);
return IASProcessFailure(request, IAS_GLOBAL_CATALOG_UNAVAILABLE);
}
if (result->rItems->status == DS_NAME_NO_ERROR)
{
IASTraceString("Successfully cracked username.");
// (5) DsCrackNames returned an NT4 Account Name, so use it.
nt4Name.setString(result->rItems->pName);
}
else
{
IASTraceString("Global Catalog could not crack username; "
"prepending default domain.");
// (6) 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;
}
cracker.freeNameResult(result);
}
// 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();
if (result) { cracker.freeNameResult(result); }
return IASProcessFailure(pRequest, ce.Error());
}
return IAS_REQUEST_STATUS_HANDLED;
}
PWSTR NTSamNames::prependDefaultDomain(PCWSTR username)
{
_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;
}