/********************************************************************/ /** 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 #include #include // prototype of I_NetGetDCList #include #include 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); }