windows-nt/Source/XPSP1/NT/ds/security/tools/trustdom2/trustdom.c
2020-09-26 16:20:57 +08:00

2535 lines
69 KiB
C

/*--
Copyright (c) 1997-1997 Microsoft Corporation
Module Name:
trustdom.c
Abstract:
Command line tool for displaying/creating/deleting trust links between 2 domains
Author:
1-Apr-1997 Mac McLain (macm) Created
14-Jun-1998 Cristian Ioneci (cristiai) Heavily modified
Environment:
User mode only.
Requires ANSI C extensions: slash-slash comments, long external names.
Revision History:
--*/
#include <nt.h>
#include <ntrtl.h>
#include <nturtl.h>
#include <windows.h>
#include <ntlsa.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <dsgetdc.h>
#include <ntrtl.h>
#include <lmcons.h>
#include <lmapibuf.h>
#include <lmaccess.h>
#include <lmserver.h>
#include <lmerr.h>
#include <string.h>
#define DEFINES_ONLY
#include "res.rc"
//taken from netlibnt.h; resides in netapi32.dll
NTSTATUS
NetpApiStatusToNtStatus(
NET_API_STATUS NetStatus
);
#define DBG 1
//dbgprintf macro: call it like dbgprint(("X:%d\n",i)); //notice the xtra paranthesis!!
#ifdef DBG
#define dbgprintf(a) if(Dbg) resprintf a
#else
#define dbgprintf(a)
#endif
/*-------------------------------------------------------*/
HINSTANCE hInst;
#define RBSZ 4096
WCHAR resbuf[RBSZ];
WCHAR outbuf[RBSZ];
#define RESPRINT_STDOUT 3
/*-------------------------------------------------------*/
//Printf message with format taken from a resource string
// where: 0 - stdout; 1- stderr; 2 - in the 'output' buffer
//take care: the resulting string must be max. RBSZ wchars (see #define above)
int resprintf(int where, UINT ids, ... )
{
va_list parlist;
va_start(parlist,ids);
if(LoadString(hInst,ids,resbuf,RBSZ)==0)
swprintf(resbuf,L"(LoadString failed with 0x%08lx)",GetLastError());
switch(where) {
case 0:
return(vwprintf(resbuf, parlist));
case 1:
return(vfwprintf(stderr, resbuf, parlist));
case 2:
return(vswprintf(outbuf, resbuf, parlist));
case RESPRINT_STDOUT:
return(vfwprintf(stdout, resbuf, parlist));
DEFAULT_UNREACHABLE;
}
}
enum DomInfoType_e { Minimal=0, Primary, DNS };
// Minimal mode is used only for 'localonly' flag...
//Minimal means that the name that was specified on the command line
//(and copied in the ArgDomName member of the _TD_DOM_INFO structure) will
//be the only information available about the target domain (that is, just
//the flat name of the domain). That could happen if the target domain is
//no longer accessible at the moment when the trustdom is run... 'TrustDom'
//will try to do its best in this case...
struct LsaTIshot {
ULONG count;
PLSA_TRUST_INFORMATION pTI;
};
typedef struct _TD_DOM_INFO {
PWSTR pArgDomName; //from the command line...
UNICODE_STRING uMinimalName; //in case it is needed...
LSA_HANDLE Policy;
DWORD majver;
LSA_HANDLE TrustedDomain;
WCHAR DCName[1024];
enum DomInfoType_e DomInfoType;
union {
PPOLICY_PRIMARY_DOMAIN_INFO PrimaryDomainInfo;
PPOLICY_DNS_DOMAIN_INFO DnsDomainInfo;
} u;
PTRUSTED_DOMAIN_INFORMATION_EX pTDIX; //one shot... Lsa memory space
ULONG TDIXcEntries;
struct LsaTIshot *pTIs; //array of TIshots
int nTIs; //no. of TIshots
ULONG TIcEntries;
USER_INFO_1 *pUI1; //one shot...
DWORD UI1cEntries;
} TD_DOM_INFO, *PTD_DOM_INFO;
typedef struct _TD_VERIFY_INFO {
PUNICODE_STRING DisplayName;
PUNICODE_STRING ShortName;
UNICODE_STRING NameBuffer;
NTSTATUS IncomingStatus;
NTSTATUS OutgoingStatus;
} TD_VERIFY_INFO, *PTD_VERIFY_INFO;
//
// Local function prototypes
//
NTSTATUS
GetDomainInfoForDomain(
IN PWSTR DomainName,
IN OPTIONAL PWSTR DCMachineName, // optional DC machine name
IN PTD_DOM_INFO Info,
IN BOOL MitTarget
);
NTSTATUS
GetTrustLinks(
IN PTD_DOM_INFO pInfo
);
VOID
FreeDomainInfo(
IN PTD_DOM_INFO Info
);
//
// Globals
//
BOOLEAN Force = FALSE;
BOOLEAN Nt4 = FALSE;
BOOLEAN Dbg = FALSE;
BOOLEAN SidList = FALSE;
//BOOLEAN Overwritesid = FALSE; actually use Force instead...
ULONG
DisplayErrorMessage(
IN NTSTATUS Status
)
/*++
Routine Description:
This function display the error string for the given error status
Arguments:
NetStatus - Status to display the message for
Return Value:
VOID
--*/
{
ULONG Size = 0;
PWSTR DisplayString;
ULONG Options = FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM;
Size = FormatMessage( Options,
NULL,
RtlNtStatusToDosError( Status ),
MAKELANGID( LANG_NEUTRAL, SUBLANG_DEFAULT ),
( LPTSTR )&DisplayString,
0,
NULL );
if ( Size != 0 ) {
fprintf( stdout, "%ws", DisplayString );
LocalFree( DisplayString );
}
return( Size );
}
VOID
Usage (
VOID
)
{
resprintf(1,IDS_USAGE,VER_FILEVERSION_LSTR);
}
/*---------------------------- printSID --------------------------*/
BOOL
PrintSID(
IN PSID s
)
{
int i;
BOOL r=TRUE;
SID_IDENTIFIER_AUTHORITY *a;
if(s==NULL) {
printf("<NULL sid>");
return(FALSE);
}
if (!IsValidSid(s)) {
printf("<invalid sid>:");
r=FALSE;
}
a = GetSidIdentifierAuthority(s);
// printf("S-0x1-%02x%02x%02x%02x%02x%02x",
// a->Value[0], a->Value[1],
// a->Value[2], a->Value[3],
// a->Value[4], a->Value[5]);
printf("S-0x1-");
for(i=0; i<6; i++)
if(a->Value[i]>0)
break;
if(i==6) // hmmm... all zeroes?
printf("0"); // out one zero then
else {
for( ; i<6; i++) // else dump the remaining ones
printf("%02x",a->Value[i]);
}
for (i = 0; i < (int)(*GetSidSubAuthorityCount(s)); i++) {
printf("-%lx", *GetSidSubAuthority(s, i));
}
return(r);
}
NTSTATUS
GenerateRandomSID(
OUT PSID *pSID
)
{
NTSTATUS Status = STATUS_SUCCESS;
SID_IDENTIFIER_AUTHORITY NtAuthority = SECURITY_NT_AUTHORITY;
LARGE_INTEGER CurrentTime;
NtQuerySystemTime(&CurrentTime);
Status = RtlAllocateAndInitializeSid(
&NtAuthority,
4,
SECURITY_NT_NON_UNIQUE,
0,
CurrentTime.LowPart,
CurrentTime.HighPart,
0,0,0,0,
pSID
);
if (!NT_SUCCESS(Status))
{
*pSID=NULL;
resprintf(0,IDS_GENERATERANDOMSID_F,Status);
}
return(Status);
}
//--------------------------zapchr------------------------------------
BOOL zapchr(WCHAR *s, // zap specified character from the end of string
WCHAR c, // usefull to cut things like '\n' or '\\'
WCHAR rc) // rc is the char to replace with
{ WCHAR *p;
if((p=wcsrchr(s,c))!=NULL) {
*p=rc;
return(TRUE); // found smth to cut...
}
return(FALSE); // the string was "clean"...
}
/*----------------------------------------------------------------------------*/
BOOL GetPassword(WCHAR *passwd, size_t n)
{
/* turn off console echo & read in the password */
HANDLE console;
DWORD mode;
*passwd=L'\0';
if((console = GetStdHandle(STD_INPUT_HANDLE))==INVALID_HANDLE_VALUE)
return(FALSE);
if (! GetConsoleMode(console, &mode))
return(FALSE);
if (! SetConsoleMode(console, (mode & ~ENABLE_ECHO_INPUT)))
return(FALSE);
//fwprintf(stderr, L"Password : ");
resprintf(1,IDS_PASSWORD_PROMPT);
if (!fgetws(passwd, n, stdin))
return(FALSE);
zapchr(passwd,L'\n',L'\0');
if (! SetConsoleMode(console, mode))
return(FALSE);
if(!CloseHandle(console))
return(FALSE);
fwprintf(stderr,L"\n");
return(TRUE);
}
//UNICODE_STRING uMinimalName; not used anymore... added a field with same name inside each
//TD_DOM_INFO structure that will be used instead... in this way two consecutive calls
//*** FOR TWO STRUCTURES*** will not overwrite it.
//------------------GetFlatName---------------------------
PLSA_UNICODE_STRING GetFlatName(IN PTD_DOM_INFO pInfo)
{
switch(pInfo->DomInfoType) {
case DNS:
return(&pInfo->u.DnsDomainInfo->Name);
case Primary:
return(&pInfo->u.PrimaryDomainInfo->Name);
default: //Minimal
RtlInitUnicodeString(&pInfo->uMinimalName,pInfo->pArgDomName);
return(&pInfo->uMinimalName);
}
}
//------------------GetName--------------------------------
PLSA_UNICODE_STRING GetName(IN PTD_DOM_INFO pInfo)
{
//simpler, just a little bit slower... (xtra call)
// if(pInfo->DomInfoType==DNS)
// return(&pInfo->u.DnsDomainInfo->DnsDomainName);
//
// return(GetFlatName(pInfo));
switch(pInfo->DomInfoType) {
case DNS:
return(&pInfo->u.DnsDomainInfo->DnsDomainName);
case Primary:
return(&pInfo->u.PrimaryDomainInfo->Name);
default: //Minimal
RtlInitUnicodeString(&pInfo->uMinimalName,pInfo->pArgDomName);
return(&pInfo->uMinimalName);
}
}
PSID GetSid(IN PTD_DOM_INFO pInfo)
{
PSID ReturnSid = NULL;
switch(pInfo->DomInfoType) {
case DNS:
ReturnSid = pInfo->u.DnsDomainInfo->Sid;
break;
case Primary:
ReturnSid = pInfo->u.PrimaryDomainInfo->Sid;
break;
}
return( ReturnSid );
}
WCHAR SrvName[1024];
//----------------MakeSrvName-------------------------------
PWSTR MakeSrvName(IN PWSTR Name) //add slashes at the beginning
{
swprintf(SrvName,L"\\\\%ws",Name);
if(SrvName[0]==L'\0')
return(NULL);
return(SrvName);
}
WCHAR Domain[1024];
//----------------AddDlrToDomainName-------------------------
PWSTR AddDlrToDomainName(IN PTD_DOM_INFO pInfo)
{
swprintf(Domain,L"%wZ$",GetFlatName(pInfo));
return(Domain);
}
WCHAR CutDlrDomain[1024];
//----------------CutDlrFromName-----------------------------
PWSTR CutDlrFromName(IN PWSTR Name)
{
wcscpy(CutDlrDomain,Name);
zapchr(CutDlrDomain,L'$',L'\0');
return(CutDlrDomain);
}
WCHAR secret[1024];
LSA_UNICODE_STRING uSecret;
//---------------------MakeSecretName------------------
PLSA_UNICODE_STRING MakeSecretName(IN PTD_DOM_INFO pInfo)
{
swprintf(secret,L"G$$%wZ",GetFlatName(pInfo));
RtlInitUnicodeString(&uSecret,secret);
return(&uSecret);
}
//start section inserted from Mac (11/05/1998(Thu) 17:08:53)
NTSTATUS
VerifyIndividualTrust(
IN PSID InboundDomainSid,
IN PUNICODE_STRING InboundDomainName,
IN PLSA_HANDLE OutboundHandle,
IN PWSTR OutboundServer,
IN OUT PNTSTATUS VerifyStatus
)
/*++
Routine Description:
This routine will verify a single trust in the one direction only.
Arguments:
InboundDomainSid -- Sid of the inbound side of the trust
OutboundHandle -- Open policy handle to a domain controller on the outbound side
OutboundServer -- Name of the domian controller on the outbound side
VerifyStatus -- Status returned from the verification attempt
Return Value:
STATUS_SUCCESS -- Success
STATUS_INVALID_SID -- The specified domain sid was invalid
--*/
{
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;
//
// Assume the trust is invalid until we can prove otherwise.
//
*VerifyStatus = STATUS_TRUSTED_DOMAIN_FAILURE;
ASSERT( RtlValidSid( InboundDomainSid ) );
if ( !RtlValidSid( InboundDomainSid ) ) {
return( STATUS_INVALID_SID );
}
//
// Check netlogons secure channel
//
if ( NT_SUCCESS( Status ) ) {
NetStatus = I_NetLogonControl2( OutboundServer,
NETLOGON_CONTROL_TC_QUERY,
2,
( LPBYTE )&InboundDomainName->Buffer,
( LPBYTE *)&NetlogonInfo2 );
if ( NetStatus == NERR_Success ) {
NetStatus = NetlogonInfo2->netlog2_pdc_connection_status;
NetApiBufferFree( NetlogonInfo2 );
if ( NetStatus != NERR_Success ) {
NetStatus = I_NetLogonControl2( OutboundServer,
NETLOGON_CONTROL_REDISCOVER,
2,
( LPBYTE )&InboundDomainName->Buffer,
( LPBYTE *)&NetlogonInfo2 );
}
}
*VerifyStatus = NetpApiStatusToNtStatus( NetStatus );
}
//
// Now, try a lookup
//
if ( NT_SUCCESS( Status ) && NT_SUCCESS( *VerifyStatus ) ) {
//
// Build the domain admins sid for the inbound side of the trust
//
RtlCopyMemory( DomAdminSid,
InboundDomainSid,
RtlLengthSid( InboundDomainSid ) );
( ( PISID )( DomAdminSid ) )->SubAuthorityCount++;
*( RtlSubAuthoritySid( DomAdminSid,
*( RtlSubAuthorityCountSid( InboundDomainSid ) ) ) ) =
DOMAIN_GROUP_RID_ADMINS;
//
// Now, we'll simply do a remote lookup, and ensure that we get back success
//
Status = LsaLookupSids( OutboundHandle,
1,
&DomAdminSid,
&Domains,
&Names );
if ( NT_SUCCESS( Status ) ) {
LsaFreeMemory( Domains );
LsaFreeMemory( Names );
*VerifyStatus = STATUS_SUCCESS;
} else if ( Status == STATUS_NONE_MAPPED ) {
*VerifyStatus = STATUS_TRUSTED_DOMAIN_FAILURE;
Status = STATUS_SUCCESS;
} else {
*VerifyStatus = Status;
}
//
// If all of that worked, check netlogons secure channel
//
if ( NT_SUCCESS( Status ) && NT_SUCCESS( *VerifyStatus ) ) {
NetStatus = I_NetLogonControl2( OutboundServer,
NETLOGON_CONTROL_TC_QUERY,
2,
( LPBYTE )&InboundDomainName->Buffer,
( LPBYTE *)&NetlogonInfo2 );
if ( NetStatus == NERR_Success ) {
NetStatus = NetlogonInfo2->netlog2_pdc_connection_status;
NetApiBufferFree( NetlogonInfo2 );
if ( NetStatus != NERR_Success ) {
NetStatus = I_NetLogonControl2( OutboundServer,
NETLOGON_CONTROL_REDISCOVER,
2,
( LPBYTE )&InboundDomainName->Buffer,
( LPBYTE *)&NetlogonInfo2 );
}
}
*VerifyStatus = NetpApiStatusToNtStatus( NetStatus );
}
}
return( Status );
}
NTSTATUS
VerifyTrustInbound(
IN PTD_DOM_INFO LocalDomain,
IN PUNICODE_STRING RemoteDomain,
IN OUT PNTSTATUS VerifyStatus
)
{
NTSTATUS Status = STATUS_SUCCESS;
TD_DOM_INFO RemoteTrustInfo;
WCHAR DCname[MAX_PATH + 1]= { L'\0' };
RtlZeroMemory( &RemoteTrustInfo, sizeof( RemoteTrustInfo ) );
Status = GetDomainInfoForDomain( RemoteDomain->Buffer, NULL, &RemoteTrustInfo, FALSE );
if ( NT_SUCCESS( Status ) ) {
Status= VerifyIndividualTrust( GetSid( LocalDomain ),
GetName( LocalDomain ),
RemoteTrustInfo.Policy,
RemoteTrustInfo.DCName,
VerifyStatus );
FreeDomainInfo( &RemoteTrustInfo );
} else {
*VerifyStatus = Status;
}
return( Status );
}
NTSTATUS
VerifyTrustOutbound(
IN PTD_DOM_INFO LocalDomain,
IN PUNICODE_STRING RemoteDomain,
IN OUT PNTSTATUS VerifyStatus
)
{
NTSTATUS Status = STATUS_SUCCESS;
TD_DOM_INFO RemoteTrustInfo;
RtlZeroMemory( &RemoteTrustInfo, sizeof( RemoteTrustInfo ) );
Status = GetDomainInfoForDomain( RemoteDomain->Buffer, NULL, &RemoteTrustInfo, FALSE );
if ( NT_SUCCESS( Status ) ) {
Status= VerifyIndividualTrust( GetSid( &RemoteTrustInfo ),
GetName( &RemoteTrustInfo ),
LocalDomain->Policy,
LocalDomain->DCName,
VerifyStatus );
FreeDomainInfo( &RemoteTrustInfo );
} else {
*VerifyStatus = Status;
}
return( Status );
}
NTSTATUS
VerifyTrusts(
IN PWSTR DomainName,
IN OPTIONAL PWSTR DCMachineName // optional DC machine name
)
/*++
Routine Description:
This routine will verify the existing trusts with all other NT domains, and display the
results.
Arguments:
DomainName -- OPTIONAL name of the domain on which to verify the information
Return Value:
STATUS_SUCCESS -- Success
STATUS_INSUFFICIENT_RESOURCES -- A memory allocation failed
--*/
{
NTSTATUS Status = STATUS_SUCCESS;
TD_DOM_INFO TrustInfo;
PTD_VERIFY_INFO VerifyList = NULL;
ULONG VerifyCount = 0, VerifyIndex = 0, i, j;
BOOLEAN InvalidIncoming = FALSE, InvalidOutgoing = FALSE, Valid = FALSE;
UNICODE_STRING *LocalDomainName = NULL, SamNameAsDomain;
WCHAR *AccountTrunc;
RtlZeroMemory( &TrustInfo, sizeof( TrustInfo ) );
Status = GetDomainInfoForDomain( DomainName, DCMachineName, &TrustInfo, FALSE );
if ( NT_SUCCESS( Status ) ) {
Status = GetTrustLinks( &TrustInfo );
}
if ( !NT_SUCCESS( Status ) ) {
goto VerifyExit;
}
LocalDomainName = GetName( &TrustInfo );
//
// Allocate a list of verify information to correspond to the list we enumerated
//
VerifyCount = max( TrustInfo.TDIXcEntries, TrustInfo.UI1cEntries + TrustInfo.TIcEntries );
VerifyList = ( PTD_VERIFY_INFO )LocalAlloc( LMEM_FIXED | LMEM_ZEROINIT,
VerifyCount * sizeof( TD_VERIFY_INFO ) );
if ( !VerifyList ) {
Status = STATUS_INSUFFICIENT_RESOURCES;
goto VerifyExit;
}
//
// Now, do the verification.
//
if ( TrustInfo.TDIXcEntries ) {
for ( i = 0; i < TrustInfo.TDIXcEntries; i++ ) {
if ( TrustInfo.pTDIX[ i ].TrustType == TRUST_TYPE_DOWNLEVEL ||
TrustInfo.pTDIX[ i ].TrustType == TRUST_TYPE_UPLEVEL ) {
VerifyList[ VerifyIndex ].DisplayName = &TrustInfo.pTDIX[ i ].Name;
VerifyList[ VerifyIndex ].ShortName = &TrustInfo.pTDIX[ i ].FlatName;
resprintf( RESPRINT_STDOUT, IDS_VERIFY_CHECK,
LocalDomainName,
VerifyList[ VerifyIndex ].DisplayName );
if ( ( TrustInfo.pTDIX[ i ].TrustDirection & TRUST_DIRECTION_INBOUND ) ) {
Status = VerifyTrustInbound( &TrustInfo,
&TrustInfo.pTDIX[ i ].Name,
&VerifyList[ VerifyIndex ].IncomingStatus );
}
if ( ( TrustInfo.pTDIX[ i ].TrustDirection & TRUST_DIRECTION_OUTBOUND ) &&
Status != STATUS_NO_SUCH_DOMAIN ) {
Status = VerifyTrustOutbound( &TrustInfo,
&TrustInfo.pTDIX[ i ].Name,
&VerifyList[ VerifyIndex ].OutgoingStatus );
}
if ( NT_SUCCESS( VerifyList[ VerifyIndex ].OutgoingStatus ) &&
NT_SUCCESS( VerifyList[ VerifyIndex ].IncomingStatus ) ) {
Valid = TRUE;
} else {
if ( !NT_SUCCESS( VerifyList[ VerifyIndex ].OutgoingStatus ) ) {
InvalidOutgoing = TRUE;
}
if ( !NT_SUCCESS( VerifyList[ VerifyIndex ].IncomingStatus ) ) {
InvalidIncoming = TRUE;
}
}
VerifyIndex++;
}
Status = STATUS_SUCCESS;
}
} else {
//
// Going to have to do the old NT4 style.
//
//for ( i = 0; i < TrustInfo.TIcEntries; i++ ) {
int shot;
for ( VerifyIndex=0, shot=0; shot<TrustInfo.nTIs; shot++)
for(i=0; i<TrustInfo.pTIs[shot].count; i++) {
VerifyList[ VerifyIndex ].DisplayName = &TrustInfo.pTIs[shot].pTI[ i ].Name;
VerifyList[ VerifyIndex ].ShortName = &TrustInfo.pTIs[shot].pTI[ i ].Name;
resprintf( RESPRINT_STDOUT, IDS_VERIFY_CHECK,
LocalDomainName,
VerifyList[ VerifyIndex ].DisplayName );
Status = VerifyTrustOutbound( &TrustInfo,
&TrustInfo.pTIs[shot].pTI[ i ].Name,
&VerifyList[ VerifyIndex ].OutgoingStatus );
if ( !NT_SUCCESS( VerifyList[ VerifyIndex ].OutgoingStatus ) ) {
InvalidOutgoing = TRUE;
}
VerifyIndex++;
}
//
// Now, the same with the sam account names
//
for ( i = 0; i < TrustInfo.UI1cEntries; i++ ) {
//
// Shorten the account name to be a domain name
//
AccountTrunc = &TrustInfo.pUI1[ i ].usri1_name[
wcslen( TrustInfo.pUI1[ i ].usri1_name ) - 1 ];
*AccountTrunc = UNICODE_NULL;
//
// See if we already have an entry for this in our verified list
//
RtlInitUnicodeString( &SamNameAsDomain, TrustInfo.pUI1[ i ].usri1_name );
for ( j = 0; j < VerifyIndex; j++ ) {
if ( RtlEqualUnicodeString( &SamNameAsDomain,
VerifyList[ j ].ShortName, TRUE ) ) {
break;
}
}
if ( j == VerifyIndex ) {
RtlCopyMemory( &VerifyList[ j ].NameBuffer, &SamNameAsDomain,
sizeof( UNICODE_STRING ) );
VerifyList[ j ].DisplayName = &VerifyList[ j ].NameBuffer;
VerifyList[ j ].ShortName = &VerifyList[ j ].NameBuffer;
VerifyIndex++;
}
resprintf( RESPRINT_STDOUT, IDS_VERIFY_CHECK,
LocalDomainName,
&SamNameAsDomain );
Status = VerifyTrustInbound( &TrustInfo,
&SamNameAsDomain,
&VerifyList[ j ].IncomingStatus );
if ( !NT_SUCCESS( VerifyList[ j ].IncomingStatus ) ) {
InvalidIncoming = TRUE;
}
*AccountTrunc = L'$';
}
//
// Now, walk the list and see if we have any valid domain pairs
//
for ( i = 0; i < VerifyIndex; i++ ) {
if ( NT_SUCCESS( VerifyList[ i ].IncomingStatus ) &&
NT_SUCCESS( VerifyList[ i ].OutgoingStatus ) ) {
Valid = TRUE;
break;
}
}
}
//
// Display the list of valid trusts
//
if ( Valid ) {
resprintf( RESPRINT_STDOUT, IDS_VERIFY_VALID );
for ( i = 0; i < VerifyIndex; i++ ) {
if ( NT_SUCCESS( VerifyList[ i ].IncomingStatus ) &&
NT_SUCCESS( VerifyList[ i ].OutgoingStatus ) ) {
fprintf(stdout, "%wZ\n", VerifyList[ i ].DisplayName );
}
}
}
if ( InvalidIncoming ) {
resprintf( RESPRINT_STDOUT, IDS_VERIFY_INVALID_INCOMING );
for ( i = 0; i < VerifyIndex; i++ ) {
if ( !NT_SUCCESS( VerifyList[ i ].IncomingStatus ) ) {
fprintf( stdout, "%wZ - ", VerifyList[ i ].DisplayName );
if ( DisplayErrorMessage( VerifyList[ i ].IncomingStatus ) == 0 ) {
resprintf( RESPRINT_STDOUT, IDS_VERIFY_UNMAPPABLE,
VerifyList[ i ].IncomingStatus );
}
}
}
}
if ( InvalidOutgoing ) {
resprintf( RESPRINT_STDOUT, IDS_VERIFY_INVALID_OUTGOING );
for ( i = 0; i < VerifyIndex; i++ ) {
if ( !NT_SUCCESS( VerifyList[ i ].OutgoingStatus ) ) {
fprintf( stdout, "%wZ - ", VerifyList[ i ].DisplayName );
if ( DisplayErrorMessage( VerifyList[ i ].OutgoingStatus ) == 0 ) {
resprintf( RESPRINT_STDOUT, IDS_VERIFY_UNMAPPABLE,
VerifyList[ i ].OutgoingStatus );
}
}
}
}
Status = STATUS_SUCCESS;
VerifyExit:
LocalFree( VerifyList );
FreeDomainInfo( &TrustInfo );
return( Status );
}
//end section insert from Mac (11/05/1998(Thu) 17:09:39)
NTSTATUS
GetDomainInfoForDomain(
IN PWSTR DomainName,
IN OPTIONAL PWSTR DCMachineName, // optional DC machine name
IN PTD_DOM_INFO Info,
BOOL MitTarget // TRUE if this call is made for the B domain in a A <-> B Mit type trust link
)
/*++
Routine Description:
Tries to fill as much as possible of the TD_DOM_INFO structure for the given domain;
For a NT4 DC, the DNS name does not exist
Arguments:
DomainName -- Optional domain to conect to
Info -- Information structure to fill in
Return Value:
STATUS_SUCCESS -- Success
STATUS_NO_SUCH_DOMAIN -- No server could be located for the domain
--*/
{
NET_API_STATUS netstatus=NERR_Success;
NTSTATUS Status = STATUS_SUCCESS;
PWSTR pMachine=NULL;
DWORD dwErr;
UNICODE_STRING Server;
// UNICODE_STRING uString;
// PLSA_UNICODE_STRING puDomName;
//
OBJECT_ATTRIBUTES ObjectAttributes;
PDOMAIN_CONTROLLER_INFO DCInfo = NULL;
SERVER_INFO_101 *p101 = NULL;
PSID sid=NULL;
WCHAR *DCInfostr=L"";
Info->DomInfoType=Minimal;
Info->pArgDomName=DomainName;
Info->majver=0; // assume nothing... or a Unix machine... (for a MIT trust)
Info->DCName[0]=L'\0';
if(MitTarget)
return(STATUS_NO_SUCH_DOMAIN);
resprintf(2,IDS_LOCAL); // printed to outbuf....
if ( (DomainName != NULL && DomainName[0]!=L'\0') || Nt4 ) { // try to get local machine name for an Nt4 style operation...
if(DCMachineName == NULL || DCMachineName[0]==L'\0') {
dwErr = DsGetDcName( NULL, (LPCWSTR)DomainName, NULL, NULL,
DS_DIRECTORY_SERVICE_PREFERRED | DS_WRITABLE_REQUIRED,
&DCInfo );
if ( dwErr == ERROR_SUCCESS ) {
wcscpy(Info->DCName,DCInfo->DomainControllerName + 2);
pMachine=Info->DCName;
//set the version
if((DCInfo->Flags&(DS_DS_FLAG|DS_WRITABLE_FLAG))==DS_WRITABLE_FLAG)
Info->majver=4;
else Info->majver=5;
dbgprintf((0,IDS_DSGETDCNAME_DC_D,DomainName!=NULL?DomainName:outbuf,Info->DCName)); //,DCInfo->Flags));
} else {
Status = STATUS_NO_SUCH_DOMAIN;
resprintf(0,IDS_DSGETDCNAME_F,DomainName!=NULL?DomainName:outbuf,dwErr);
if(Force)
resprintf(0,IDS_DSGETDCNAME_FFORCE);
else resprintf(0,IDS_DSGETDCNAME_FRET,Status);
}
}
else {
wcscpy(Info->DCName,DCMachineName);
pMachine=Info->DCName;
dbgprintf((0,IDS_DSGETDCNAME_DC_D,DomainName!=NULL?DomainName:outbuf,Info->DCName));
//now trying to get version using some other method than based on the flags returned by DsGetDcName...
netstatus = NetServerGetInfo( MakeSrvName(pMachine), 101, ( LPBYTE *) &p101 );
if(netstatus != NERR_Success) {
Status = STATUS_UNSUCCESSFUL;
fprintf(stderr,"NetServerGetInfo (101) failed: err 0x%08lx;\n"
" ...now returning Status 0x%08lx (STATUS_UNSUCCESSFUL)\n",
netstatus,Status);
goto cleanup;
}
Info->majver=(p101->sv101_version_major & MAJOR_VERSION_MASK);
}
}
RtlInitUnicodeString( &Server, Info->DCName );
if(Nt4) { // force Nt4 style
Info->majver=4;
dbgprintf( (0, IDS_FORCENT4, DomainName!=NULL?DomainName:outbuf) );
}
// if ( NT_SUCCESS( Status ) )
// {
//
// netstatus = NetServerGetInfo( pMachine, 101, ( LPBYTE *) &p101 );
// if(netstatus != NERR_Success) {
// Status = STATUS_UNSUCCESSFUL;
// fprintf(stderr,"NetServerGetInfo (101) failed: err 0x%08lx;\n"
// " ...now returning Status 0x%08lx (STATUS_UNSUCCESSFUL)\n",
// netstatus,Status);
// goto cleanup;
// }
// Info->majver=(p101->sv101_version_major & MAJOR_VERSION_MASK);
// }
if ( NT_SUCCESS( Status ) ) {
RtlZeroMemory( &ObjectAttributes, sizeof( ObjectAttributes ) );
Status = LsaOpenPolicy( DomainName == NULL ? NULL : &Server,
&ObjectAttributes,
MAXIMUM_ALLOWED,
&Info->Policy
);
if(!NT_SUCCESS(Status)) {
resprintf(0,IDS_LSAOPENPOLICY_F1, (Info->DCName[0]==L'\0')?outbuf:Info->DCName);
if ( Status == STATUS_ACCESS_DENIED)
resprintf(0,IDS_ACCESS_DENIED);
else resprintf(0,IDS_ERROR_FORMAT,Status);
goto cleanup;
}
Info->DomInfoType=DNS;
DCInfostr=L"DNS";
Status = LsaQueryInformationPolicy( Info->Policy,
PolicyDnsDomainInformation,
&(Info->u.DnsDomainInfo ) //the SID is in here...
);
dbgprintf( (0,IDS_GETDOMAININFOFORDOMAIN_D, DomainName!=NULL?DomainName:outbuf, DCInfostr, Status ));
if( !NT_SUCCESS( Status ) || Nt4) { // try at least Primary....
Info->majver=4;
DCInfostr=L"Primary";
dbgprintf( (0,IDS_PRIMARY_D) );
Info->DomInfoType=Primary;
Status = LsaQueryInformationPolicy( Info->Policy,
PolicyPrimaryDomainInformation,
&(Info->u.PrimaryDomainInfo ) //the SID is in here...
);
dbgprintf( (0,IDS_GETDOMAININFOFORDOMAIN_D, DomainName!=NULL?DomainName:outbuf, DCInfostr, Status ) );
}
else {
Info->majver=5;
}
switch(Info->DomInfoType) {
case DNS: sid = Info->u.DnsDomainInfo->Sid;
break;
case Primary: sid = Info->u.PrimaryDomainInfo->Sid;
break;
}
if(Dbg) {
printf("Domain %ws Sid=",DCInfostr);
PrintSID(sid);
printf("\n");
}
}
if(Info->DomInfoType==DNS)
dbgprintf( (0,IDS_DOMAINNAMED,&(Info->u.DnsDomainInfo->DnsDomainName) ) );
if ( !NT_SUCCESS( Status ) )
//well...
goto cleanup;
//...
cleanup:
if(DCInfo!=NULL)
NetApiBufferFree( DCInfo );
if(p101!=NULL)
NetApiBufferFree( p101 );
return( Status );
}
VOID
FreeDomainInfo(
IN PTD_DOM_INFO Info
)
/*++
Routine Description:
Frees the info returned from GetDomainInfoForDomain
Arguments:
Info -- Information structure to free
Return Value:
STATUS_SUCCESS -- Success
--*/
{
int i;
if ( Info->Policy ) {
LsaClose( Info->Policy );
Info->Policy=NULL;
}
if ( Info->u.DnsDomainInfo != NULL )
LsaFreeMemory( Info->u.DnsDomainInfo );
// Info->u.DnsDomainInfo is inside an union with Info->u.PrimaryDomainInfo
//on the same position
if(Info->pTDIX!=NULL)
LsaFreeMemory(Info->pTDIX);
//if there's an array of pointers to TI shots returned from LsaEnumerateTrustedDomains...
if(Info->pTIs!=NULL) {
for(i=0; i<Info->nTIs; i++)
LsaFreeMemory(Info->pTIs[i].pTI);
}
if(Info->pUI1!=NULL)
NetApiBufferFree(Info->pUI1);
}
NTSTATUS
GetTrustLinks(
IN PTD_DOM_INFO pInfo
)
/*++
Fills an array of trust links information.
Usually that information will be printed in the form:
domain_name, trust direction, type, attributes
)
--*/
{
NET_API_STATUS netstatus=NERR_Success;
NTSTATUS Status = STATUS_SUCCESS;
LSA_ENUMERATION_HANDLE EnumerationContext = 0;
//needed for NT4 enumeration...
DWORD UIRead=0L;
DWORD UITotal=0L;
DWORD reshandle=0; // Put 0 for enumeration handles !!!!
//A value like INVALID_HANDLE_VALUE (that is -1) would make the NetUserEnum to return 0 users...
if(pInfo->majver>=5) {
Status = LsaEnumerateTrustedDomainsEx( pInfo->Policy,
&EnumerationContext,
&pInfo->pTDIX,
0xffffffff, //ULONG_MAX,
&pInfo->TDIXcEntries );
dbgprintf( (0,IDS_LSAENUMERATETRUSTEDDOMAINSEX_D,GetName(pInfo),Status,pInfo->TDIXcEntries) );
if(Status==STATUS_NO_MORE_ENTRIES && pInfo->pTDIX==NULL) {
pInfo->TDIXcEntries=0L;
Status=STATUS_SUCCESS; //that means "0 entries"
}
return( Status );
}
//Enumerate NT4 Inbound trusts:
netstatus = NetUserEnum( MakeSrvName(pInfo->DCName),
1,
FILTER_INTERDOMAIN_TRUST_ACCOUNT,
(LPBYTE*)(&pInfo->pUI1),
0xffffffff, //ULONG_MAX
&UIRead,
&UITotal,
&reshandle
);
dbgprintf( (0,IDS_NETUSERENUM_D,GetName(pInfo),netstatus,UIRead) );
if(netstatus!=NERR_Success) {
Status = STATUS_UNSUCCESSFUL;
goto cleanup;
}
pInfo->UI1cEntries=UIRead;
//Enumerate NT4 Outbound trusts:
{ PLSA_TRUST_INFORMATION pTIShot=NULL;
ULONG nShotsize=0;
struct LsaTIshot *pTIsav=NULL;
do {
Status=LsaEnumerateTrustedDomains( pInfo->Policy,
&EnumerationContext,
&pTIShot,
0xffffffff, //ULONG_MAX,
&nShotsize);
dbgprintf( (0,IDS_LSAENUMERATETRUSTEDDOMAINS_D,GetName(pInfo),Status,nShotsize) );
if( (Status != STATUS_SUCCESS) &&
(Status != STATUS_MORE_ENTRIES) &&
(Status != STATUS_NO_MORE_ENTRIES)
) {
SetLastError( LsaNtStatusToWinError(Status) );
goto cleanup;
}
if(pTIShot!=NULL) {
if((pInfo->pTIs=realloc(pTIsav=pInfo->pTIs,pInfo->nTIs+1))==NULL) {
free(pTIsav);
Status = ERROR_NOT_ENOUGH_MEMORY;
goto cleanup;
}
pInfo->TIcEntries+=nShotsize;
pInfo->pTIs[pInfo->nTIs].count=nShotsize;
pInfo->pTIs[pInfo->nTIs].pTI=pTIShot;
pInfo->nTIs++;
}
} while (Status != STATUS_NO_MORE_ENTRIES);
if(Dbg) printf("Total number of entries: %u\n",pInfo->TIcEntries);
dbgprintf( (0,IDS_LSAENUMERATETRUSTEDDOMAINS_D,GetName(pInfo),Status,pInfo->TIcEntries) );
if(Status==STATUS_NO_MORE_ENTRIES)
Status=STATUS_SUCCESS;
if(pInfo->pTIs==NULL) {
pInfo->TIcEntries=0L;
}
}
cleanup:
return( Status );
}
struct bidir_st {
ULONG index; // index in the 'Inbound' vector
char type; // 'O' - Outbound, 'B' - Bidirectional
};
int __cdecl cmpbidir(const struct bidir_st *pb1, const struct bidir_st *pb2)
{
if(pb1->index==pb2->index)
return(0);
if(pb1->index>pb2->index)
return(1);
return(-1);
}
NTSTATUS
PrintTrustLinks(
IN PTD_DOM_INFO Info
)
/*++
Print Trust Links
--*/
{
ULONG i,j;
if(Info->majver>=5) {
for(i=0; i<Info->TDIXcEntries; i++) {
char c;
switch(Info->pTDIX[i].TrustDirection)
{
case TRUST_DIRECTION_DISABLED: c='D'; break;
case TRUST_DIRECTION_INBOUND: c='I'; break;
case TRUST_DIRECTION_OUTBOUND: c='O'; break;
case TRUST_DIRECTION_BIDIRECTIONAL: c='B'; break;
default: c='-'; break;
}
printf("%-32wZ,%c",&Info->pTDIX[i].Name,c);
switch(Info->pTDIX[i].TrustType&0x000FFFFF)
{
case TRUST_TYPE_DOWNLEVEL:
printf(",T_downlevel"); break;
case TRUST_TYPE_UPLEVEL:
printf(",T_uplevel"); break;
case TRUST_TYPE_MIT:
printf(",T_mit"); break;
case TRUST_TYPE_DCE:
printf(",T_DCE"); break;
default:
printf("-"); break;
}
if(Info->pTDIX[i].TrustAttributes & TRUST_ATTRIBUTE_NON_TRANSITIVE)
printf(",A_NonTran");
else printf(",_");
if(Info->pTDIX[i].TrustAttributes & TRUST_ATTRIBUTE_UPLEVEL_ONLY )
printf(",A_UpLevelOnly");
else printf(",_");
if(Info->pTDIX[i].TrustAttributes & TRUST_ATTRIBUTE_FILTER_SIDS )
printf(",A_FilterSids");
else printf(",_");
if(Info->pTDIX[i].TrustAttributes & TRUST_ATTRIBUTE_FOREST_TRANSITIVE)
printf(",A_ForestTrust");
else printf(",_");
if(SidList) {
printf(",");
PrintSID(Info->pTDIX[i].Sid);
}
printf("\n");
}
}
else {
//Info->majver<=4
int shot;
struct bidir_st *p=NULL, *q=NULL;
if((p=calloc(Info->TIcEntries,sizeof(struct bidir_st)))==NULL)
return(ERROR_NOT_ENOUGH_MEMORY);
//for(q=p,i=0; i<Info->TIcEntries; q++,i++) {
for(q=p,shot=0; shot<Info->nTIs; shot++) {
for(i=0; i<Info->pTIs[shot].count; i++,q++) {
WCHAR buf[1024];
swprintf(buf,L"%wZ",&Info->pTIs[shot].pTI[i].Name);
for(j=0; j<Info->UI1cEntries; j++)
if(wcscmp(buf,CutDlrFromName(Info->pUI1[j].usri1_name))==0)
break;
if((q->index=j)<Info->UI1cEntries) //found...
q->type='B'; //actually it's a Bidir link...
else q->type='O'; //or this is a "true" Outbound...
}
}
//print Outbound and Bidirectional links
//for(q=p,i=0; i<Info->TIcEntries; q++,i++)
for(q=p,shot=0; shot<Info->nTIs; shot++)
for(i=0; i<Info->pTIs[shot].count; i++,q++)
printf("%-32wZ,%c,T_downlevel,_,_,_,_\n",&Info->pTIs[shot].pTI[i].Name,q->type);
qsort(p,Info->TIcEntries,sizeof(struct bidir_st),cmpbidir);
//print Inbound links
for(q=p,j=i=0; i<Info->UI1cEntries; i++) {
if(j<Info->TIcEntries && q->index==i) { //if it was a Bidirectional, it was already printed...
j++; q++;
continue;
}
printf("%-32ws,I,T_downlevel,_,_,_,_\n",CutDlrFromName(Info->pUI1[i].usri1_name));
}
if(p!=NULL)
free(p);
}
return( STATUS_SUCCESS );
}
NTSTATUS
CreateNT5TrustDomObject(
IN PTD_DOM_INFO Local,
IN PTD_DOM_INFO Remote,
IN PWSTR Password,
IN BOOLEAN Downlevel,
IN BOOLEAN Mit,
IN ULONG Direction
)
/*++
Routine Description:
Creates the trusted domain object on an NT5 domain (DS based)
Arguments:
Local -- Information about the domain doing the trust
Remote -- Information about the domain being trusted
Password -- Password to set on the trust
Downlevel -- If TRUE, create this as a downlevel trust
Mit -- If TRUE, creates this as an Mit style trust
Direction -- Which direction to make the link in.
Return Value:
STATUS_SUCCESS -- Success
--*/
{
NTSTATUS Status = STATUS_SUCCESS;
WCHAR Domain[1024]={L'\0'};
WCHAR DnsDomain[1024]={L'\0'};
TRUSTED_DOMAIN_INFORMATION_EX TDIEx;
LSA_AUTH_INFORMATION AuthData;
TRUSTED_DOMAIN_AUTH_INFORMATION AuthInfoEx;
PSID Sid = NULL;
swprintf(Domain,L"%wZ",GetFlatName(Remote));
swprintf(DnsDomain,L"%wZ",GetName(Remote));
Status = NtQuerySystemTime( &AuthData.LastUpdateTime );
if ( !NT_SUCCESS( Status ) ) {
return( Status );
}
AuthData.AuthType = TRUST_AUTH_TYPE_CLEAR;
AuthData.AuthInfoLength = wcslen( Password ) * sizeof( WCHAR );
AuthData.AuthInfo = (PUCHAR)Password;
RtlZeroMemory( &AuthInfoEx, sizeof( LSA_AUTH_INFORMATION ) );
if ( Direction & TRUST_DIRECTION_INBOUND ) {
AuthInfoEx.IncomingAuthInfos = 1;
AuthInfoEx.IncomingAuthenticationInformation = &AuthData;
AuthInfoEx.IncomingPreviousAuthenticationInformation = NULL;
}
if ( Direction & TRUST_DIRECTION_OUTBOUND ) {
AuthInfoEx.OutgoingAuthInfos = 1;
AuthInfoEx.OutgoingAuthenticationInformation = &AuthData;
AuthInfoEx.OutgoingPreviousAuthenticationInformation = NULL;
}
if (!Mit)
{
RtlCopyMemory( &TDIEx.Name, GetName(Remote), sizeof( UNICODE_STRING ) );
RtlCopyMemory( &TDIEx.FlatName, GetFlatName(Remote), sizeof( UNICODE_STRING ) );
switch(Remote->DomInfoType) {
case DNS: TDIEx.Sid = Remote->u.DnsDomainInfo->Sid;
break;
case Primary: TDIEx.Sid = Remote->u.PrimaryDomainInfo->Sid;
break;
default: Status = GenerateRandomSID( &Sid);
if (!NT_SUCCESS(Status))
{
return(Status);
}
TDIEx.Sid = Sid;
break;
}
//TDIEx.Sid = (Remote->DomInfoType==DNS?Remote->u.DnsDomainInfo->Sid:Remote->u.PrimaryDomainInfo->Sid);
}
else
{
// printf("****Set %ws for the Name and FlatName in the trust object... GetFlatName(Local)=%wZ\n",
// Domain,GetFlatName(Local));
RtlInitUnicodeString(
&TDIEx.Name,
Domain
);
RtlInitUnicodeString(
&TDIEx.FlatName,
Domain
);
Status = GenerateRandomSID( &Sid);
if (!NT_SUCCESS(Status))
{
return(Status);
}
TDIEx.Sid = Sid;
}
TDIEx.TrustDirection = Direction;
TDIEx.TrustType = Mit ? TRUST_TYPE_MIT : (Downlevel ? TRUST_TYPE_DOWNLEVEL : TRUST_TYPE_UPLEVEL);
TDIEx.TrustAttributes = 0;
Status = LsaCreateTrustedDomainEx( Local->Policy,
&TDIEx,
&AuthInfoEx,
TRUSTED_ALL_ACCESS,
&Local->TrustedDomain );
if (!NT_SUCCESS(Status)) {
dbgprintf( (0,IDS_LSACREATETRUSTEDDOMAINEX_F, GetName(Local), DnsDomain, Status) );
if(Status==STATUS_OBJECT_NAME_COLLISION)
dbgprintf( (0,IDS_STATUS_OBJECT_NAME_COLLISION, GetName(Local), DnsDomain) );
}
else LsaClose(Local->TrustedDomain); //not interested in the actual handle...
if (Sid != NULL)
{
RtlFreeSid(Sid);
}
return( Status );
}
NTSTATUS
CreateTrustLink(
IN PTD_DOM_INFO pInfoA,
IN PTD_DOM_INFO pInfoB,
IN PWSTR Password,
IN BOOLEAN Downlevel,
IN BOOLEAN Mit,
IN BOOLEAN ParentChild,
IN ULONG Direction
)
{
NET_API_STATUS netstatus=NERR_Success;
NTSTATUS Status = STATUS_SUCCESS;
PWSTR pDomain=NULL;
if( !Force // if -force was NOT specified...
&&
!Mit // for a non-MIT trust...
&&
(pInfoA->DomInfoType==Minimal || pInfoB->DomInfoType==Minimal)
// creating links not supported in 'Minimal' mode...
)
return( STATUS_UNSUCCESSFUL );
if(pInfoA->majver>=5) {
Status = CreateNT5TrustDomObject(
pInfoA,
pInfoB,
Password,
Downlevel,Mit,Direction
);
return( Status );
}
////////////////////////////////////////////////////////////////////////
//for a NT4 domain...
if(Mit || ParentChild)
return (STATUS_INVALID_PARAMETER);
if(Direction & TRUST_DIRECTION_INBOUND) {
USER_INFO_1 UI1;
DWORD dwParmErr=0xffffffff;
memset(&UI1,0,sizeof(UI1));
pDomain=AddDlrToDomainName(pInfoB);
// Create the necessary SAM account.
UI1.usri1_name = pDomain;
UI1.usri1_password = Password;
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;
netstatus = NetUserAdd(
MakeSrvName(pInfoA->DCName),
1,
(LPBYTE)&UI1,
&dwParmErr
);
if(netstatus != NERR_Success) {
resprintf(0,IDS_NETUSERADD_F,pInfoA->DCName,pDomain,netstatus);
if(netstatus==NERR_UserExists)
resprintf(0,IDS_NERR_UserExists,pInfoA->DCName,pDomain);
goto Done;
}
}
if(Direction & TRUST_DIRECTION_OUTBOUND) {
LSA_TRUST_INFORMATION TI;
PUNICODE_STRING puSecret;
UNICODE_STRING uPass;
LSA_HANDLE hSecret;
swprintf(Domain,L"%wZ",GetFlatName(pInfoB));
RtlInitUnicodeString(&TI.Name,Domain);
{ PSID Sid = NULL;
switch(pInfoB->DomInfoType) {
case DNS: TI.Sid = pInfoB->u.DnsDomainInfo->Sid;
break;
case Primary: TI.Sid = pInfoB->u.PrimaryDomainInfo->Sid;
break;
default: Status = GenerateRandomSID( &Sid);
if (!NT_SUCCESS(Status))
{
return(Status);
}
TI.Sid = Sid;
break;
}
}
//TI.Sid=(pInfoB->DomInfoType==DNS?pInfoB->u.DnsDomainInfo->Sid:pInfoB->u.PrimaryDomainInfo->Sid);
Status = LsaCreateTrustedDomain(
pInfoA->Policy,
&TI,
TRUSTED_ALL_ACCESS,
&pInfoA->TrustedDomain
);
if( !NT_SUCCESS(Status)) {
resprintf(0,IDS_LSACREATETRUSTEDDOMAIN_F,Status);
goto Done;
}
else LsaClose(pInfoA->TrustedDomain); //not interested in the actual handle...
puSecret=MakeSecretName(pInfoB);
Status = LsaCreateSecret(
pInfoA->Policy,
puSecret,
SECRET_ALL_ACCESS,
&hSecret
);
if(!NT_SUCCESS(Status)) {
resprintf(0,IDS_LSACREATESECRET_F,Status);
goto Done;
}
RtlInitUnicodeString(&uPass,Password);
Status=LsaSetSecret(
hSecret,
&uPass,
NULL
);
if(!NT_SUCCESS(Status)) {
resprintf(0,IDS_LSASETSECRET_F,Status);
LsaDelete(hSecret); hSecret=NULL;
goto Done;
}
if(hSecret!=NULL)
LsaClose(hSecret);
}
Done:
// if(pInfoA->TrustedDomain!=NULL)
// LsaClose(pInfoA->TrustedDomain);
return(Status);
}
NTSTATUS
DeleteTrustLink(
IN PTD_DOM_INFO pInfoA,
IN PTD_DOM_INFO pInfoB
)
/*++
Routine Description:
Deletes on A trusted domain things related to a trust to B
Return Value:
STATUS_SUCCESS -- Success
--*/
{
NET_API_STATUS netstatus=NERR_Success;
NTSTATUS Status=STATUS_SUCCESS;
//#define NO_TRUST_OBJECTS 1
//#define NO_SECRETS 2
//#define NO_TRUST_ACCOUNTS 4
// DWORD dwFlag=0;
ULONG i, ix=0;
PSID sid=NULL;
PTRUSTED_DOMAIN_INFORMATION_EX pTDIx = NULL;
PLSA_UNICODE_STRING puDomBName=GetName(pInfoB);
PLSA_UNICODE_STRING puDomBFlatName=GetFlatName(pInfoB);
PLSA_UNICODE_STRING puSecret;
dbgprintf( (0,IDS_DELTRUSTFROMTO,GetName(pInfoA),puDomBName) );
Status = GetTrustLinks(pInfoA);
if(!NT_SUCCESS(Status)) {
resprintf(0,IDS_GETTRUSTLINKS_F,GetName(pInfoA),Status);
return(Status);
}
//try to find a trust link to B...
if(pInfoA->majver>=5) {
// now try to get a LSA_HANDLE to a (possible) trust object with this domain...
// if not found any, that Info->TrustedDomain will remain NULL
Status = LsaQueryTrustedDomainInfoByName(
pInfoA->Policy,
puDomBName, //IN PLSA_UNICODE_STRING TrustedDomainName
TrustedDomainInformationEx, //IN TRUSTED_INFORMATION_CLASS InformationClass,
&pTDIx //OUT PVOID *Buffer
);
if ( !NT_SUCCESS( Status ) ) {
if(Status==STATUS_OBJECT_NAME_NOT_FOUND) {
pInfoA->TrustedDomain=NULL;
dbgprintf( (0,IDS_NO_TRUST_OBJECT_D,GetName(pInfoA),puDomBName) );
Status=STATUS_SUCCESS;
}
else resprintf(0,IDS_LSAQUERYTRUSTEDDOMAININFOBYNAME_F,GetName(pInfoA),puDomBName,Status);
goto cleanup;
}
if(pTDIx->Sid==NULL)
dbgprintf( (0,IDS_LSAQUERYNULLSID) ); //"NULL sid returned by LsaQueryTrustedDomainInfoByName\n"
sid=pTDIx->Sid;
//check to see if the trusted domain object can be opened with that sid...
if(sid!=NULL) {
LSA_HANDLE td;
Status=LsaOpenTrustedDomain(pInfoA->Policy,
sid,TRUSTED_ALL_ACCESS,&td);
if(NT_SUCCESS( Status )) // if it was ok...
LsaClose(td); // ...just close the handle (leave the following code to open it again later)
else { // if failed...
if(Status==STATUS_INVALID_PARAMETER && Force) {
printf("LsaOpenTrustedDomain for the trust to %wZ failed with STATUS_INVALID_PARAMETER (i.e. the sid is bad)\n"
"Trying to set a valid sid...\n",puDomBName);
// if was STATUS_INVALID_PARAMETER (i.e. the sid) and '-overwritesid' option used...
RtlFreeSid(sid); // free the old sid
sid=NULL; // make it NULL as a signal to the next 'if' (see below) to
} // pick it up and set a new random valid sid in its place
}
}
if(sid==NULL) {
// try to put a sid THERE ...
Status = GenerateRandomSID ( &pTDIx->Sid );
if (!NT_SUCCESS( Status ))
goto cleanup;
Status = LsaSetTrustedDomainInfoByName(
pInfoA->Policy,
puDomBName, //IN PLSA_UNICODE_STRING TrustedDomainName
TrustedDomainInformationEx, //IN TRUSTED_INFORMATION_CLASS InformationClass,
pTDIx //IN PVOID Buffer
);
if(!NT_SUCCESS( Status )) {
resprintf(0,IDS_LSASETTRUSTEDDOMAININFOBYNAME_F,GetName(pInfoA),puDomBName,Status);
goto cleanup;
}
sid=pTDIx->Sid;
if(sid==NULL)
resprintf(0,IDS_LSASETNULLSID); //"LsaSetTrustedDomainInfoByName: NULL sid\n"
}
}
else { // pInfoA->majver<=4
//check for Outbound....
int shot;
//for(ix=0; ix<pInfoA->TIcEntries; ix++)
for(shot=0; shot<pInfoA->nTIs; shot++)
for(ix=0; ix<pInfoA->pTIs[shot].count; ix++)
if(RtlEqualUnicodeString(&pInfoA->pTIs[shot].pTI[ix].Name,puDomBFlatName,TRUE))
//it was FALSE, i.e. case sensitive
goto out_of_loop;
out_of_loop:
if(ix<pInfoA->TIcEntries)
sid=pInfoA->pTIs[shot].pTI[ix].Sid;
else {
printf("No OUTBOUND trust to %wZ found...\n",puDomBFlatName);
}
}
if(sid==NULL)
dbgprintf( (0,IDS_NULLSID) ); //"#### NULL sid\n"
if(sid!=NULL) {
Status=LsaOpenTrustedDomain(
pInfoA->Policy,
sid,
TRUSTED_ALL_ACCESS,
&pInfoA->TrustedDomain
);
//printf("Handle=0x%08lx (Status: 0x%08lx)\n",pInfoA->TrustedDomain,Status);
if(!NT_SUCCESS(Status)) {
resprintf(0,IDS_LSAOPENTRUSTEDDOMAIN_F,Status);
//return(Status);
}
dbgprintf( (0,IDS_LSATRUSTHANDLE,pInfoA->TrustedDomain,Status) );
}
else {
if( //pInfoA->majver<=4 &&
ix<pInfoA->TIcEntries) {
resprintf(0,IDS_NONNULL_SID,puDomBName);
Status=STATUS_INVALID_SID;
}
else //simply no trust objects...
Status=STATUS_SUCCESS;
}
if(pInfoA->TrustedDomain) {
dbgprintf( (0,IDS_LSADELOBJ,pInfoA->TrustedDomain) );
Status = LsaDelete( pInfoA->TrustedDomain );
}
if (!NT_SUCCESS(Status))
dbgprintf( (0,IDS_DELETION_F,GetName(pInfoA), Status) );
//NT4 only section...
if(pInfoA->majver<=4) {
LSA_HANDLE hSecret;
//delete secret thing...
puSecret=MakeSecretName(pInfoB);
Status = LsaOpenSecret(
pInfoA->Policy,
puSecret,
SECRET_ALL_ACCESS,
&hSecret
);
if(Status==STATUS_OBJECT_NAME_NOT_FOUND) {
dbgprintf( (0,IDS_SECRET_NOT_FOUND_D,puSecret) );
Status=STATUS_SUCCESS;
}
else {
if(!NT_SUCCESS(Status)) {
resprintf(0,IDS_LSAOPENSECRET_F,Status);
}
else {
Status = LsaDelete(hSecret); hSecret=NULL;
if(!NT_SUCCESS(Status))
resprintf(0,IDS_LSADELETE_F,puSecret,Status);
}
}
//delete Interdomain Trust Account....
netstatus = NetUserDel(
MakeSrvName(pInfoA->DCName),
AddDlrToDomainName(pInfoB)
);
if(netstatus!=NERR_Success && netstatus!=NERR_UserNotFound) {
resprintf(0,IDS_NETUSERDEL_F,AddDlrToDomainName(pInfoB),netstatus);
Status=STATUS_UNSUCCESSFUL;
}
}
cleanup:
if(pTDIx!=NULL)
LsaFreeMemory(pTDIx);
if(sid!=NULL)
RtlFreeSid(sid);
return( Status );
}
NTSTATUS
CheckTrustLink(
IN PTD_DOM_INFO pInfoA,
IN PTD_DOM_INFO pInfoB
)
/*++
Routine Description:
Checks on A trusted domain sids related to a trust to B
Return Value:
STATUS_SUCCESS -- Success
--*/
{
NET_API_STATUS netstatus=NERR_Success;
NTSTATUS Status=STATUS_SUCCESS;
PSID sid=NULL, sidb=NULL;
BOOL UnknownRemoteSid=FALSE;
PTRUSTED_DOMAIN_INFORMATION_EX pTDIx = NULL;
PLSA_UNICODE_STRING puDomBName=GetName(pInfoB);
PLSA_UNICODE_STRING puDomBFlatName=GetFlatName(pInfoB);
dbgprintf( (0,IDS_CHKTRUSTFROMTO,GetName(pInfoA),puDomBName) );
//try to find a trust link to B...
if(pInfoA->majver>=5) {
// now try to get a LSA_HANDLE to a (possible) trust object with this domain...
// if not found any, that Info->TrustedDomain will remain NULL
Status = LsaQueryTrustedDomainInfoByName(
pInfoA->Policy,
puDomBName, //IN PLSA_UNICODE_STRING TrustedDomainName
TrustedDomainInformationEx, //IN TRUSTED_INFORMATION_CLASS InformationClass,
&pTDIx //OUT PVOID *Buffer
);
if ( !NT_SUCCESS( Status ) ) {
if(Status==STATUS_OBJECT_NAME_NOT_FOUND) {
pInfoA->TrustedDomain=NULL;
dbgprintf( (0,IDS_NO_TRUST_OBJECT_D,GetName(pInfoA),puDomBName) );
Status=STATUS_SUCCESS;
}
else resprintf(0,IDS_LSAQUERYTRUSTEDDOMAININFOBYNAME_F,GetName(pInfoA),puDomBName,Status);
goto cleanup;
}
sid=pTDIx->Sid;
}
else { // pInfoA->majver<=4
//check for Outbound....
int shot;
ULONG i;
for(shot=0; shot<pInfoA->nTIs; shot++)
for(i=0; i<pInfoA->pTIs[shot].count; i++)
if(RtlEqualUnicodeString(&pInfoA->pTIs[shot].pTI[i].Name,puDomBFlatName,TRUE))
//it was FALSE, i.e. case sensitive
goto out_of_loop;
out_of_loop:
if(i<pInfoA->TIcEntries)
sid=pInfoA->pTIs[shot].pTI[i].Sid;
}
switch(pInfoB->DomInfoType) {
case DNS: sidb = pInfoB->u.DnsDomainInfo->Sid;
break;
case Primary: sidb = pInfoB->u.PrimaryDomainInfo->Sid;
break;
default: sidb=NULL;
UnknownRemoteSid=TRUE;
break;
}
//now sid contains the Sid of the trust object, if any... (on NT4, only the OUTBOUND
//end of the trust has a trust object...)
//Print them and compare them...
printf("TDO on %wZ: sid:\t",GetName(pInfoA));
PrintSID(sid);
printf("\n");
if(UnknownRemoteSid) {
if(pInfoB->majver<=4)
printf("Domain %wZ does not have a trust object (possibly an NT4 only with an INBOUND trust)\n",puDomBName);
else
printf("Sid for domain %wZ is unknown (possible localonly used or error getting Dns/Primary DomainInfo)",puDomBName);
}
else {
if(sid!=NULL && IsValidSid(sid) && sidb!=NULL && IsValidSid(sidb)) {
if(EqualSid(sid,sidb))
printf("Sid on %wZ is the same:\t",puDomBName);
else printf("Sid on %wZ is different:\t",puDomBName);
}
else {
printf("Sid on %wZ: ",puDomBName);
}
PrintSID(sidb);
}
printf("\n");
cleanup:
if(pTDIx!=NULL)
LsaFreeMemory(pTDIx);
return( Status );
}
void ParseForDCName(WCHAR DomBuf[], WCHAR DCBuf[])
{
WCHAR *pw;
DCBuf[0]=L'\0';
wcstok(DomBuf,L":");
if((pw=wcstok(NULL,L":"))!=NULL)
wcscpy(DCBuf,pw);
if(DomBuf[0]==L'*' || wcscmp(DomBuf,L"(local)")==0)
DomBuf[0]=L'\0';
}
//the NameValidate stuff below taken from icanon.h
NET_API_STATUS
NET_API_FUNCTION
I_NetNameValidate(
IN LPWSTR ServerName OPTIONAL,
IN LPWSTR Name,
IN DWORD NameType,
IN DWORD Flags );
//NameType:
#define NAMETYPE_DOMAIN 6
//Flags:
//#define LM2X_COMPATIBLE 0x80000000L
#ifdef COMMENT
#define CTRL_CHARS_0 L"\001\002\003\004\005\006\007"
#define CTRL_CHARS_1 L"\010\011\012\013\014\015\016\017"
#define CTRL_CHARS_2 L"\020\021\022\023\024\025\026\027"
#define CTRL_CHARS_3 L"\030\031\032\033\034\035\036\037"
#define CTRL_CHARS_STR CTRL_CHARS_0 CTRL_CHARS_1 CTRL_CHARS_2 CTRL_CHARS_3
#define ILLEGAL_DOMAIN_CHARS_STR
#define ILLEGAL_DOMAIN_NAME_CHARS_STR L"\"/\\[]:|<>+;,?" CTRL_CHARS_STR L"*" L" "
#endif //COMMENT
BOOL ValidateDomain(WCHAR DomBuf[])
{ WCHAR Buf[MAX_PATH + 1]= { L'\0' };
WCHAR *p;
int DomBuf_len, oem_name_len;
NET_API_STATUS netstatus=NERR_Success;
if(DomBuf==NULL || DomBuf[0]==L'\0')
return(TRUE);
wcscpy(Buf,DomBuf);
//for DNS name, test each component; for a flat name, there's already only one...
for(p=wcstok(Buf,L"."); p!=NULL; p=wcstok(NULL,L"."))
if((netstatus=I_NetNameValidate(NULL,p,NAMETYPE_DOMAIN,0))!=NERR_Success)
return(FALSE);
return(TRUE);
#ifdef COMMENT
DomBuf_len=wcslen(DomBuf);
// oem_name_len : length in bytes in oem character set
// name_len : ifdef UNICODE
// character length in unicode
// else
// length in bytes in oem character set
//
{
BOOL fUsedDefault;
oem_name_len = WideCharToMultiByte(
CP_OEMCP, // UINT CodePage
0, // DWORD dwFlags
DomBuf, // LPWSTR lpWideChar
DomBuf_len, // int cchWideChar
NULL, // LPSTR lpMultiByteStr
0, // int cchMultiByte
NULL, // use system default char
&fUsedDefault); //
}
if(oem_name_len<1 || oem_name_len>=DNLEN)
return(FALSE);
if(wcscspn(DomBuf,ILLEGAL_DOMAIN_NAME_CHARS_STR) < DomBuf_len)
return(FALSE);
return(TRUE);
#endif //COMMENT
}
#define ARG_CASE_S 0x8000 // case sensitive argument
#define BOOL_ARG(argvec,a_index,var) {if((argvec)[(a_index)].b) (var)=(BOOLEAN)((argvec)[(a_index)].b);}
enum e_arg_type { ARG_S, ARG_U, ARG_B, ARG_I, ARG_L, ARG_UD };
struct _arg_st {
char *name;
union {
char *s;
ULONG u;
BOOL b;
int i;
long l;
void (*fct)(char *);
};
enum e_arg_type t;
};
#define NELEMS(a) (sizeof(a)/sizeof(a[0]))
int process_opt(int argc, char **argv, struct _arg_st arg[])
{ // command line parameters processing
int i,j,k; char *p; struct _arg_st *pa;
int r=1;
// process options
for (i=1; i<argc; i++) {
if (argv[i][0]=='/' || argv[i][0]=='-') {
p=strtok(argv[i]+1,":");
for(j=0; arg[j].name!=NULL; j++) {
if(p!=NULL && ( ((arg[j].t & ARG_CASE_S) && strcmp(p,arg[j].name)==0) ||
_stricmp(p,arg[j].name)==0 ) )
break;
}
if(arg[j].name==NULL) {
resprintf(1,IDS_UNKNOWN_OPTION,p);
r=0;
continue;
}
switch(arg[j].t)
{
case ARG_B:
if( (p=strtok(NULL,""))==NULL
|| _stricmp(p,"on")==0
|| _stricmp(p,"true")==0)
arg[j].b=TRUE;
else arg[j].b=FALSE;
break;
case ARG_S:
if((p=strtok(NULL,""))!=NULL)
arg[j].s=_strdup(p);
else arg[j].s=NULL;
break;
case ARG_U:
if((p=strtok(NULL,""))!=NULL)
arg[j].u=(ULONG)atol(p);
break;
case ARG_L:
if((p=strtok(NULL,""))!=NULL)
arg[j].l=atol(p);
break;
case ARG_I:
if((p=strtok(NULL,""))!=NULL)
arg[j].i=atoi(p);
break;
case ARG_UD:
p=strtok(NULL,"");
(*arg[j].fct)(p);
break;
}
}
}
return(r);
}
// options
enum e_arg_idx { A_LIST, A_BOTH, A_IN, A_OUT,
A_UNTRUST, A_CHECK,
A_VERIFY,
A_LOCALONLY, A_DOWNLEVEL, A_MIT, A_PARENT,
A_DEBUG,
A_PW,
A_FORCE,
A_NT4,
A_SIDLIST,
A_LASTARG };
struct _arg_st opt_arg[]={
{"list", NULL, ARG_B}, // A_LIST
{"both", NULL, ARG_B}, // A_BOTH
{"in", NULL, ARG_B}, // A_IN
{"out", NULL, ARG_B}, // A_OUT
{"untrust", NULL, ARG_B}, // A_UNTRUST
{"sidcheck", NULL, ARG_B}, // A_CHECK
{"verify", NULL, ARG_B}, // A_VERIFY
{"localonly", NULL, ARG_B}, // A_LOCALONLY
{"downlevel", NULL, ARG_B}, // A_DOWNLEVEL
{"mit", NULL, ARG_B}, // A_MIT
{"parent", NULL, ARG_B}, // A_PARENT
{"debug", NULL, ARG_B}, // A_DEBUG
{"pw", NULL, ARG_S}, // A_PW
{"force", NULL, ARG_B}, // A_FORCE
{"nt4", NULL, ARG_B}, // A_NT4
{"sidlist", NULL, ARG_B}, // A_SIDLIST
{NULL, NULL}
};
INT
__cdecl main ( // it was _CRTAPI1
int argc,
char **argv)
{
NTSTATUS Status = STATUS_SUCCESS;
NTSTATUS StatusL = STATUS_SUCCESS;
NTSTATUS StatusR = STATUS_SUCCESS;
WCHAR ADomain[MAX_PATH + 1]= { L'\0' };
WCHAR BDomain[MAX_PATH + 1]= { L'\0' };
WCHAR ADC[MAX_PATH + 1]= { L'\0' };
WCHAR BDC[MAX_PATH + 1]= { L'\0' }; // BDC means just DC for machine B and NOT B(ackup) DC !!!...
WCHAR Xbuf[MAX_PATH + 1]= { L'\0' }; // general purpose buffer
WCHAR Ybuf[MAX_PATH + 1]= { L'\0' }; // general purpose buffer
INT i,j;
BOOLEAN List=FALSE;
BOOLEAN Both = FALSE, DirIn=FALSE, DirOut=FALSE;
BOOLEAN LocalOnly = FALSE, Untrust = FALSE, Downlevel = FALSE, Parent = FALSE;
BOOLEAN Check=FALSE;
BOOLEAN Mit = FALSE;
BOOLEAN LocalCreated = FALSE;
BOOL Verify = FALSE;
// BOOLEAN Force = FALSE; moved global
// BOOLEAN Nt4 = FALSE; this is global
// BOOLEAN Dbg = FALSE; this is global
DWORD DirectionLocal=0, DirectionRemote=0;
WCHAR PasswordBuff[1024];
PWSTR Password = NULL;
TD_DOM_INFO Local={0},
Remote={0};
LSA_UNICODE_STRING uDomNameL,uDomNameR;
// help requested? display it and exit ...
if ( argc<2 ||
_stricmp( argv[1], "-?") == 0 ||
_stricmp( argv[1], "/?") == 0 ) {
Usage();
goto Done;
}
hInst=GetModuleHandle(NULL);
RtlZeroMemory( &Local, sizeof( TD_DOM_INFO ) );
RtlZeroMemory( &Remote, sizeof (TD_DOM_INFO ) );
if(!process_opt(argc,argv, opt_arg)) {
Status = STATUS_INVALID_PARAMETER;
Usage();
goto Done;
}
BOOL_ARG(opt_arg,A_LIST, List );
BOOL_ARG(opt_arg,A_BOTH, Both );
BOOL_ARG(opt_arg,A_IN, DirIn );
BOOL_ARG(opt_arg,A_OUT, DirOut );
BOOL_ARG(opt_arg,A_UNTRUST, Untrust );
BOOL_ARG(opt_arg,A_CHECK, Check );
BOOL_ARG(opt_arg,A_VERIFY, Verify );
BOOL_ARG(opt_arg,A_LOCALONLY,LocalOnly );
BOOL_ARG(opt_arg,A_DOWNLEVEL,Downlevel );
BOOL_ARG(opt_arg,A_MIT, Mit );
BOOL_ARG(opt_arg,A_PARENT, Parent );
BOOL_ARG(opt_arg,A_DEBUG, Dbg );
BOOL_ARG(opt_arg,A_FORCE, Force );
BOOL_ARG(opt_arg,A_NT4, Nt4 );
BOOL_ARG(opt_arg,A_SIDLIST, SidList );
//put this after Dbg variable is set
if(Dbg)
printf("TRUSTDOM - (ver %ws)\n",VER_FILEVERSION_LSTR);
//get password (if any)
if(opt_arg[A_PW].s)
mbstowcs( PasswordBuff, opt_arg[A_PW].s, strlen( opt_arg[A_PW].s )+1 );
else PasswordBuff[0]='\0';
// process normal command line arguments (positional)
for (j=0,i=1; i<argc; i++) {
if (!(argv[i][0]=='/' || argv[i][0]=='-')) {
switch(j) {
case 0:
{ WCHAR *pws;
mbstowcs(ADomain, argv[i], strlen(argv[i]) + 1 );
if((pws=wcschr(ADomain,L','))!=NULL) {
*pws=L'\0';
wcscpy(BDomain,pws+1);
}
else {
wcscpy(BDomain,ADomain);
ADomain[0]=L'\0';
}
}
break;
}
j++;
}
}
ParseForDCName(ADomain,ADC);
ParseForDCName(BDomain,BDC);
dbgprintf( (0,IDS_DOMARGUMENTS,ADomain,ADC[0]?L":":L"",ADC,BDomain,BDC[0]?L":":L"",BDC) );
resprintf(2,IDS_WARNING);
wcscpy(Xbuf,outbuf);
resprintf(2,IDS_ERROR);
wcscpy(Ybuf,outbuf);
//Parameter adjust
if(SidList)
List=TRUE;
//Domain names check:
{ WCHAR *s=NULL;
BOOL ba, bb;
if( !(ba=ValidateDomain(s=ADomain)) ||
!(bb=ValidateDomain(s=BDomain))) {
resprintf(0,IDS_INVALID_DOMAIN_NAME,s);
Status = STATUS_INVALID_PARAMETER;
goto Done;
}
}
// Parameter constraints:
// '-parent' REQUIRES '-both'
if (Parent && !Both) {
if(!Force)
Status = STATUS_INVALID_PARAMETER;
resprintf(0,IDS_PARENT_REQ_BOTH,(Force?Xbuf:Ybuf));
}
// MIT trusts are always local only
if (Mit && (!LocalOnly || !Both)) {
resprintf(0,IDS_MIT_LOCAL_ONLY_BOTH);
LocalOnly = TRUE;
Both = TRUE;
}
//
// Validate the parameters
//
//specifying both in and out means, yes, '-both'...
if(DirIn && DirOut)
Both=TRUE;
if(List && Mit)
Status = STATUS_INVALID_PARAMETER;
if((!List && BDomain[0]==L'\0') || (List && ADomain[0]!=L'\0'))
Status = STATUS_INVALID_PARAMETER;
if ( Untrust == TRUE && (Downlevel)) // || Mit || Both ) ) // changed from Both || LocalOnly ||...
Status = STATUS_INVALID_PARAMETER;
// if(LocalOnly == TRUE && Both == FALSE)
// Status = STATUS_INVALID_PARAMETER;;
if (Mit && (Downlevel || Parent ))
Status = STATUS_INVALID_PARAMETER;
// end validating parameters
if( Status == STATUS_INVALID_PARAMETER ) {
Usage();
goto Done;
}
if(!Untrust && !List && !Verify) { //check password... otherwise ignore
if(wcscmp(PasswordBuff,L"*")==0)
GetPassword(PasswordBuff,1024);
Password = PasswordBuff;
}
////////////////////////////////////////////////////////////////////////////////
// list || verify operation: simplified GetDomainInfo scenario...
////////////////////////////////////////////////////////////////////////////////
if ( List ) {
ULONG i;
Status = GetDomainInfoForDomain((BDomain[0]==L'\0'?NULL:BDomain),BDC,&Remote, Mit );
if (!NT_SUCCESS( Status ) )
goto Done;
Status = GetTrustLinks( &Remote );
if (Status!=STATUS_NO_MORE_ENTRIES && !NT_SUCCESS( Status ) )
goto Done;
Status = PrintTrustLinks( &Remote );
goto Done;
} else
////////////////////////////////////////////////////////////////////////////////
// verify operation
////////////////////////////////////////////////////////////////////////////////
if ( Verify ) {
Status = VerifyTrusts( BDomain[ 0 ]==L'\0' ? NULL : BDomain, BDC );
goto Done;
}
// regular operation: create/delete trust...
// get info about the domain(s) involved...
Status = GetDomainInfoForDomain((ADomain[0]==L'\0'?NULL:ADomain), ADC, &Local, FALSE );
if ( !NT_SUCCESS( Status ) )
goto Done;
Status = GetDomainInfoForDomain( BDomain, BDC, &Remote, Mit );
if ( !NT_SUCCESS( Status ) ) {
if(Mit) { //assuming a Unix machine...
dbgprintf( (0,IDS_DSGETDCNAME_MIT, BDomain) );
}
else {
if(!( Force
//&& (Status==STATUS_NO_SUCH_DOMAIN)
//&& LocalOnly && Untrust
)) // if -force not specified...
// continue anyway
goto Done;
}
}
//
// Ok, now check or or delete or create the trust objects...
//
////////////////////////////////////////////////////////////////////////////////
// check trust link
////////////////////////////////////////////////////////////////////////////////
if ( Check ) {
dbgprintf( (0, IDS_PROCESSDOM, GetName(&Local)) );
Status = CheckTrustLink( &Local, &Remote );
if (Status!=NERR_Success)
dbgprintf( (0,IDS_LOCAL_CHK_TRUST_F,Status) );
if ( !LocalOnly ) {
dbgprintf( (0, IDS_PROCESSDOM, GetName(&Remote)) );
Status = CheckTrustLink( &Remote, &Local );
if (Status!=NERR_Success)
dbgprintf( (IDS_REMOTE_CHK_TRUST_F,Status) );
}
// end check block...
} else
////////////////////////////////////////////////////////////////////////////////
// delete trust object
////////////////////////////////////////////////////////////////////////////////
if ( Untrust ) {
dbgprintf( (0, IDS_PROCESSDOM, GetName(&Local)) );
Status = DeleteTrustLink( &Local, &Remote );
if (Status!=NERR_Success)
dbgprintf( (0,IDS_LOCAL_DEL_TRUST_F,Status) );
if ( !LocalOnly ) {
dbgprintf( (0, IDS_PROCESSDOM, GetName(&Remote)) );
Status = DeleteTrustLink( &Remote, &Local );
if (Status!=NERR_Success)
dbgprintf( (IDS_REMOTE_DEL_TRUST_F,Status) );
}
// end untrust block...
} else {
////////////////////////////////////////////////////////////////////////////
// create trust links
////////////////////////////////////////////////////////////////////////////
if ( Password == NULL ) {
Password = L""; // no password specified? then use void password: ""
}
if((Local.majver==4 || Remote.majver==4) && !Downlevel) {
if(!Force)
Status = STATUS_INVALID_PARAMETER;
resprintf(0,IDS_NT4_REQ_DOWNLEVEL,(Force?Xbuf:Ybuf));
if(!NT_SUCCESS(Status))
goto Done;
}
//compute direction of trust based on the values of Both, DirIn, DirOut
//'Both' has higher priority
if(Both) {
DirectionLocal=DirectionRemote=TRUST_DIRECTION_BIDIRECTIONAL;
} else {
//default is 'OUTBOUND'... as being DirIn==FALSE and DirOut==TRUE
DirectionLocal =(DirIn?TRUST_DIRECTION_INBOUND:TRUST_DIRECTION_OUTBOUND);
DirectionRemote =(DirIn?TRUST_DIRECTION_OUTBOUND:TRUST_DIRECTION_INBOUND);
}
swprintf(Xbuf,L"%wZ",GetName(&Local));
swprintf(Ybuf,L"%wZ",GetName(&Remote));
//RtlCopyUnicodeString(&uDomNameL,GetName(&Local));
//RtlCopyUnicodeString(&uDomNameR,GetName(&Remote));
dbgprintf( (0, IDS_PROCESSDOM, GetName(&Local)) );
StatusL = CreateTrustLink( &Local, &Remote,
Password,
Downlevel,
Mit,
Parent,
DirectionLocal //Both ? TRUST_DIRECTION_BIDIRECTIONAL :
// TRUST_DIRECTION_OUTBOUND
);
if (!NT_SUCCESS(StatusL))
dbgprintf( (0,IDS_CREATE_TRUST_F, Xbuf,Ybuf,StatusL) );
//if ( NT_SUCCESS( StatusL ) ) { not needed...
// LocalCreated = TRUE;
//}
if ( NT_SUCCESS( StatusL ) && !LocalOnly ) {
dbgprintf( (0, IDS_PROCESSDOM, GetName(&Remote)) );
StatusR = CreateTrustLink( &Remote, &Local,
Password,
Downlevel,
Mit,
FALSE,
DirectionRemote //Both ? TRUST_DIRECTION_BIDIRECTIONAL :
// TRUST_DIRECTION_INBOUND
);
if (!NT_SUCCESS(StatusR))
dbgprintf( (0,IDS_CREATE_TRUST_F, Ybuf,Xbuf, StatusR) );
}
if ( !NT_SUCCESS( StatusR ) && NT_SUCCESS( StatusL ) ) { //LocalCreated not used anymore....
DeleteTrustLink( &Local, &Remote );
}
}
Status = StatusL;
if( NT_SUCCESS(Status) ) //maybe the 'Remote' attempt failed ?...
Status = StatusR;
Done:
FreeDomainInfo( &Local );
FreeDomainInfo( &Remote );
if( NT_SUCCESS( Status ) ) {
//No message; in this way will be easier also to get a count of the trust links for a list:
// by example, 'trustdom <dom> -list | findstr ",B," | wc' will get a count of the
// bidirectional trusts of domain <dom>
//printf("The command completed successfully\n");
} else {
resprintf(0,IDS_COMMAND_FAILED, Status );
}
// return 0 for SUCCESS and 1 for some error
return( !NT_SUCCESS( Status ) );
}