/*++ Microsoft Windows Copyright (C) Microsoft Corporation, 1998 - 2001 Module Name: trust.cxx Abstract: Handles the various functions for managing domain trust. --*/ #include "pch.h" #pragma hdrstop #include VOID NetDompFreeDomInfo( IN OUT PND5_TRUST_INFO TrustInfo ) { if ( TrustInfo->Connected ) { LOG_VERBOSE(( MSG_VERBOSE_DELETE_SESSION, TrustInfo->Server )); NetpManageIPCConnect( TrustInfo->Server, NULL, NULL, NETSETUPP_DISCONNECT_IPC ); } NetApiBufferFree( TrustInfo->Server ); if (TrustInfo->BlobToFree) { LsaFreeMemory( TrustInfo->BlobToFree ); } else { if (TrustInfo->DomainName && TrustInfo->DomainName->Buffer) { NetApiBufferFree(TrustInfo->DomainName->Buffer); NetApiBufferFree(TrustInfo->DomainName); } } if ( TrustInfo->LsaHandle ) { LsaClose( TrustInfo->LsaHandle ); } if ( TrustInfo->TrustHandle ) { LsaClose( TrustInfo->TrustHandle ); } } DWORD NetDompTrustGetDomInfo( IN PWSTR Domain, IN PWSTR DomainController OPTIONAL, IN PND5_AUTH_INFO AuthInfo, IN OUT PND5_TRUST_INFO TrustInfo, IN BOOL ManageTrust, IN BOOL fForce, IN BOOL fUseNullSession ) /*++ Routine Description: This function will connect to the domain controller for a given domain and determine the appropriate information required for managing trusts. Arguments: Domain - Domain to connect to DomainController - Optional name of a dc within the domain to connect to TrustInfo - Trusted domain info to accumulate ManageTrust - Determines how the Lsa is opened. Also if true, and DomainController is NULL, find a writable DC fForce - If true, set the name info even if the domain can't be contacted. Return Value: ERROR_SUCCESS - Success ERROR_INVALID_PARAMETER - No object name was supplied --*/ { DWORD Win32Err = ERROR_SUCCESS; PDOMAIN_CONTROLLER_INFO DcInfo = NULL; PPOLICY_PRIMARY_DOMAIN_INFO PolicyPDI = NULL; PPOLICY_DNS_DOMAIN_INFO PolicyDDI = NULL; OBJECT_ATTRIBUTES OA; PWSTR pwzDomainName; UNICODE_STRING ServerU, DomainNameU; NTSTATUS Status; ULONG DsGetDcOptions = DS_DIRECTORY_SERVICE_PREFERRED, LsaOptions; if ( !DomainController ) { if ( ManageTrust ) { DsGetDcOptions |= DS_WRITABLE_REQUIRED; } if (TrustInfo->Flags & NETDOM_TRUST_PDC_REQUIRED) { DsGetDcOptions |= DS_PDC_REQUIRED; } Win32Err = DsGetDcName( NULL, Domain, NULL, NULL, DsGetDcOptions, &DcInfo ); if ( Win32Err == ERROR_SUCCESS ) { DomainController = DcInfo->DomainControllerName; } } // // Save off the server name // if ( Win32Err == ERROR_SUCCESS ) { Win32Err = NetApiBufferAllocate( ( wcslen( DomainController ) + 1 ) * sizeof( WCHAR ), (PVOID*)&( TrustInfo->Server ) ); if ( Win32Err == ERROR_SUCCESS ) { wcscpy( TrustInfo->Server, DomainController ); } } // // Connect to the machine // if ( Win32Err == ERROR_SUCCESS ) { LOG_VERBOSE(( MSG_VERBOSE_ESTABLISH_SESSION, DomainController )); Win32Err = NetpManageIPCConnect( DomainController, (fUseNullSession) ? L"" : AuthInfo->User, (fUseNullSession) ? L"" : AuthInfo->Password, (fUseNullSession) ? NETSETUPP_NULL_SESSION_IPC : NETSETUPP_CONNECT_IPC); if ( Win32Err == ERROR_SUCCESS ) { TrustInfo->Connected = TRUE; } if (ERROR_SESSION_CREDENTIAL_CONFLICT == Win32Err) { if (fUseNullSession) { // Ignore conflict of creds. // Win32Err = ERROR_SUCCESS; } else { NetDompDisplayMessage(MSG_ALREADY_CONNECTED, DomainController); } } if (ERROR_LOGON_FAILURE == Win32Err) { NetApiBufferFree( DcInfo ); return Win32Err; } } // // Get the domain information // if ( Win32Err == ERROR_SUCCESS ) { RtlInitUnicodeString( &ServerU, DomainController ); InitializeObjectAttributes( &OA, NULL, 0, NULL, NULL ); LOG_VERBOSE(( MSG_VERBOSE_GET_LSA )); LsaOptions = POLICY_VIEW_LOCAL_INFORMATION | POLICY_LOOKUP_NAMES; if ( ManageTrust ) { LsaOptions |= POLICY_CREATE_SECRET | POLICY_TRUST_ADMIN; } Status = LsaOpenPolicy( &ServerU, &OA, LsaOptions, &( TrustInfo->LsaHandle ) ); if ( NT_SUCCESS( Status ) ) { Status = LsaQueryInformationPolicy( TrustInfo->LsaHandle, PolicyDnsDomainInformation, ( PVOID * )&PolicyDDI ); if ( NT_SUCCESS( Status ) ) { TrustInfo->DomainName = &PolicyDDI->DnsDomainName; TrustInfo->FlatName = &PolicyDDI->Name; TrustInfo->Sid = PolicyDDI->Sid; TrustInfo->BlobToFree = ( PVOID )PolicyDDI; TrustInfo->Uplevel = TRUE; TrustInfo->fWasDownlevel = FALSE; } else if ( Status == RPC_NT_PROCNUM_OUT_OF_RANGE ) { Status = LsaQueryInformationPolicy( TrustInfo->LsaHandle, PolicyPrimaryDomainInformation, ( PVOID * )&PolicyPDI ); if ( NT_SUCCESS( Status ) ) { TrustInfo->DomainName = &PolicyPDI->Name; TrustInfo->FlatName = &PolicyPDI->Name; TrustInfo->Sid = PolicyPDI->Sid; TrustInfo->BlobToFree = ( PVOID )PolicyPDI; TrustInfo->Uplevel = TrustInfo->fWasDownlevel = FALSE; } } } Win32Err = RtlNtStatusToDosError( Status ); if ( Win32Err != ERROR_SUCCESS ) { LOG_VERBOSE(( MSG_VERBOSE_DELETE_SESSION, DomainController )); NetpManageIPCConnect( DomainController, NULL, NULL, NETSETUPP_DISCONNECT_IPC ); TrustInfo->Connected = FALSE; } } else if (fForce) { LOG_VERBOSE(( MSG_VERBOSE_DOMAIN_NOT_FOUND, Domain )); TrustInfo->Flags = NETDOM_TRUST_FLAG_DOMAIN_NOT_FOUND; // Allocate space to save the user-entered domain name. Win32Err = NetApiBufferAllocate((wcslen(Domain) + 1) * sizeof(WCHAR), (PVOID*)&pwzDomainName); if ( Win32Err == ERROR_SUCCESS ) { Win32Err = NetApiBufferAllocate(sizeof(UNICODE_STRING), (PVOID*)&TrustInfo->DomainName); if ( Win32Err == ERROR_SUCCESS ) { wcscpy( pwzDomainName, Domain ); RtlInitUnicodeString( TrustInfo->DomainName, pwzDomainName ); TrustInfo->FlatName = TrustInfo->DomainName; } } } else { LOG_VERBOSE((MSG_DOMAIN_NOT_FOUND, Domain)); } NetApiBufferFree( DcInfo ); return( Win32Err ); } DWORD NetDompTrustRemoveIncomingDownlevelObject( IN PND5_TRUST_INFO TrustingInfo, IN PND5_TRUST_INFO TrustedInfo ) /*++ Routine Description: This function removes the interdomain trust account object on the trusted domain. Arguments: TrustingInfo - Info on the trusting domain TrustedInfo - Info on the trusted domain Return Value: ERROR_INVALID_PARAMETER - No object name was supplied --*/ { DWORD Win32Err = ERROR_SUCCESS; WCHAR AccountName[ UNLEN + 1 ]; PWSTR FullServer = NULL; if ( TrustingInfo->FlatName->Length > DNLEN * sizeof( WCHAR ) ) { return( ERROR_INVALID_DOMAINNAME ); } if ( TrustedInfo->Server && *( TrustedInfo->Server ) != L'\\' ) { Win32Err = NetApiBufferAllocate( ( wcslen( TrustedInfo->Server ) + 3 ) * sizeof( WCHAR ), ( PVOID * )&FullServer ); if ( Win32Err == ERROR_SUCCESS ) { swprintf( FullServer, L"\\\\%ws", TrustedInfo->Server ); } } else { FullServer = TrustedInfo->Server; } // // Build the account name... // if ( Win32Err == ERROR_SUCCESS ) { swprintf( AccountName, L"%ws$", TrustingInfo->FlatName->Buffer ); LOG_VERBOSE(( MSG_VERBOSE_DELETE_TACCT, AccountName )); Win32Err = NetUserDel( FullServer, AccountName ); } if ( FullServer != TrustedInfo->Server ) { NetApiBufferFree( FullServer ); } return( Win32Err ); } DWORD NetDompTrustRemoveOutgoingDownlevelObject( IN PND5_TRUST_INFO TrustingInfo, IN PND5_TRUST_INFO TrustedInfo ) /*++ Routine Description: This function deletes the trust object/secret on the trusting domain. Arguments: TrustingInfo - Info on the trusting domain TrustedInfo - Info on the trusted domain Return Value: ERROR_INVALID_PARAMETER - No object name was supplied --*/ { DWORD Win32Err = ERROR_SUCCESS; WCHAR SecretName[ DNLEN + 4 ]; LSA_HANDLE TrustedDomain, SecretHandle; NTSTATUS Status; UNICODE_STRING Secret; if ( TrustedInfo->FlatName->Length > DNLEN * sizeof( WCHAR ) ) { return( ERROR_INVALID_DOMAINNAME ); } if ( !TrustedInfo->Sid ) { // Must be an orphaned trust, nothing we can do. return NO_ERROR; } // // Build the secret name // swprintf( SecretName, L"%ws$%ws", LSA_GLOBAL_SECRET_PREFIX, TrustedInfo->FlatName->Buffer ); // // Ok, first, delete the trust object. It's ok if the trust object is deleted but the // secret is not // LOG_VERBOSE(( MSG_VERBOSE_OPEN_TRUST, TrustedInfo->DomainName->Buffer )); Status = LsaOpenTrustedDomain( TrustingInfo->LsaHandle, TrustedInfo->Sid, DELETE, &TrustedDomain ); if ( NT_SUCCESS( Status ) ) { LOG_VERBOSE(( MSG_VERBOSE_DELETE_TRUST, TrustedInfo->DomainName->Buffer )); Status = LsaDelete( TrustedDomain ); if ( !NT_SUCCESS( Status ) ) { LsaClose( TrustedDomain ); } } // // Now, the same with the secret // if ( NT_SUCCESS( Status ) ) { RtlInitUnicodeString( &Secret, SecretName ); LOG_VERBOSE(( MSG_VERBOSE_OPEN_SECRET, Secret.Buffer )); Status = LsaOpenSecret( TrustingInfo->LsaHandle, &Secret, DELETE, &SecretHandle ); if ( NT_SUCCESS( Status ) ) { LOG_VERBOSE(( MSG_VERBOSE_DELETE_SECRET, Secret.Buffer )); Status = LsaDelete( SecretHandle ); if ( !NT_SUCCESS( Status ) ) { LsaClose( SecretHandle ); } } } Win32Err = RtlNtStatusToDosError( Status ); return( Win32Err ); } DWORD NetDompTrustSetPasswordSam( IN PWSTR Server, IN PWSTR AccountName, IN PWSTR Password ) /*++ Routine Description: This function will set the password on a SAM trust object Arguments: Server - Server that holds the account object AccountName - Name of the account Password - Password to set Return Value: ERROR_SUCCESS - The function succeeded --*/ { DWORD Win32Err = ERROR_SUCCESS; USER_INFO_1 UI1, *ReadUI1 = NULL; PWSTR FullServer = NULL; if ( Server && *( Server ) != L'\\' ) { Win32Err = NetApiBufferAllocate( ( wcslen( Server ) + 3 ) * sizeof( WCHAR ), ( PVOID * )&FullServer ); if ( Win32Err == ERROR_SUCCESS ) { swprintf( FullServer, L"\\\\%ws", Server ); } } else { FullServer = Server; } if ( Win32Err == ERROR_SUCCESS ) { Win32Err = NetUserGetInfo( FullServer, AccountName, 1, ( LPBYTE * )&ReadUI1 ); if ( Win32Err == ERROR_SUCCESS ) { if ( !FLAG_ON( ReadUI1->usri1_flags, UF_INTERDOMAIN_TRUST_ACCOUNT ) ) { Win32Err = ERROR_SPECIAL_ACCOUNT; } else { ReadUI1->usri1_password = Password; ReadUI1->usri1_flags = UF_INTERDOMAIN_TRUST_ACCOUNT | UF_SCRIPT; Win32Err = NetUserSetInfo( FullServer, AccountName, 1, ( LPBYTE )ReadUI1, NULL ); } NetApiBufferFree( ReadUI1 ); } } if ( FullServer != Server ) { NetApiBufferFree( FullServer ); } return( Win32Err ); } DWORD NetDompTrustAddIncomingDownlevelObject( IN PND5_TRUST_INFO TrustingInfo, IN PND5_TRUST_INFO TrustedInfo, IN PWSTR TrustPassword, IN ULONG PasswordLength ) /*++ Routine Description: This function creates the interdomain trust account object on the trusted domain. Arguments: TrustingInfo - Info on the trusting domain TrustedInfo - Info on the trusted domain Return Value: ERROR_INVALID_PARAMETER - No object name was supplied --*/ { DWORD Win32Err = ERROR_SUCCESS; WCHAR AccountName[ UNLEN + 1 ]; USER_INFO_1 UI1; PWSTR FullServer = NULL; if ( TrustingInfo->FlatName->Length > DNLEN * sizeof( WCHAR ) ) { return( ERROR_INVALID_DOMAINNAME ); } // // Build the account name... // swprintf( AccountName, L"%ws$", TrustingInfo->FlatName->Buffer ); LOG_VERBOSE(( MSG_VERBOSE_ADD_TACCT, AccountName )); UI1.usri1_name = AccountName; UI1.usri1_password = TrustPassword; UI1.usri1_password_age = 0; UI1.usri1_priv = USER_PRIV_USER; UI1.usri1_home_dir = NULL; UI1.usri1_comment = NULL; UI1.usri1_flags = UF_INTERDOMAIN_TRUST_ACCOUNT | UF_SCRIPT; UI1.usri1_script_path = NULL; if ( TrustedInfo->Server && *( TrustedInfo->Server ) != L'\\' ) { Win32Err = NetApiBufferAllocate( ( wcslen( TrustedInfo->Server ) + 3 ) * sizeof( WCHAR ), ( PVOID * )&FullServer ); if ( Win32Err == ERROR_SUCCESS ) { swprintf( FullServer, L"\\\\%ws", TrustedInfo->Server ); } } else { FullServer = TrustedInfo->Server; } if ( Win32Err == ERROR_SUCCESS ) { Win32Err = NetUserAdd( FullServer, 1, ( LPBYTE )&UI1, NULL ); if ( Win32Err == NERR_UserExists ) { Win32Err = NetDompTrustSetPasswordSam( FullServer, AccountName, TrustPassword ); } } if ( FullServer != TrustedInfo->Server ) { NetApiBufferFree( FullServer ); } return( Win32Err ); } DWORD NetDompTrustAddOutgoingDownlevelObject( IN PND5_TRUST_INFO TrustingInfo, IN PND5_TRUST_INFO TrustedInfo, IN PWSTR TrustPassword, IN ULONG PasswordLength ) /*++ Routine Description: This function creates the trust secret on the trusting domain. Arguments: TrustingInfo - Info on the trusting domain TrustedInfo - Info on the trusted domain Return Value: ERROR_INVALID_PARAMETER - No object name was supplied --*/ { DWORD Win32Err = ERROR_SUCCESS; WCHAR SecretName[ DNLEN + 4 ]; LSA_HANDLE TrustedDomain = NULL, SecretHandle = NULL; NTSTATUS Status; UNICODE_STRING Secret, Password; BOOL DeleteSecret = FALSE; LSA_TRUST_INFORMATION TrustInfo; if ( TrustedInfo->FlatName->Length > DNLEN * sizeof( WCHAR ) ) { return( ERROR_INVALID_DOMAINNAME ); } // // Build the secret name // swprintf( SecretName, L"%ws$%ws", LSA_GLOBAL_SECRET_PREFIX, TrustedInfo->FlatName->Buffer ); // // Ok, first, create the secret, It's ok if the secret is created but the // trust object is not // RtlInitUnicodeString( &Secret, SecretName ); LOG_VERBOSE(( MSG_VERBOSE_CREATE_SECRET, Secret.Buffer )); Status = LsaCreateSecret( TrustingInfo->LsaHandle, &Secret, SECRET_SET_VALUE, &SecretHandle ); if ( NT_SUCCESS( Status ) ) { DeleteSecret = TRUE; } else if ( Status == STATUS_OBJECT_NAME_COLLISION ) { LOG_VERBOSE(( MSG_VERBOSE_OPEN_SECRET, Secret.Buffer )); Status = LsaOpenSecret( TrustingInfo->LsaHandle, &Secret, SECRET_SET_VALUE, &SecretHandle ); } if ( NT_SUCCESS( Status ) ) { RtlInitUnicodeString( &Password, TrustPassword ); Status = LsaSetSecret( SecretHandle, &Password, NULL ); } // // Ok, now create the trust object // if ( NT_SUCCESS( Status ) ) { TrustInfo.Sid = TrustedInfo->Sid; RtlCopyMemory( &TrustInfo.Name, TrustedInfo->FlatName, sizeof( UNICODE_STRING ) ); LOG_VERBOSE(( MSG_VERBOSE_CREATE_TRUST, TrustInfo.Name.Buffer )); Status = LsaCreateTrustedDomain( TrustingInfo->LsaHandle, &TrustInfo, TRUSTED_QUERY_DOMAIN_NAME, &TrustedDomain ); if ( Status == STATUS_OBJECT_NAME_COLLISION ) { Status = STATUS_SUCCESS; } } if ( !NT_SUCCESS( Status ) && DeleteSecret ) { LsaDelete( SecretHandle ); SecretHandle = NULL; } Win32Err = RtlNtStatusToDosError( Status ); if ( SecretHandle ) { LsaClose( SecretHandle ); } if ( TrustedDomain ) { LsaClose( TrustedDomain ); } return( Win32Err ); } DWORD NetDompAddOnTrustingSide( IN PND5_TRUST_INFO TrustedInfo, IN PND5_TRUST_INFO TrustingInfo, IN PWSTR TrustPassword, IN ULONG Direction, IN BOOL Mit ) /*++ Routine Description: This function creates the trust object on the trusting domain. Arguments: TrustedInfo - Info on the trusted domain TrustingInfo - Info on the trusting domain Direction - The direction of the trust Mit - If true, this is an MIT style trust Return Value: ERROR_INVALID_PARAMETER - No object name was supplied --*/ { DWORD Win32Err = ERROR_SUCCESS; TRUSTED_DOMAIN_INFORMATION_EX TDIEx; PTRUSTED_DOMAIN_FULL_INFORMATION pFullInfo; TRUSTED_DOMAIN_AUTH_INFORMATION TDAI; LSA_AUTH_INFORMATION AuthData; ULONG PasswordLength = wcslen(TrustPassword); NTSTATUS Status; BOOL fSidSet = FALSE; PLSA_UNICODE_STRING pName; if (TrustingInfo->Flags & NETDOM_TRUST_FLAG_DOMAIN_NOT_FOUND) { if (Mit) { return ERROR_SUCCESS; } else { return ERROR_INVALID_PARAMETER; } } if ( TrustingInfo->Uplevel ) { RtlCopyMemory( &TDIEx.Name, TrustedInfo->DomainName, sizeof( UNICODE_STRING ) ); TDIEx.Sid = TrustedInfo->Sid; TDIEx.TrustDirection = Direction; if ( Mit ) { TDIEx.TrustType = TRUST_TYPE_MIT; TDIEx.TrustAttributes = TRUST_ATTRIBUTE_NON_TRANSITIVE; RtlCopyMemory( &TDIEx.FlatName, TrustedInfo->DomainName, sizeof( UNICODE_STRING ) ); } else { TDIEx.TrustType = TrustedInfo->Uplevel ? TRUST_TYPE_UPLEVEL : TRUST_TYPE_DOWNLEVEL; TDIEx.TrustAttributes = 0; RtlCopyMemory( &TDIEx.FlatName, TrustedInfo->FlatName, sizeof( UNICODE_STRING ) ); } Status = NtQuerySystemTime( &AuthData.LastUpdateTime ); if ( NT_SUCCESS( Status ) ) { AuthData.AuthType = TRUST_AUTH_TYPE_CLEAR; AuthData.AuthInfoLength = PasswordLength * sizeof( WCHAR ); AuthData.AuthInfo = ( PUCHAR )TrustPassword; TDAI.OutgoingAuthInfos = 1; TDAI.OutgoingAuthenticationInformation = &AuthData; TDAI.OutgoingPreviousAuthenticationInformation = NULL; if ( FLAG_ON( Direction, TRUST_DIRECTION_INBOUND ) ) { TDAI.IncomingAuthInfos = 1; TDAI.IncomingAuthenticationInformation = &AuthData; TDAI.IncomingPreviousAuthenticationInformation = NULL; } else { TDAI.IncomingAuthInfos = 0; TDAI.IncomingAuthenticationInformation = NULL; TDAI.IncomingPreviousAuthenticationInformation = NULL; } Status = LsaCreateTrustedDomainEx( TrustingInfo->LsaHandle, &TDIEx, &TDAI, TRUSTED_ALL_ACCESS, &( TrustingInfo->TrustHandle ) ); // // If the object already exists, morph our info into it. // if ( Status == STATUS_OBJECT_NAME_COLLISION ) { pName = &TDIEx.Name; Status = LsaQueryTrustedDomainInfoByName(TrustingInfo->LsaHandle, pName, TrustedDomainFullInformation, (PVOID*)&pFullInfo); if (STATUS_OBJECT_NAME_NOT_FOUND == Status) { // Now try by flat name; can get here if a downlevel domain // is upgraded to NT5. The name used above was the DNS name // but the TDO would be named after the flat name. // pName = TrustingInfo->FlatName; Status = LsaQueryTrustedDomainInfoByName(TrustedInfo->LsaHandle, pName, TrustedDomainFullInformation, (PVOID*)&pFullInfo); } if ( NT_SUCCESS( Status ) ) { if ( pFullInfo->Information.TrustDirection == Direction ) { Status = STATUS_OBJECT_NAME_COLLISION; } else { pFullInfo->Information.TrustDirection = TRUST_DIRECTION_BIDIRECTIONAL; pFullInfo->AuthInformation.OutgoingAuthInfos = 1; pFullInfo->AuthInformation.OutgoingAuthenticationInformation = &AuthData; pFullInfo->AuthInformation.OutgoingPreviousAuthenticationInformation = NULL; // Check for a NULL domain SID. The SID can be NULL if the // trust was created when the domain was still NT4. // if (!pFullInfo->Information.Sid) { pFullInfo->Information.Sid = TrustedInfo->Sid; fSidSet = TRUE; } Status = LsaSetTrustedDomainInfoByName(TrustingInfo->LsaHandle, pName, TrustedDomainFullInformation, pFullInfo); } } if (fSidSet) { // Sid memory is owned by the TrustingInfo struct, so don't free // it here. // pFullInfo->Information.Sid = NULL; } LsaFreeMemory( pFullInfo ); } } Win32Err = RtlNtStatusToDosError( Status ); } else { // // Doing downlevel // if ( Mit ) { Win32Err = ERROR_INVALID_PARAMETER; } else { Win32Err = NetDompTrustAddOutgoingDownlevelObject( TrustingInfo, TrustedInfo, TrustPassword, PasswordLength ); if ( Win32Err == ERROR_SUCCESS && Direction == TRUST_DIRECTION_BIDIRECTIONAL ) { Win32Err = NetDompTrustAddIncomingDownlevelObject( TrustedInfo, TrustingInfo, TrustPassword, PasswordLength ); if ( Win32Err != ERROR_SUCCESS ) { Win32Err = NetDompTrustRemoveOutgoingDownlevelObject( TrustingInfo, TrustedInfo ); } } } } if (ERROR_SUCCESS == Win32Err) { LOG_VERBOSE((MSG_VERBOSE_CREATED_TRUST, TrustedInfo->DomainName->Buffer, TrustingInfo->DomainName->Buffer)); } return( Win32Err ); } DWORD NetDompAddOnTrustedSide( IN PND5_TRUST_INFO TrustedInfo, IN PND5_TRUST_INFO TrustingInfo, IN PWSTR TrustPassword, IN ULONG Direction, IN BOOL Mit ) /*++ Routine Description: This function creates the trust object on the trusted domain. Arguments: TrustedInfo - Info on the trusted domain TrustingInfo - Info on the trusting domain Direction - The direction of the trust Mit - If true, this is an MIT style trust Return Value: ERROR_INVALID_PARAMETER - No object name was supplied --*/ { DWORD Win32Err = ERROR_SUCCESS; TRUSTED_DOMAIN_INFORMATION_EX TDIEx; PTRUSTED_DOMAIN_FULL_INFORMATION pFullInfo; LSA_AUTH_INFORMATION AuthData; TRUSTED_DOMAIN_AUTH_INFORMATION TDAI; ULONG PasswordLength = wcslen(TrustPassword); NTSTATUS Status; BOOL fSidSet = FALSE; PLSA_UNICODE_STRING pName; if (TrustedInfo->Flags & NETDOM_TRUST_FLAG_DOMAIN_NOT_FOUND) { if (Mit) { return ERROR_SUCCESS; } else { return ERROR_INVALID_PARAMETER; } } if ( TrustedInfo->Uplevel) { RtlCopyMemory( &TDIEx.Name, TrustingInfo->DomainName, sizeof( UNICODE_STRING ) ); TDIEx.Sid = TrustingInfo->Sid; TDIEx.TrustDirection = Direction; if ( Mit ) { TDIEx.TrustType = TRUST_TYPE_MIT; TDIEx.TrustAttributes = TRUST_ATTRIBUTE_NON_TRANSITIVE; RtlCopyMemory( &TDIEx.FlatName, TrustingInfo->DomainName, sizeof( UNICODE_STRING ) ); } else { TDIEx.TrustType = TrustingInfo->Uplevel ? TRUST_TYPE_UPLEVEL : TRUST_TYPE_DOWNLEVEL; TDIEx.TrustAttributes = 0; RtlCopyMemory( &TDIEx.FlatName, TrustingInfo->FlatName, sizeof( UNICODE_STRING ) ); } Status = NtQuerySystemTime( &AuthData.LastUpdateTime ); if ( NT_SUCCESS( Status ) ) { AuthData.AuthType = TRUST_AUTH_TYPE_CLEAR; AuthData.AuthInfoLength = PasswordLength * sizeof( WCHAR ); AuthData.AuthInfo = ( PUCHAR )TrustPassword; TDAI.IncomingAuthInfos = 1; TDAI.IncomingAuthenticationInformation = &AuthData; TDAI.IncomingPreviousAuthenticationInformation = &AuthData; if ( FLAG_ON( Direction, TRUST_DIRECTION_OUTBOUND ) ) { TDAI.OutgoingAuthInfos = 1; TDAI.OutgoingAuthenticationInformation = &AuthData; TDAI.OutgoingPreviousAuthenticationInformation = NULL; } else { TDAI.OutgoingAuthInfos = 0; TDAI.OutgoingAuthenticationInformation = NULL; TDAI.OutgoingPreviousAuthenticationInformation = NULL; } Status = LsaCreateTrustedDomainEx( TrustedInfo->LsaHandle, &TDIEx, &TDAI, TRUSTED_ALL_ACCESS, &( TrustedInfo->TrustHandle ) ); // // If the object already exists, morph our info into it. // if ( Status == STATUS_OBJECT_NAME_COLLISION ) { pName = &TDIEx.Name; Status = LsaQueryTrustedDomainInfoByName(TrustedInfo->LsaHandle, pName, TrustedDomainFullInformation, (PVOID*)&pFullInfo); if (STATUS_OBJECT_NAME_NOT_FOUND == Status) { // Now try by flat name; can get here if a downlevel domain // is upgraded to NT5. The name used above was the DNS name // but the TDO would be named after the flat name. // pName = TrustingInfo->FlatName; Status = LsaQueryTrustedDomainInfoByName(TrustedInfo->LsaHandle, pName, TrustedDomainFullInformation, (PVOID*)&pFullInfo); } if ( NT_SUCCESS( Status ) ) { if ( pFullInfo->Information.TrustDirection == Direction ) { Status = STATUS_OBJECT_NAME_COLLISION; } else { pFullInfo->Information.TrustDirection = TRUST_DIRECTION_BIDIRECTIONAL; pFullInfo->AuthInformation.IncomingAuthInfos = 1; pFullInfo->AuthInformation.IncomingAuthenticationInformation = &AuthData; pFullInfo->AuthInformation.IncomingPreviousAuthenticationInformation = NULL; // Check for a NULL domain SID. The SID can be NULL if the // trust was created when the domain was still NT4. // if (!pFullInfo->Information.Sid) { pFullInfo->Information.Sid = TrustingInfo->Sid; fSidSet = TRUE; } Status = LsaSetTrustedDomainInfoByName(TrustedInfo->LsaHandle, pName, TrustedDomainFullInformation, pFullInfo); } } if (fSidSet) { // Sid memory is owned by the TrustingInfo struct, so don't free // it here. // pFullInfo->Information.Sid = NULL; } LsaFreeMemory( pFullInfo ); } } Win32Err = RtlNtStatusToDosError( Status ); } else { // // Doing downlevel // if ( Mit ) { Win32Err = ERROR_INVALID_PARAMETER; } else { Win32Err = NetDompTrustAddIncomingDownlevelObject( TrustingInfo, TrustedInfo, TrustPassword, PasswordLength ); if ( Win32Err == ERROR_SUCCESS && FLAG_ON( Direction, TRUST_DIRECTION_OUTBOUND ) ) { Win32Err = NetDompTrustAddOutgoingDownlevelObject( TrustedInfo, TrustingInfo, TrustPassword, PasswordLength ); if ( Win32Err != ERROR_SUCCESS ) { Win32Err = NetDompTrustRemoveIncomingDownlevelObject( TrustingInfo, TrustedInfo ); } } } } if (ERROR_SUCCESS == Win32Err) { LOG_VERBOSE((MSG_VERBOSE_CREATED_TRUST, TrustingInfo->DomainName->Buffer, TrustedInfo->DomainName->Buffer)); } return( Win32Err ); } DWORD NetDompResetTrustSC( IN PND5_TRUST_INFO TrustingInfo, IN PND5_TRUST_INFO TrustedInfo ) /*++ Routine Description: This function will reset the secure channel between two domains Arguments: TrustingInfo - Information on the trusting side of the domain TrustedInfo - Information on the trusted side of the domain Return Value: ERROR_SUCCESS - The function succeeded --*/ { DWORD Win32Err = ERROR_SUCCESS; PWSTR ScDomain = NULL; PNETLOGON_INFO_2 NetlogonInfo2 = NULL; PWSTR FullServer = NULL, pwzDomName = NULL; if (!TrustingInfo->Server) { return ERROR_INVALID_PARAMETER; } Win32Err = NetApiBufferAllocate( TrustedInfo->DomainName->Length + sizeof( WCHAR ) + ( wcslen( TrustedInfo->Server ) * sizeof( WCHAR ) ) + sizeof( WCHAR ), (PVOID*)&ScDomain ); if (ERROR_SUCCESS != Win32Err) { return Win32Err; } if (*(TrustingInfo->Server) == L'\\') { FullServer = TrustingInfo->Server; } else { Win32Err = NetApiBufferAllocate((wcslen(TrustingInfo->Server) + 3) * sizeof(WCHAR), (PVOID *)&FullServer); if (ERROR_SUCCESS != Win32Err) { return Win32Err; } swprintf(FullServer, L"\\\\%ws", TrustingInfo->Server); } if (TrustedInfo->fWasDownlevel) { pwzDomName = TrustedInfo->FlatName->Buffer; } else { pwzDomName = TrustedInfo->DomainName->Buffer; } if (*(TrustedInfo->Server) == L'\\') { swprintf(ScDomain, L"%ws%ws", pwzDomName, TrustedInfo->Server + 1); } else { swprintf(ScDomain, L"%ws\\%ws", pwzDomName, TrustedInfo->Server); } if ( Win32Err == ERROR_SUCCESS ) { LOG_VERBOSE(( MSG_VERBOSE_RESET_SC, ScDomain )); Win32Err = I_NetLogonControl2( FullServer, NETLOGON_CONTROL_REDISCOVER, 2, ( LPBYTE )&ScDomain, ( LPBYTE *)&NetlogonInfo2 ); if ( Win32Err == ERROR_NO_SUCH_DOMAIN || Win32Err == ERROR_INVALID_PARAMETER ) { LOG_VERBOSE(( MSG_VERBOSE_RETRY_RESET_SC, ScDomain, TrustedInfo->DomainName->Buffer )); // // Must be using an downlevel domain, so try it again with out the server // Win32Err = I_NetLogonControl2( FullServer, NETLOGON_CONTROL_REDISCOVER, 2, ( LPBYTE )&( TrustedInfo->DomainName->Buffer ), ( LPBYTE *)&NetlogonInfo2 ); if ( Win32Err == ERROR_SUCCESS ) { LOG_VERBOSE(( MSG_VERBOSE_RESET_NOT_NAMED, TrustedInfo->Server )); } } } NetApiBufferFree( ScDomain ); if (FullServer != TrustingInfo->Server) { NetApiBufferFree(FullServer); } return( Win32Err ); } DWORD NetDompTrustRemoveObject( IN PND5_TRUST_INFO LocalDomainInfo, IN PND5_TRUST_INFO TrustDomainInfo, IN ULONG Direction, IN BOOL fForce, IN PND5_AUTH_INFO pAuthInfo ) /*++ Routine Description: This function removes the specified trust Arguments: LocalDomainInfo - Info on the domain where the operation is being performed TrustDomainInfo - Info on the trusted/trusting domain Direction - The direction of the trust fForce - If true, remove even if a child. Return Value: ERROR_INVALID_PARAMETER - No object name was supplied --*/ { DWORD Win32Err = ERROR_SUCCESS; NTSTATUS Status = STATUS_SUCCESS; PTRUSTED_DOMAIN_INFORMATION_EX ReadTDIEx = NULL; BOOL fChild = (TrustDomainInfo->Flags & NETDOM_TRUST_FLAG_CHILD); PLDAP pLdap; HANDLE hDS; PWSTR pwzConfigPath, pwzPartitionsPath, pwzFSMORoleOwner, pwzServerPath, pwzServerDNSname, pwzDomainName, pwzNcName, pwzServerObjDN, pwzSettingsDN, pwzFilter; WCHAR wzPartition[] = L"CN=Partitions,"; WCHAR wzFilterFormat[] = L"(&(objectClass=nTDSDSA)(hasMasterNCs=%s))"; RPC_AUTH_IDENTITY_HANDLE AuthHandle; PDS_NAME_RESULT pNameResult; LDAPMessage *Message = NULL, *Entry; PWSTR Attrib[2] = { L"foo", NULL }; ASSERT(!(fChild && !fForce)); ASSERT(!(TrustDomainInfo->Flags & NETDOM_TRUST_FLAG_PARENT)); if ( LocalDomainInfo->Uplevel ) { LOG_VERBOSE(( MSG_VERBOSE_OPEN_TRUST, TrustDomainInfo->DomainName->Buffer )); if ( !LocalDomainInfo->TrustHandle ) { Status = LsaOpenTrustedDomainByName( LocalDomainInfo->LsaHandle, TrustDomainInfo->DomainName, TRUSTED_ALL_ACCESS, // DELETE | TRUSTED_QUERY_DOMAIN_NAME, &( LocalDomainInfo->TrustHandle ) ); } if (STATUS_OBJECT_NAME_NOT_FOUND == Status && TrustDomainInfo->Uplevel) { // Pre-existing TDOs for domains upgraded from NT4 to NT5 will continue to // have a flat name. // TrustDomainInfo->fWasDownlevel = TRUE; Status = LsaOpenTrustedDomainByName( LocalDomainInfo->LsaHandle, TrustDomainInfo->FlatName, TRUSTED_ALL_ACCESS, // DELETE | TRUSTED_QUERY_DOMAIN_NAME, &( LocalDomainInfo->TrustHandle ) ); } if ( NT_SUCCESS( Status ) ) { if ( Direction == TRUST_DIRECTION_BIDIRECTIONAL ) { LOG_VERBOSE(( MSG_VERBOSE_DELETE_TRUST, TrustDomainInfo->DomainName->Buffer )); Status = LsaDelete( LocalDomainInfo->TrustHandle ); if ( NT_SUCCESS( Status ) ) { LocalDomainInfo->TrustHandle = NULL; } } else { LOG_VERBOSE(( MSG_VERBOSE_GET_TRUST, TrustDomainInfo->DomainName->Buffer )); Status = LsaQueryInfoTrustedDomain( LocalDomainInfo->TrustHandle, TrustedDomainInformationEx, (PVOID*)&ReadTDIEx ); if ( NT_SUCCESS( Status ) ) { ReadTDIEx->TrustDirection &= ~Direction; if ( 0 == ReadTDIEx->TrustDirection ) { LOG_VERBOSE(( MSG_VERBOSE_DELETE_TRUST, TrustDomainInfo->DomainName->Buffer )); Status = LsaDelete( LocalDomainInfo->TrustHandle ); if ( NT_SUCCESS( Status ) ) { LocalDomainInfo->TrustHandle = NULL; } } else { LOG_VERBOSE(( MSG_VERBOSE_SET_TRUST, TrustDomainInfo->DomainName->Buffer )); Status = LsaSetInformationTrustedDomain( LocalDomainInfo->TrustHandle, TrustedDomainInformationEx, ReadTDIEx ); } LsaFreeMemory( ReadTDIEx ); } } } else { if (STATUS_OBJECT_NAME_NOT_FOUND == Status) { LOG_VERBOSE(( MSG_VERBOSE_TDO_NOT_FOUND, TrustDomainInfo->DomainName->Buffer )); } } Win32Err = LsaNtStatusToWinError(Status); DBG_VERBOSE(("Return code from LsaOpen: %d (LSA: %x)\n", Win32Err, Status)); if (fChild) { // Remove the cross ref object. To do that, locate and bind to the naming // FSMO DC. // LOG_VERBOSE((MSG_VERBOSE_DELETE_CROSS_REF, TrustDomainInfo->DomainName->Buffer)); Win32Err = NetDompLdapBind(LocalDomainInfo->Server + 2, // skip the backslashes (pAuthInfo->pwzUsersDomain) ? pAuthInfo->pwzUsersDomain : NULL, (pAuthInfo->pwzUserWoDomain) ? pAuthInfo->pwzUserWoDomain : pAuthInfo->User, pAuthInfo->Password, LDAP_AUTH_SSPI, &pLdap); if (Win32Err != ERROR_SUCCESS) { return Win32Err; } DBG_VERBOSE(("bind succeeded\n")); Win32Err = NetDompLdapReadOneAttribute(pLdap, NULL, //L"RootDSE", L"configurationNamingContext", &pwzConfigPath); if (Win32Err != ERROR_SUCCESS) { NetDompLdapUnbind(pLdap); return Win32Err; } Win32Err = NetApiBufferAllocate((wcslen(pwzConfigPath) + wcslen(wzPartition) + 1) * sizeof(WCHAR), (PVOID*)&pwzPartitionsPath); if (Win32Err != ERROR_SUCCESS) { NetApiBufferFree(pwzConfigPath); NetDompLdapUnbind(pLdap); return Win32Err; } wsprintf(pwzPartitionsPath, L"%s%s", wzPartition, pwzConfigPath); DBG_VERBOSE(("path: %ws\n", pwzPartitionsPath)); Win32Err = NetDompLdapReadOneAttribute(pLdap, pwzPartitionsPath, L"fSMORoleOwner", &pwzFSMORoleOwner); NetApiBufferFree(pwzPartitionsPath); if (Win32Err != ERROR_SUCCESS) { NetApiBufferFree(pwzConfigPath); NetDompLdapUnbind(pLdap); return Win32Err; } DBG_VERBOSE(("fSMORoleOwner: %ws\n", pwzFSMORoleOwner)); pwzServerPath = wcschr(pwzFSMORoleOwner, L','); if (!pwzServerPath) { NetApiBufferFree(pwzConfigPath); NetApiBufferFree(pwzFSMORoleOwner); NetDompLdapUnbind(pLdap); return ERROR_INVALID_DATA; } pwzServerPath++; Win32Err = NetDompLdapReadOneAttribute(pLdap, pwzServerPath, L"dNSHostName", &pwzServerDNSname); NetApiBufferFree(pwzFSMORoleOwner); if (Win32Err != ERROR_SUCCESS) { NetApiBufferFree(pwzConfigPath); NetDompLdapUnbind(pLdap); return Win32Err; } DBG_VERBOSE(("Role owner server DNS name: %ws\n", pwzServerDNSname)); Win32Err = DsMakePasswordCredentials((pAuthInfo->pwzUserWoDomain) ? pAuthInfo->pwzUserWoDomain : pAuthInfo->User, (pAuthInfo->pwzUsersDomain) ? pAuthInfo->pwzUsersDomain : NULL, pAuthInfo->Password, &AuthHandle); if (Win32Err != ERROR_SUCCESS) { NetApiBufferFree(pwzConfigPath); NetApiBufferFree(pwzServerDNSname); return Win32Err; } Win32Err = DsBindWithCred(pwzServerDNSname, NULL, AuthHandle, &hDS); NetApiBufferFree(pwzServerDNSname); DsFreePasswordCredentials(AuthHandle); if (Win32Err != ERROR_SUCCESS) { NetApiBufferFree(pwzConfigPath); return Win32Err; } DBG_VERBOSE(("DsBind Succeeded, getting domain NC name.\n")); Win32Err = NetApiBufferAllocate((wcslen(TrustDomainInfo->DomainName->Buffer) + 2) * sizeof(WCHAR), (PVOID*)&pwzDomainName); if (Win32Err != ERROR_SUCCESS) { NetApiBufferFree(pwzConfigPath); NetDompLdapUnbind(pLdap); DsUnBind(&hDS); return Win32Err; } // // Get the domain Naming Context DN to use for the removal of the cross-ref. // On the first try to get the domain NC DN, assume that the domain name is // an NT4 flat name. A backslash must be appended. // wcscpy(pwzDomainName, TrustDomainInfo->DomainName->Buffer); wcscat(pwzDomainName, L"\\"); DBG_VERBOSE(("Name passed to DsCrackNames is %ws.\n", pwzDomainName)); Win32Err = DsCrackNames(hDS, DS_NAME_NO_FLAGS, DS_NT4_ACCOUNT_NAME, DS_FQDN_1779_NAME, 1, &pwzDomainName, &pNameResult); if (Win32Err != ERROR_SUCCESS) { NetApiBufferFree(pwzConfigPath); NetApiBufferFree(pwzDomainName); NetDompLdapUnbind(pLdap); DsUnBind(&hDS); return Win32Err; } ASSERT(pNameResult); ASSERT(pNameResult->cItems == 1); if (DS_NAME_NO_ERROR != pNameResult->rItems->status) { // Try again, this time assume that it might be a DNS name. Replace // the back slash with a forward slash. // pwzDomainName[wcslen(pwzDomainName) -1] = L'/'; DsFreeNameResultW(pNameResult); DBG_VERBOSE(("Try again, name passed to DsCrackNames is %ws.\n", pwzDomainName)); Win32Err = DsCrackNames(hDS, DS_NAME_NO_FLAGS, DS_CANONICAL_NAME, DS_FQDN_1779_NAME, 1, &pwzDomainName, &pNameResult); if (Win32Err != ERROR_SUCCESS) { NetApiBufferFree(pwzConfigPath); NetApiBufferFree(pwzDomainName); NetDompLdapUnbind(pLdap); DsUnBind(&hDS); return Win32Err; } ASSERT(pNameResult); ASSERT(pNameResult->cItems == 1); } NetApiBufferFree(pwzDomainName); if (DS_NAME_NO_ERROR != pNameResult->rItems->status) { NetApiBufferFree(pwzConfigPath); DsFreeNameResultW(pNameResult); NetDompLdapUnbind(pLdap); DsUnBind(&hDS); return ERROR_NO_SUCH_DOMAIN; } // // Delete the Server object for the domain. Get the name of the server // object by searching for the NTDS-Settings object that references the // domain NC. // Win32Err = NetApiBufferAllocate((wcslen(wzFilterFormat) + wcslen(pNameResult->rItems->pName) + 1) * sizeof(WCHAR), (PVOID*)&pwzFilter); if (Win32Err != ERROR_SUCCESS) { DsFreeNameResultW(pNameResult); NetApiBufferFree(pwzConfigPath); NetDompLdapUnbind(pLdap); DsUnBind(&hDS); return Win32Err; } swprintf(pwzFilter, wzFilterFormat, pNameResult->rItems->pName); DBG_VERBOSE(("search filter: %ws\n", pwzFilter)); Win32Err = LdapMapErrorToWin32(ldap_search_s(pLdap, pwzConfigPath, LDAP_SCOPE_SUBTREE, pwzFilter, Attrib, 0, &Message)); if (Win32Err != ERROR_SUCCESS) { DBG_VERBOSE(("search for Settings object failed with error %d\n", Win32Err)); NetApiBufferFree(pwzConfigPath); DsFreeNameResultW(pNameResult); NetDompLdapUnbind(pLdap); DsUnBind(&hDS); return Win32Err; } Entry = ldap_first_entry(pLdap, Message); if (Entry) { pwzSettingsDN = ldap_get_dnW(pLdap, Entry); DBG_VERBOSE(("NTDS Settings object DN: %ws\n", pwzSettingsDN)); if (pwzSettingsDN) { pwzServerObjDN = wcschr(pwzSettingsDN, L','); if (pwzServerObjDN) { pwzServerObjDN++; Win32Err = DsRemoveDsServerW(hDS, pwzServerObjDN, NULL, NULL, TRUE); if (Win32Err == ERROR_SUCCESS) { LOG_VERBOSE((MSG_VERBOSE_NTDSDSA_DELETED, pwzServerObjDN)); } else { DBG_VERBOSE(("DsRemoveDsServer failed with error %d\n", Win32Err)); LOG_VERBOSE((MSG_VERBOSE_NTDSDSA_NOT_REMOVED, pwzServerObjDN)); } } } ldap_memfree(pwzSettingsDN); } else { Win32Err = LdapMapErrorToWin32(pLdap->ld_errno); DBG_VERBOSE(("search results for Settings object failed with error %d\n", Win32Err)); } ldap_msgfree(Message); NetDompLdapUnbind(pLdap); // add to error returns above. NetApiBufferFree(pwzConfigPath); // // Now remove the cross-ref object. // DBG_VERBOSE(("About to remove the cross-ref for NC %ws.\n", pNameResult->rItems->pName)); Win32Err = DsRemoveDsDomainW(hDS, pNameResult->rItems->pName); if (Win32Err == ERROR_SUCCESS) { LOG_VERBOSE((MSG_VERBOSE_CROSS_REF_DELETED, pNameResult->rItems->pName)); } else { DBG_VERBOSE(("DsRemoveDsDomain returned %d.\n", Win32Err)); if (ERROR_DS_NO_CROSSREF_FOR_NC == Win32Err) { LOG_VERBOSE((MSG_VERBOSE_CROSS_REF_NOT_FOUND, pNameResult->rItems->pName)); } } DsFreeNameResultW(pNameResult); DsUnBind(&hDS); } } else { if ( FLAG_ON( Direction, TRUST_DIRECTION_INBOUND ) ) { Win32Err = NetDompTrustRemoveIncomingDownlevelObject( TrustDomainInfo, LocalDomainInfo ); if ( Win32Err == NERR_UserNotFound ) { Win32Err = ERROR_SUCCESS; } } if ( Win32Err == ERROR_SUCCESS && FLAG_ON( Direction, TRUST_DIRECTION_OUTBOUND ) ) { Win32Err = NetDompTrustRemoveOutgoingDownlevelObject( LocalDomainInfo, TrustDomainInfo ); } } return( Win32Err ); } DWORD NetDompCreateTrustObject( IN ARG_RECORD * rgNetDomArgs, IN PWSTR TrustingDomain, IN PWSTR TrustedDomain, IN PND5_AUTH_INFO pTrustingCreds, IN PND5_AUTH_INFO pTrustedCreds, IN PWSTR pwzTrustPW, IN PWSTR pwzWhichSide ) /*++ Routine Description: This function will handle the adding of a trusted domain object Arguments: rgNetDomArgs - List of arguments present in the Args list TrustingDomain - Trusting side of the trust TrustedDomain - Trusted side of the trust pTrustingCreds - Credentials to use when connecting to a domain controller in the trusting domain pTrustedCreds - Credentials to use when connecting to a domain controller in the trusted domain pwzTrustPW - Required for creating MIT trust. pwzWhichSide - Required for creating one-side-only, names the side. Return Value: ERROR_INVALID_PARAMETER - No object name was supplied --*/ { DWORD Win32Err = ERROR_SUCCESS; ND5_TRUST_INFO TrustingInfo, TrustedInfo; WCHAR TrustPassword[ LM20_PWLEN + 1 ]; PWSTR pwzPW = NULL; WCHAR wzTrusted[NETDOM_STR_LEN], wzTrusting[NETDOM_STR_LEN]; BOOL fCreateOnTrusted = FALSE, fCreateOnTrusting = FALSE; RtlZeroMemory( &TrustingInfo, sizeof( TrustingInfo ) ); RtlZeroMemory( &TrustedInfo, sizeof( TrustedInfo ) ); if (CmdFlagOn(rgNetDomArgs, eTrustOneSide)) { if (!LoadString(g_hInstance, IDS_ONESIDE_TRUSTED, wzTrusted, NETDOM_STR_LEN) || !LoadString(g_hInstance, IDS_ONESIDE_TRUSTING, wzTrusting, NETDOM_STR_LEN)) { printf("LoadString FAILED!\n"); return ERROR_RESOURCE_NAME_NOT_FOUND; } // Determine on which domain the trust should be created. // if (_wcsicmp(pwzWhichSide, wzTrusted) == 0) { fCreateOnTrusted = TRUE; } else if (_wcsicmp(pwzWhichSide, wzTrusting) == 0) { fCreateOnTrusting = TRUE; } else { NetDompDisplayMessage(MSG_ONESIDE_ARG_STRING); return ERROR_INVALID_PARAMETER; } } Win32Err = NetDompTrustGetDomInfo( TrustingDomain, NULL, pTrustingCreds, &TrustingInfo, !fCreateOnTrusted, CmdFlagOn(rgNetDomArgs, eTrustRealm), fCreateOnTrusted); if ( Win32Err == ERROR_SUCCESS ) { Win32Err = NetDompTrustGetDomInfo( TrustedDomain, NULL, pTrustedCreds, &TrustedInfo, !fCreateOnTrusting, CmdFlagOn(rgNetDomArgs, eTrustRealm), fCreateOnTrusting); } if ( Win32Err != ERROR_SUCCESS ) { goto TrustAddExit; } if (CmdFlagOn(rgNetDomArgs, eTrustRealm)) { if (!(TrustingInfo.Flags & NETDOM_TRUST_FLAG_DOMAIN_NOT_FOUND) && !(TrustedInfo.Flags & NETDOM_TRUST_FLAG_DOMAIN_NOT_FOUND)) { // Both domains found (both are Windows domains), can't establish an MIT trust. // NetDompDisplayMessage(MSG_RESET_MIT_TRUST_NOT_MIT); Win32Err = ERROR_INVALID_PARAMETER; goto TrustAddExit; } pwzPW = pwzTrustPW; } else if (CmdFlagOn(rgNetDomArgs, eTrustOneSide)) { // Create the trust on only one of the two domains. // if (fCreateOnTrusted) { Win32Err = NetDompAddOnTrustedSide(&TrustedInfo, &TrustingInfo, pwzTrustPW, CmdFlagOn(rgNetDomArgs, eTrustTwoWay) ? TRUST_DIRECTION_BIDIRECTIONAL : TRUST_DIRECTION_INBOUND, FALSE); } else { Win32Err = NetDompAddOnTrustingSide(&TrustedInfo, &TrustingInfo, pwzTrustPW, CmdFlagOn(rgNetDomArgs, eTrustTwoWay) ? TRUST_DIRECTION_BIDIRECTIONAL : TRUST_DIRECTION_OUTBOUND, FALSE); } goto TrustAddExit; } else { Win32Err = NetDompGenerateRandomPassword( TrustPassword, LM20_PWLEN ); if ( Win32Err != ERROR_SUCCESS ) { goto TrustAddExit; } pwzPW = TrustPassword; } // // Ok, now that we have the password, let's create the trust // Win32Err = NetDompAddOnTrustedSide( &TrustedInfo, &TrustingInfo, pwzPW, CmdFlagOn(rgNetDomArgs, eTrustTwoWay) ? TRUST_DIRECTION_BIDIRECTIONAL : TRUST_DIRECTION_INBOUND, CmdFlagOn(rgNetDomArgs, eTrustRealm)); if ( Win32Err == ERROR_SUCCESS ) { Win32Err = NetDompAddOnTrustingSide( &TrustedInfo, &TrustingInfo, pwzPW, CmdFlagOn(rgNetDomArgs, eTrustTwoWay) ? TRUST_DIRECTION_BIDIRECTIONAL : TRUST_DIRECTION_OUTBOUND, CmdFlagOn(rgNetDomArgs, eTrustRealm)); } TrustAddExit: NetDompFreeDomInfo( &TrustedInfo ); NetDompFreeDomInfo( &TrustingInfo ); return( Win32Err ); } DWORD NetDompRemoveTrustObject( IN ARG_RECORD * rgNetDomArgs, IN PWSTR TrustingDomain, IN PWSTR TrustedDomain, IN PND5_AUTH_INFO pTrustingCreds, IN PND5_AUTH_INFO pTrustedCreds ) /*++ Routine Description: This function will handle the removal of a trusted domain object Arguments: rgNetDomArgs - List of arguments present in the Args list TrustingDomain - Trusting side of the trust TrustedDomain - Trusted side of the trust pTrustingCreds - Credentials to use when connecting to a domain controller in the trusting domain pTrustedCreds - Credentials to use when connecting to a domain controller in the trusted domain Return Value: ERROR_INVALID_PARAMETER - No object name was supplied --*/ { DWORD Win32Err = ERROR_SUCCESS; ND5_TRUST_INFO TrustingInfo, TrustedInfo; BOOL fForce = CmdFlagOn(rgNetDomArgs, eCommForce); BOOL TwoWay = CmdFlagOn(rgNetDomArgs, eTrustTwoWay); BOOL fParentChild = FALSE, fVerifyChildDelete = FALSE; RtlZeroMemory( &TrustingInfo, sizeof( TrustingInfo ) ); RtlZeroMemory( &TrustedInfo, sizeof( TrustedInfo ) ); Win32Err = NetDompTrustGetDomInfo( TrustingDomain, NULL, pTrustingCreds, &TrustingInfo, TRUE, fForce, FALSE ); if ( Win32Err == ERROR_SUCCESS ) { Win32Err = NetDompTrustGetDomInfo( TrustedDomain, NULL, pTrustedCreds, &TrustedInfo, TRUE, fForce, FALSE ); } if ( Win32Err != ERROR_SUCCESS ) { if (ERROR_NO_SUCH_DOMAIN == Win32Err) { NetDompDisplayMessage(MSG_TRUST_DOMAIN_NOT_FOUND); } goto TrustRemoveExit; } Win32Err = NetDompIsParentChild(&TrustingInfo, &TrustedInfo, &fParentChild); if ( Win32Err != ERROR_SUCCESS ) { goto TrustRemoveExit; } if (fParentChild) { // Enforce rules for parent-child trust. // if (TrustingInfo.Flags & NETDOM_TRUST_FLAG_DOMAIN_NOT_FOUND) { if (TrustingInfo.Flags & NETDOM_TRUST_FLAG_PARENT) { // The domain that wasn't found was the parent, deletion not allowed. // NetDompDisplayMessage(MSG_CANT_DELETE_PARENT); printf("\n"); Win32Err = ERROR_NOT_SUPPORTED; goto TrustRemoveExit; } else { if (!fForce) { // Force flag required to delete non-existant child. // NetDompDisplayMessage(MSG_DELETE_CHILD_FORCE_REQ); printf("\n"); Win32Err = ERROR_NOT_SUPPORTED; goto TrustRemoveExit; } else { fVerifyChildDelete = TRUE; } } } else { if (TrustedInfo.Flags & NETDOM_TRUST_FLAG_DOMAIN_NOT_FOUND) { if (TrustedInfo.Flags & NETDOM_TRUST_FLAG_PARENT) { // The domain that wasn't found was the parent, deletion not allowed. // NetDompDisplayMessage(MSG_CANT_DELETE_PARENT); printf("\n"); Win32Err = ERROR_NOT_SUPPORTED; goto TrustRemoveExit; } else { if (!fForce) { // Force flag required to delete non-existant child. // NetDompDisplayMessage(MSG_DELETE_CHILD_FORCE_REQ); printf("\n"); Win32Err = ERROR_NOT_SUPPORTED; goto TrustRemoveExit; } else { fVerifyChildDelete = TRUE; } } } else { // Both domains were found, don't allow deletion. // NetDompDisplayMessage(MSG_CANT_DELETE_PARENT_CHILD); printf("\n"); Win32Err = ERROR_NOT_SUPPORTED; goto TrustRemoveExit; } } } if (fVerifyChildDelete) { // Put up a message box asking for confirmation of the deletion. // PWSTR pwzDomain = (TrustingInfo.Flags & NETDOM_TRUST_FLAG_DOMAIN_NOT_FOUND) ? TrustingInfo.DomainName->Buffer : TrustedInfo.DomainName->Buffer; if (!NetDompGetUserConfirmation(IDS_PROMPT_DEL_TRUST, pwzDomain)) { goto TrustRemoveExit; } } if (!(TrustingInfo.Flags & NETDOM_TRUST_FLAG_DOMAIN_NOT_FOUND)) { Win32Err = NetDompTrustRemoveObject( &TrustingInfo, &TrustedInfo, (TwoWay || fForce) ? TRUST_DIRECTION_BIDIRECTIONAL : TRUST_DIRECTION_OUTBOUND, fForce, pTrustingCreds ); } if ((Win32Err == ERROR_SUCCESS && !CmdFlagOn(rgNetDomArgs, eTrustRealm)) && !(TrustedInfo.Flags & NETDOM_TRUST_FLAG_DOMAIN_NOT_FOUND)) { Win32Err = NetDompTrustRemoveObject( &TrustedInfo, &TrustingInfo, (TwoWay || fForce) ? TRUST_DIRECTION_BIDIRECTIONAL : TRUST_DIRECTION_INBOUND, fForce, pTrustedCreds ); } TrustRemoveExit: NetDompFreeDomInfo( &TrustedInfo ); NetDompFreeDomInfo( &TrustingInfo ); return( Win32Err ); } DWORD NetDompSetTrustPW( IN PND5_TRUST_INFO pDomain1Info, IN PND5_TRUST_INFO pDomain2Info, IN PWSTR pwzNewTrustPW, OUT PDWORD pDirection ) /*++ Routine Description: This function will set the trust password on Domain1. Arguments: pDomain1Info - Domain on which to set the trust passwords pDomain2Info - Domain whose TDO should be set. pwzNewTrustPW - new trust password to use. Return Value: ERROR_INVALID_PARAMETER - No object name was supplied --*/ { DWORD Win32Err = ERROR_SUCCESS; PTRUSTED_DOMAIN_FULL_INFORMATION pOldDomain1TDFInfo = NULL; TRUSTED_DOMAIN_FULL_INFORMATION NewDomain1TDFInfo; LSA_AUTH_INFORMATION NewAuthInfo; LARGE_INTEGER ft; NTSTATUS Status = STATUS_SUCCESS; LOG_VERBOSE((MSG_VERBOSE_GET_TRUST, pDomain1Info->DomainName->Buffer)); if (pDomain2Info->Sid) { Status = LsaQueryTrustedDomainInfo(pDomain1Info->LsaHandle, pDomain2Info->Sid, TrustedDomainFullInformation, (PVOID *)&pOldDomain1TDFInfo); } else { Status = LsaQueryTrustedDomainInfoByName(pDomain1Info->LsaHandle, pDomain2Info->DomainName, TrustedDomainFullInformation, (PVOID *)&pOldDomain1TDFInfo); } Win32Err = LsaNtStatusToWinError(Status); CHECK_WIN32(Win32Err, goto TrustPwSetExit); if (pDirection) { *pDirection = pOldDomain1TDFInfo->Information.TrustDirection; } GetSystemTimeAsFileTime((PFILETIME)&ft); // // Set the current password data. // NewAuthInfo.LastUpdateTime = ft; NewAuthInfo.AuthType = TRUST_AUTH_TYPE_CLEAR; NewAuthInfo.AuthInfoLength = wcslen(pwzNewTrustPW) * sizeof(WCHAR); NewAuthInfo.AuthInfo = (PUCHAR)pwzNewTrustPW; ZeroMemory(&NewDomain1TDFInfo, sizeof(TRUSTED_DOMAIN_FULL_INFORMATION)); if (pOldDomain1TDFInfo->Information.TrustDirection & TRUST_DIRECTION_INBOUND) { NewDomain1TDFInfo.AuthInformation.IncomingAuthInfos = 1; NewDomain1TDFInfo.AuthInformation.IncomingAuthenticationInformation = &NewAuthInfo; NewDomain1TDFInfo.AuthInformation.IncomingPreviousAuthenticationInformation = NULL; NewDomain1TDFInfo.Information = pOldDomain1TDFInfo->Information; } if (pOldDomain1TDFInfo->Information.TrustDirection & TRUST_DIRECTION_OUTBOUND) { NewDomain1TDFInfo.AuthInformation.OutgoingAuthInfos = 1; NewDomain1TDFInfo.AuthInformation.OutgoingAuthenticationInformation = &NewAuthInfo; NewDomain1TDFInfo.AuthInformation.OutgoingPreviousAuthenticationInformation = NULL; NewDomain1TDFInfo.Information = pOldDomain1TDFInfo->Information; } LOG_VERBOSE((MSG_VERBOSE_SET_TRUST, pDomain1Info->DomainName->Buffer)); // Save changes. // Status = LsaSetTrustedDomainInfoByName(pDomain1Info->LsaHandle, pDomain2Info->DomainName, TrustedDomainFullInformation, &NewDomain1TDFInfo); if (STATUS_OBJECT_NAME_NOT_FOUND == Status && pDomain2Info->Uplevel) { // Pre-existing TDOs for domains upgraded from NT4 to NT5 will continue to // have a flat name. // pDomain2Info->fWasDownlevel = TRUE; Status = LsaSetTrustedDomainInfoByName(pDomain1Info->LsaHandle, pDomain2Info->FlatName, TrustedDomainFullInformation, &NewDomain1TDFInfo); } Win32Err = LsaNtStatusToWinError(Status); CHECK_WIN32(Win32Err, goto TrustPwSetExit); TrustPwSetExit: if (pOldDomain1TDFInfo) LsaFreeMemory(pOldDomain1TDFInfo); return Win32Err; } DWORD NetDompResetTrustPasswords( IN PWSTR pwzDomain1, IN PWSTR pwzDomain2, IN PND5_AUTH_INFO pDomain1Creds, IN PND5_AUTH_INFO pDomain2Creds ) /*++ Routine Description: This function will handle the password reset of the trusted domain objects Arguments: pwzDomain1, pwzDomain2 - Names of domains with trust. pDomain1Creds, pDomain2Creds - Credentials to use when connecting to a domain controller in the domain --*/ { DWORD Win32Err = ERROR_SUCCESS; NTSTATUS Status = STATUS_SUCCESS; ND5_TRUST_INFO Domain1Info, Domain2Info; WCHAR wzNewPw[MAX_COMPUTERNAME_LENGTH]; ULONG Length, i; LARGE_INTEGER ft; DWORD Direction; RtlZeroMemory( &Domain1Info, sizeof( Domain1Info ) ); RtlZeroMemory( &Domain2Info, sizeof( Domain2Info ) ); Win32Err = NetDompTrustGetDomInfo( pwzDomain1, NULL, pDomain1Creds, &Domain1Info, TRUE, FALSE, FALSE ); if (Win32Err == ERROR_SUCCESS) { Win32Err = NetDompTrustGetDomInfo( pwzDomain2, NULL, pDomain2Creds, &Domain2Info, TRUE, FALSE, FALSE ); } CHECK_WIN32(Win32Err, goto TrustResetExit); if (Domain1Info.Uplevel && Domain2Info.Uplevel) { NetDompDisplayMessage(MSG_RESET_TRUST_STARTING, pwzDomain1, pwzDomain2); } else { NetDompDisplayMessage(MSG_RESET_TRUST_NOT_UPLEVEL); goto TrustResetExit; } // // Build a random password // CDGenerateRandomBits((PUCHAR)wzNewPw, sizeof(wzNewPw)); // Terminate the password Length = MAX_COMPUTERNAME_LENGTH; Length--; wzNewPw[Length] = L'\0'; // Make sure there aren't any NULL's in the password for (i = 0; i < Length; i++) { if (wzNewPw[i] == L'\0') { // arbitrary letter wzNewPw[i] = L'c'; } } Win32Err = NetDompSetTrustPW(&Domain1Info, &Domain2Info, wzNewPw, &Direction); CHECK_WIN32(Win32Err, goto TrustResetExit); Win32Err = NetDompSetTrustPW(&Domain2Info, &Domain1Info, wzNewPw, NULL); CHECK_WIN32(Win32Err, goto TrustResetExit); // // Verify the repaired trust. // if (Direction & TRUST_DIRECTION_OUTBOUND) { Win32Err = NetDompResetTrustSC( &Domain1Info, &Domain2Info ); CHECK_WIN32(Win32Err, goto TrustResetExit); } if (Direction & TRUST_DIRECTION_INBOUND) { Win32Err = NetDompResetTrustSC( &Domain2Info, &Domain1Info ); CHECK_WIN32(Win32Err, goto TrustResetExit); } NetDompDisplayMessage(MSG_RESET_TRUST_OK, pwzDomain1, pwzDomain2); TrustResetExit: NetDompFreeDomInfo( &Domain2Info ); NetDompFreeDomInfo( &Domain1Info ); return( Win32Err ); } DWORD NetDompSetMitTrustPW( IN PWSTR pwzDomain1, IN PWSTR pwzDomain2, IN PND5_AUTH_INFO pDomain1Creds, IN PND5_AUTH_INFO pDomain2Creds, IN PWSTR pwzNewTrustPW ) /*++ Routine Description: This function will handle the password reset of an MIT trusted domain object Arguments: pwzDomain1, pwzDomain2 - Trusted domains pDomain1Creds - Credentials to use when connecting to a domain controller in domain21 pDomain2Creds - Credentials to use when connecting to a domain controller in domain 2 pwzNewTrustPW - new trust password to use. Return Value: ERROR_INVALID_PARAMETER - No object name was supplied --*/ { DWORD Win32Err = ERROR_SUCCESS; ND5_TRUST_INFO Domain1Info, Domain2Info; PND5_TRUST_INFO pDomFoundInfo, pMitDomInfo; RtlZeroMemory( &Domain1Info, sizeof( Domain1Info ) ); RtlZeroMemory( &Domain2Info, sizeof( Domain2Info ) ); Win32Err = NetDompTrustGetDomInfo( pwzDomain1, NULL, pDomain1Creds, &Domain1Info, TRUE, TRUE, FALSE ); CHECK_WIN32(Win32Err, goto MitTrustPwSetExit); Win32Err = NetDompTrustGetDomInfo( pwzDomain2, NULL, pDomain2Creds, &Domain2Info, TRUE, TRUE, FALSE ); CHECK_WIN32(Win32Err, goto MitTrustPwSetExit); if ((Domain1Info.Flags & NETDOM_TRUST_FLAG_DOMAIN_NOT_FOUND) && (Domain2Info.Flags & NETDOM_TRUST_FLAG_DOMAIN_NOT_FOUND)) { // at least one must be found. // Win32Err = ERROR_NO_SUCH_DOMAIN; goto MitTrustPwSetExit; } if (Domain1Info.Flags & NETDOM_TRUST_FLAG_DOMAIN_NOT_FOUND) { pMitDomInfo = &Domain1Info; pDomFoundInfo = &Domain2Info; } else { if (Domain2Info.Flags & NETDOM_TRUST_FLAG_DOMAIN_NOT_FOUND) { pMitDomInfo = &Domain2Info; pDomFoundInfo = &Domain1Info; } else { NetDompDisplayMessage(MSG_RESET_MIT_TRUST_NOT_MIT); Win32Err = ERROR_INVALID_PARAMETER; goto MitTrustPwSetExit; } } NetDompDisplayMessage(MSG_RESET_MIT_TRUST_STARTING, pDomFoundInfo->DomainName->Buffer, pMitDomInfo->DomainName->Buffer); Win32Err = NetDompSetTrustPW(pDomFoundInfo, pMitDomInfo, pwzNewTrustPW, NULL); CHECK_WIN32(Win32Err, goto MitTrustPwSetExit); NetDompDisplayMessage(MSG_RESET_MIT_TRUST_OK, pMitDomInfo->DomainName->Buffer); MitTrustPwSetExit: NetDompFreeDomInfo( &Domain2Info ); NetDompFreeDomInfo( &Domain1Info ); return( Win32Err ); } DWORD NetDomTransitivity(PWSTR pwzTransArg, PWSTR pwzDomain1, PWSTR pwzDomain2, PND5_AUTH_INFO pDomain1Creds, PND5_AUTH_INFO pDomain2Creds) /*++ Routine Description: This routine will display or change the transitivity of a trust. Arguments: pwzTransArg -- Either blank (display the transitivity) or one of yes or no (change the transitivity). pwzDomain1 -- Name of one domain pwzDomain2 -- Name of the other domain pDomain1Creds -- Credentials of the user of domain1 pDomain2Creds -- Credentials of the user of domain2 Return Value: STATUS_SUCCESS -- Success --*/ { DWORD Win32Err = ERROR_SUCCESS; NTSTATUS Status = STATUS_SUCCESS; BOOL fDisplayOnly = FALSE; BOOL fTransOn = FALSE; ND5_TRUST_INFO Domain1Info, Domain2Info; PND5_TRUST_INFO pDomFoundInfo, pMitDomInfo; PTRUSTED_DOMAIN_INFORMATION_EX pTDIx = NULL; WCHAR wzYes[NETDOM_STR_LEN], wzNo[NETDOM_STR_LEN]; RtlZeroMemory( &Domain1Info, sizeof( Domain1Info ) ); RtlZeroMemory( &Domain2Info, sizeof( Domain2Info ) ); Win32Err = NetDompTrustGetDomInfo( pwzDomain1, NULL, pDomain1Creds, &Domain1Info, TRUE, TRUE, FALSE ); CHECK_WIN32(Win32Err, goto TrustSetTransExit); Win32Err = NetDompTrustGetDomInfo( pwzDomain2, NULL, pDomain2Creds, &Domain2Info, TRUE, TRUE, FALSE ); CHECK_WIN32(Win32Err, goto TrustSetTransExit); if ((Domain1Info.Flags & NETDOM_TRUST_FLAG_DOMAIN_NOT_FOUND) && (Domain2Info.Flags & NETDOM_TRUST_FLAG_DOMAIN_NOT_FOUND)) { // at least one must be found. // Win32Err = ERROR_NO_SUCH_DOMAIN; goto TrustSetTransExit; } if (Domain1Info.Flags & NETDOM_TRUST_FLAG_DOMAIN_NOT_FOUND) { pMitDomInfo = &Domain1Info; pDomFoundInfo = &Domain2Info; } else { if (Domain2Info.Flags & NETDOM_TRUST_FLAG_DOMAIN_NOT_FOUND) { pMitDomInfo = &Domain2Info; pDomFoundInfo = &Domain1Info; } else { NetDompDisplayMessage(MSG_RESET_MIT_TRUST_NOT_MIT); Win32Err = ERROR_INVALID_PARAMETER; goto TrustSetTransExit; } } if (NULL == pwzTransArg) { fDisplayOnly = TRUE; } else { if (!LoadString(g_hInstance, IDS_YES, wzYes, NETDOM_STR_LEN) || !LoadString(g_hInstance, IDS_NO, wzNo, NETDOM_STR_LEN)) { printf("LoadString FAILED!\n"); Win32Err = ERROR_RESOURCE_NAME_NOT_FOUND; goto TrustSetTransExit; } if (_wcsicmp(wzYes, pwzTransArg) == 0) { fTransOn = TRUE; } else { if (_wcsicmp(wzNo, pwzTransArg) != 0) { fDisplayOnly = TRUE; } } } LOG_VERBOSE((MSG_VERBOSE_GET_TRUST, pDomFoundInfo->DomainName->Buffer)); if (pMitDomInfo->Sid) { Status = LsaQueryTrustedDomainInfo(pDomFoundInfo->LsaHandle, pMitDomInfo->Sid, TrustedDomainInformationEx, (PVOID *)&pTDIx); } else { Status = LsaQueryTrustedDomainInfoByName(pDomFoundInfo->LsaHandle, pMitDomInfo->DomainName, TrustedDomainInformationEx, (PVOID *)&pTDIx); } Win32Err = LsaNtStatusToWinError(Status); CHECK_WIN32(Win32Err, goto TrustSetTransExit); if (TRUST_TYPE_MIT != pTDIx->TrustType) { NetDompDisplayMessage(MSG_RESET_MIT_TRUST_NOT_MIT); Win32Err = ERROR_INVALID_PARAMETER; goto TrustSetTransExit; } if (fDisplayOnly) { NetDompDisplayMessage((pTDIx->TrustAttributes & TRUST_ATTRIBUTE_NON_TRANSITIVE) ? MSG_TRUST_NON_TRANSITIVE : MSG_TRUST_TRANSITIVE); goto TrustSetTransExit; } if (fTransOn) { if (pTDIx->TrustAttributes & TRUST_ATTRIBUTE_NON_TRANSITIVE) { NetDompDisplayMessage(MSG_TRUST_SET_TRANSITIVE); pTDIx->TrustAttributes &= ~(TRUST_ATTRIBUTE_NON_TRANSITIVE); } else { NetDompDisplayMessage(MSG_TRUST_ALREADY_TRANSITIVE); goto TrustSetTransExit; } } else { if (pTDIx->TrustAttributes & TRUST_ATTRIBUTE_NON_TRANSITIVE) { NetDompDisplayMessage(MSG_TRUST_ALREADY_NON_TRANSITIVE); goto TrustSetTransExit; } else { NetDompDisplayMessage(MSG_TRUST_SET_NON_TRANSITIVE); pTDIx->TrustAttributes |= TRUST_ATTRIBUTE_NON_TRANSITIVE; } } LOG_VERBOSE((MSG_VERBOSE_SET_TRUST, pDomFoundInfo->DomainName->Buffer)); Status = LsaSetTrustedDomainInfoByName(pDomFoundInfo->LsaHandle, pMitDomInfo->DomainName, TrustedDomainInformationEx, pTDIx); Win32Err = LsaNtStatusToWinError(Status); CHECK_WIN32(Win32Err, goto TrustSetTransExit); TrustSetTransExit: NetDompFreeDomInfo( &Domain2Info ); NetDompFreeDomInfo( &Domain1Info ); if (pTDIx) LsaFreeMemory(pTDIx); return Win32Err; } DWORD NetDomFilterSID(PWSTR pwzFilterArg, PWSTR pwzTrustingDomain, PWSTR pwzTrustedDomain, PND5_AUTH_INFO pTrustingDomainCreds, PND5_AUTH_INFO pDomain2Creds) /*++ Routine Description: This routine will display or change the SID filtering state of a trust. Arguments: pwzFilterArg -- Either blank (display the filtering state) or one of yes or no (change the filtering state). pwzTrustingDomain -- Name of the trusting domain domain pwzTrustedDomain -- Name of the trusted domain pTrustingDomainCreds -- Credentials of the user of the trusting domain pDomain2Creds -- Credentials of the user of domain2 BUGBUG not needed? Return Value: STATUS_SUCCESS -- Success --*/ { DWORD Win32Err = ERROR_SUCCESS; NTSTATUS Status = STATUS_SUCCESS; BOOL fDisplayOnly = FALSE; BOOL fFilterOn = FALSE; ND5_TRUST_INFO TrustingDomainInfo, TrustedDomainInfo; PTRUSTED_DOMAIN_INFORMATION_EX pTDIx = NULL; WCHAR wzYes[NETDOM_STR_LEN], wzNo[NETDOM_STR_LEN]; RtlZeroMemory(&TrustingDomainInfo, sizeof(TrustingDomainInfo)); RtlZeroMemory(&TrustedDomainInfo, sizeof(TrustedDomainInfo)); Win32Err = NetDompTrustGetDomInfo(pwzTrustingDomain, NULL, pTrustingDomainCreds, &TrustingDomainInfo, TRUE, FALSE, FALSE); CHECK_WIN32(Win32Err, goto TrustSetFilterExit); Win32Err = NetDompTrustGetDomInfo(pwzTrustedDomain, NULL, NULL, &TrustedDomainInfo, FALSE, TRUE, TRUE); CHECK_WIN32(Win32Err, goto TrustSetFilterExit); if (TrustingDomainInfo.Flags & NETDOM_TRUST_FLAG_DOMAIN_NOT_FOUND) { Win32Err = ERROR_NO_SUCH_DOMAIN; goto TrustSetFilterExit; } if (NULL == pwzFilterArg) { fDisplayOnly = TRUE; } else { if (!LoadString(g_hInstance, IDS_YES, wzYes, NETDOM_STR_LEN) || !LoadString(g_hInstance, IDS_NO, wzNo, NETDOM_STR_LEN)) { printf("LoadString FAILED!\n"); Win32Err = ERROR_RESOURCE_NAME_NOT_FOUND; NetApiBufferFree(pwzFilterArg); goto TrustSetFilterExit; } if (_wcsicmp(wzYes, pwzFilterArg) == 0) { fFilterOn = TRUE; } else { if (_wcsicmp(wzNo, pwzFilterArg) != 0) { fDisplayOnly = TRUE; } } NetApiBufferFree(pwzFilterArg); } LOG_VERBOSE((MSG_VERBOSE_GET_TRUST, TrustingDomainInfo.DomainName->Buffer)); if (TrustedDomainInfo.Sid) { Status = LsaQueryTrustedDomainInfo(TrustingDomainInfo.LsaHandle, TrustedDomainInfo.Sid, TrustedDomainInformationEx, (PVOID *)&pTDIx); } else { Status = LsaQueryTrustedDomainInfoByName(TrustingDomainInfo.LsaHandle, TrustedDomainInfo.DomainName, TrustedDomainInformationEx, (PVOID *)&pTDIx); } Win32Err = LsaNtStatusToWinError(Status); CHECK_WIN32(Win32Err, goto TrustSetFilterExit); if (!(pTDIx->TrustDirection & TRUST_DIRECTION_OUTBOUND)) { NetDompDisplayMessage(MSG_TRUST_FILTER_SIDS_WRONG_DIR, pwzTrustingDomain); goto TrustSetFilterExit; } if (fDisplayOnly) { NetDompDisplayMessage((pTDIx->TrustAttributes & TRUST_ATTRIBUTE_FILTER_SIDS) ? MSG_TRUST_FILTER_SIDS : MSG_TRUST_DONT_FILTER_SIDS); goto TrustSetFilterExit; } if (fFilterOn) { if (pTDIx->TrustAttributes & TRUST_ATTRIBUTE_FILTER_SIDS) { NetDompDisplayMessage(MSG_TRUST_ALREADY_FILTER_SIDS); goto TrustSetFilterExit; } else { NetDompDisplayMessage(MSG_TRUST_SET_FILTER_SIDS); pTDIx->TrustAttributes |= TRUST_ATTRIBUTE_FILTER_SIDS; } } else { if (pTDIx->TrustAttributes & TRUST_ATTRIBUTE_FILTER_SIDS) { NetDompDisplayMessage(MSG_TRUST_SET_DONT_FILTER_SIDS); pTDIx->TrustAttributes &= ~(TRUST_ATTRIBUTE_FILTER_SIDS); } else { NetDompDisplayMessage(MSG_TRUST_ALREADY_DONT_FILTER_SIDS); goto TrustSetFilterExit; } } LOG_VERBOSE((MSG_VERBOSE_SET_TRUST, TrustingDomainInfo.DomainName->Buffer)); Status = LsaSetTrustedDomainInfoByName(TrustingDomainInfo.LsaHandle, TrustedDomainInfo.DomainName, TrustedDomainInformationEx, pTDIx); Win32Err = LsaNtStatusToWinError(Status); CHECK_WIN32(Win32Err, goto TrustSetFilterExit); TrustSetFilterExit: NetDompFreeDomInfo( &TrustedDomainInfo ); NetDompFreeDomInfo( &TrustingDomainInfo ); if (pTDIx) LsaFreeMemory(pTDIx); return Win32Err; } typedef INT_PTR (*DSPROP_DumpFTInfos)(PCWSTR pwzDcName, PCWSTR pwzTrust, PCWSTR pwzUser, PCWSTR pwzPw); typedef INT_PTR (*DSPROP_ToggleFTName)(PCWSTR pwzLocalDc, PWSTR pwzTrust, ULONG iSel, PCWSTR pwzUser, PCWSTR pwzPW); DWORD NetDomForestSuffix(PWSTR pwzTrustPartnerArg, ULONG iSel, PWSTR pwzLocalDomain, PND5_AUTH_INFO pLocalDomainCreds) /*++ Routine Description: This routine will toggle the status of a name suffix claimed by a forest trust domain or if iSel is zero will display the name suffixes claimed by a forest trust domain. Arguments: pwzTrustPartnerArg -- the domain whose TDO will be read for the name suffixes attribute (ms-DS-Trust-Forest-Trust-Info). iSel -- the one-based index of the name to toggle (if zero, display the names). pwzLocalDomain -- Name of the domain on which the TDO resides. pLocalDomainCreds -- Credentials of the user of the local domain Return Value: STATUS_SUCCESS -- Success --*/ { DWORD Win32Err = ERROR_SUCCESS; PDOMAIN_CONTROLLER_INFO pDcInfo = NULL; PWSTR pwzDcName = NULL; NTSTATUS Status = STATUS_SUCCESS; HMODULE hm = NULL; DSPROP_DumpFTInfos pDumpFTInfos = NULL; DSPROP_ToggleFTName pToggleFTName = NULL; ASSERT(pwzTrustPartnerArg && pwzLocalDomain && pLocalDomainCreds); Win32Err = DsGetDcName(NULL, pwzLocalDomain, NULL, NULL, DS_PDC_REQUIRED, &pDcInfo ); CHECK_WIN32(Win32Err, return Win32Err); ASSERT(pDcInfo); pwzDcName = pDcInfo->DomainControllerName; hm = LoadLibrary(L"adprop.dll"); if (!hm) { Win32Err = GetLastError(); NetApiBufferFree(pDcInfo); return Win32Err; } if (0 == iSel) { pDumpFTInfos = (DSPROP_DumpFTInfos)GetProcAddress(hm, "DSPROP_DumpFTInfos"); } else { pToggleFTName = (DSPROP_ToggleFTName)GetProcAddress(hm, "DSPROP_ToggleFTName"); } if (!pDumpFTInfos && !pToggleFTName) { Win32Err = GetLastError(); NetApiBufferFree(pDcInfo); NetDompDisplayMessage(MSG_WRONG_DSPROP_DLL); return Win32Err; } if (0 == iSel) { Win32Err = (DWORD)(*pDumpFTInfos)(pwzDcName, pwzTrustPartnerArg, pLocalDomainCreds->User, pLocalDomainCreds->Password); } else { Win32Err = (DWORD)(*pToggleFTName)(pwzDcName, pwzTrustPartnerArg, iSel, pLocalDomainCreds->User, pLocalDomainCreds->Password); } NetApiBufferFree(pDcInfo); return Win32Err; } DWORD NetDompVerifyIndividualTrustKerberos( IN PWSTR TrustingDomain, IN PWSTR TrustedDomain, IN PND5_AUTH_INFO pTrustingCreds, IN PND5_AUTH_INFO pTrustedCreds ) /*++ Routine Description: This routine will verify a single trust in the one direction only. Arguments: TrustingDomain -- Name of the domain on the outbound side TrustedDomain -- Name of the domain on the inbound side pTrustingCreds -- Credentials of the user on the outbound side pTrustedCreds -- Credentials of the user on the inbound side Return Value: STATUS_SUCCESS -- Success --*/ { // // Copy the relevant info into local pointers so that I don't have // to rewrite the rest of the function. // PWSTR PackageName = NULL; PWSTR UserNameU = pTrustedCreds->pwzUserWoDomain; PWSTR DomainNameU = pTrustedCreds->pwzUsersDomain; PWSTR PasswordU = pTrustedCreds->Password; PWSTR ServerUserNameU = pTrustingCreds->pwzUserWoDomain; PWSTR ServerDomainNameU = pTrustingCreds->pwzUsersDomain; PWSTR ServerPasswordU = pTrustingCreds->Password; ULONG ContextReq = 0; ULONG CredFlags = 0; SECURITY_STATUS SecStatus; SECURITY_STATUS AcceptStatus; SECURITY_STATUS InitStatus; CredHandle CredentialHandle2; CtxtHandle ClientContextHandle; CtxtHandle ServerContextHandle; TimeStamp Lifetime; ULONG ContextAttributes; ULONG PackageCount; PSecPkgInfo PackageInfo = NULL; ULONG ClientFlags; ULONG ServerFlags; BOOLEAN AcquiredServerCred = FALSE; LPWSTR DomainName = NULL; LPWSTR UserName = NULL; TCHAR TargetName[256]; PSEC_WINNT_AUTH_IDENTITY_EXW AuthIdentity = NULL; PSEC_WINNT_AUTH_IDENTITY_W ServerAuthIdentity = NULL; PUCHAR Where; ULONG CredSize; SecBufferDesc NegotiateDesc; SecBuffer NegotiateBuffer; SecBufferDesc ChallengeDesc; SecBuffer ChallengeBuffer; SecBufferDesc AuthenticateDesc; SecBuffer AuthenticateBuffer; SecPkgCredentials_Names CredNames; CredHandle ServerCredHandleStorage; PCredHandle ServerCredHandle = NULL; // // Set the package to wide-char // if (PackageName == NULL) { PackageName = MICROSOFT_KERBEROS_NAME_W; } // // Allocate the Authentication Identity for the outbound trust // if ((UserNameU != NULL) || (DomainNameU != NULL) || (PasswordU != NULL) || (CredFlags != 0)) { CredSize = (((UserNameU != NULL) ? wcslen(UserNameU) + 1 : 0) + ((DomainNameU != NULL) ? wcslen(DomainNameU) + 1 : 0 ) + ((PasswordU != NULL) ? wcslen(PasswordU) + 1 : 0) ) * sizeof(WCHAR) + sizeof(SEC_WINNT_AUTH_IDENTITY_EXW); AuthIdentity = (PSEC_WINNT_AUTH_IDENTITY_EXW) LocalAlloc(LMEM_ZEROINIT,CredSize); if (!AuthIdentity) { return ERROR_NOT_ENOUGH_MEMORY; } AuthIdentity->Version = SEC_WINNT_AUTH_IDENTITY_VERSION; Where = (PUCHAR) (AuthIdentity + 1); if (UserNameU != NULL) { AuthIdentity->UserLength = wcslen(UserNameU); AuthIdentity->User = (LPWSTR) Where; wcscpy( (LPWSTR) Where, UserNameU ); Where += (wcslen(UserNameU) + 1) * sizeof(WCHAR); } if (DomainNameU != NULL) { AuthIdentity->DomainLength = wcslen(DomainNameU); AuthIdentity->Domain = (LPWSTR) Where; wcscpy( (LPWSTR) Where, DomainNameU ); Where += (wcslen(DomainNameU) + 1) * sizeof(WCHAR); } if (PasswordU != NULL) { AuthIdentity->PasswordLength = wcslen(PasswordU); AuthIdentity->Password = (LPWSTR) Where; wcscpy( (LPWSTR) Where, PasswordU ); Where += (wcslen(PasswordU) + 1) * sizeof(WCHAR); } AuthIdentity->Flags = SEC_WINNT_AUTH_IDENTITY_UNICODE | CredFlags; } // // Allocate the Authentication Identity for the outbound trust // if ((ServerUserNameU != NULL) || (ServerDomainNameU != NULL) || (ServerPasswordU != NULL)) { CredSize = (((ServerUserNameU != NULL) ? wcslen(ServerUserNameU) + 1 : 0) + ((ServerDomainNameU != NULL) ? wcslen(ServerDomainNameU) + 1 : 0 ) + ((ServerPasswordU != NULL) ? wcslen(ServerPasswordU) + 1 : 0) ) * sizeof(WCHAR) + sizeof(SEC_WINNT_AUTH_IDENTITY); ServerAuthIdentity = (PSEC_WINNT_AUTH_IDENTITY_W) LocalAlloc(LMEM_ZEROINIT,CredSize); if (!ServerAuthIdentity) { return ERROR_NOT_ENOUGH_MEMORY; } Where = (PUCHAR) (ServerAuthIdentity + 1); if (ServerUserNameU != NULL) { ServerAuthIdentity->UserLength = wcslen(ServerUserNameU); ServerAuthIdentity->User = (LPWSTR) Where; wcscpy( (LPWSTR) Where, ServerUserNameU ); Where += (wcslen(ServerUserNameU) + 1) * sizeof(WCHAR); } if (ServerDomainNameU != NULL) { ServerAuthIdentity->DomainLength = wcslen(ServerDomainNameU); ServerAuthIdentity->Domain = (LPWSTR) Where; wcscpy( (LPWSTR) Where, ServerDomainNameU ); Where += (wcslen(ServerDomainNameU) + 1) * sizeof(WCHAR); } if (ServerPasswordU != NULL) { ServerAuthIdentity->PasswordLength = wcslen(ServerPasswordU); ServerAuthIdentity->Password = (LPWSTR) Where; wcscpy( (LPWSTR) Where, ServerPasswordU ); Where += (wcslen(ServerPasswordU) + 1) * sizeof(WCHAR); } ServerAuthIdentity->Flags = SEC_WINNT_AUTH_IDENTITY_UNICODE | SEC_WINNT_AUTH_IDENTITY_MARSHALLED; } CredNames.sUserName = NULL; NegotiateBuffer.pvBuffer = NULL; ChallengeBuffer.pvBuffer = NULL; AuthenticateBuffer.pvBuffer = NULL; DomainName = _wgetenv(L"USERDOMAIN"); UserName = _wgetenv(L"USERNAME"); // // Get info about the security packages. // SecStatus = EnumerateSecurityPackages( &PackageCount, &PackageInfo ); if ( SecStatus != STATUS_SUCCESS ) { NetDompDisplayMessage( MSG_KERBEROS_TRUST_FAILED, TrustingDomain, TrustedDomain ); return SecStatus; } // // Get info about the security packages. // SecStatus = QuerySecurityPackageInfo( PackageName, &PackageInfo ); if ( SecStatus != STATUS_SUCCESS ) { NetDompDisplayMessage( MSG_KERBEROS_TRUST_FAILED, TrustingDomain, TrustedDomain ); return SecStatus; } // // Acquire a credential handle for the server side // if (ServerCredHandle == NULL) { ServerCredHandle = &ServerCredHandleStorage; AcquiredServerCred = TRUE; SecStatus = AcquireCredentialsHandle( NULL, PackageName, SECPKG_CRED_INBOUND, NULL, ServerAuthIdentity, NULL, NULL, ServerCredHandle, &Lifetime ); if ( SecStatus != STATUS_SUCCESS ) { NetDompDisplayMessage( MSG_KERBEROS_TRUST_FAILED, TrustingDomain, TrustedDomain ); return SecStatus; } } // // Acquire a credential handle for the client side // SecStatus = AcquireCredentialsHandle( NULL, // New principal PackageName, SECPKG_CRED_OUTBOUND, NULL, AuthIdentity, NULL, NULL, &CredentialHandle2, &Lifetime ); if ( SecStatus != STATUS_SUCCESS ) { NetDompDisplayMessage( MSG_KERBEROS_TRUST_FAILED, TrustingDomain, TrustedDomain ); return SecStatus; } // // Query some cred attributes // SecStatus = QueryCredentialsAttributes( &CredentialHandle2, SECPKG_CRED_ATTR_NAMES, &CredNames ); if ( SecStatus != STATUS_SUCCESS ) { if ( !NT_SUCCESS(SecStatus) ) { NetDompDisplayMessage( MSG_KERBEROS_TRUST_FAILED, TrustingDomain, TrustedDomain ); return SecStatus; } } else { FreeContextBuffer(CredNames.sUserName); } // // Do the same for the client // SecStatus = QueryCredentialsAttributes( ServerCredHandle, SECPKG_CRED_ATTR_NAMES, &CredNames ); if ( SecStatus != STATUS_SUCCESS ) { if ( !NT_SUCCESS(SecStatus) ) { NetDompDisplayMessage( MSG_KERBEROS_TRUST_FAILED, TrustingDomain, TrustedDomain ); return SecStatus; } } else { FreeContextBuffer(CredNames.sUserName); } // // Get the NegotiateMessage (ClientSide) // NegotiateDesc.ulVersion = 0; NegotiateDesc.cBuffers = 1; NegotiateDesc.pBuffers = &NegotiateBuffer; NegotiateBuffer.cbBuffer = PackageInfo->cbMaxToken; NegotiateBuffer.BufferType = SECBUFFER_TOKEN; NegotiateBuffer.pvBuffer = LocalAlloc( 0, NegotiateBuffer.cbBuffer ); if ( NegotiateBuffer.pvBuffer == NULL ) { DWORD dwError = GetLastError(); NetDompDisplayMessage( MSG_KERBEROS_TRUST_FAILED, TrustingDomain, TrustedDomain ); return dwError; } if (ContextReq == 0) { ClientFlags = ISC_REQ_MUTUAL_AUTH | ISC_REQ_REPLAY_DETECT | ISC_REQ_CONFIDENTIALITY; // USE_DCE_STYLE | ISC_REQ_MUTUAL_AUTH | ISC_REQ_USE_SESSION_KEY; // | ISC_REQ_DATAGRAM; } else { ClientFlags = ContextReq; } if (ServerUserNameU != NULL && ServerDomainNameU != NULL) { wcscpy( TargetName, ServerUserNameU ); wcscat( TargetName, L"@" ); wcscat( TargetName, ServerDomainNameU ); } InitStatus = InitializeSecurityContext( &CredentialHandle2, NULL, // No Client context yet TargetName, // Faked target name ClientFlags, 0, // Reserved 1 SECURITY_NATIVE_DREP, NULL, // No initial input token 0, // Reserved 2 &ClientContextHandle, &NegotiateDesc, &ContextAttributes, &Lifetime ); if ( InitStatus != STATUS_SUCCESS ) { if ( !NT_SUCCESS(InitStatus) ) { NetDompDisplayMessage( MSG_KERBEROS_TRUST_FAILED, TrustingDomain, TrustedDomain ); return InitStatus; } } // // Get the ChallengeMessage (ServerSide) // NegotiateBuffer.BufferType |= SECBUFFER_READONLY; ChallengeDesc.ulVersion = 0; ChallengeDesc.cBuffers = 1; ChallengeDesc.pBuffers = &ChallengeBuffer; ChallengeBuffer.cbBuffer = PackageInfo->cbMaxToken; ChallengeBuffer.BufferType = SECBUFFER_TOKEN; ChallengeBuffer.pvBuffer = LocalAlloc( 0, ChallengeBuffer.cbBuffer ); if ( ChallengeBuffer.pvBuffer == NULL ) { DWORD dwError = GetLastError(); NetDompDisplayMessage( MSG_KERBEROS_TRUST_FAILED, TrustingDomain, TrustedDomain ); return dwError; } ServerFlags = ASC_REQ_EXTENDED_ERROR; AcceptStatus = AcceptSecurityContext( ServerCredHandle, NULL, // No Server context yet &NegotiateDesc, ServerFlags, SECURITY_NATIVE_DREP, &ServerContextHandle, &ChallengeDesc, &ContextAttributes, &Lifetime ); if ( AcceptStatus != STATUS_SUCCESS ) { if ( !NT_SUCCESS(AcceptStatus) ) { NetDompDisplayMessage( MSG_KERBEROS_TRUST_FAILED, TrustingDomain, TrustedDomain ); return AcceptStatus; } } while (InitStatus != STATUS_SUCCESS) { // // Get the AuthenticateMessage (ClientSide) // ChallengeBuffer.BufferType |= SECBUFFER_READONLY; AuthenticateDesc.ulVersion = 0; AuthenticateDesc.cBuffers = 1; AuthenticateDesc.pBuffers = &AuthenticateBuffer; AuthenticateBuffer.cbBuffer = PackageInfo->cbMaxToken; AuthenticateBuffer.BufferType = SECBUFFER_TOKEN; if (AuthenticateBuffer.pvBuffer == NULL) { AuthenticateBuffer.pvBuffer = LocalAlloc( 0, AuthenticateBuffer.cbBuffer ); if ( AuthenticateBuffer.pvBuffer == NULL ) { DWORD dwError = GetLastError(); NetDompDisplayMessage( MSG_KERBEROS_TRUST_FAILED, TrustingDomain, TrustedDomain ); return dwError; } } InitStatus = InitializeSecurityContext( NULL, &ClientContextHandle, TargetName, ClientFlags, 0, // Reserved 1 SECURITY_NATIVE_DREP, &ChallengeDesc, 0, // Reserved 2 &ClientContextHandle, &AuthenticateDesc, &ContextAttributes, &Lifetime ); if ( InitStatus != STATUS_SUCCESS ) { if ( !NT_SUCCESS(InitStatus) ) { NetDompDisplayMessage( MSG_KERBEROS_TRUST_FAILED, TrustingDomain, TrustedDomain ); return InitStatus; } } if (AcceptStatus != STATUS_SUCCESS) { // // Finally authenticate the user (ServerSide) // AuthenticateBuffer.BufferType |= SECBUFFER_READONLY; ChallengeBuffer.BufferType = SECBUFFER_TOKEN; ChallengeBuffer.cbBuffer = PackageInfo->cbMaxToken; AcceptStatus = AcceptSecurityContext( NULL, &ServerContextHandle, &AuthenticateDesc, ServerFlags, SECURITY_NATIVE_DREP, &ServerContextHandle, &ChallengeDesc, &ContextAttributes, &Lifetime ); if ( AcceptStatus != STATUS_SUCCESS ) { if ( !NT_SUCCESS(AcceptStatus) ) { NetDompDisplayMessage( MSG_KERBEROS_TRUST_FAILED, TrustingDomain, TrustedDomain ); return AcceptStatus; } } } } NetDompDisplayMessage( MSG_KERBEROS_TRUST_SUCCEEDED, TrustedDomain, TrustingDomain ); return AcceptStatus; } DWORD NetDompVerifyTrust( IN PND5_TRUST_INFO pTrustingInfo, // outbound IN PND5_TRUST_INFO pTrustedInfo, // inbound BOOL fShowResults ) /*++ Routine Description: This function will verify a trust connection Arguments: TrustingInfo - Information on the trusting (outbound) side of the domain TrustedInfo - Information on the trusted (inbound) side of the domain Return Value: ERROR_SUCCESS - The function succeeded --*/ { NTSTATUS Status = STATUS_SUCCESS; DWORD SidBuff[ sizeof( SID ) / sizeof( DWORD ) + 5 ]; PSID DomAdminSid = ( PSID )SidBuff; PLSA_REFERENCED_DOMAIN_LIST Domains = NULL; PLSA_TRANSLATED_NAME Names = NULL; NET_API_STATUS NetStatus; PNETLOGON_INFO_2 NetlogonInfo2 = NULL; PWSTR pwzDomSvr = pTrustedInfo->DomainName->Buffer; PWSTR pwzTrustedDomain = pTrustedInfo->DomainName->Buffer; BOOL fBufferAlloced = FALSE; ASSERT( RtlValidSid( pTrustedInfo->Sid ) ); if ( !RtlValidSid( pTrustedInfo->Sid ) ) { return( ERROR_INVALID_SID ); } if (!pTrustingInfo->Uplevel) { pwzTrustedDomain = pwzDomSvr = pTrustedInfo->FlatName->Buffer; } // // Check netlogon's secure channel // NetStatus = I_NetLogonControl2(pTrustingInfo->Server, NETLOGON_CONTROL_TC_VERIFY, 2, (LPBYTE)&pwzTrustedDomain, (LPBYTE *)&NetlogonInfo2); if (ERROR_NO_SUCH_DOMAIN == NetStatus && pTrustingInfo->Uplevel) { // Pre-existing TDOs for domains upgraded from NT4 to NT5 will continue to // have a flat name. // pwzTrustedDomain = pwzDomSvr = pTrustedInfo->FlatName->Buffer; pTrustedInfo->fWasDownlevel = TRUE; NetStatus = I_NetLogonControl2(pTrustingInfo->Server, NETLOGON_CONTROL_TC_VERIFY, 2, (LPBYTE)&pwzTrustedDomain, (LPBYTE *)&NetlogonInfo2); } if (ERROR_NOT_SUPPORTED == NetStatus) { // Must be remoted to a Win2k/NT4 DC that doesn't support SC verify. // NetStatus = I_NetLogonControl2(pTrustingInfo->Server, NETLOGON_CONTROL_TC_QUERY, 2, (LPBYTE)&pwzTrustedDomain, (LPBYTE *)&NetlogonInfo2); } if (NERR_Success == NetStatus) { NetStatus = NetlogonInfo2->netlog2_tc_connection_status; if (NERR_Success == NetStatus) { if (pTrustingInfo->Uplevel) { // Form the name domain\DC so a reset can done against the same // DC that is currently being used for the secure channel. // NetStatus = NetApiBufferAllocate((wcslen(NetlogonInfo2->netlog2_trusted_dc_name) + wcslen(pwzTrustedDomain) + 1) * sizeof(WCHAR), (PVOID*)&pwzDomSvr); if (NERR_Success != NetStatus) { NetApiBufferFree( NetlogonInfo2 ); return ERROR_NOT_ENOUGH_MEMORY; } fBufferAlloced = TRUE; wsprintf(pwzDomSvr, L"%s\\%s", pwzTrustedDomain, (L'\\' == *NetlogonInfo2->netlog2_trusted_dc_name) ? NetlogonInfo2->netlog2_trusted_dc_name + 2 : NetlogonInfo2->netlog2_trusted_dc_name); } } else { if (fShowResults) { // Report Query failure. // NetDompDisplayMessage(MSG_VERIFY_TRUST_QUERY_FAILED, pTrustingInfo->Server, pwzTrustedDomain); NetDompDisplayErrorMessage(NetStatus); } } NetApiBufferFree( NetlogonInfo2 ); } else { if (fShowResults) { // Report I_NetLogonControl2 error. // NetDompDisplayMessage(MSG_VERIFY_TRUST_NLQUERY_FAILED, pTrustingInfo->Server, pwzTrustedDomain); NetDompDisplayErrorMessage(NetStatus); return NetStatus; } } NetStatus = I_NetLogonControl2(pTrustingInfo->Server, NETLOGON_CONTROL_REDISCOVER, 2, (LPBYTE)&pwzDomSvr, (LPBYTE *)&NetlogonInfo2 ); if (fBufferAlloced) { NetApiBufferFree(pwzDomSvr); } if (NERR_Success == NetStatus) { NetStatus = NetlogonInfo2->netlog2_tc_connection_status; if (NERR_Success != NetStatus) { if (fShowResults) { // Report Reset failure. // NetDompDisplayMessage(MSG_VERIFY_TRUST_RESET_FAILED, pTrustingInfo->Server, pwzTrustedDomain); NetDompDisplayErrorMessage(NetStatus); } NetApiBufferFree( NetlogonInfo2 ); return NetStatus; } NetApiBufferFree( NetlogonInfo2 ); } else { if (fShowResults) { // Report failure // NetDompDisplayMessage(MSG_VERIFY_TRUST_NLRESET_FAILED, pTrustingInfo->Server, pwzTrustedDomain); NetDompDisplayErrorMessage(NetStatus); } return NetStatus; } // // Now, try a lookup // if (ERROR_SUCCESS == NetStatus) { // // Build the domain admins sid for the inbound side of the trust // RtlCopyMemory( DomAdminSid, pTrustedInfo->Sid, RtlLengthSid( pTrustedInfo->Sid ) ); ( ( PISID )( DomAdminSid ) )->SubAuthorityCount++; *( RtlSubAuthoritySid( DomAdminSid, *( RtlSubAuthorityCountSid( pTrustedInfo->Sid ) ) ) ) = DOMAIN_GROUP_RID_ADMINS; // // Now, we'll simply do a remote lookup, and ensure that we get back success // Status = LsaLookupSids( pTrustingInfo->LsaHandle, 1, &DomAdminSid, &Domains, &Names ); if ( NT_SUCCESS( Status ) ) { LsaFreeMemory( Domains ); LsaFreeMemory( Names ); NetStatus = ERROR_SUCCESS; } else { if ( Status == STATUS_NONE_MAPPED ) { NetStatus = ERROR_TRUSTED_DOMAIN_FAILURE; } else { NetStatus = RtlNtStatusToDosError( Status ); } if (fShowResults) { // Report failure // NetDompDisplayMessage(MSG_VERIFY_TRUST_LOOKUP_FAILED, pTrustingInfo->Server, pwzTrustedDomain); NetDompDisplayErrorMessage(NetStatus); } } } return NetStatus; } DWORD NetDompVerifyTrustObject( IN ARG_RECORD * rgNetDomArgs, IN PWSTR pwzDomain1, IN PWSTR pwzDomain2, IN PND5_AUTH_INFO pDomain1Creds, IN PND5_AUTH_INFO pDomain2Creds ) /*++ Routine Description: This function will handle the adding of a trusted domain object Arguments: rgNetDomArgs - List of arguments present in the Args list pwzDomain1, pwzDomain2 - domains with trust pDomain1Creds, pDomain2Creds - Credentials to use when connecting to the domain controllers Return Value: ERROR_INVALID_PARAMETER - No object name was supplied --*/ { DWORD Win32Err, Win32Err1; ND5_TRUST_INFO TrustInfo1, TrustInfo2; PND5_TRUST_INFO pTrustInfoUplevel, pTrustInfoOther; DWORD Direction; if (CmdFlagOn(rgNetDomArgs, eTrustRealm)) { return ERROR_INVALID_PARAMETER; } RtlZeroMemory( &TrustInfo1, sizeof( TrustInfo1 ) ); RtlZeroMemory( &TrustInfo2, sizeof( TrustInfo2 ) ); Win32Err = NetDompTrustGetDomInfo( pwzDomain1, NULL, pDomain1Creds, &TrustInfo1, TRUE, FALSE, FALSE ); if (ERROR_SUCCESS == Win32Err) { Win32Err = NetDompTrustGetDomInfo( pwzDomain2, NULL, pDomain2Creds, &TrustInfo2, TRUE, FALSE, FALSE ); } if (ERROR_SUCCESS != Win32Err) { goto TrustVerifyExit; } Win32Err = NetDompGetTrustDirection(&TrustInfo1, &TrustInfo2, &Direction); if (ERROR_SUCCESS != Win32Err) { goto TrustVerifyExit; } if (TRUST_DIRECTION_DISABLED == Direction) { NetDompDisplayMessage(MSG_VERIFY_TRUST_DISABLED); goto TrustVerifyExit; } if (Direction & TRUST_DIRECTION_OUTBOUND) { LOG_VERBOSE((MSG_VERBOSE_VERIFY_TRUST, pwzDomain1, pwzDomain2)); Win32Err = NetDompVerifyTrust(&TrustInfo1, &TrustInfo2, TRUE); } if (Direction & TRUST_DIRECTION_INBOUND) { LOG_VERBOSE((MSG_VERBOSE_VERIFY_TRUST, pwzDomain2, pwzDomain1)); Win32Err1 = NetDompVerifyTrust(&TrustInfo2, &TrustInfo1, TRUE); } if (ERROR_SUCCESS == Win32Err && ERROR_SUCCESS == Win32Err1) { NetDompDisplayMessage(MSG_VERIFY_TRUST_OK, pwzDomain1, pwzDomain2); } TrustVerifyExit: NetDompFreeDomInfo( &TrustInfo2 ); NetDompFreeDomInfo( &TrustInfo1 ); return( Win32Err ); } DWORD NetDompHandleTrust(ARG_RECORD * rgNetDomArgs) /*++ Routine Description: This function manages inter-domain trust Arguments: Args - List of command line arguments Return Value: ERROR_INVALID_PARAMETER - No object name was supplied --*/ { DWORD Win32Err = ERROR_SUCCESS; ULONG Ops = 0, i; NETDOM_ARG_ENUM BadOp = eArgBegin; PWSTR TrustedDomain = NULL, pwzArgValue = NULL, pwzArgValue2 = NULL; ND5_AUTH_INFO TrustedDomainUser, TrustingDomainUser; RtlZeroMemory( &TrustedDomainUser, sizeof( ND5_AUTH_INFO ) ); RtlZeroMemory( &TrustingDomainUser, sizeof( ND5_AUTH_INFO ) ); PWSTR TrustingDomain = rgNetDomArgs[eObject].strValue; if ( !TrustingDomain ) { DisplayHelp(ePriTrust); return( ERROR_INVALID_PARAMETER ); } Win32Err = NetDompValidateSecondaryArguments(rgNetDomArgs, eObject, eCommDomain, eCommUserNameO, eCommPasswordO, eCommUserNameD, eCommPasswordD, eTrustRealm, eTrustPasswordT, eCommAdd, eCommRemove, eTrustTwoWay, eTrustKerberos, eTrustTransitive, eTrustOneSide, eTrustNameSuffixes, eTrustToggleSuffixes, eTrustFilterSIDs, eCommVerify, eCommReset, eCommForce, eCommVerbose, eArgEnd); if ( Win32Err != ERROR_SUCCESS ) { DisplayHelp(ePriTrust); return Win32Err; } // // See if we are doing an add, remove, or verify // if ( CmdFlagOn(rgNetDomArgs, eCommAdd) ) { Ops++; if (CmdFlagOn(rgNetDomArgs, eTrustTransitive)) { BadOp = eTrustTransitive; } } if ( CmdFlagOn(rgNetDomArgs, eCommRemove) ) { if ( Ops ) { BadOp = eCommRemove; } else { Ops++; } if ( CmdFlagOn(rgNetDomArgs, eTrustRealm) ) { BadOp = eTrustRealm; } } if ( CmdFlagOn(rgNetDomArgs, eCommVerify) ) { if ( Ops ) { BadOp = eCommVerify; } else { Ops++; } } if (BadOp != eArgBegin) { Win32Err = ERROR_INVALID_PARAMETER; NetDompDisplayUnexpectedParameter(rgNetDomArgs[BadOp].strArg1); goto HandleTrustExit; } if (!CmdFlagOn(rgNetDomArgs, eTrustNameSuffixes)) { // // Make sure that we have a specified domain (if not listing claimed names). // Win32Err = NetDompGetDomainForOperation(rgNetDomArgs, NULL, // no server specified FALSE, // don't default to current domain. &TrustedDomain); if ( Win32Err != ERROR_SUCCESS ) { goto HandleTrustExit; } } // // Get the password and user if it exists // if ( CmdFlagOn(rgNetDomArgs, eCommUserNameD) ) { Win32Err = NetDompGetUserAndPasswordForOperation(rgNetDomArgs, eCommUserNameD, TrustedDomain, &TrustedDomainUser ); if ( Win32Err != ERROR_SUCCESS ) { goto HandleTrustExit; } } if ( CmdFlagOn(rgNetDomArgs, eCommUserNameO) ) { Win32Err = NetDompGetUserAndPasswordForOperation(rgNetDomArgs, eCommUserNameO, TrustingDomain, &TrustingDomainUser ); if ( Win32Err != ERROR_SUCCESS ) { goto HandleTrustExit; } } if ( CmdFlagOn(rgNetDomArgs, eCommAdd) ) { if (CmdFlagOn(rgNetDomArgs, eTrustRealm)) { // Get the trust PW. // Win32Err = NetDompGetArgumentString(rgNetDomArgs, eTrustPasswordT, &pwzArgValue2); if (ERROR_SUCCESS != Win32Err) { goto HandleTrustExit; } if (pwzArgValue2) { Win32Err = NetDompCreateTrustObject( rgNetDomArgs, TrustingDomain, TrustedDomain, &TrustingDomainUser, &TrustedDomainUser, pwzArgValue2, NULL); NetApiBufferFree(pwzArgValue2); } else { NetDompDisplayMessage(MSG_TRUST_PW_MISSING); Win32Err = ERROR_INVALID_PARAMETER; } } else if (CmdFlagOn(rgNetDomArgs, eTrustOneSide)) { // Get the trust PW. // Win32Err = NetDompGetArgumentString(rgNetDomArgs, eTrustPasswordT, &pwzArgValue2); if (ERROR_SUCCESS != Win32Err) { goto HandleTrustExit; } if (!pwzArgValue2) { NetDompDisplayMessage(MSG_TRUST_PW_MISSING); Win32Err = ERROR_INVALID_PARAMETER; goto HandleTrustExit; } // Get the side on which to create the trust. // Win32Err = NetDompGetArgumentString(rgNetDomArgs, eTrustOneSide, &pwzArgValue); if (ERROR_SUCCESS != Win32Err) { NetApiBufferFree(pwzArgValue2); goto HandleTrustExit; } if (!pwzArgValue) { NetDompDisplayMessage(MSG_ONESIDE_ARG_STRING); Win32Err = ERROR_INVALID_PARAMETER; NetApiBufferFree(pwzArgValue2); goto HandleTrustExit; } Win32Err = NetDompCreateTrustObject( rgNetDomArgs, TrustingDomain, TrustedDomain, &TrustingDomainUser, &TrustedDomainUser, pwzArgValue2, pwzArgValue); NetApiBufferFree(pwzArgValue2); } else { Win32Err = NetDompCreateTrustObject( rgNetDomArgs, TrustingDomain, TrustedDomain, &TrustingDomainUser, &TrustedDomainUser, NULL, NULL); } } else if ( CmdFlagOn(rgNetDomArgs, eCommRemove) ) { Win32Err = NetDompRemoveTrustObject( rgNetDomArgs, TrustingDomain, TrustedDomain, &TrustingDomainUser, &TrustedDomainUser ); } else if ( CmdFlagOn(rgNetDomArgs, eCommReset) ) { // // See if a password is specifed // if (CmdFlagOn(rgNetDomArgs, eTrustPasswordT)) { Win32Err = NetDompGetArgumentString(rgNetDomArgs, eTrustPasswordT, &pwzArgValue2); if (ERROR_SUCCESS != Win32Err) { goto HandleTrustExit; } if (pwzArgValue2) { Win32Err = NetDompSetMitTrustPW(TrustingDomain, TrustedDomain, &TrustingDomainUser, &TrustedDomainUser, pwzArgValue2); NetApiBufferFree(pwzArgValue2); } else { Win32Err = ERROR_INVALID_PARAMETER; } } else { Win32Err = NetDompResetTrustPasswords(TrustingDomain, TrustedDomain, &TrustingDomainUser, &TrustedDomainUser); } } else if ( CmdFlagOn(rgNetDomArgs, eCommVerify ) ) { Win32Err = NetDompVerifyTrustObject( rgNetDomArgs, TrustingDomain, TrustedDomain, &TrustingDomainUser, &TrustedDomainUser ); } else if ( CmdFlagOn(rgNetDomArgs, eTrustKerberos) ) { Win32Err = NetDompVerifyIndividualTrustKerberos( TrustingDomain, TrustedDomain, &TrustingDomainUser, &TrustedDomainUser ); } else if (CmdFlagOn(rgNetDomArgs, eTrustTransitive)) { // // Get the transitivity parameter // Win32Err = NetDompGetArgumentString(rgNetDomArgs, eTrustTransitive, &pwzArgValue); if (ERROR_SUCCESS != Win32Err) { goto HandleTrustExit; } Win32Err = NetDomTransitivity(pwzArgValue, TrustingDomain, TrustedDomain, &TrustingDomainUser, &TrustedDomainUser ); } else if (CmdFlagOn(rgNetDomArgs, eTrustFilterSIDs)) { // // Get the transitivity parameter // Win32Err = NetDompGetArgumentString(rgNetDomArgs, eTrustFilterSIDs, &pwzArgValue); if (ERROR_SUCCESS != Win32Err) { goto HandleTrustExit; } Win32Err = NetDomFilterSID(pwzArgValue, TrustingDomain, TrustedDomain, &TrustingDomainUser, &TrustedDomainUser ); } else if (CmdFlagOn(rgNetDomArgs, eTrustNameSuffixes)) { // // Get the name of the domain whose name is to be toggled. // Win32Err = NetDompGetArgumentString(rgNetDomArgs, eTrustNameSuffixes, &pwzArgValue); if (ERROR_SUCCESS != Win32Err) { goto HandleTrustExit; } if (CmdFlagOn(rgNetDomArgs, eTrustToggleSuffixes)) { // // Get the number of the name to toggle. // Win32Err = NetDompGetArgumentString(rgNetDomArgs, eTrustToggleSuffixes, &pwzArgValue2); if (ERROR_SUCCESS != Win32Err) { goto HandleTrustExit; } if (!pwzArgValue2) { NetDompDisplayMessage(MSG_SUFFIX_INDEX_MISSING); Win32Err = ERROR_INVALID_PARAMETER; goto HandleTrustExit; } i = wcstoul(pwzArgValue2, L'\0', 10); NetApiBufferFree(pwzArgValue2); pwzArgValue2 = NULL; if (1 > i) { NetDompDisplayMessage(MSG_SUFFIX_INDEX_BOUNDS); Win32Err = ERROR_INVALID_PARAMETER; goto HandleTrustExit; } Win32Err = NetDomForestSuffix(pwzArgValue, i, TrustingDomain, &TrustingDomainUser); } else { Win32Err = NetDomForestSuffix(pwzArgValue, 0, TrustingDomain, &TrustingDomainUser); } } else { Win32Err = ERROR_INVALID_PARAMETER; } HandleTrustExit: if (pwzArgValue) { NetApiBufferFree(pwzArgValue); } NetApiBufferFree( TrustedDomain ); NetDompFreeAuthIdent( &TrustedDomainUser ); NetDompFreeAuthIdent( &TrustingDomainUser ); if (NO_ERROR != Win32Err) { NetDompDisplayErrorMessage(Win32Err); } return( Win32Err ); } DWORD NetDompIsParentChild( IN PND5_TRUST_INFO pFirstDomainInfo, IN PND5_TRUST_INFO pSecondDomainInfo, OUT BOOL * pfParentChild) /*++ Routine Description: Is the domain named by the second argument a child or parent of the first argument domain. The Parent or Child bits of the trust-info Flags element is set as appropriate. --*/ { DWORD Win32Err; PDS_DOMAIN_TRUSTS rgDomains = NULL; ULONG ulCount = 0, i, ulLocalDomainIdx = (ULONG)-1, ulOtherDomainIdx = (ULONG)-1; ULONG ulLocalDomainParent = (ULONG)-1, ulOtherDomainParent = (ULONG)-1; PWSTR pwzServer, pwzOtherDomainDnsName, pwzOtherDomainNetbiosName; BOOL fFirstIsLocal = TRUE; *pfParentChild = FALSE; // The domain which is used as the starting point for the enumeration is // called the "local" domain and the remaining domain is the "other" domain. // if (pFirstDomainInfo->Flags & NETDOM_TRUST_FLAG_DOMAIN_NOT_FOUND) { // The first domain does not exist, so use the second domain's server // as the starting point for the domain enumeration. // pwzServer = pSecondDomainInfo->Server; ASSERT(!(pSecondDomainInfo->Flags & NETDOM_TRUST_FLAG_DOMAIN_NOT_FOUND)); pwzOtherDomainDnsName = pFirstDomainInfo->DomainName->Buffer; pwzOtherDomainNetbiosName = pFirstDomainInfo->DomainName->Buffer; fFirstIsLocal = FALSE; } else { // The first domain exists, so use its server as the starting point // for the domain enumeration. // pwzServer = pFirstDomainInfo->Server; pwzOtherDomainDnsName = pSecondDomainInfo->DomainName->Buffer; pwzOtherDomainNetbiosName = (pSecondDomainInfo->Flags & NETDOM_TRUST_FLAG_DOMAIN_NOT_FOUND) ? pSecondDomainInfo->DomainName->Buffer : pSecondDomainInfo->FlatName->Buffer; } ASSERT(pwzServer); // Specifying DS_DOMAIN_IN_FOREST will eliminate any external trusts // in the result set. // Win32Err = DsEnumerateDomainTrusts(pwzServer, DS_DOMAIN_IN_FOREST, &rgDomains, &ulCount); if (Win32Err != ERROR_SUCCESS) { return Win32Err; } for (i = 0; i < ulCount; i++) { ASSERT(rgDomains[i].TrustType & TRUST_TYPE_UPLEVEL); if (rgDomains[i].Flags & DS_DOMAIN_PRIMARY) { ulLocalDomainIdx = i; DBG_VERBOSE(("%2d: Local domain: %ws (%ws)\n", i, rgDomains[i].DnsDomainName, rgDomains[i].NetbiosDomainName)) if (!(rgDomains[i].Flags & DS_DOMAIN_TREE_ROOT)) { DBG_VERBOSE(("\tParent index of above domain: %d\n", rgDomains[i].ParentIndex)) ulLocalDomainParent = rgDomains[i].ParentIndex; } else { DBG_VERBOSE(("\tThis domain is a tree root\n")) } continue; } #if DBG == 1 DBG_VERBOSE(("%2d: Domain: %ws (%ws)\n", i, rgDomains[i].DnsDomainName, rgDomains[i].NetbiosDomainName)) if (rgDomains[i].Flags & DS_DOMAIN_TREE_ROOT) DBG_VERBOSE(("\tThis domain is a tree root\n")) else DBG_VERBOSE(("\tParent index of above domain: %d\n", rgDomains[i].ParentIndex)) #endif if ((rgDomains[i].NetbiosDomainName && _wcsicmp(rgDomains[i].NetbiosDomainName, pwzOtherDomainNetbiosName) == 0) || (rgDomains[i].DnsDomainName && _wcsicmp(rgDomains[i].DnsDomainName, pwzOtherDomainDnsName) == 0)) { ulOtherDomainIdx = i; DBG_VERBOSE(("\tThis domain is the second domain\n")) if (!(rgDomains[i].Flags & DS_DOMAIN_TREE_ROOT)) { ulOtherDomainParent = rgDomains[i].ParentIndex; } continue; } #if DBG == 1 if (!(rgDomains[i].Flags & DS_DOMAIN_DIRECT_OUTBOUND)) { DBG_VERBOSE(("%2d: Indirectly trusted domain: %ws (%ws)\n", i, rgDomains[i].DnsDomainName, rgDomains[i].NetbiosDomainName)) } #endif } if (rgDomains) { NetApiBufferFree(rgDomains); } // Determine the relationship between the two domains. One of three // situations will apply: // 1. The local domain is the parent of the other domain. If true, then // the parent index of the other domain will point to the local domain. // In addition, the other domain cannot be a tree root. // 2. The local domain is the child of the other domain. If true, then // the parent index of the local domain will point to the other domain. // The local domain then cannot be a tree root. // 3. The two domains don't have a parent-child relationship. It must be // a shortcut or external trust. // if (ulOtherDomainIdx == (ULONG)-1) { // Other domain not found, it must be a shortcut trust (case 3 above). // DBG_VERBOSE(("\n")) return ERROR_SUCCESS; } if (ulLocalDomainParent == ulOtherDomainIdx) { // Case 2, the local domain is the child of the other domain. // if (fFirstIsLocal) { pFirstDomainInfo->Flags |= NETDOM_TRUST_FLAG_CHILD; pSecondDomainInfo->Flags |= NETDOM_TRUST_FLAG_PARENT; } else { pSecondDomainInfo->Flags |= NETDOM_TRUST_FLAG_CHILD; pFirstDomainInfo->Flags |= NETDOM_TRUST_FLAG_PARENT; } } else { // // Case 1 above, the local domain is the parent of the other domain. // if (fFirstIsLocal) { pFirstDomainInfo->Flags |= NETDOM_TRUST_FLAG_PARENT; pSecondDomainInfo->Flags |= NETDOM_TRUST_FLAG_CHILD; } else { pSecondDomainInfo->Flags |= NETDOM_TRUST_FLAG_PARENT; pFirstDomainInfo->Flags |= NETDOM_TRUST_FLAG_CHILD; } } *pfParentChild = TRUE; DBG_VERBOSE(("\tpfParentChild set to TRUE\n\n")) return ERROR_SUCCESS; }