2025-11-28 00:35:46 +09:00

191 lines
4.5 KiB
C

/* Copyright (c) Microsoft Corporation. All rights reserved. */
#include "windows.h"
#include "authif.h"
#include "lmcons.h"
#include "ntsecapi.h"
#include "radutil.h"
/* The registry value where this extension is registered. */
LPCWSTR pwszDllType = AUTHSRV_EXTENSIONS_VALUE_W;
/* Global handle to the LSA. This is initialized once at start-up and reused
* until shutdown. */
LSA_HANDLE hPolicy = NULL;
DWORD
WINAPI
RadiusExtensionInit( VOID )
{
NTSTATUS status;
LSA_OBJECT_ATTRIBUTES objectAttributes;
memset(&objectAttributes, 0, sizeof(objectAttributes));
status = LsaOpenPolicy(
NULL,
&objectAttributes,
POLICY_LOOKUP_NAMES,
&hPolicy
);
return LsaNtStatusToWinError(status);
}
VOID
WINAPI
RadiusExtensionTerm( VOID )
{
LsaClose(hPolicy);
hPolicy = NULL;
}
DWORD
WINAPI
RadiusExtensionProcess2(
PRADIUS_EXTENSION_CONTROL_BLOCK pECB
)
{
PRADIUS_ATTRIBUTE_ARRAY pInAttrs;
const RADIUS_ATTRIBUTE* pAttr;
int nChar;
WCHAR wszUserName[UNLEN];
CHAR szUserName[UNLEN];
LSA_UNICODE_STRING lusName;
NTSTATUS status;
PLSA_REFERENCED_DOMAIN_LIST pReferencedDomains;
PLSA_TRANSLATED_SID pSids;
DWORD cbDataLength, dwResult;
RADIUS_ATTRIBUTE raNewName;
/* We only process authentication. */
if (pECB->repPoint != repAuthentication)
{
return NO_ERROR;
}
/* We only process Access-Requests. */
if (pECB->rcRequestType != rcAccessRequest)
{
return NO_ERROR;
}
/* Don't process if it's already been rejected. */
if (pECB->rcResponseType == rcAccessReject)
{
return NO_ERROR;
}
/* Get the attributes from the Access-Request. */
pInAttrs = pECB->GetRequest(pECB);
/* Only process Windows users */
pAttr = RadiusFindFirstAttribute(pInAttrs, ratProvider);
if ((pAttr == NULL) || (pAttr->dwValue != rapWindowsNT))
{
return NO_ERROR;
}
/* Retrieve the username. If it doesn't exist, there's nothing to do. */
pAttr = RadiusFindFirstAttribute(pInAttrs, ratUserName);
if (pAttr == NULL)
{
return NO_ERROR;
}
/* If the username already includes a domain name or if it's a user
* principal name (UPN), then we don't have to do anything. */
if (memchr(pAttr->lpValue, '\\', pAttr->cbDataLength) != NULL)
{
return NO_ERROR;
}
if (memchr(pAttr->lpValue, '@', pAttr->cbDataLength) != NULL)
{
return NO_ERROR;
}
/* Convert the username to Unicode. */
nChar = MultiByteToWideChar(
CP_ACP,
MB_ERR_INVALID_CHARS,
pAttr->lpValue,
pAttr->cbDataLength,
wszUserName,
UNLEN
);
if (nChar == 0)
{
return GetLastError();
}
/* Pack the username into an LSA_UNICODE_STRING struct. */
lusName.Length = nChar * sizeof(WCHAR);
lusName.MaximumLength = lusName.Length;
lusName.Buffer = wszUserName;
/* Lookup the name. */
status = LsaLookupNames(
hPolicy,
1,
&lusName,
&pReferencedDomains,
&pSids
);
if (!LSA_SUCCESS(status))
{
return LsaNtStatusToWinError(status);
}
/* Get the domain which has the user account */
lusName = pReferencedDomains->Domains[pSids[0].DomainIndex].Name;
/* Convert to an ANSI string. */
nChar = WideCharToMultiByte(
CP_ACP,
0,
lusName.Buffer,
(lusName.Length / sizeof(WCHAR)),
szUserName,
UNLEN,
NULL,
NULL
);
LsaFreeMemory(pReferencedDomains);
LsaFreeMemory(pSids);
if (nChar == 0)
{
return GetLastError();
}
/* New attribute is the domain plus delimiter plus username */
cbDataLength = nChar + 1 + pAttr->cbDataLength;
if (cbDataLength > UNLEN)
{
return ERROR_BAD_USERNAME;
}
/* Build the fully-qualified username. */
szUserName[nChar] = '\\';
memcpy(szUserName + nChar + 1, pAttr->lpValue, pAttr->cbDataLength);
/* Fill in the RADIUS_ATTRIBUTE struct. */
raNewName.fDataType = rdtString;
raNewName.cbDataLength = cbDataLength;
raNewName.lpValue = szUserName;
/* Add as both the ratStrippedUserName and the ratFQUserName. */
raNewName.dwAttrType = ratStrippedUserName;
dwResult = RadiusReplaceFirstAttribute(pInAttrs, &raNewName);
if (dwResult == NO_ERROR)
{
raNewName.dwAttrType = ratFQUserName;
dwResult = RadiusReplaceFirstAttribute(pInAttrs, &raNewName);
}
return dwResult;
}