/*-- 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 #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #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(""); return(FALSE); } if (!IsValidSid(s)) { printf(":"); 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 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; inTIs; 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; iTDIXcEntries; 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; iTIcEntries; q++,i++) { for(q=p,shot=0; shotnTIs; shot++) { for(i=0; ipTIs[shot].count; i++,q++) { WCHAR buf[1024]; swprintf(buf,L"%wZ",&Info->pTIs[shot].pTI[i].Name); for(j=0; jUI1cEntries; j++) if(wcscmp(buf,CutDlrFromName(Info->pUI1[j].usri1_name))==0) break; if((q->index=j)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; iTIcEntries; q++,i++) for(q=p,shot=0; shotnTIs; shot++) for(i=0; ipTIs[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; iUI1cEntries; i++) { if(jTIcEntries && 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; ixTIcEntries; ix++) for(shot=0; shotnTIs; shot++) for(ix=0; ixpTIs[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(ixTIcEntries) 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 && ixTIcEntries) { 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; shotnTIs; shot++) for(i=0; ipTIs[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(iTIcEntries) 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 -list | findstr ",B," | wc' will get a count of the // bidirectional trusts of domain //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 ) ); }