|
|
/********************************************************************/ /** Copyright(c) 1989 Microsoft Corporation. **/ /********************************************************************/
//***
//
// Filename: srvrhlpr.c
//
// Description: This module will contain code to process specific security
// information requests by the server. This is done because
// the api's required to obtain this information cannot be
// called from kernel mode. The following functionality is
// supported:
//
// 1) Name to Sid Lookup.
// 2) Sid to Name lookup.
// 3) Enumerate Posix offsets of all domains.
// 4) Change password.
// 5) Log an event.
//
// History: August 18,1992. NarenG Created original version.
//
#include <afpsvcp.h>
#include <lm.h>
#include <logonmsv.h> // prototype of I_NetGetDCList
#include <seposix.h>
#include <dsgetdc.h>
static PPOLICY_ACCOUNT_DOMAIN_INFO pAccountDomainInfo = NULL; static PPOLICY_PRIMARY_DOMAIN_INFO pPrimaryDomainInfo = NULL;
static HANDLE hmutexThreadCount = NULL;
NTSTATUS AfpNameToSid( IN LSA_HANDLE hLsa, IN PAFP_FSD_CMD_PKT pAfpFsdCmd, OUT PAFP_FSD_CMD_PKT *ppAfpFsdCmdResponse, OUT LPDWORD pcbResponse );
NTSTATUS AfpSidToName( IN LSA_HANDLE hLsa, IN PPOLICY_ACCOUNT_DOMAIN_INFO pAccountDomainInfo, IN PPOLICY_PRIMARY_DOMAIN_INFO pPrimaryDomainInfo, IN PAFP_FSD_CMD_PKT pAfpFsdCmd, OUT PAFP_FSD_CMD_PKT *ppAfpFsdCmdResponse, OUT LPDWORD pcbResponse );
NTSTATUS AfpChangePassword( IN LSA_HANDLE hLsa, IN PPOLICY_ACCOUNT_DOMAIN_INFO pAccountDomainInfo, IN PPOLICY_PRIMARY_DOMAIN_INFO pPrimaryDomainInfo, IN PAFP_FSD_CMD_PKT pAfpFsdCmd, OUT PAFP_FSD_CMD_PKT *ppAfpFsdCmdResponse, OUT LPDWORD pcbResponse );
NTSTATUS AfpChangePasswordOnDomain( IN PAFP_PASSWORD_DESC pPassword, IN PUNICODE_STRING pDomainName, IN PSID pDomainSid );
NTSTATUS AfpCreateWellknownSids( OUT AFP_SID_OFFSET pWellKnownSids[] );
NTSTATUS AfpInsertSidOffset( IN PAFP_SID_OFFSET pSidOffset, IN LPBYTE pbVariableData, IN PSID pSid, IN DWORD Offset, IN AFP_SID_TYPE SidType );
DWORD AfpGetDomainInfo( IN LSA_HANDLE hLsa, IN OUT PLSA_HANDLE phLsaController, IN OUT PPOLICY_ACCOUNT_DOMAIN_INFO* ppAccountDomainInfo, IN OUT PPOLICY_PRIMARY_DOMAIN_INFO* ppPrimaryDomainInfo );
DWORD AfpIOCTLDomainOffsets( IN LSA_HANDLE hLsa, IN PPOLICY_ACCOUNT_DOMAIN_INFO pAccountDomainInfo, IN PPOLICY_PRIMARY_DOMAIN_INFO pPrimaryDomainInfo );
DWORD AfpOpenLsa( IN PUNICODE_STRING pSystem OPTIONAL, IN OUT PLSA_HANDLE phLsa );
NTSTATUS AfpChangePwdArapStyle( IN PAFP_PASSWORD_DESC pPassword, IN PUNICODE_STRING pDomainName, IN PSID pDomainSid );
//**
//
// Call: AfpServerHelper
//
// Returns: NO_ERROR
//
// Description: This is the main function for each helper thread. If sits
// in a loop processing commands from the server. It is terminated
// by command from the server.
//
DWORD AfpServerHelper( IN LPVOID fFirstThread ) { NTSTATUS ntStatus; DWORD dwRetCode; PAFP_FSD_CMD_PKT pAfpFsdCmdResponse; AFP_FSD_CMD_HEADER AfpCmdHeader; PAFP_FSD_CMD_PKT pAfpFsdCmd; PBYTE pOutputBuffer; DWORD cbOutputBuffer; PBYTE pInputBuffer; DWORD cbInputBuffer; IO_STATUS_BLOCK IoStatus; BYTE OutputBuffer[MAX_FSD_CMD_SIZE]; HANDLE hFSD = NULL; LSA_HANDLE hLsa = NULL; BOOLEAN fFirstLoop=TRUE;
// Open the AFP Server FSD and obtain a handle to it
//
if ( ( dwRetCode = AfpFSDOpen( &hFSD ) ) != NO_ERROR ) { AfpGlobals.dwSrvrHlprCode = dwRetCode; AfpLogEvent( AFPLOG_OPEN_FSD, 0, NULL, dwRetCode, EVENTLOG_ERROR_TYPE ); SetEvent( AfpGlobals.heventSrvrHlprThread ); return( dwRetCode ); }
// Open the Local LSA
//
if ( ( dwRetCode = AfpOpenLsa( NULL, &hLsa ) ) != NO_ERROR ) { AfpFSDClose( hFSD ); AfpGlobals.dwSrvrHlprCode = dwRetCode; AfpLogEvent( AFPLOG_OPEN_LSA, 0, NULL, dwRetCode, EVENTLOG_ERROR_TYPE ); SetEvent( AfpGlobals.heventSrvrHlprThread ); return( dwRetCode ); }
// If this is the first server helper thread then enumerate and
// IOCTL down the list of domains and their offsets.
//
if ( (BOOL)(ULONG_PTR)fFirstThread ) {
LSA_HANDLE hLsaController = NULL;
//
// Create the event object for mutual exclusion around the thread
// count
//
if ( (hmutexThreadCount = CreateMutex( NULL, FALSE, NULL ) ) == NULL) { AFP_PRINT( ( "SFMSVC: CreateMutex failed\n")); return( GetLastError() ); }
while (1) { // Get the account, primary and all trusted domain info
//
dwRetCode = AfpGetDomainInfo( hLsa, &hLsaController, &pAccountDomainInfo, &pPrimaryDomainInfo);
AfpGlobals.dwSrvrHlprCode = dwRetCode;
if (dwRetCode == NO_ERROR) { break; } else if (dwRetCode != ERROR_CANT_ACCESS_DOMAIN_INFO) { AFP_PRINT( ( "SFMSVC: Get Domain Info failed %ld\n",dwRetCode)); AfpLogEvent( AFPLOG_CANT_GET_DOMAIN_INFO, 0, NULL, dwRetCode, EVENTLOG_ERROR_TYPE ); AfpFSDClose( hFSD ); LsaClose( hLsa ); SetEvent( AfpGlobals.heventSrvrHlprThread ); return( dwRetCode ); }
// ok, we couldn't access domain info. keep retrying until we
// are successful (or until the service is stopped!)
AfpGlobals.dwServerState |= AFPSTATE_BLOCKED_ON_DOMINFO;
if (fFirstLoop) { fFirstLoop = FALSE;
AFP_PRINT( ( "SFMSVC: first loop, telling service controller to go ahead\n"));
// tell the service controller we're running, so the user
// doesn't have to wait as long as we're blocked here!
//
AfpGlobals.ServiceStatus.dwCurrentState = SERVICE_RUNNING; AfpGlobals.ServiceStatus.dwControlsAccepted = SERVICE_ACCEPT_STOP | SERVICE_ACCEPT_PAUSE_CONTINUE; AfpGlobals.ServiceStatus.dwCheckPoint = 0; AfpGlobals.ServiceStatus.dwWaitHint = 0;
AfpAnnounceServiceStatus();
// log an event to give the bad news..
AfpLogEvent( AFPLOG_DOMAIN_INFO_RETRY, 0, NULL, dwRetCode, EVENTLOG_WARNING_TYPE ); }
AFP_PRINT( ( "SFMSVC: sleeping 20 sec before retrying domain info\n"));
// wait for 20 seconds before retrying for the domain info
// Meanwhile, watch to see if the service is stopping. If so, we
// must do the necessary setevent and get out
if (WaitForSingleObject( AfpGlobals.heventSrvrHlprSpecial, 20000 ) == 0) { AfpFSDClose( hFSD ); LsaClose( hLsa ); SetEvent( AfpGlobals.heventSrvrHlprThread ); return( dwRetCode ); }
AFP_PRINT( ( "SFMSVC: retrying getdomain info\n")); }
// if we were blocked retrying the domain-info, log an event that we
// are ok now
if (AfpGlobals.dwServerState & AFPSTATE_BLOCKED_ON_DOMINFO) { AFP_PRINT( ( "SFMSVC: domain info stuff finally worked\n"));
AfpGlobals.dwServerState &= ~AFPSTATE_BLOCKED_ON_DOMINFO;
AfpLogEvent( AFPLOG_SFM_STARTED_OK, 0, NULL, 0, EVENTLOG_SUCCESS ); }
//
// IOCTL all the domain offsets
// if hLsaController is NULL, the server is in a WorkGroup, not domain
//
if ( ( dwRetCode = AfpIOCTLDomainOffsets( hLsaController, pAccountDomainInfo, pPrimaryDomainInfo) ) != NO_ERROR ) {
AFP_PRINT( ( "SFMSVC: Ioctl Domain Offsets failed.\n"));
AfpLogEvent( AFPLOG_CANT_INIT_DOMAIN_INFO, 0, NULL, dwRetCode, EVENTLOG_ERROR_TYPE );
// First clean up
//
AfpFSDClose( hFSD ); // If the local machine is not a controller
//
if ( (hLsaController != NULL) && (hLsa != hLsaController) ) { LsaClose( hLsaController ); }
LsaClose( hLsa );
if ( pAccountDomainInfo != NULL ) { LsaFreeMemory( pAccountDomainInfo ); }
if ( pPrimaryDomainInfo != NULL ) { LsaFreeMemory( pPrimaryDomainInfo ); }
AfpGlobals.dwSrvrHlprCode = dwRetCode; SetEvent( AfpGlobals.heventSrvrHlprThread );
return( dwRetCode ); }
// If the local machine is not a controller, then close the handle
// since we have all the information we need.
//
if ( (hLsaController != NULL) && (hLsa != hLsaController) ) { LsaClose( hLsaController ); }
}
// OK everything initialize OK. Tell parent (init) thread that it may
// continue.
//
AfpGlobals.dwSrvrHlprCode = dwRetCode; SetEvent( AfpGlobals.heventSrvrHlprThread );
WaitForSingleObject( hmutexThreadCount, INFINITE ); AfpGlobals.nThreadCount++; ReleaseMutex( hmutexThreadCount );
pOutputBuffer = OutputBuffer; cbOutputBuffer = sizeof( OutputBuffer ); pAfpFsdCmd = (PAFP_FSD_CMD_PKT)pOutputBuffer;
pInputBuffer = NULL; cbInputBuffer = 0; pAfpFsdCmdResponse = (PAFP_FSD_CMD_PKT)NULL;
while( TRUE ) {
// IOCTL the FSD
//
ntStatus = NtFsControlFile( hFSD, NULL, NULL, NULL, &IoStatus, OP_GET_FSD_COMMAND, pInputBuffer, cbInputBuffer, pOutputBuffer, cbOutputBuffer );
if (!NT_SUCCESS(ntStatus)) AFP_PRINT(("SFMSVC: NtFsControlFile Returned %lx\n", ntStatus));
ASSERT( NT_SUCCESS( ntStatus ));
// Free previous call's input buffer
//
if ( pAfpFsdCmdResponse != NULL ) LocalFree( pAfpFsdCmdResponse );
// Process the command
//
switch( pAfpFsdCmd->Header.FsdCommand ) { case AFP_FSD_CMD_NAME_TO_SID:
ntStatus = AfpNameToSid( hLsa, pAfpFsdCmd, &pAfpFsdCmdResponse, &cbInputBuffer );
if ( NT_SUCCESS( ntStatus )) pInputBuffer = (PBYTE)pAfpFsdCmdResponse; else { pInputBuffer = (PBYTE)&AfpCmdHeader; cbInputBuffer = sizeof( AFP_FSD_CMD_HEADER ); pAfpFsdCmdResponse = NULL; }
break;
case AFP_FSD_CMD_SID_TO_NAME:
ntStatus = AfpSidToName( hLsa, pAccountDomainInfo, pPrimaryDomainInfo, pAfpFsdCmd, &pAfpFsdCmdResponse, &cbInputBuffer );
if ( NT_SUCCESS( ntStatus )) pInputBuffer = (PBYTE)pAfpFsdCmdResponse; else { pInputBuffer = (PBYTE)&AfpCmdHeader; cbInputBuffer = sizeof( AFP_FSD_CMD_HEADER ); pAfpFsdCmdResponse = NULL; }
break;
case AFP_FSD_CMD_CHANGE_PASSWORD:
ntStatus = AfpChangePassword( hLsa, pAccountDomainInfo, pPrimaryDomainInfo, pAfpFsdCmd, &pAfpFsdCmdResponse, &cbInputBuffer );
pInputBuffer = (PBYTE)&AfpCmdHeader; cbInputBuffer = sizeof( AFP_FSD_CMD_HEADER ); pAfpFsdCmdResponse = NULL;
break;
case AFP_FSD_CMD_LOG_EVENT:
AfpLogServerEvent(pAfpFsdCmd);
pInputBuffer = (PBYTE)&AfpCmdHeader; cbInputBuffer = sizeof( AFP_FSD_CMD_HEADER ); pAfpFsdCmdResponse = NULL; ntStatus = STATUS_SUCCESS;
break;
case AFP_FSD_CMD_TERMINATE_THREAD:
// Do clean up
//
LsaClose( hLsa ); AfpFSDClose( hFSD );
WaitForSingleObject( hmutexThreadCount, INFINITE );
AfpGlobals.nThreadCount --; // This is the last thread so clean up all the global stuff.
//
if ( AfpGlobals.nThreadCount == 0 ) {
if ( pAccountDomainInfo != NULL ) { LsaFreeMemory( pAccountDomainInfo ); pAccountDomainInfo = NULL; }
if ( pPrimaryDomainInfo != NULL ) LsaFreeMemory( pPrimaryDomainInfo );
SetEvent(AfpGlobals.heventSrvrHlprThreadTerminate); }
ReleaseMutex( hmutexThreadCount );
return( NO_ERROR );
break;
default: ntStatus = STATUS_NOT_SUPPORTED; pInputBuffer = (PBYTE)&AfpCmdHeader; cbInputBuffer = sizeof( AFP_FSD_CMD_HEADER ); pAfpFsdCmdResponse = NULL; break;
}
CopyMemory( pInputBuffer, pAfpFsdCmd, sizeof( AFP_FSD_CMD_HEADER ) );
((PAFP_FSD_CMD_HEADER)pInputBuffer)->ntStatus = ntStatus; }
return( NO_ERROR ); }
//**
//
// Call: AfpGetDomainInfo
//
// Returns: LsaQueryInformationPolicy, I_NetGetDCList and AfpOpenLsa
//
// Description: Will retrieve information regarding the account, primary and
// trusted domains.
//
DWORD AfpGetDomainInfo( IN LSA_HANDLE hLsa, IN OUT PLSA_HANDLE phLsaController, IN OUT PPOLICY_ACCOUNT_DOMAIN_INFO* ppAccountDomainInfo, IN OUT PPOLICY_PRIMARY_DOMAIN_INFO* ppPrimaryDomainInfo ) { DWORD dwRetCode = 0; NTSTATUS ntStatus = STATUS_SUCCESS; LSA_ENUMERATION_HANDLE hLsaEnum = 0; LPWSTR DomainName = NULL; PDOMAIN_CONTROLLER_INFO pDCInfo = NULL; UNICODE_STRING DCName;
// This is not a loop.
//
do {
*phLsaController = NULL; *ppAccountDomainInfo = NULL; *ppPrimaryDomainInfo = NULL;
// Get the account domain
//
ntStatus = LsaQueryInformationPolicy( hLsa, PolicyAccountDomainInformation, (PVOID*)ppAccountDomainInfo );
if ( !NT_SUCCESS( ntStatus ) ) { AFP_PRINT( ( "SFMSVC: Lsa..Policy for Acct dom failed %lx\n",ntStatus)); break; }
// Get the primary domain
//
ntStatus = LsaQueryInformationPolicy( hLsa, PolicyPrimaryDomainInformation, (PVOID*)ppPrimaryDomainInfo ); if ( !NT_SUCCESS( ntStatus ) ) { AFP_PRINT( ( "SFMSVC: Lsa..Policy for Primary dom failed %lx\n",ntStatus)); break; }
// If this machine is part of a domain (not standalone), then we need
// to get a list of trusted domains. Note that a workstation and a
// member server can both join a domain, but they don't have to.
//
if ( (*ppPrimaryDomainInfo)->Sid != NULL ) {
// To obtain a list of trusted domains, we need to first open
// the LSA on a domain controller. If we are an PDC/BDC
// (NtProductLanManNt) then the local LSA will do, otherwise we need
// to search for domain controllers (NtProductServer, NtProductWinNt).
//
if ( AfpGlobals.NtProductType != NtProductLanManNt ) {
ULONG ulCount; ULONG ControllerCount = 0; PUNICODE_STRING ControllerNames = NULL; PUNICODE_STRING DomainController = NULL;
DomainName = (LPWSTR)LocalAlloc( LPTR, (*ppPrimaryDomainInfo)->Name.Length+sizeof(WCHAR));
if ( DomainName == NULL ) { dwRetCode = ERROR_NOT_ENOUGH_MEMORY; break; }
CopyMemory( DomainName, (*ppPrimaryDomainInfo)->Name.Buffer, (*ppPrimaryDomainInfo)->Name.Length );
DomainName[(*ppPrimaryDomainInfo)->Name.Length/sizeof(WCHAR)] = 0;
dwRetCode = DsGetDcName( NULL, (LPWSTR)DomainName, NULL, // domain
NULL, // site name
DS_DIRECTORY_SERVICE_PREFERRED, &pDCInfo);
if ( dwRetCode != NO_ERROR ) { AFP_PRINT( ( "SFMSVC: DsGetDcName failed 0x%lx\n",dwRetCode)); dwRetCode = ERROR_CANT_ACCESS_DOMAIN_INFO; break; }
AFP_PRINT(("SFMSVC: AfpOpenLsa on DC %ws for domain %ws\n", pDCInfo->DomainControllerName,DomainName));
RtlInitUnicodeString(&DCName, pDCInfo->DomainControllerName);
dwRetCode = AfpOpenLsa(&DCName, phLsaController );
//
// it's possible that this DC is down: force discovery
//
if (dwRetCode != NO_ERROR) {
AFP_PRINT(("SFMSVC: DC %ws unreachable, forcing discovery\n", pDCInfo->DomainControllerName));
NetApiBufferFree(pDCInfo);
pDCInfo = NULL;
dwRetCode = DsGetDcName( NULL, (LPWSTR)DomainName, NULL, NULL, (DS_DIRECTORY_SERVICE_PREFERRED | DS_FORCE_REDISCOVERY), &pDCInfo);
if ( dwRetCode != NO_ERROR ) { AFP_PRINT(("SFMSVC: second DsGetDcName failed %lx\n",dwRetCode)); dwRetCode = ERROR_CANT_ACCESS_DOMAIN_INFO; break; }
RtlInitUnicodeString(&DCName, pDCInfo->DomainControllerName);
dwRetCode = AfpOpenLsa(&DCName, phLsaController ); }
} else {
*phLsaController = hLsa;
// Since the local server is an PDC/BDC, it's account
// domain is the same as it's primary domain so set the
// account domain info to NULL
//
LsaFreeMemory( *ppAccountDomainInfo ); *ppAccountDomainInfo = NULL; }
} else { LsaFreeMemory( *ppPrimaryDomainInfo ); *ppPrimaryDomainInfo = NULL; }
} while( FALSE );
if (DomainName) { LocalFree( DomainName ); }
if (pDCInfo) { NetApiBufferFree(pDCInfo); }
if ( !NT_SUCCESS( ntStatus ) || ( dwRetCode != NO_ERROR ) ) { if ( *ppAccountDomainInfo != NULL ) { LsaFreeMemory( *ppAccountDomainInfo ); }
if ( *ppPrimaryDomainInfo != NULL ) { LsaFreeMemory( *ppPrimaryDomainInfo ); }
if ( *phLsaController != NULL ) { LsaClose( *phLsaController ); } if ( dwRetCode == NO_ERROR ) { dwRetCode = RtlNtStatusToDosError( ntStatus ); } }
return( dwRetCode );
}
//**
//
// Call: AfpOpenLsa
//
// Returns: Returns from LsaOpenPolicy.
//
// Description: The LSA will be opened.
//
DWORD AfpOpenLsa( IN PUNICODE_STRING pSystem OPTIONAL, IN OUT PLSA_HANDLE phLsa ) { SECURITY_QUALITY_OF_SERVICE QOS; OBJECT_ATTRIBUTES ObjectAttributes; NTSTATUS ntStatus;
// Open the LSA and obtain a handle to it.
//
QOS.Length = sizeof( QOS ); QOS.ImpersonationLevel = SecurityImpersonation; QOS.ContextTrackingMode = SECURITY_DYNAMIC_TRACKING; QOS.EffectiveOnly = FALSE;
InitializeObjectAttributes( &ObjectAttributes, NULL, 0L, NULL, NULL );
ObjectAttributes.SecurityQualityOfService = &QOS;
ntStatus = LsaOpenPolicy( pSystem, &ObjectAttributes, POLICY_VIEW_LOCAL_INFORMATION | POLICY_LOOKUP_NAMES, phLsa );
if ( !NT_SUCCESS( ntStatus )) { AFP_PRINT(("SFMSVC: AfpOpenLsa: LsaOpenPolicy failed %lx\n",ntStatus)); return( RtlNtStatusToDosError( ntStatus ) ); }
return( NO_ERROR ); }
//
// Call: AfpNameToSid
//
// Returns: NT_SUCCESS
// error return codes from LSA apis.
//
// Description: Will use LSA API's to translate a name to a SID. On a
// successful return, the pSid should be freed using LocalFree.
//
NTSTATUS AfpNameToSid( IN LSA_HANDLE hLsa, IN PAFP_FSD_CMD_PKT pAfpFsdCmd, OUT PAFP_FSD_CMD_PKT *ppAfpFsdCmdResponse, OUT LPDWORD pcbResponse ) { NTSTATUS ntStatus; UNICODE_STRING Name; PLSA_REFERENCED_DOMAIN_LIST pDomainList; PLSA_TRANSLATED_SID pSids; UCHAR AuthCount; PSID pDomainSid; PSID pSid;
// This do - while(FALSE) loop facilitates a single exit and clean-up point.
//
do {
*ppAfpFsdCmdResponse = NULL; pDomainList = NULL; pSids = NULL;
RtlInitUnicodeString( &Name, (LPWSTR)(pAfpFsdCmd->Data.Name) );
ntStatus = LsaLookupNames( hLsa, 1, &Name, &pDomainList, &pSids );
if ( !NT_SUCCESS( ntStatus ) ) return( ntStatus );
if ( pSids->Use == SidTypeDeletedAccount ){ ntStatus = STATUS_NO_SUCH_USER; break; }
if ( ( pDomainList->Entries == 0 ) || ( pSids->Use == SidTypeDomain ) || ( pSids->Use == SidTypeInvalid ) || ( pSids->Use == SidTypeUnknown ) || ( pSids->DomainIndex == -1 )) {
ntStatus = STATUS_NONE_MAPPED; break; }
pDomainSid = pDomainList->Domains[pSids->DomainIndex].Sid;
AuthCount = *RtlSubAuthorityCountSid( pDomainSid ) + 1;
*pcbResponse = sizeof(AFP_FSD_CMD_PKT)+RtlLengthRequiredSid(AuthCount);
*ppAfpFsdCmdResponse = (PAFP_FSD_CMD_PKT)LocalAlloc(LPTR,*pcbResponse); if ( *ppAfpFsdCmdResponse == NULL ) { ntStatus = STATUS_NO_MEMORY ; break; }
pSid = (*ppAfpFsdCmdResponse)->Data.Sid;
// Copy the Domain Sid.
//
RtlCopySid( RtlLengthRequiredSid(AuthCount), pSid, pDomainSid );
// Append the Relative Id.
//
*RtlSubAuthorityCountSid( pSid ) += 1; *RtlSubAuthoritySid( pSid, AuthCount - 1) = pSids->RelativeId;
} while( FALSE );
if ( (!NT_SUCCESS( ntStatus )) && ( *ppAfpFsdCmdResponse != NULL ) ) LocalFree( *ppAfpFsdCmdResponse );
if ( pSids != NULL ) LsaFreeMemory( pSids );
if ( pDomainList != NULL ) LsaFreeMemory( pDomainList );
return( ntStatus ); }
//**
//
// Call: AfpSidToName
//
// Returns: NT_SUCCESS
// error return codes from LSA apis.
//
// Description: Given a SID, this routine will find the corresponding
// UNICODE name.
//
NTSTATUS AfpSidToName( IN LSA_HANDLE hLsa, IN PPOLICY_ACCOUNT_DOMAIN_INFO pAccountDomainInfo, IN PPOLICY_PRIMARY_DOMAIN_INFO pPrimaryDomainInfo, IN PAFP_FSD_CMD_PKT pAfpFsdCmd, OUT PAFP_FSD_CMD_PKT *ppAfpFsdCmdResponse, OUT LPDWORD pcbResponse ) { NTSTATUS ntStatus; PLSA_REFERENCED_DOMAIN_LIST pDomainList = NULL; PLSA_TRANSLATED_NAME pNames = NULL; PSID pSid = (PSID)&(pAfpFsdCmd->Data.Sid); WCHAR * pWchar; BOOL fDoNotCopyDomainName = TRUE; DWORD cbResponse; DWORD dwUse; SID AfpBuiltInSid = { 1, 1, SECURITY_NT_AUTHORITY, SECURITY_BUILTIN_DOMAIN_RID };
do {
*ppAfpFsdCmdResponse = NULL;
ntStatus = LsaLookupSids( hLsa, 1, &pSid, &pDomainList, &pNames );
if ( !NT_SUCCESS( ntStatus ) ) { if ( ntStatus == STATUS_NONE_MAPPED ) {
dwUse = SidTypeUnknown; ntStatus = STATUS_SUCCESS; } else break; } else dwUse = pNames->Use;
cbResponse = sizeof( AFP_FSD_CMD_PKT );
switch( dwUse ){
case SidTypeInvalid: cbResponse += ((wcslen(AfpGlobals.wchInvalid)+1) * sizeof(WCHAR)); break;
case SidTypeDeletedAccount: cbResponse += ((wcslen(AfpGlobals.wchDeleted)+1) * sizeof(WCHAR)); break;
case SidTypeUnknown: cbResponse += ((wcslen(AfpGlobals.wchUnknown)+1) * sizeof(WCHAR)); break;
case SidTypeWellKnownGroup: cbResponse += ( pNames->Name.Length + sizeof(WCHAR) ); break;
case SidTypeDomain: cbResponse += ( pDomainList->Domains->Name.Length + sizeof(WCHAR) ); break;
default:
if ((pNames->DomainIndex == -1) || (pNames->Name.Buffer == NULL)){ ntStatus = STATUS_NONE_MAPPED; break; }
// Do not copy the domain name if the name is either a well known
// group or if the SID belongs to the ACCOUNT or BUILTIN domains.
// Note, the pAccountDomainInfo is NULL is this is an advanced
// server, in that case we check to see if the domain name is
// the primary domain name.
//
if (( RtlEqualSid( &AfpBuiltInSid, pDomainList->Domains->Sid )) || (( pAccountDomainInfo != NULL ) && (RtlEqualUnicodeString( &(pAccountDomainInfo->DomainName), &(pDomainList->Domains->Name), TRUE ))) || ((pAccountDomainInfo == NULL) && (pPrimaryDomainInfo != NULL) && (RtlEqualUnicodeString( &(pPrimaryDomainInfo->Name), &(pDomainList->Domains->Name), TRUE )))){ cbResponse += ( pNames->Name.Length + sizeof(WCHAR) );
fDoNotCopyDomainName = TRUE; } else {
fDoNotCopyDomainName = FALSE;
cbResponse += ( pDomainList->Domains->Name.Length + sizeof(TEXT('\\')) + pNames->Name.Length + sizeof(WCHAR) ); } }
if ( !NT_SUCCESS( ntStatus ) ) break;
*pcbResponse = cbResponse;
*ppAfpFsdCmdResponse = (PAFP_FSD_CMD_PKT)LocalAlloc(LPTR,cbResponse);
if ( *ppAfpFsdCmdResponse == NULL ){ ntStatus = STATUS_NO_MEMORY ; break; }
pWchar = (WCHAR*)((*ppAfpFsdCmdResponse)->Data.Name);
switch( dwUse ){
case SidTypeInvalid: wcscpy( pWchar, AfpGlobals.wchInvalid ); break;
case SidTypeDeletedAccount: wcscpy( pWchar, AfpGlobals.wchDeleted ); break;
case SidTypeUnknown: wcscpy( pWchar, AfpGlobals.wchUnknown ); break;
case SidTypeWellKnownGroup: CopyMemory( pWchar, pNames->Name.Buffer, pNames->Name.Length ); break;
case SidTypeDomain: CopyMemory( pWchar, pDomainList->Domains->Name.Buffer, pDomainList->Domains->Name.Length ); break;
default:
if ( !fDoNotCopyDomainName ) {
CopyMemory( pWchar, pDomainList->Domains->Name.Buffer, pDomainList->Domains->Name.Length );
pWchar += wcslen( pWchar );
CopyMemory( pWchar, TEXT("\\"), sizeof(TEXT("\\")) );
pWchar += wcslen( pWchar ); }
CopyMemory( pWchar, pNames->Name.Buffer, pNames->Name.Length ); } } while( FALSE );
if ( (!NT_SUCCESS( ntStatus )) && ( *ppAfpFsdCmdResponse != NULL ) ) LocalFree( *ppAfpFsdCmdResponse );
if ( pNames != NULL ) LsaFreeMemory( pNames );
if ( pDomainList != NULL ) LsaFreeMemory( pDomainList );
return( ntStatus ); }
//**
//
// Call: AfpChangePassword
//
// Returns: NT_SUCCESS
// error return codes from LSA apis.
//
// Description: Given the AFP_PASSWORD_DESC data structure, this procedure
// will change the password of a given user.
// If the passwords are supplied in clear text, then it calculate
// the OWF's (encrypt OWF = One Way Function) them.
// If the domain name that the user
// belongs to is not supplied then a list of domains are tried
// in sequence. The sequence is 1) ACCOUNT domain
// 2) PRIMARY domain
// 3) All trusted domains.
//
NTSTATUS AfpChangePassword( IN LSA_HANDLE hLsa, IN PPOLICY_ACCOUNT_DOMAIN_INFO pAccountDomainInfo, IN PPOLICY_PRIMARY_DOMAIN_INFO pPrimaryDomainInfo, IN PAFP_FSD_CMD_PKT pAfpFsdCmd, OUT PAFP_FSD_CMD_PKT *ppAfpFsdCmdResponse, OUT LPDWORD pcbResponse ) {
PAFP_PASSWORD_DESC pPassword = &(pAfpFsdCmd->Data.Password); NTSTATUS ntStatus=STATUS_SUCCESS; PSID pDomainSid; UNICODE_STRING TargetDomainName; WCHAR RefDomainName[DNLEN+1]; DWORD cbRefDomainNameLen; DWORD cbSidLen; PSID pUserSid=NULL; PLSA_TRANSLATED_SID pTransSids; SID_NAME_USE peUse; PLSA_REFERENCED_DOMAIN_LIST pDomainList=NULL; DWORD dwRetCode;
AFP_PRINT(("SFMSVC: entered AfpChangePassword for user %ws\n",(LPWSTR)pPassword->UserName));
do {
//
// Was the domain on which the account name exists specified ??
//
if ( pPassword->DomainName[0] != TEXT('\0') ) { RtlInitUnicodeString(&TargetDomainName, (LPWSTR)pPassword->DomainName); }
//
// hmmm, no domain name. We must first find which domain this user belongs to
//
else { cbRefDomainNameLen = DNLEN+1;
cbSidLen = 100;
do { dwRetCode = ERROR_SUCCESS; if (pUserSid) { LocalFree(pUserSid); }
pUserSid = (PSID)LocalAlloc(LPTR, cbSidLen);
if (pUserSid == NULL) { dwRetCode = ERROR_NO_SYSTEM_RESOURCES; break; }
if (!LookupAccountName( NULL, (LPWSTR)pPassword->UserName, pUserSid, &cbSidLen, RefDomainName, &cbRefDomainNameLen, &peUse)) { ntStatus = (NTSTATUS)GetLastError(); }
AFP_PRINT(("SFMSVC: LookupAccountName in loop: %d\n",GetLastError()));
} while ( dwRetCode == ERROR_INSUFFICIENT_BUFFER );
if (dwRetCode != ERROR_SUCCESS) { AFP_PRINT(("SFMSVC: LookupAccountName on %ws failed with %ld\n",(LPWSTR)pPassword->UserName,dwRetCode)); ntStatus = (NTSTATUS)dwRetCode; break; }
LocalFree(pUserSid);
RtlInitUnicodeString(&TargetDomainName, RefDomainName); }
AFP_PRINT(("SFMSVC: changing pwd for user %ws, domain %ws\n", (LPWSTR)pPassword->UserName,TargetDomainName.Buffer));
//
// now, we must find the sid for this domain
//
ntStatus = LsaLookupNames(hLsa, 1, &TargetDomainName, &pDomainList, &pTransSids);
if (!NT_SUCCESS(ntStatus)) { AFP_PRINT(("SFMSVC: LsaLookupNames failed %lx\n",ntStatus)); break; }
if ((pDomainList->Entries == 0) || (pTransSids->DomainIndex == -1) || (pTransSids->Use != SidTypeDomain) || (pTransSids->Use == SidTypeInvalid) || (pTransSids->Use == SidTypeUnknown)) { AFP_PRINT(("SFMSVC: invalide type? Entries = %d, DomIndex = %d, Use = %d\n", pDomainList->Entries,pTransSids->DomainIndex,pTransSids->Use)); ntStatus = STATUS_NONE_MAPPED; break; }
pDomainSid = pDomainList->Domains[pTransSids->DomainIndex].Sid;
//
// call our function to change the password
//
ntStatus = AfpChangePasswordOnDomain( pPassword, &TargetDomainName, pDomainSid );
AFP_PRINT(("SFMSVC: AfpChangePasswordOnDomain returned %lx\n",ntStatus));
} while ( FALSE );
if (pDomainList) { LsaFreeMemory( pDomainList ); }
return( ntStatus ); }
//**
//
// Call: AfpChangePasswordOnDomain
//
// Returns: NT_SUCCESS
// STATUS_NONE_MAPPED - If the user account does not
// exist in the specified domain.
// error return codes from LSA apis.
//
// Description: This procedure will try to change the user's password on a
// specified domain. It is assumed that this procedure will be
// called with either the pDomainName pointing to the domain, or
// the pPassword->DomainName field containing the domain.
//
NTSTATUS AfpChangePasswordOnDomain( IN PAFP_PASSWORD_DESC pPassword, IN PUNICODE_STRING pDomainName, IN PSID pDomainSid ) {
LPWSTR DCName = (LPWSTR)NULL; SAM_HANDLE hServer = (SAM_HANDLE)NULL; SAM_HANDLE hDomain = (SAM_HANDLE)NULL; SAM_HANDLE hUser = (SAM_HANDLE)NULL; PULONG pUserId = (PULONG)NULL; PSID_NAME_USE pUse = (PSID_NAME_USE)NULL; OBJECT_ATTRIBUTES ObjectAttributes; UNICODE_STRING UserName; ANSI_STRING AOldPassword; UNICODE_STRING UOldPassword; ANSI_STRING ANewPassword; UNICODE_STRING UNewPassword; POEM_STRING pOemSrvName; OEM_STRING OemServerName; OEM_STRING OemUserName; SECURITY_QUALITY_OF_SERVICE QOS; PPOLICY_ACCOUNT_DOMAIN_INFO pDomainInfo = NULL; NTSTATUS ntStatus; UNICODE_STRING PDCServerName; PUNICODE_STRING pPDCServerName = &PDCServerName; PDOMAIN_PASSWORD_INFORMATION pPasswordInfo = NULL; BYTE EncryptedPassword[LM_OWF_PASSWORD_LENGTH]; WCHAR wchDomain[DNLEN+1]; PDOMAIN_CONTROLLER_INFO pDCInfo = NULL; PUSER_INFO_1 pUserInfo = NULL; DWORD dwRetCode;
if ((pPassword->AuthentMode == RANDNUM_EXCHANGE) || (pPassword->AuthentMode == TWOWAY_EXCHANGE)) { AFP_PRINT(("SFMSVC: Entering AfpChangePwdArapStyle for RANDNUM_EXCHANGE || TWOWAY_EXCHANGE\n")); ntStatus = AfpChangePwdArapStyle(pPassword, pDomainName, pDomainSid); AFP_PRINT(("SFMSVC: Returned from AfpChangePwdArapStyle with error %lx\n", ntStatus)); return(ntStatus); }
OemServerName.Buffer = NULL; OemUserName.Buffer = NULL;
InitializeObjectAttributes( &ObjectAttributes, NULL, 0L, NULL, NULL );
QOS.Length = sizeof( QOS ); QOS.ImpersonationLevel = SecurityImpersonation; QOS.ContextTrackingMode = SECURITY_DYNAMIC_TRACKING; QOS.EffectiveOnly = FALSE;
ObjectAttributes.SecurityQualityOfService = &QOS;
// If the domain was not the account domain then we try to get the
// primary domain controller for the domain.
//
if ((pDomainName != NULL) && (pAccountDomainInfo != NULL) && !(RtlEqualUnicodeString( &(pAccountDomainInfo->DomainName),pDomainName, TRUE))) { ZeroMemory( wchDomain, sizeof( wchDomain ) );
CopyMemory( wchDomain, pDomainName->Buffer, pDomainName->Length );
// Get the PDC for the domain if this is not the account domain
//
dwRetCode = DsGetDcName( NULL, wchDomain, NULL, NULL, (DS_DIRECTORY_SERVICE_PREFERRED | DS_WRITABLE_REQUIRED), &pDCInfo);
if ( dwRetCode != NO_ERROR ) { AFP_PRINT (("SFMSVC: AfpChange... DsGetDcName failed %lx\n",dwRetCode)); return( STATUS_CANT_ACCESS_DOMAIN_INFO ); }
RtlInitUnicodeString(pPDCServerName, pDCInfo->DomainControllerName);
DCName = pDCInfo->DomainControllerName; } else { pPDCServerName = NULL;
DCName = NULL; }
do { //
// first and foremost: make sure this user can actually change pwd
//
if ((ntStatus= NetUserGetInfo( (LPWSTR)DCName, pPassword->UserName, 1, (LPBYTE*)&pUserInfo )) == NO_ERROR ) {
if ( ( pUserInfo->usri1_flags & UF_PASSWD_CANT_CHANGE ) || ( pUserInfo->usri1_flags & UF_LOCKOUT ) ) { AFP_PRINT(("SFMSVC: can't change pwd: %s\n", (pUserInfo->usri1_flags & UF_LOCKOUT) ? "account is locked out" : "user not allowed to change pwd"));
ntStatus = STATUS_ACCESS_DENIED; break; } else if ( pUserInfo->usri1_flags & UF_ACCOUNTDISABLE ) { AFP_PRINT(("SFMSVC: can't change pwd: user account is disabled\n")); ntStatus = STATUS_ACCOUNT_DISABLED; break; } } else { AFP_PRINT(("SFMSVC: can't change pwd: NetUserGetInfo failed\n"));
if (ntStatus == ERROR_ACCESS_DENIED) { ntStatus = STATUS_SUCCESS; } else { ntStatus = STATUS_PASSWORD_RESTRICTION; break; } }
//
// if this is a password change request coming from MSUAM Version 2,
// then we are getting passwords (and not OWFs) encrypted. Use a
// different scheme of changing password
//
if (pPassword->AuthentMode == CUSTOM_UAM_V2) { OemServerName.MaximumLength = OemServerName.Length = 0; OemUserName.MaximumLength = OemUserName.Length = 0;
RtlInitUnicodeString( &UserName, pPassword->UserName );
if (pPDCServerName) { ntStatus = RtlUnicodeStringToOemString( &OemServerName, pPDCServerName, TRUE // allocate buffer
); if (!NT_SUCCESS(ntStatus)) { AFP_PRINT(("SFMSVC: 1st Rtl..OemString failed %lx\n",ntStatus)); break; }
pOemSrvName = &OemServerName; } else { pOemSrvName = NULL; }
ntStatus = RtlUnicodeStringToOemString( &OemUserName, &UserName, TRUE // allocate buffer
); if (!NT_SUCCESS(ntStatus)) { AFP_PRINT(("SFMSVC: 2nd Rtl..OemString failed %lx\n",ntStatus)); break; }
ntStatus = SamiOemChangePasswordUser2( pOemSrvName, &OemUserName, (PSAMPR_ENCRYPTED_USER_PASSWORD)pPassword->NewPassword, (PENCRYPTED_LM_OWF_PASSWORD)pPassword->OldPassword);
AFP_PRINT(("SFMSVC: change pwd for MSUAM V2.0 user done, status = %lx\n",ntStatus));
// done here
break; }
AFP_PRINT(("SFMSVC: AuthMode != MSUAM\n"));
// Connect to the PDC of that domain
//
ntStatus = SamConnect( pPDCServerName, &hServer, SAM_SERVER_EXECUTE, &ObjectAttributes);
if ( !NT_SUCCESS( ntStatus )) { AFP_PRINT(("SFMSVC: SamConnect to %ws failed %lx\n", (pPDCServerName)?pPDCServerName->Buffer:L"LOCAL",ntStatus)); break; }
// Get Sid of Domain and open the domain
//
ntStatus = SamOpenDomain( hServer, DOMAIN_EXECUTE, pDomainSid, &hDomain );
if ( !NT_SUCCESS( ntStatus )) { AFP_PRINT(("SFMSVC: SamOpenDomain failed %lx\n",ntStatus)); break; }
// Get this user's ID
//
RtlInitUnicodeString( &UserName, pPassword->UserName );
ntStatus = SamLookupNamesInDomain( hDomain, 1, &UserName, &pUserId, &pUse );
if ( !NT_SUCCESS( ntStatus )) { AFP_PRINT(("SFMSVC: SamLookupNamesInDomain failed %lx\n",ntStatus)); break; }
// Open the user account for this user
//
ntStatus = SamOpenUser( hDomain, USER_CHANGE_PASSWORD, *pUserId, &hUser );
if ( !NT_SUCCESS( ntStatus )) { AFP_PRINT(("SFMSVC: SamOpenUser failed %lx\n",ntStatus)); break; } // First get the minimum password length requred
//
ntStatus = SamQueryInformationDomain( hDomain, DomainPasswordInformation, &pPasswordInfo );
if ( !NT_SUCCESS( ntStatus ) ) { AFP_PRINT(("SFMSVC: SamQueryInformationDomain failed %lx\n",ntStatus)); break; }
// First we check to see if the passwords passed are in cleartext.
// If they are, we need to calculate the OWF's for them.
// (OWF = "One Way Function")
//
if ( pPassword->AuthentMode == CLEAR_TEXT_AUTHENT ) { AFP_PRINT(("SFMSVC: AuthentMode == CLEAR_TEXT_AUTHENT\n"));
// First check to see if the new password is long enough
//
if ( strlen( pPassword->NewPassword ) < pPasswordInfo->MinPasswordLength ) { ntStatus = STATUS_PWD_TOO_SHORT; break; }
RtlInitAnsiString( &AOldPassword, pPassword->OldPassword );
RtlInitAnsiString( &ANewPassword, pPassword->NewPassword );
RtlInitUnicodeString( &UserName, pPassword->UserName ); if ((ntStatus = RtlAnsiStringToUnicodeString( &UOldPassword, &AOldPassword, TRUE )) != STATUS_SUCCESS) { AFP_PRINT(("SFMSVC: RtlAnsiStringToUnicodeString: UOldPassword failed with error %lx]n", ntStatus)); break; } if ((ntStatus = RtlAnsiStringToUnicodeString( &UNewPassword, &ANewPassword, TRUE )) != STATUS_SUCCESS) { AFP_PRINT(("SFMSVC: RtlAnsiStringToUnicodeString: UNewPassword failed with error %lx]n", ntStatus)); RtlFreeUnicodeString (&UOldPassword); break; } AFP_PRINT(("SFMSVC: Calling SamChangePasswordUser2 \n")); // Change the password for this user
//
ntStatus = SamChangePasswordUser2 ( pPDCServerName, &UserName, &UOldPassword, &UNewPassword ); AFP_PRINT(("SFMSVC: SamChangePasswordUser2 returned %lx\n", ntStatus)); RtlFreeUnicodeString (&UOldPassword); RtlFreeUnicodeString (&UNewPassword); break; } else {
if (pPassword->bPasswordLength < pPasswordInfo->MinPasswordLength) { AFP_PRINT(("SFMSVC: AfpChangePasswordOnDomain: pwd is too short\n")); ntStatus = STATUS_PWD_TOO_SHORT; break; } }
AFP_PRINT(("SFMSVC: Invalid UAM type\n")); ntStatus = STATUS_INVALID_PARAMETER; break;
} while( FALSE );
if ( pUserInfo != NULL ) { NetApiBufferFree( pUserInfo ); }
if ( hServer != (SAM_HANDLE)NULL ) { SamCloseHandle( hServer ); }
if ( hDomain != (SAM_HANDLE)NULL ) { SamCloseHandle( hDomain ); }
if ( hUser != (SAM_HANDLE)NULL ) { SamCloseHandle( hUser ); } if ( pDomainInfo != NULL ) { LsaFreeMemory( pDomainInfo ); }
if ( pUserId != (PULONG)NULL ) { SamFreeMemory( pUserId ); }
if ( pUse != (PSID_NAME_USE)NULL ) { SamFreeMemory( pUse ); }
if ( pPasswordInfo != (PDOMAIN_PASSWORD_INFORMATION)NULL ) { SamFreeMemory( pPasswordInfo ); }
if (pDCInfo) { NetApiBufferFree(pDCInfo); }
if (OemServerName.Buffer) { RtlFreeAnsiString(&OemServerName); }
if (OemUserName.Buffer) { RtlFreeAnsiString(&OemUserName); }
return( ntStatus ); }
//**
//
// Call: AfpIOCTLDomainOffsets
//
// Returns: NT_SUCCESS
// error return codes from LSA apis.
//
// Description: Will IOCTL a list of SIDs and corresponding POSIX offsets
// of all trusted domains and other well known domains.
//
//
DWORD AfpIOCTLDomainOffsets( IN LSA_HANDLE hLsa, IN PPOLICY_ACCOUNT_DOMAIN_INFO pAccountDomainInfo, IN PPOLICY_PRIMARY_DOMAIN_INFO pPrimaryDomainInfo ) { NTSTATUS ntStatus; LSA_HANDLE hLsaDomain; PTRUSTED_POSIX_OFFSET_INFO pPosixOffset; PAFP_SID_OFFSET pSidOffset; ULONG cbSids; PBYTE pbVariableData; AFP_SID_OFFSET pWellKnownSids[20]; DWORD dwIndex; DWORD dwCount; AFP_REQUEST_PACKET AfpRequestPkt; PAFP_SID_OFFSET_DESC pAfpSidOffsets = NULL; DWORD cbSidOffsets; DWORD dwRetCode;
// Null this array out.
//
ZeroMemory( pWellKnownSids, sizeof(AFP_SID_OFFSET)*20 );
// This is a dummy loop used only so that the break statement may
// be used to localize all the clean up in one place.
//
do {
// Create all the well known SIDs
//
ntStatus = AfpCreateWellknownSids( pWellKnownSids );
if ( !NT_SUCCESS( ntStatus ) ) { break; }
// Add the size of the all the well known SIDS
//
for( dwCount = 0, cbSids = 0; pWellKnownSids[dwCount].pSid != (PBYTE)NULL; dwCount++ ) { cbSids += RtlLengthSid( (PSID)(pWellKnownSids[dwCount].pSid) ); }
// Insert the SID of the Account domain if is is not an advanced server
//
if ( pAccountDomainInfo != NULL ) { cbSids += RtlLengthSid( pAccountDomainInfo->DomainSid ); dwCount++; }
// Add the primary domain Sids only if this machine
// is a member of a domain.
//
if ( pPrimaryDomainInfo != NULL ) { cbSids += RtlLengthSid( pPrimaryDomainInfo->Sid ); dwCount++; }
// OK, now allocate space for all these SIDS plus their offsets
//
cbSidOffsets = (dwCount * sizeof(AFP_SID_OFFSET)) + cbSids + (sizeof(AFP_SID_OFFSET_DESC) - sizeof(AFP_SID_OFFSET));
pAfpSidOffsets = (PAFP_SID_OFFSET_DESC)LocalAlloc( LPTR, cbSidOffsets );
if ( pAfpSidOffsets == NULL ) { ntStatus = STATUS_NO_MEMORY ; break; }
// First insert all the well known SIDS
//
for( dwIndex = 0, pAfpSidOffsets->CountOfSidOffsets = dwCount, pSidOffset = pAfpSidOffsets->SidOffsetPairs, pbVariableData = (LPBYTE)pAfpSidOffsets + cbSidOffsets;
pWellKnownSids[dwIndex].pSid != (PBYTE)NULL;
dwIndex++ ) {
pbVariableData-=RtlLengthSid((PSID)(pWellKnownSids[dwIndex].pSid));
ntStatus = AfpInsertSidOffset( pSidOffset++, pbVariableData, (PSID)(pWellKnownSids[dwIndex].pSid), pWellKnownSids[dwIndex].Offset, pWellKnownSids[dwIndex].SidType );
if ( !NT_SUCCESS( ntStatus ) ) { break; } }
if ( !NT_SUCCESS( ntStatus ) ) { break; }
// Now insert the Account domain's SID/OFFSET pair if there is one
//
if ( pAccountDomainInfo != NULL ) { pbVariableData -= RtlLengthSid( pAccountDomainInfo->DomainSid );
ntStatus = AfpInsertSidOffset( pSidOffset++, pbVariableData, pAccountDomainInfo->DomainSid, SE_ACCOUNT_DOMAIN_POSIX_OFFSET, AFP_SID_TYPE_DOMAIN );
if ( !NT_SUCCESS( ntStatus ) ) { break; }
// Construct the "None" sid if we are a standalone server (i.e. not
// a PDC or BDC). This will be used when querying the group ID of
// a directory so the the UI will never show this group to the user.
//
if ( AfpGlobals.NtProductType != NtProductLanManNt ) { ULONG SubAuthCount, SizeNoneSid = 0;
SubAuthCount = *RtlSubAuthorityCountSid(pAccountDomainInfo->DomainSid);
SizeNoneSid = RtlLengthRequiredSid(SubAuthCount + 1);
if ((AfpGlobals.pSidNone = (PSID)LocalAlloc(LPTR,SizeNoneSid)) == NULL) { ntStatus = STATUS_INSUFFICIENT_RESOURCES; break; }
RtlCopySid(SizeNoneSid, AfpGlobals.pSidNone, pAccountDomainInfo->DomainSid);
// Add the relative ID
*RtlSubAuthorityCountSid(AfpGlobals.pSidNone) = (UCHAR)(SubAuthCount+1);
// Note that the "None" sid on standalone is the same as the
// "Domain Users" Sid on PDC/BDC. (On PDC/BDC the primary
// domain is the same as the account domain).
*RtlSubAuthoritySid(AfpGlobals.pSidNone, SubAuthCount) = DOMAIN_GROUP_RID_USERS;
}
}
// Now insert the primary domain if this machine is a member of a domain
//
if ( pPrimaryDomainInfo != NULL ) {
// Insert the primary domain's SID/OFFSET pair
//
pbVariableData -= RtlLengthSid( pPrimaryDomainInfo->Sid );
ntStatus = AfpInsertSidOffset( pSidOffset++, pbVariableData, pPrimaryDomainInfo->Sid, SE_PRIMARY_DOMAIN_POSIX_OFFSET, AFP_SID_TYPE_PRIMARY_DOMAIN );
if ( !NT_SUCCESS( ntStatus ) ) { break; } }
} while( FALSE );
// IOCTL down the information if all was OK
//
if ( NT_SUCCESS( ntStatus ) ) { AfpRequestPkt.dwRequestCode = OP_SERVER_ADD_SID_OFFSETS; AfpRequestPkt.dwApiType = AFP_API_TYPE_ADD; AfpRequestPkt.Type.SetInfo.pInputBuf = pAfpSidOffsets; AfpRequestPkt.Type.Add.cbInputBufSize = cbSidOffsets;
dwRetCode = AfpServerIOCtrl( &AfpRequestPkt ); } else { dwRetCode = RtlNtStatusToDosError( ntStatus ); }
if ( pAfpSidOffsets != NULL ) { LocalFree( pAfpSidOffsets ); }
// Free all the well known SIDS
//
for( dwIndex = 0; pWellKnownSids[dwIndex].pSid != (PBYTE)NULL; dwIndex++ ) { RtlFreeSid( (PSID)(pWellKnownSids[dwIndex].pSid) ); }
return( dwRetCode );
}
//**
//
// Call: AfpInsertSidOffset
//
// Returns: NT_SUCCESS
// error return codes from RtlCopySid
//
// Description: Will insert a SID/OFFSET pair in the slot pointed to by
// pSidOffset. The pbVariableData will point to where the
// SID will be stored.
//
NTSTATUS AfpInsertSidOffset( IN PAFP_SID_OFFSET pSidOffset, IN LPBYTE pbVariableData, IN PSID pSid, IN DWORD Offset, IN AFP_SID_TYPE afpSidType ) { NTSTATUS ntStatus;
// Copy the offset
//
pSidOffset->Offset = Offset;
// Set the SID type
//
pSidOffset->SidType = afpSidType;
// Copy Sid at the end of the buffer and set the offset to it
//
ntStatus = RtlCopySid( RtlLengthSid( pSid ), pbVariableData, pSid );
if ( !NT_SUCCESS( ntStatus ) ) return( ntStatus );
pSidOffset->pSid = pbVariableData;
POINTER_TO_OFFSET( (pSidOffset->pSid), pSidOffset );
return( STATUS_SUCCESS );
}
//**
//
// Call: AfpCreateWellknownSids
//
// Returns: NT_SUCCESS
// STATUS_NO_MEMORY
// non-zero returns from RtlAllocateAndInitializeSid
//
// Description: Will allocate and initialize all well known SIDs.
// The array is terminated by a NULL pointer.
//
NTSTATUS AfpCreateWellknownSids( OUT AFP_SID_OFFSET pWellKnownSids[] ) { PSID pSid; DWORD dwIndex = 0; NTSTATUS ntStatus; SID_IDENTIFIER_AUTHORITY NullSidAuthority = SECURITY_NULL_SID_AUTHORITY; SID_IDENTIFIER_AUTHORITY WorldSidAuthority = SECURITY_WORLD_SID_AUTHORITY; SID_IDENTIFIER_AUTHORITY LocalSidAuthority = SECURITY_LOCAL_SID_AUTHORITY; SID_IDENTIFIER_AUTHORITY CreatorSidAuthority= SECURITY_CREATOR_SID_AUTHORITY; SID_IDENTIFIER_AUTHORITY NtAuthority = SECURITY_NT_AUTHORITY;
do {
//
// OK, create all the well known SIDS
//
// Create NULL SID
//
ntStatus = RtlAllocateAndInitializeSid( &NullSidAuthority, 1, SECURITY_NULL_RID, 0,0,0,0,0,0,0, &pSid ); if ( !NT_SUCCESS( ntStatus ) ) break;
pWellKnownSids[dwIndex].pSid = (PBYTE)pSid; pWellKnownSids[dwIndex].Offset = SE_NULL_POSIX_ID; pWellKnownSids[dwIndex].SidType = AFP_SID_TYPE_WELL_KNOWN; dwIndex++;
// Create WORLD SID
//
ntStatus = RtlAllocateAndInitializeSid( &WorldSidAuthority, 1, SECURITY_WORLD_RID, 0,0,0,0,0,0,0, &pSid ); if ( !NT_SUCCESS( ntStatus ) ) break;
pWellKnownSids[dwIndex].pSid = (PBYTE)pSid; pWellKnownSids[dwIndex].Offset = SE_WORLD_POSIX_ID; pWellKnownSids[dwIndex].SidType = AFP_SID_TYPE_WELL_KNOWN; dwIndex++;
// Create LOCAL SID
//
ntStatus = RtlAllocateAndInitializeSid( &LocalSidAuthority, 1, SECURITY_LOCAL_RID, 0,0,0,0,0,0,0, &pSid ); if ( !NT_SUCCESS( ntStatus ) ) break;
pWellKnownSids[dwIndex].pSid = (PBYTE)pSid; pWellKnownSids[dwIndex].Offset = SE_LOCAL_POSIX_ID; pWellKnownSids[dwIndex].SidType = AFP_SID_TYPE_WELL_KNOWN; dwIndex++;
// Create CREATOR OWNER SID
//
ntStatus = RtlAllocateAndInitializeSid( &CreatorSidAuthority, 1, SECURITY_CREATOR_OWNER_RID, 0,0,0,0,0,0,0, &pSid ); if ( !NT_SUCCESS( ntStatus ) ) break;
pWellKnownSids[dwIndex].pSid = (PBYTE)pSid; pWellKnownSids[dwIndex].Offset = SE_CREATOR_OWNER_POSIX_ID; pWellKnownSids[dwIndex].SidType = AFP_SID_TYPE_WELL_KNOWN; dwIndex++;
// Create CREATOR GROUP SID
//
ntStatus = RtlAllocateAndInitializeSid( &CreatorSidAuthority, 1, SECURITY_CREATOR_GROUP_RID, 0,0,0,0,0,0,0, &pSid ); if ( !NT_SUCCESS( ntStatus ) ) break;
pWellKnownSids[dwIndex].pSid = (PBYTE)pSid; pWellKnownSids[dwIndex].Offset = SE_CREATOR_GROUP_POSIX_ID; pWellKnownSids[dwIndex].SidType = AFP_SID_TYPE_WELL_KNOWN; dwIndex++;
// Create SECURITY_NT_AUTHORITY Sid
//
ntStatus = RtlAllocateAndInitializeSid( &NtAuthority, 0,0,0,0,0,0,0,0,0, &pSid ); if ( !NT_SUCCESS( ntStatus ) ) break;
pWellKnownSids[dwIndex].pSid = (PBYTE)pSid; pWellKnownSids[dwIndex].Offset = SE_AUTHORITY_POSIX_ID; pWellKnownSids[dwIndex].SidType = AFP_SID_TYPE_WELL_KNOWN; dwIndex++;
// Create SECURITY_DIALUP Sid
//
ntStatus = RtlAllocateAndInitializeSid( &NtAuthority, 1, SECURITY_DIALUP_RID, 0,0,0,0,0,0,0, &pSid ); if ( !NT_SUCCESS( ntStatus ) ) break;
pWellKnownSids[dwIndex].pSid = (PBYTE)pSid; pWellKnownSids[dwIndex].Offset = SE_DIALUP_POSIX_ID; pWellKnownSids[dwIndex].SidType = AFP_SID_TYPE_WELL_KNOWN; dwIndex++;
// Create SECURITY_NETWORK Sid
//
ntStatus = RtlAllocateAndInitializeSid( &NtAuthority, 1, SECURITY_NETWORK_RID, 0,0,0,0,0,0,0, &pSid ); if ( !NT_SUCCESS( ntStatus ) ) break;
pWellKnownSids[dwIndex].pSid = (PBYTE)pSid; pWellKnownSids[dwIndex].Offset = SE_NETWORK_POSIX_ID; pWellKnownSids[dwIndex].SidType = AFP_SID_TYPE_WELL_KNOWN; dwIndex++;
// Create SECURITY_BATCH Sid
//
ntStatus = RtlAllocateAndInitializeSid( &NtAuthority, 1, SECURITY_BATCH_RID, 0,0,0,0,0,0,0, &pSid ); if ( !NT_SUCCESS( ntStatus ) ) break;
pWellKnownSids[dwIndex].pSid = (PBYTE)pSid; pWellKnownSids[dwIndex].Offset = SE_NETWORK_POSIX_ID; pWellKnownSids[dwIndex].SidType = AFP_SID_TYPE_WELL_KNOWN; dwIndex++;
// Create SECURITY_INTERACTIVE Sid
//
ntStatus = RtlAllocateAndInitializeSid( &NtAuthority, 1, SECURITY_INTERACTIVE_RID, 0,0,0,0,0,0,0, &pSid ); if ( !NT_SUCCESS( ntStatus ) ) break;
pWellKnownSids[dwIndex].pSid = (PBYTE)pSid; pWellKnownSids[dwIndex].Offset = SE_INTERACTIVE_POSIX_ID; pWellKnownSids[dwIndex].SidType = AFP_SID_TYPE_WELL_KNOWN; dwIndex++;
// Create SECURITY_SERVICE Sid
//
ntStatus = RtlAllocateAndInitializeSid( &NtAuthority, 1, SECURITY_SERVICE_RID, 0,0,0,0,0,0,0, &pSid ); if ( !NT_SUCCESS( ntStatus ) ) break;
pWellKnownSids[dwIndex].pSid = (PBYTE)pSid; pWellKnownSids[dwIndex].Offset = SE_SERVICE_POSIX_ID; pWellKnownSids[dwIndex].SidType = AFP_SID_TYPE_WELL_KNOWN; dwIndex++;
// Create the built in domain SID
//
ntStatus = RtlAllocateAndInitializeSid( &NtAuthority, 1, SECURITY_BUILTIN_DOMAIN_RID, 0,0,0,0,0,0,0, &pSid ); if ( !NT_SUCCESS( ntStatus ) ) break;
pWellKnownSids[dwIndex].pSid = (PBYTE)pSid; pWellKnownSids[dwIndex].Offset = SE_BUILT_IN_DOMAIN_POSIX_OFFSET; pWellKnownSids[dwIndex].SidType = AFP_SID_TYPE_DOMAIN; dwIndex++;
pWellKnownSids[dwIndex].pSid = (PBYTE)NULL;
} while( FALSE );
if ( !NT_SUCCESS( ntStatus ) ) {
while( dwIndex > 0 ) RtlFreeSid( pWellKnownSids[--dwIndex].pSid ); }
return( ntStatus ); }
//**
//
// Call: AfpChangePwdArapStyle
//
// Returns: return code
//
// Description: This procedure will try to change the user's password on a
// specified domain. This does it only for native Apple UAM clients
// i.e., the user's password is stored in the DS in a reversibly-encrypted
// form, and the client sends the old and the new password (not owf as in
// MS-UAM case). This is what ARAP does, that's why the name.
// This function is big time cut-n-paste from the ARAP code
//
NTSTATUS AfpChangePwdArapStyle( IN PAFP_PASSWORD_DESC pPassword, IN PUNICODE_STRING pDomainName, IN PSID pDomainSid ) {
NTSTATUS status; NTSTATUS PStatus; PMSV1_0_PASSTHROUGH_REQUEST pPassThruReq; PMSV1_0_SUBAUTH_REQUEST pSubAuthReq; PMSV1_0_PASSTHROUGH_RESPONSE pPassThruResp; PMSV1_0_SUBAUTH_RESPONSE pSubAuthResp; DWORD dwSubmitBufLen; DWORD dwSubmitBufOffset; PRAS_SUBAUTH_INFO pRasSubAuthInfo; PARAP_SUBAUTH_REQ pArapSubAuthInfo; ARAP_SUBAUTH_RESP ArapResp; PARAP_SUBAUTH_RESP pArapRespBuffer; PVOID RetBuf; DWORD dwRetBufLen;
// if our registeration with lsa process failed at init time, or if
// there is no domain name for this user, just fail the succer
// (if the user logged on successfully using native Apple UAM, then
// there had better be a domain!)
if ((SfmLsaHandle == NULL) ||(pDomainName == NULL)) { return(STATUS_LOGON_FAILURE); }
if (pDomainName != NULL) { if (pDomainName->Length == 0) { return(STATUS_LOGON_FAILURE); } }
dwSubmitBufLen = sizeof(MSV1_0_PASSTHROUGH_REQUEST) + sizeof(WCHAR)*(MAX_ARAP_USER_NAMELEN+1) + // domain name
sizeof(TEXT(MSV1_0_PACKAGE_NAME)) + // package name
sizeof(MSV1_0_SUBAUTH_REQUEST) + sizeof(RAS_SUBAUTH_INFO) + sizeof(ARAP_SUBAUTH_REQ) + ALIGN_WORST; // for alignment
pPassThruReq = (PMSV1_0_PASSTHROUGH_REQUEST) GlobalAlloc(GMEM_FIXED, dwSubmitBufLen);
if (!pPassThruReq) { return (STATUS_INSUFFICIENT_RESOURCES); }
RtlZeroMemory((PBYTE)pPassThruReq, dwSubmitBufLen);
//
// Set up the MSV1_0_PASSTHROUGH_REQUEST structure
//
// tell MSV that it needs to visit our subauth pkg (for change pwd)
pPassThruReq->MessageType = MsV1_0GenericPassthrough;
pPassThruReq->DomainName.Length = pDomainName->Length;
pPassThruReq->DomainName.MaximumLength = (sizeof(WCHAR) * (MAX_ARAP_USER_NAMELEN+1));
pPassThruReq->DomainName.Buffer = (PWSTR) (pPassThruReq + 1);
RtlMoveMemory(pPassThruReq->DomainName.Buffer, pDomainName->Buffer, pPassThruReq->DomainName.Length);
pPassThruReq->PackageName.Length = (sizeof(WCHAR) * wcslen(TEXT(MSV1_0_PACKAGE_NAME)));
pPassThruReq->PackageName.MaximumLength = sizeof(TEXT(MSV1_0_PACKAGE_NAME));
pPassThruReq->PackageName.Buffer = (PWSTR)((PBYTE)(pPassThruReq->DomainName.Buffer) + pPassThruReq->DomainName.MaximumLength);
RtlMoveMemory(pPassThruReq->PackageName.Buffer, TEXT(MSV1_0_PACKAGE_NAME), sizeof(TEXT(MSV1_0_PACKAGE_NAME)));
pPassThruReq->DataLength = sizeof(MSV1_0_SUBAUTH_REQUEST) + sizeof(RAS_SUBAUTH_INFO) + sizeof(ARAP_SUBAUTH_REQ);
pPassThruReq->LogonData = ROUND_UP_POINTER( ((PBYTE)pPassThruReq->PackageName.Buffer + pPassThruReq->PackageName.MaximumLength), ALIGN_WORST );
if (pPassThruReq->LogonData >= ((PCHAR)pPassThruReq + dwSubmitBufLen)) { AFP_PRINT (("srvrhlpr.c: Error in ROUND_UP_POINTER\n")); GlobalFree((HGLOBAL)pPassThruReq); return STATUS_INVALID_BUFFER_SIZE; }
pSubAuthReq = (PMSV1_0_SUBAUTH_REQUEST)pPassThruReq->LogonData;
pSubAuthReq->MessageType = MsV1_0SubAuth; pSubAuthReq->SubAuthPackageId = MSV1_0_SUBAUTHENTICATION_DLL_RAS;
pSubAuthReq->SubAuthInfoLength = sizeof(RAS_SUBAUTH_INFO) + sizeof(ARAP_SUBAUTH_REQ);
//
// this pointer is self-relative
//
pSubAuthReq->SubAuthSubmitBuffer = (PUCHAR)sizeof(MSV1_0_SUBAUTH_REQUEST);
//
// copy the structure our subauth pkg will use at the other end
//
pRasSubAuthInfo = (PRAS_SUBAUTH_INFO)(pSubAuthReq + 1);
pRasSubAuthInfo->ProtocolType = RAS_SUBAUTH_PROTO_ARAP; pRasSubAuthInfo->DataSize = sizeof(ARAP_SUBAUTH_REQ);
pArapSubAuthInfo = (PARAP_SUBAUTH_REQ)&pRasSubAuthInfo->Data[0];
pArapSubAuthInfo->PacketType = SFM_SUBAUTH_CHGPWD_PKT;
wcscpy(pArapSubAuthInfo->ChgPwd.UserName, pPassword->UserName);
RtlCopyMemory(pArapSubAuthInfo->ChgPwd.OldMunge, pPassword->OldPassword, MAX_MAC_PWD_LEN);
pArapSubAuthInfo->ChgPwd.OldMunge[MAX_MAC_PWD_LEN] = 0;
RtlCopyMemory(pArapSubAuthInfo->ChgPwd.NewMunge, pPassword->NewPassword, MAX_MAC_PWD_LEN);
pArapSubAuthInfo->ChgPwd.NewMunge[MAX_MAC_PWD_LEN] = 0;
//
// whew! finally done setting up all the parms: now call that api
//
status = LsaCallAuthenticationPackage ( SfmLsaHandle, SfmAuthPkgId, pPassThruReq, dwSubmitBufLen, &RetBuf, &dwRetBufLen, &PStatus);
if (status != STATUS_SUCCESS || PStatus != STATUS_SUCCESS) { GlobalFree((HGLOBAL)pPassThruReq);
if (status == STATUS_SUCCESS) { status = PStatus; } return(status); }
pPassThruResp = (PMSV1_0_PASSTHROUGH_RESPONSE)RetBuf;
pSubAuthResp = (PMSV1_0_SUBAUTH_RESPONSE)(pPassThruResp->ValidationData);
// our return buffer is in self-relative format
pArapRespBuffer = (PARAP_SUBAUTH_RESP)((PBYTE)pSubAuthResp + (ULONG_PTR)(pSubAuthResp->SubAuthReturnBuffer));
RtlCopyMemory(&ArapResp, (PUCHAR)pArapRespBuffer, pSubAuthResp->SubAuthInfoLength);
GlobalFree((HGLOBAL)pPassThruReq);
LsaFreeReturnBuffer(RetBuf);
if(ArapResp.Result != 0) { return(STATUS_UNSUCCESSFUL); }
return(STATUS_SUCCESS);
}
|