/*++ Copyright (c) 1987-1996 Microsoft Corporation Module Name: ftinfo.c Abstract: Utilities routine to manage the forest trust info list Author: 27-Jul-00 (cliffv) Environment: User mode only. Contains NT-specific code. Requires ANSI C extensions: slash-slash comments, long external names. Revision History: --*/ // // Common include files. // #include "logonsrv.h" // Include files common to entire service #pragma hdrstop #include NTSTATUS NlpUpdateFtinfo( IN PDOMAIN_INFO DomainInfo, IN LPWSTR TrustedDomainName, IN BOOLEAN ImpersonateCaller, IN PLSA_FOREST_TRUST_INFORMATION NewForestTrustInfo ) /*++ Routine Description: This function write the specified NewForestTrustInfo onto the named TDO. The NewForestTrustInfo is merged with the exsiting information using the following algorithm: The FTinfo records written are described in the NetpMergeFTinfo routine. Arguments: DomainInfo - Hosted Domain that trusts the domain to query. TrustedDomainName - Trusted domain that is to be updated. This domain must have the TRUST_ATTRIBUTE_FOREST_TRANSITIVE bit set. ImpersonateCaller - TRUE if the caller is to be impersonated. FALSE, if the trusted policy handle should be used to write the local LSA. NewForestTrustInfo - Specified the new array of FTinfo records as returned from the trusted domain. Return Value: STATUS_SUCCESS: Success. --*/ { NTSTATUS Status; LSAPR_HANDLE PolicyHandle = NULL; UNICODE_STRING TrustedDomainNameString; // // Open a handle to the LSA. // if ( ImpersonateCaller ) { OBJECT_ATTRIBUTES ObjectAttributes; InitializeObjectAttributes(&ObjectAttributes, NULL, 0, NULL, NULL); Status = LsarOpenPolicy( NULL, // local server (PLSAPR_OBJECT_ATTRIBUTES) &ObjectAttributes, POLICY_TRUST_ADMIN, &PolicyHandle ); if ( !NT_SUCCESS(Status) ) { NlPrint(( NL_CRITICAL, "NlpUpdateTdo: %ws: Cannot LsarOpenPolicy 0x%lx\n", TrustedDomainName, Status )); goto Cleanup; } } else { PolicyHandle = DomainInfo->DomLsaPolicyHandle; } // // Read the existing FTINFO // RtlInitUnicodeString( &TrustedDomainNameString, TrustedDomainName ); Status = LsaIUpdateForestTrustInformation( PolicyHandle, &TrustedDomainNameString, NewForestTrustInfo ); if ( !NT_SUCCESS( Status )) { goto Cleanup; } Cleanup: if ( PolicyHandle != NULL ) { if ( ImpersonateCaller ) { (VOID) LsarClose( &PolicyHandle ); } } return Status; } NTSTATUS NlpGetForestTrustInfoHigher( IN PCLIENT_SESSION ClientSession, IN DWORD Flags, IN BOOLEAN ImpersonateCaller, OUT PLSA_FOREST_TRUST_INFORMATION *ForestTrustInfo ) /*++ Routine Description: This function is the client side stub for getting the forest trust info from a trusted forest. Arguments: ClientSession - Trusted domain that is to be queried. This domain must have the TRUST_ATTRIBUTE_FOREST_TRANSITIVE bit set. Flags - Specifies a set of bits that modify the behavior of the API. Valid bits are: DS_GFTI_UPDATE_TDO - If this bit is set, the API will update the FTinfo attribute of the TDO named by the ClientSession parameter. The caller must have access to modify the FTinfo attribute or ERROR_ACCESS_DENIED will be returned. The algorithm describing how the FTinfo from the trusted domain is merged with the FTinfo from the TDO is described below. This bit in only valid if ServerName specifies the PDC of its domain. ImpersonateCaller - TRUE if the caller is to be impersonated. FALSE, if the trusted policy handle should be used to write the local LSA. ForestTrustInfo - Returns a pointer to a structure containing a count and an array of FTInfo records describing the namespaces claimed by the domain specified by ClientSession. The Accepted field and Time field of all returned records will be zero. The buffer should be freed by calling NetApiBufferFree. Return Value: STATUS_SUCCESS: Message successfully sent --*/ { NTSTATUS Status; NETLOGON_AUTHENTICATOR OurAuthenticator; NETLOGON_AUTHENTICATOR ReturnAuthenticator; SESSION_INFO SessionInfo; BOOLEAN FirstTry = TRUE; NlAssert( ClientSession->CsReferenceCount > 0 ); NlAssert( ClientSession->CsFlags & CS_WRITER ); // // Only allow TDO update on the PDC. // if ( (Flags & DS_GFTI_UPDATE_TDO) != 0 && ClientSession->CsDomainInfo->DomRole != RolePrimary ) { Status = STATUS_BACKUP_CONTROLLER; goto Cleanup; } // // Ensure the F bit is set. // if ( (ClientSession->CsTrustAttributes & TRUST_ATTRIBUTE_FOREST_TRANSITIVE) == 0 ) { NlPrintCs((NL_CRITICAL, ClientSession, "NlpGetForestTrustInfoHigher: trust isn't marked as cross forest trust: 0x%lX\n", ClientSession->CsTrustAttributes )); Status = STATUS_NO_SUCH_DOMAIN; goto Cleanup; } // // If the session isn't authenticated, // do so now. // FirstTryFailed: Status = NlEnsureSessionAuthenticated( ClientSession, 0 ); if ( !NT_SUCCESS(Status) ) { goto Cleanup; } SessionInfo.SessionKey = ClientSession->CsSessionKey; SessionInfo.NegotiatedFlags = ClientSession->CsNegotiatedFlags; // // If the DC doesn't support the new function, // fail now. // if ( (SessionInfo.NegotiatedFlags & NETLOGON_SUPPORTS_CROSS_FOREST) == 0 ) { NlPrintCs((NL_CRITICAL, ClientSession, "NlpGetForestTrustInfoHigher: remote DC doesn't support this function.\n" )); Status = STATUS_NOT_SUPPORTED; goto Cleanup; } // // Build the Authenticator for this request to the PDC. // NlBuildAuthenticator( &ClientSession->CsAuthenticationSeed, &ClientSession->CsSessionKey, &OurAuthenticator); // // Remote the request to the trusted DC. // NL_API_START( Status, ClientSession, TRUE ) { NlAssert( ClientSession->CsUncServerName != NULL ); Status = I_NetGetForestTrustInformation( ClientSession->CsUncServerName, ClientSession->CsDomainInfo->DomUnicodeComputerNameString.Buffer, &OurAuthenticator, &ReturnAuthenticator, 0, // No flags yet ForestTrustInfo ); // NOTE: This call may drop the secure channel behind our back } NL_API_ELSE( Status, ClientSession, TRUE ) { } NL_API_END; // // Now verify authenticator and update our seed // if ( NlpDidDcFail( Status ) || !NlUpdateSeed( &ClientSession->CsAuthenticationSeed, &ReturnAuthenticator.Credential, &ClientSession->CsSessionKey) ) { NlPrintCs(( NL_CRITICAL, ClientSession, "NlpDidDcFail: denying access after status: 0x%lx\n", Status )); // // Preserve any status indicating a communication error. // if ( NT_SUCCESS(Status) ) { Status = STATUS_ACCESS_DENIED; } NlSetStatusClientSession( ClientSession, Status ); // // Perhaps the netlogon service on the server has just restarted. // Try just once to set up a session to the server again. // if ( FirstTry ) { FirstTry = FALSE; goto FirstTryFailed; } } // // Handle failures // if ( !NT_SUCCESS(Status) ) { goto Cleanup; } // // Handle updating the FTINFO on the TDO // if ( (Flags & DS_GFTI_UPDATE_TDO) != 0 ) { LOCK_TRUST_LIST( ClientSession->CsDomainInfo ); Status = NlpUpdateFtinfo( ClientSession->CsDomainInfo, ClientSession->CsDnsDomainName.Buffer, ImpersonateCaller, *ForestTrustInfo ); UNLOCK_TRUST_LIST( ClientSession->CsDomainInfo ); if ( !NT_SUCCESS(Status) ) { goto Cleanup; } } Status = STATUS_SUCCESS; // // Common exit // Cleanup: if ( !NT_SUCCESS(Status) ) { NlPrintCs((NL_CRITICAL, ClientSession, "NlpGetForestTrustInfoHigher: failed %lX\n", Status)); } return Status; } NET_API_STATUS DsrGetForestTrustInformation ( IN LPWSTR ServerName OPTIONAL, IN LPWSTR TrustedDomainName OPTIONAL, IN ULONG Flags, OUT PLSA_FOREST_TRUST_INFORMATION *ForestTrustInfo ) /*++ Routine Description: This is the server side stub for DsGetForestTrustInformationW. See that routine for documentation. Arguments: See DsGetForestTrustInformationW Return Value: See DsGetForestTrustInformationW --*/ { NET_API_STATUS NetStatus; PDOMAIN_INFO DomainInfo = NULL; PCLIENT_SESSION ClientSession = NULL; BOOLEAN AmWriter = FALSE; // // Perform access validation on the caller // NetStatus = NetpAccessCheck( NlGlobalNetlogonSecurityDescriptor, // Security descriptor NETLOGON_FTINFO_ACCESS, // Desired access &NlGlobalNetlogonInfoMapping ); // Generic mapping if ( NetStatus != NERR_Success) { NetStatus = ERROR_ACCESS_DENIED; goto Cleanup; } // // This API is not supported on workstations. // if ( NlGlobalMemberWorkstation ) { NetStatus = ERROR_NOT_SUPPORTED; goto Cleanup; } // // Validate the Flags parameter // if ((Flags & ~DS_GFTI_VALID_FLAGS) != 0 ) { NetStatus = ERROR_INVALID_FLAGS; goto Cleanup; } // // Find the referenced domain // DomainInfo = NlFindDomainByServerName( ServerName ); // Primary domain if ( DomainInfo == NULL ) { // Default to primary domain to handle the case where the ComputerName // is an IP address. DomainInfo = NlFindNetbiosDomain( NULL, TRUE ); if ( DomainInfo == NULL ) { NetStatus = ERROR_INVALID_COMPUTERNAME; goto Cleanup; } } NlPrintDom((NL_SESSION_SETUP, DomainInfo, "DsrGetForestTrustInformation: %ws called: 0x%lx\n", TrustedDomainName, Flags )); // // Get the ForestTrustInformation for a particular TDO // if ( TrustedDomainName != NULL && *TrustedDomainName != L'\0' ) { NTSTATUS Status; UNICODE_STRING TrustedDomainNameString; // // Only allow TDO update on the PDC. // if ( (Flags & DS_GFTI_UPDATE_TDO) != 0 && DomainInfo->DomRole != RolePrimary ) { NetStatus = NERR_NotPrimary; goto Cleanup; } // // Find the client session to the trusted domain. // RtlInitUnicodeString(&TrustedDomainNameString, TrustedDomainName ); ClientSession = NlFindNamedClientSession( DomainInfo, &TrustedDomainNameString, NL_DIRECT_TRUST_REQUIRED, NULL ); if( ClientSession == NULL ) { NlPrintDom((NL_CRITICAL, DomainInfo, "DsrGetForestTrustInformation: %ws: can't find the client structure of the domain specified.\n", TrustedDomainName )); NetStatus = ERROR_NO_SUCH_DOMAIN; goto Cleanup; } // // Become a Writer of the ClientSession. // if ( !NlTimeoutSetWriterClientSession( ClientSession, WRITER_WAIT_PERIOD ) ) { NlPrintCs((NL_CRITICAL, ClientSession, "NlpGetForestTrustInfoHigher: Can't become writer of client session.\n" )); Status = STATUS_NO_LOGON_SERVERS; goto Cleanup; } AmWriter = TRUE; // // Call the DC in the trusted domain. // Status = NlpGetForestTrustInfoHigher( ClientSession, Flags, TRUE, // Impersonate caller ForestTrustInfo ); if ( !NT_SUCCESS(Status ) ) { NetStatus = NetpNtStatusToApiStatus( Status ); goto Cleanup; } // // Get the local ForestTrustInformation. // } else { NTSTATUS Status; // // Don't allow an Update TDO request if there is no TDO. // if ( Flags & DS_GFTI_UPDATE_TDO ) { NetStatus = ERROR_INVALID_FLAGS; goto Cleanup; } // // Simply grab the local ForestTrustInformation // Status = LsaIGetForestTrustInformation( ForestTrustInfo ); if ( !NT_SUCCESS( Status )) { NetStatus = NetpNtStatusToApiStatus( Status ); goto Cleanup; } } NetStatus = NO_ERROR; Cleanup: if ( ClientSession != NULL ) { if ( AmWriter ) { NlResetWriterClientSession( ClientSession ); } NlUnrefClientSession( ClientSession ); } NlPrintDom(( NL_SESSION_SETUP, DomainInfo, "DsrGetForestTrustInformation: %ws returns %ld\n", TrustedDomainName, NetStatus )); if ( DomainInfo != NULL ) { NlDereferenceDomain( DomainInfo ); } return NetStatus; } NTSTATUS NetrGetForestTrustInformation ( IN LPWSTR ServerName OPTIONAL, IN LPWSTR ComputerName, IN PNETLOGON_AUTHENTICATOR Authenticator, OUT PNETLOGON_AUTHENTICATOR ReturnAuthenticator, IN DWORD Flags, OUT PLSA_FOREST_TRUST_INFORMATION *ForestTrustInfo ) /*++ Routine Description: The server side of the secure channel version of DsGetForestTrustInformation. The inbound secure channel identified by ComputerName must be for an interdomain trust and the inbound TDO must have the TRUST_ATTRIBUTE_FOREST_TRANSITIVE bit set. Arguments: ServerName - The name of the domain controller this API is remoted to. ComputerName -- Name of the DC server making the call. Authenticator -- supplied by the server. ReturnAuthenticator -- Receives an authenticator returned by the PDC. Flags - Specifies a set of bits that modify the behavior of the API. No values are currently defined. The caller should pass zero. ForestTrustInfo - Returns a pointer to a structure containing a count and an array of FTInfo records describing the namespaces claimed by the domain specified by TrustedDomainName. The Accepted field and Time field of all returned records will be zero. The buffer should be freed by calling NetApiBufferFree. Return Value: STATUS_SUCCESS -- The function completed successfully. STATUS_ACCESS_DENIED -- The replicant should re-authenticate with the PDC. --*/ { NTSTATUS Status; NET_API_STATUS NetStatus; PDOMAIN_INFO DomainInfo = NULL; PSERVER_SESSION ServerSession; NETLOGON_SECURE_CHANNEL_TYPE SecureChannelType; // // Lookup which domain this call pertains to. // *ForestTrustInfo = NULL; DomainInfo = NlFindDomainByServerName( ServerName ); NlPrintDom((NL_SESSION_SETUP, DomainInfo, "NetrGetForestTrustInformation: %ws called: 0x%lx\n", ComputerName, Flags )); if ( DomainInfo == NULL ) { Status = STATUS_INVALID_COMPUTER_NAME; goto Cleanup; } // // This API is not supported on workstations. // if ( NlGlobalMemberWorkstation ) { Status = STATUS_NOT_SUPPORTED; goto Cleanup; } // // Find the server session entry for this secure channel. // LOCK_SERVER_SESSION_TABLE( DomainInfo ); ServerSession = NlFindNamedServerSession( DomainInfo, ComputerName ); if (ServerSession == NULL) { UNLOCK_SERVER_SESSION_TABLE( DomainInfo ); Status = STATUS_ACCESS_DENIED; goto Cleanup; } // // Now verify the Authenticator and update seed if OK // Status = NlCheckAuthenticator( ServerSession, Authenticator, ReturnAuthenticator); if ( !NT_SUCCESS(Status) ) { UNLOCK_SERVER_SESSION_TABLE( DomainInfo ); goto Cleanup; } SecureChannelType = ServerSession->SsSecureChannelType; // // This call is only valid on FOREST_TRANSITIVE trusts // if ( (ServerSession->SsFlags & SS_FOREST_TRANSITIVE) == 0 ) { UNLOCK_SERVER_SESSION_TABLE( DomainInfo ); NlPrintDom((NL_SESSION_SETUP, DomainInfo, "NetrGetForestTrustInformation: %ws failed because F bit isn't set on the TDO\n", ComputerName )); Status = STATUS_NOT_IMPLEMENTED; goto Cleanup; } UNLOCK_SERVER_SESSION_TABLE( DomainInfo ); if ( !IsDomainSecureChannelType( SecureChannelType ) ) { NlPrintDom((NL_SESSION_SETUP, DomainInfo, "NetrGetForestTrustInformation: %ws failed because secure channel isn't a domain secure channel\n", ComputerName )); Status = STATUS_NOT_IMPLEMENTED; goto Cleanup; } // // Get the forest trust information for the local machine // Status = LsaIGetForestTrustInformation( ForestTrustInfo ); if ( !NT_SUCCESS( Status )) { goto Cleanup; } Status = STATUS_SUCCESS; Cleanup: // // If the request failed, be carefull to not leak authentication // information. // if ( Status == STATUS_ACCESS_DENIED ) { if ( ReturnAuthenticator != NULL ) { RtlZeroMemory( ReturnAuthenticator, sizeof(*ReturnAuthenticator) ); } } NlPrintDom(( NL_SESSION_SETUP, DomainInfo, "NetrGetForestTrustInformation: %ws returns %lX\n", ComputerName, Status )); if ( DomainInfo != NULL ) { NlDereferenceDomain( DomainInfo ); } return Status; }