/////////////////////////////////////////////////////////////////////////////// // // Copyright (c) Microsoft Corp. All rights reserved. // // FILE // // IdentityHelper.cpp // // SYNOPSIS // // This file defines the class IdentityHelper. // /////////////////////////////////////////////////////////////////////////////// #include "ias.h" #include "iaslsa.h" #include "memory" #include "samutil.h" #include "identityhelper.h" #define STRSAFE_NO_DEPRECATE #include bool IdentityHelper::initialized = false; ///////// // 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"; HRESULT IdentityHelper::IASReadRegistryDword( HKEY& hKey, PCWSTR valueName, DWORD defaultValue, DWORD* result ) { _ASSERT (result != 0); DWORD cbData = sizeof(DWORD); DWORD type; LONG status = RegQueryValueExW( hKey, valueName, NULL, &type, (LPBYTE)result, &cbData ); if (status != ERROR_SUCCESS) { if (status != ERROR_FILE_NOT_FOUND) { IASTracePrintf("Cannot read value %S. error %ld", valueName, status ); return HRESULT_FROM_WIN32(status); } else { // The attribute does not exist. Set the default IASTracePrintf("The registry value %S does not exist. Using default %ld", valueName, defaultValue ); *result = defaultValue; return S_OK; } } if (type != REG_DWORD || cbData != sizeof(DWORD)) { IASTracePrintf("Cannot read value %S. Wrong type %ld. Size = %ld", valueName, type, cbData ); return E_INVALIDARG; } return S_OK; } // // IdentityHelper::initialize // CAUTION: calls to this API MUST be serialized. // i.e. the handler's calling MapNAme::Initialize MUST complete a successful // init of each handler's before calling the Init of the next one. // HRESULT IdentityHelper::initialize() throw() { if (initialized) { return S_OK; } // Open the Parameters registry key. HKEY hKey = (HKEY) INVALID_HANDLE_VALUE; LONG status = RegOpenKeyExW( HKEY_LOCAL_MACHINE, PARAMETERS_KEY, 0, KEY_READ, &hKey ); if (status != ERROR_SUCCESS) { IASTracePrintf("Cannot open the reg key %S, error %ld", PARAMETERS_KEY, status ); return HRESULT_FROM_WIN32(status); } HRESULT hr = S_OK; do { // Query the Identity Attribute. hr = IASReadRegistryDword( hKey, IDENTITY_ATTR_VALUE, RADIUS_ATTRIBUTE_USER_NAME, &identityAttr ); if (FAILED(hr)) { break; } // Query the Override User-Name flag. hr = IASReadRegistryDword( hKey, OVERRIDE_USERNAME_VALUE, FALSE, &overrideUsername ); if (FAILED(hr)) { break; } DWORD type; // Query the length of the Default Identity. defaultLength = 0; status = RegQueryValueExW( hKey, DEFAULT_IDENTITY_VALUE, NULL, &type, NULL, &defaultLength ); if (status != ERROR_SUCCESS) { if (status != ERROR_FILE_NOT_FOUND) { IASTracePrintf("Cannot read value %S. error %ld", DEFAULT_IDENTITY_VALUE, status ); hr = HRESULT_FROM_WIN32(status); break; } else { // Done. defaultIdentity = NULL; defaultLength = 0; break; } } if (type != REG_SZ) { IASTracePrintf("Cannot read value %S. Wrong type %ld", DEFAULT_IDENTITY_VALUE, type ); hr = E_INVALIDARG; break; } if (defaultLength < sizeof(WCHAR)) { IASTracePrintf("Cannot read value %S. Wrong Size = %ld", DEFAULT_IDENTITY_VALUE, defaultLength ); hr = E_INVALIDARG; break; } // Allocate memory to hold the Default Identity. defaultIdentity = new (std::nothrow) WCHAR[defaultLength / sizeof(WCHAR)]; if (defaultIdentity == 0) { IASTraceString("Default Identity could not be retrieved due to" "out of memory condition."); defaultLength = 0; hr = E_OUTOFMEMORY; break; } // Query the value of the Default Identity. status = RegQueryValueExW( hKey, DEFAULT_IDENTITY_VALUE, NULL, &type, (LPBYTE)defaultIdentity, &defaultLength ); if (status != ERROR_SUCCESS) { delete[] defaultIdentity; defaultIdentity = NULL; defaultLength = 0; hr = HRESULT_FROM_WIN32(status); IASTracePrintf("Failed to read the value %S. Error is %ld", DEFAULT_IDENTITY_VALUE, status); break; } } while(false); if (hKey != INVALID_HANDLE_VALUE) { RegCloseKey(hKey); } if (SUCCEEDED(hr)) { IASTracePrintf("User identity attribute: %lu", identityAttr); IASTracePrintf("Override User-Name: %s", overrideUsername ? "TRUE" : "FALSE"); IASTracePrintf("Default user identity: %S", (defaultIdentity ? defaultIdentity : L"")); // Initialized completed initialized = true; } return hr; } IdentityHelper::IdentityHelper() throw() : identityAttr(1), defaultIdentity(NULL), defaultLength(0) { } IdentityHelper::~IdentityHelper() throw() { delete[] defaultIdentity; } bool IdentityHelper::getIdentity(IASRequest& request, wchar_t* pIdentity, size_t& identitySize) { wchar_t* identity; IASAttribute attr; HRESULT hr; WCHAR name[DNLEN + UNLEN + 2]; if ((identityAttr == RADIUS_ATTRIBUTE_USER_NAME) || (!overrideUsername)) { // identity chosen for override is user-name // can be more than 1 RADIUS_ATTRIBUTE_USER_NAME attribute // Use the one from access accept. getRadiusUserName(request, attr); } if (!attr && (identityAttr != RADIUS_ATTRIBUTE_USER_NAME)) { // identity chosen is not user-name // only one possible identity attribute attr.load(request, identityAttr, IASTYPE_OCTET_STRING); } // if an 'identity' was retrieved, convert it then return if (attr != NULL && attr->Value.OctetString.dwLength != 0) { // previous step was successful // Convert it to a UNICODE string. if (identitySize < IAS_OCT2WIDE_LEN(attr->Value.OctetString)) { IASTraceString("IASOctetStringToWide failed"); identitySize = IAS_OCT2WIDE_LEN(attr->Value.OctetString); return false; } IASOctetStringToWide(attr->Value.OctetString, pIdentity); // if that fails, then the string is empty ("") if (wcslen(pIdentity) == 0) { IASTraceString("IASOctetStringToWide failed"); return false; } else { IASTracePrintf( "NT-SAM Names handler received request with user identity %S.", pIdentity ); return true; } } else { // previous step was not successful (No identity attribute) // use default identity or guest if (defaultIdentity) { // Use the default identity if set. identity = defaultIdentity; } else { // Otherwise use the guest account for the default domain. IASGetGuestAccountName(name); identity = name; } IASTracePrintf( "NT-SAM Names handler using default user identity %S.", identity ); } IASTracePrintf("identity is \"%S\"", identity); hr = StringCbCopyW(pIdentity, identitySize, identity); if (FAILED(hr)) { identitySize = (wcslen(identity) + 1) * sizeof(wchar_t) ; return false; } return true; } // // set attr to the RADIUS_ATTRIBUTE_USER_NAME found if any // if 2 are present, take the one returned by the backend server. // // void IdentityHelper::getRadiusUserName(IASRequest& request, IASAttribute &attr) { IASAttributeVectorWithBuffer<2> vector; DWORD Error = vector.load(request, RADIUS_ATTRIBUTE_USER_NAME); switch (vector.size()) { case 0: // no attribute found break; case 1: // only one attribute: use it attr = vector.front().pAttribute; break; case 2: attr = vector.front().pAttribute; // IAS_RECVD_FROM_CLIENT is set when the proxy receives the attribute // in the access request or accounting request if(attr.testFlag(IAS_RECVD_FROM_CLIENT)) { // if the 1st attribute was received from the client (i.e. was in the // REQUEST, then use the other one, the other should come from the // back-end server attr = vector.back().pAttribute; if (attr.testFlag(IAS_RECVD_FROM_CLIENT)) { IASTraceString("ERROR 2 RADIUS_ATTRIBUTE_USER_NAME found in the" "request but both came from the client"); _com_issue_error(IAS_PROXY_MALFORMED_RESPONSE); } } break; default: _com_issue_error(IAS_PROXY_MALFORMED_RESPONSE); } }