|
|
///////////////////////////////////////////////////////////////////////////////
//
// 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; }
|