/*-- Copyright (c) 1987-1993 Microsoft Corporation Module Name: nltest.c Abstract: Test program for the Netlogon service. Author: 13-Apr-1992 (cliffv) Environment: User mode only. Contains NT-specific code. Requires ANSI C extensions: slash-slash comments, long external names. Revision History: Madana - added various options. --*/ // // Common include files. // #define NLTEST_IMAGE #include // Include files common to entire service // // Include files specific to this .c file // #include #include #include #include #include #include #include #include #include #include "..\..\..\..\newsam\server\samsrvp.h" #include #include #include VOID ListDeltas( LPWSTR DeltaFileName, BOOLEAN ListRedoFile ); DWORD NlGlobalTrace =0xFFFFFFFF; typedef struct _MAILSLOT_INFO { CHAR Name[DNLEN+NETLOGON_NT_MAILSLOT_LEN+3]; HANDLE ResponseHandle; BOOL State; NETLOGON_SAM_LOGON_RESPONSE SamLogonResponse; OVERLAPPED OverLapped; BOOL ReadPending; } MAIL_INFO, PMAIL_INFO; MAIL_INFO GlobalMailInfo[64]; DWORD GlobalIterationCount = 0; LPWSTR GlobalAccountName; HANDLE GlobalPostEvent; CRITICAL_SECTION GlobalPrintCritSect; VOID DumpBuffer( PVOID Buffer, DWORD BufferSize ) /*++ Routine Description: Dumps the buffer content on to the debugger output. Arguments: Buffer: buffer pointer. BufferSize: size of the buffer. Return Value: none --*/ { DWORD j; PULONG LongBuffer; ULONG LongLength; LongBuffer = Buffer; LongLength = min( BufferSize, 24 )/4; for(j = 0; j < LongLength; j++) { printf("%08lx ", LongBuffer[j]); } if ( BufferSize != LongLength*4 ) { printf( "..." ); } printf("\n"); } VOID NlpDumpSid( IN DWORD DebugFlag, IN PSID Sid OPTIONAL ) /*++ Routine Description: Dumps a SID to the debugger output Arguments: DebugFlag - Debug flag to pass on to NlPrintRoutine Sid - SID to output Return Value: none --*/ { // // Output the SID // if ( Sid == NULL ) { NlPrint((0, "(null)\n")); } else { UNICODE_STRING SidString; NTSTATUS Status; Status = RtlConvertSidToUnicodeString( &SidString, Sid, TRUE ); if ( !NT_SUCCESS(Status) ) { NlPrint((0, "Invalid 0x%lX\n", Status )); } else { NlPrint((0, "%wZ\n", &SidString )); RtlFreeUnicodeString( &SidString ); } } UNREFERENCED_PARAMETER( DebugFlag ); } VOID NlpDumpHexData( IN DWORD DebugFlag, LPDWORD Buffer, DWORD BufferSize ) /*++ Routine Description: Dumps the buffer content on to the debugger output. Arguments: Buffer: buffer pointer. BufferSize: size of the buffer. Return Value: none --*/ { DumpBuffer( Buffer, BufferSize ); UNREFERENCED_PARAMETER( DebugFlag ); } VOID PrintTime( LPSTR Comment, LARGE_INTEGER ConvertTime ) /*++ Routine Description: Print the specified time Arguments: Comment - Comment to print in front of the time Time - GMT time to print (Nothing is printed if this is zero) Return Value: None --*/ { // // If we've been asked to convert an NT GMT time to ascii, // Do so // if ( ConvertTime.QuadPart != 0 ) { LARGE_INTEGER LocalTime; TIME_FIELDS TimeFields; NTSTATUS Status; printf( "%s", Comment ); Status = RtlSystemTimeToLocalTime( &ConvertTime, &LocalTime ); if ( !NT_SUCCESS( Status )) { printf( "Can't convert time from GMT to Local time\n" ); LocalTime = ConvertTime; } RtlTimeToTimeFields( &LocalTime, &TimeFields ); printf( "%8.8lx %8.8lx = %ld/%ld/%ld %ld:%2.2ld:%2.2ld\n", ConvertTime.LowPart, ConvertTime.HighPart, TimeFields.Month, TimeFields.Day, TimeFields.Year, TimeFields.Hour, TimeFields.Minute, TimeFields.Second ); } } LPSTR FindSymbolicNameForStatus( DWORD Id ) { ULONG i; i = 0; if (Id == 0) { return "STATUS_SUCCESS"; } if (Id & 0xC0000000) { while (ntstatusSymbolicNames[ i ].SymbolicName) { if (ntstatusSymbolicNames[ i ].MessageId == (NTSTATUS)Id) { return ntstatusSymbolicNames[ i ].SymbolicName; } else { i += 1; } } } while (winerrorSymbolicNames[ i ].SymbolicName) { if (winerrorSymbolicNames[ i ].MessageId == Id) { return winerrorSymbolicNames[ i ].SymbolicName; } else { i += 1; } } #ifdef notdef while (neteventSymbolicNames[ i ].SymbolicName) { if (neteventSymbolicNames[ i ].MessageId == Id) { return neteventSymbolicNames[ i ].SymbolicName } else { i += 1; } } #endif // notdef return NULL; } VOID PrintStatus( NET_API_STATUS NetStatus ) /*++ Routine Description: Print a net status code. Arguments: NetStatus - The net status code to print. Return Value: None --*/ { printf( "Status = %lu 0x%lx", NetStatus, NetStatus ); switch (NetStatus) { case NERR_Success: printf( " NERR_Success" ); break; case NERR_DCNotFound: printf( " NERR_DCNotFound" ); break; case NERR_UserNotFound: printf( " NERR_UserNotFound" ); break; case NERR_NetNotStarted: printf( " NERR_NetNotStarted" ); break; case NERR_WkstaNotStarted: printf( " NERR_WkstaNotStarted" ); break; case NERR_ServerNotStarted: printf( " NERR_ServerNotStarted" ); break; case NERR_BrowserNotStarted: printf( " NERR_BrowserNotStarted" ); break; case NERR_ServiceNotInstalled: printf( " NERR_ServiceNotInstalled" ); break; case NERR_BadTransactConfig: printf( " NERR_BadTransactConfig" ); break; default: printf( " %s", FindSymbolicNameForStatus( NetStatus ) ); break; } printf( "\n" ); } VOID NlAssertFailed( IN PVOID FailedAssertion, IN PVOID FileName, IN ULONG LineNumber, IN PCHAR Message OPTIONAL ) { printf( "\n*** Assertion failed: %s%s\n*** Source File: %s, line %ld\n\n", Message ? Message : "", FailedAssertion, FileName, LineNumber ); } VOID WhoWillLogMeOnResponse( ) /*++ Routine Description: This routine reads the responses that are received for the query messages sent from the main thread. Arguments: none Return Value: None --*/ { DWORD i; DWORD WaitCount; DWORD IndexArray[64]; HANDLE HandleArray[64]; LPWSTR LocalDomainName; LPWSTR LocalServerName; LPWSTR LocalUserName; PCHAR Where; DWORD Version; DWORD VersionFlags; SYSTEMTIME SystemTime; NETLOGON_SAM_LOGON_RESPONSE SamLogonResponse; DWORD SamLogonResponseSize; DWORD WaitStatus; NET_API_STATUS NetStatus; BOOL AllReceived; for(;;) { // // make wait array. // WaitCount = 0; AllReceived = TRUE; for (i = 0; i < GlobalIterationCount; i++ ) { // if( GlobalMailInfo[i].State == TRUE ) { // // if a response is received. // continue; } AllReceived = FALSE; // // post a read. // if( GlobalMailInfo[i].ReadPending == FALSE ) { if ( !ReadFile( GlobalMailInfo[i].ResponseHandle, (PCHAR)&GlobalMailInfo[i].SamLogonResponse, sizeof(NETLOGON_SAM_LOGON_RESPONSE), &SamLogonResponseSize, &GlobalMailInfo[i].OverLapped )) { // Overlapped I/O NetStatus = GetLastError(); if( NetStatus != ERROR_IO_PENDING ) { printf( "Cannot read mailslot (%s) : %ld\n", GlobalMailInfo[i].Name, NetStatus); goto Cleanup; } } GlobalMailInfo[i].ReadPending = TRUE; } HandleArray[WaitCount] = GlobalMailInfo[i].ResponseHandle; IndexArray[WaitCount] = i; WaitCount++; } if( (WaitCount == 0) ) { if( AllReceived ) { // // we received responses for all messages, so we are // done. // goto Cleanup; } else { // wait for an query posted // WaitStatus = WaitForSingleObject( GlobalPostEvent, (DWORD) -1 ); if( WaitStatus != 0 ) { printf("Can't successfully wait post event %ld\n", WaitStatus ); goto Cleanup; } continue; } } // // wait for response. // WaitStatus = WaitForMultipleObjects( WaitCount, HandleArray, FALSE, // Wait for ANY handle 15000 ); // 3 * 5 Secs if( WaitStatus == WAIT_TIMEOUT ) { // we are done. break; } if( (WaitStatus < 0) || (WaitStatus >= WaitCount ) ) { printf("Invalid WaitStatus returned %ld\n", WaitStatus ); goto Cleanup; } // // get index // i = IndexArray[WaitStatus]; // // read response // if( !GetOverlappedResult( GlobalMailInfo[i].ResponseHandle, &GlobalMailInfo[i].OverLapped, &SamLogonResponseSize, TRUE) ) { // wait for the read complete. printf("can't read overlapped response %ld",GetLastError() ); goto Cleanup; } SamLogonResponse = GlobalMailInfo[i].SamLogonResponse; // // indicate that we received a response. // GlobalMailInfo[i].State = TRUE; GlobalMailInfo[i].ReadPending = FALSE; GetLocalTime( &SystemTime ); EnterCriticalSection( &GlobalPrintCritSect ); printf( "[%02u:%02u:%02u] ", SystemTime.wHour, SystemTime.wMinute, SystemTime.wSecond ); printf( "Response %ld: ", i); // // Ensure the version is expected. // Version = NetpLogonGetMessageVersion( &SamLogonResponse, &SamLogonResponseSize, &VersionFlags ); if ( Version != LMNT_MESSAGE ) { printf("Response version not valid.\n"); goto Continue; } // // Pick up the name of the server that responded. // Where = (PCHAR) &SamLogonResponse.UnicodeLogonServer; if ( !NetpLogonGetUnicodeString( (PCHAR)&SamLogonResponse, SamLogonResponseSize, &Where, sizeof(SamLogonResponse.UnicodeLogonServer), &LocalServerName ) ) { printf("Response server name not formatted right\n"); goto Continue; } // // Pick up the name of the account the response is for. // if ( !NetpLogonGetUnicodeString( (PCHAR)&SamLogonResponse, SamLogonResponseSize, &Where, sizeof(SamLogonResponse.UnicodeUserName ), &LocalUserName ) ) { printf("Response User name not formatted right\n"); goto Continue; } // // Pick up the name of the domain the response is for. // if ( !NetpLogonGetUnicodeString( (PCHAR)&SamLogonResponse, SamLogonResponseSize, &Where, sizeof(SamLogonResponse.UnicodeUserName ), &LocalDomainName ) ) { printf("Response Domain name not formatted right\n"); goto Continue; } // // If the response is for the correct account, // break out of the loop. // if ( NlNameCompare( GlobalAccountName, LocalUserName, NAMETYPE_USER)!=0){ printf("Response not for correct User name " FORMAT_LPWSTR " s.b. " FORMAT_LPWSTR "\n", LocalUserName, GlobalAccountName ); goto Continue; } printf( "S:" FORMAT_LPWSTR " D:" FORMAT_LPWSTR " A:" FORMAT_LPWSTR, LocalServerName, LocalDomainName, LocalUserName ); // // If the DC recognizes our account, // we've successfully found the DC. // switch (SamLogonResponse.Opcode) { case LOGON_SAM_LOGON_RESPONSE: printf( " (Act found)\n" ); break; case LOGON_SAM_USER_UNKNOWN: printf( " (Act not found)\n" ); break; case LOGON_PAUSE_RESPONSE: printf( " (netlogon paused)\n" ); break; default: printf( " (Unknown opcode: %lx)\n", SamLogonResponse.Opcode ); break; } Continue: LeaveCriticalSection( &GlobalPrintCritSect ); } Cleanup: // // print non-responsed mailslots. // for( i = 0; i < GlobalIterationCount; i++ ) { if( GlobalMailInfo[i].State == FALSE ) { printf("Didn't receive a response for mail " "message %ld (%s)\n", i, GlobalMailInfo[i].Name ); } } return; } VOID WhoWillLogMeOn( IN LPWSTR DomainName, IN LPWSTR AccountName, IN DWORD IterationCount ) /*++ Routine Description: Determine which DC will log the specified account on Arguments: DomainName - name of the "doamin" to send the message to AccountName - Name of our user account to find. IterationCount - Number of consecutive messages to send. Return Value: None --*/ { NET_API_STATUS NetStatus; ULONG AllowableAccountControlBits = USER_ACCOUNT_TYPE_MASK; WCHAR NetlogonMailslotName[DNLEN+NETLOGON_NT_MAILSLOT_LEN+4]; NETLOGON_SAM_LOGON_REQUEST SamLogonRequest; PCHAR Where; PCHAR WhereResponseMailslot; HANDLE *ResponseMailslotHandle = NULL; WCHAR ComputerName[MAX_COMPUTERNAME_LENGTH+1]; DWORD ComputerNameLength = MAX_COMPUTERNAME_LENGTH+1; DWORD i; DWORD j; SYSTEMTIME SystemTime; HANDLE ResponseThreadHandle; DWORD ThreadId; DWORD WaitStatus; DWORD SamLogonResponseSize; // // support only 64 iterations // if( IterationCount > 64 ) { printf("Interations set to 64, maximum supported\n"); IterationCount = 64; } GlobalIterationCount = IterationCount; GlobalAccountName = AccountName; InitializeCriticalSection( &GlobalPrintCritSect ); // // Get out computer name // if (!GetComputerName( ComputerName, &ComputerNameLength ) ) { printf( "Can't GetComputerName\n" ); return; } // // create mailslots // for (i = 0; i < IterationCount; i++ ) { // // Create a mailslot for the DC's to respond to. // if (NetStatus = NetpLogonCreateRandomMailslot( GlobalMailInfo[i].Name, &GlobalMailInfo[i].ResponseHandle)){ printf( "Cannot create temp mailslot %ld\n", NetStatus ); goto Cleanup; } if ( !SetMailslotInfo( GlobalMailInfo[i].ResponseHandle, (DWORD) MAILSLOT_WAIT_FOREVER ) ) { printf( "Cannot set mailslot info %ld\n", GetLastError() ); goto Cleanup; } (void) memset( &GlobalMailInfo[i].OverLapped, '\0', sizeof(OVERLAPPED) ); GlobalMailInfo[i].State = FALSE; GlobalMailInfo[i].ReadPending = FALSE; } // // create post event // GlobalPostEvent = CreateEvent( NULL, FALSE, FALSE, NULL ); if( GlobalPostEvent == NULL ) { printf("can't create post event %ld \n", GetLastError() ); goto Cleanup; } InitializeCriticalSection( &GlobalPrintCritSect ); // // start response thread. // ResponseThreadHandle = CreateThread( NULL, // No security attributes THREAD_STACKSIZE, (LPTHREAD_START_ROUTINE) WhoWillLogMeOnResponse, NULL, 0, // No special creation flags &ThreadId ); if ( ResponseThreadHandle == NULL ) { printf("can't create response thread %ld\n", GetLastError() ); goto Cleanup; } // // Build the query message. // SamLogonRequest.Opcode = LOGON_SAM_LOGON_REQUEST; SamLogonRequest.RequestCount = 0; Where = (PCHAR) &SamLogonRequest.UnicodeComputerName; NetpLogonPutUnicodeString( ComputerName, sizeof(SamLogonRequest.UnicodeComputerName), &Where ); NetpLogonPutUnicodeString( AccountName, sizeof(SamLogonRequest.UnicodeUserName), &Where ); WhereResponseMailslot = Where; wcscpy( NetlogonMailslotName, L"\\\\" ); wcscat( NetlogonMailslotName, DomainName ); // wcscat( NetlogonMailslotName, L"*" ); // Don't add for computer name wcscat( NetlogonMailslotName, NETLOGON_NT_MAILSLOT_W); // // Send atmost 3 messages/mailslot // for( j = 0; j < 3; j++ ) { // // Repeat the message multiple times to load the servers // for (i = 0; i < IterationCount; i++ ) { if( GlobalMailInfo[i].State == TRUE ) { // // if a response is received. // continue; } Where = WhereResponseMailslot; NetpLogonPutOemString( GlobalMailInfo[i].Name, sizeof(SamLogonRequest.MailslotName), &Where ); NetpLogonPutBytes( &AllowableAccountControlBits, sizeof(SamLogonRequest.AllowableAccountControlBits), &Where ); NetpLogonPutNtToken( &Where ); // // Send the message to a DC for the domain. // NetStatus = NetpLogonWriteMailslot( NetlogonMailslotName, (PCHAR)&SamLogonRequest, Where - (PCHAR)(&SamLogonRequest) ); if ( NetStatus != NERR_Success ) { printf( "Cannot write netlogon mailslot: %ld\n", NetStatus); goto Cleanup; } GetLocalTime( &SystemTime ); EnterCriticalSection( &GlobalPrintCritSect ); printf( "[%02u:%02u:%02u] ", SystemTime.wHour, SystemTime.wMinute, SystemTime.wSecond ); printf( "Mail message %ld sent successfully (%s) \n", i, GlobalMailInfo[i].Name ); LeaveCriticalSection( &GlobalPrintCritSect ); if( !SetEvent( GlobalPostEvent ) ) { printf("Can't set post event %ld \n", GetLastError() ); goto Cleanup; } } // // wait 5 secs to see response thread received all responses. // WaitStatus = WaitForSingleObject( ResponseThreadHandle, 5000 ); // 15 secs. TIMEOUT if( WaitStatus != WAIT_TIMEOUT ) { if( WaitStatus != 0 ) { printf("can't do WaitForSingleObject %ld\n", WaitStatus); } goto Cleanup; } } Cleanup: // // Wait for the response thread to complete. // if( ResponseThreadHandle != NULL ) { WaitStatus = WaitForSingleObject( ResponseThreadHandle, 15000 ); // 15 secs. TIMEOUT if( WaitStatus ) { if( WaitStatus == WAIT_TIMEOUT ) { printf("Can't stop response thread (TIMEOUT) \n"); } else { printf("Can't stop response thread %ld \n", WaitStatus); } } } for (i = 0; i < IterationCount; i++ ) { if( GlobalMailInfo[i].ResponseHandle != NULL ) { CloseHandle( GlobalMailInfo[i].ResponseHandle); } } if( GlobalPostEvent != NULL ) { CloseHandle( GlobalPostEvent ); } DeleteCriticalSection( &GlobalPrintCritSect ); return; } #define MAX_PRINTF_LEN 1024 // Arbitrary. VOID NlPrintRoutine( IN DWORD DebugFlag, IN LPSTR Format, ... ) { va_list arglist; char OutputBuffer[MAX_PRINTF_LEN]; // // Put a the information requested by the caller onto the line // va_start(arglist, Format); (VOID) vsprintf(OutputBuffer, Format, arglist); va_end(arglist); printf( "%s", OutputBuffer ); return; UNREFERENCED_PARAMETER( DebugFlag ); } NTSTATUS SimulateFullSync( LPWSTR PdcName, LPWSTR MachineName ) /*++ Routine Description: This function simulate a full sync replication by calling NetDatabaseSync API and simply ignoring successfully returned data. Arguments: PdcName - Name of the PDC from where the database replicated. MachineName - Name of the machine account used to authenticate. Return Value: Network Status code. --*/ { NTSTATUS Status; NETLOGON_CREDENTIAL ServerChallenge; NETLOGON_CREDENTIAL ClientChallenge; NETLOGON_CREDENTIAL ComputedServerCredential; NETLOGON_CREDENTIAL ReturnedServerCredential; NETLOGON_CREDENTIAL AuthenticationSeed; NETLOGON_SESSION_KEY SessionKey; NETLOGON_AUTHENTICATOR OurAuthenticator; NETLOGON_AUTHENTICATOR ReturnAuthenticator; UNICODE_STRING Password; NT_OWF_PASSWORD NtOwfPassword; ULONG SamSyncContext = 0; PNETLOGON_DELTA_ENUM_ARRAY DeltaArray = NULL; DWORD DatabaseIndex; DWORD i; WCHAR AccountName[SSI_ACCOUNT_NAME_LENGTH+1]; // // Prepare our challenge // NlComputeChallenge( &ClientChallenge ); printf("ClientChallenge = %lx %lx\n", ((DWORD*)&ClientChallenge)[0], ((DWORD *)&ClientChallenge)[1]); // // Get the primary's challenge // Status = I_NetServerReqChallenge(PdcName, MachineName, &ClientChallenge, &ServerChallenge ); if ( !NT_SUCCESS( Status ) ) { fprintf( stderr, "I_NetServerReqChallenge to " FORMAT_LPWSTR " returned 0x%lx\n", PdcName, Status ); return(Status); } printf("ServerChallenge = %lx %lx\n", ((DWORD *)&ServerChallenge)[0], ((DWORD *)&ServerChallenge)[1]); Password.Length = Password.MaximumLength = wcslen(MachineName) * sizeof(WCHAR); Password.Buffer = MachineName; // // Compute the NT OWF password for this user. // Status = RtlCalculateNtOwfPassword( &Password, &NtOwfPassword ); if ( !NT_SUCCESS( Status ) ) { fprintf(stderr, "Can't compute OWF password 0x%lx \n", Status ); return(Status); } printf("Password = %lx %lx %lx %lx\n", ((DWORD *) (&NtOwfPassword))[0], ((DWORD *) (&NtOwfPassword))[1], ((DWORD *) (&NtOwfPassword))[2], ((DWORD *) (&NtOwfPassword))[3]); // // Actually compute the session key given the two challenges and the // password. // NlMakeSessionKey( &NtOwfPassword, &ClientChallenge, &ServerChallenge, &SessionKey ); printf("SessionKey = %lx %lx %lx %lx\n", ((DWORD *) (&SessionKey))[0], ((DWORD *) (&SessionKey))[1], ((DWORD *) (&SessionKey))[2], ((DWORD *) (&SessionKey))[3]); // // Prepare credentials using our challenge. // NlComputeCredentials( &ClientChallenge, &AuthenticationSeed, &SessionKey ); printf("ClientCredential = %lx %lx\n", ((DWORD *) (&AuthenticationSeed))[0], ((DWORD *) (&AuthenticationSeed))[1]); // // Send these credentials to primary. The primary will compute // credentials using the challenge supplied by us and compare // with these. If both match then it will compute credentials // using its challenge and return it to us for verification // wcscpy( AccountName, MachineName ); wcscat( AccountName, SSI_ACCOUNT_NAME_POSTFIX); Status = I_NetServerAuthenticate( PdcName, AccountName, ServerSecureChannel, MachineName, &AuthenticationSeed, &ReturnedServerCredential ); if ( !NT_SUCCESS( Status ) ) { fprintf(stderr, "I_NetServerAuthenticate to " FORMAT_LPWSTR " 0x%lx\n", &PdcName, Status ); return(Status); } printf("ServerCredential GOT = %lx %lx\n", ((DWORD *) (&ReturnedServerCredential))[0], ((DWORD *) (&ReturnedServerCredential))[1]); // // The DC returned a server credential to us, // ensure the server credential matches the one we would compute. // NlComputeCredentials( &ServerChallenge, &ComputedServerCredential, &SessionKey); printf("ServerCredential MADE =%lx %lx\n", ((DWORD *) (&ComputedServerCredential))[0], ((DWORD *) (&ComputedServerCredential))[1]); if (RtlCompareMemory( &ReturnedServerCredential, &ComputedServerCredential, sizeof(ReturnedServerCredential)) != sizeof(ReturnedServerCredential)) { fprintf( stderr, "Access Denied \n"); return( STATUS_ACCESS_DENIED ); } printf("Session Setup to " FORMAT_LPWSTR " completed successfully \n", PdcName); // // retrive database info // for( DatabaseIndex = 0 ; DatabaseIndex < 3; DatabaseIndex++) { SamSyncContext = 0; for( i = 0; ; i++) { NlBuildAuthenticator( &AuthenticationSeed, &SessionKey, &OurAuthenticator); Status = I_NetDatabaseSync( PdcName, MachineName, &OurAuthenticator, &ReturnAuthenticator, DatabaseIndex, &SamSyncContext, &DeltaArray, 128 * 1024 ); // 128K if ( !NT_SUCCESS( Status ) ) { fprintf( stderr, "I_NetDatabaseSync to " FORMAT_LPWSTR " failed 0x%lx\n", PdcName, Status ); return(Status); } if ( ( !NlUpdateSeed( &AuthenticationSeed, &ReturnAuthenticator.Credential, &SessionKey) ) ) { fprintf(stderr, "NlUpdateSeed failed \n" ); return( STATUS_ACCESS_DENIED ); } printf( "Received %ld Buffer data \n", i); // // ignore return data // MIDL_user_free( DeltaArray ); if ( Status == STATUS_SUCCESS ) { break; } } printf("FullSync replication of database %ld completed " "successfully \n", DatabaseIndex ); } } LONG ForceRegOpenSubkey( HKEY ParentHandle, LPSTR KeyName, LPSTR Subkey, REGSAM DesiredAccess, PHKEY ReturnHandle ) /*++ Routine Description: Open the specified key one subkey at a time defeating access denied by setting the DACL to allow us access. This kludge is needed since the security tree is shipped not allowing Administrators access. Arguments: ParentHandle - Currently open handle KeyName - Entire key name (for error messages only) Subkey - Direct subkey of ParentHandle DesiredAccess - Desired access to the new key ReturnHandle - Returns an open handle to the newly opened key. Return Value: Return TRUE for success. --*/ { LONG RegStatus; LONG SavedStatus; HKEY TempHandle = NULL; BOOLEAN DaclChanged = FALSE; SECURITY_INFORMATION SecurityInformation = DACL_SECURITY_INFORMATION; DWORD OldSecurityDescriptorSize; CHAR OldSecurityDescriptor[1024]; CHAR NewSecurityDescriptor[1024]; SID_IDENTIFIER_AUTHORITY NtAuthority = SECURITY_NT_AUTHORITY; PSID AdminSid = NULL; BOOL DaclPresent; BOOL DaclDefaulted; PACL Dacl; ACL_SIZE_INFORMATION AclSizeInfo; ACCESS_ALLOWED_ACE *Ace; DWORD i; // // Open the sub-key // SavedStatus = RegOpenKeyExA( ParentHandle, Subkey, 0, //Reserved DesiredAccess, ReturnHandle ); if ( SavedStatus != ERROR_ACCESS_DENIED ) { return SavedStatus; } // // If access is denied, // try changing the DACL to give us access // // printf( "Cannot RegOpenKey %s subkey %s ", KeyName, Subkey ); // PrintStatus( SavedStatus ); // // Open again asking to change the DACL // RegStatus = RegOpenKeyExA( ParentHandle, Subkey, 0, //Reserved WRITE_DAC | READ_CONTROL, &TempHandle ); if ( RegStatus != ERROR_SUCCESS) { printf( "Cannot RegOpenKey to change DACL %s subkey %s ", KeyName, Subkey ); PrintStatus( RegStatus ); goto Cleanup; } // // Get the current DACL so we can restore it. // OldSecurityDescriptorSize = sizeof(OldSecurityDescriptor); RegStatus = RegGetKeySecurity( TempHandle, SecurityInformation, (PSECURITY_DESCRIPTOR) OldSecurityDescriptor, &OldSecurityDescriptorSize ); if ( RegStatus != ERROR_SUCCESS ) { printf( "Cannot RegGetKeySecurity for %s subkey %s ", KeyName, Subkey ); PrintStatus( RegStatus ); goto Cleanup; } // // Build the Administrators SID // if ( !AllocateAndInitializeSid( &NtAuthority, 2, // two subauthorities SECURITY_BUILTIN_DOMAIN_RID, DOMAIN_ALIAS_RID_ADMINS, 0, 0, 0, 0, 0, 0, &AdminSid ) ) { printf( "Cannot AllocateAndInitializeSid " ); PrintStatus( GetLastError() ); goto Cleanup; } // // Change the DACL to allow all access // RtlCopyMemory( NewSecurityDescriptor, OldSecurityDescriptor, OldSecurityDescriptorSize ); if ( !GetSecurityDescriptorDacl( (PSECURITY_DESCRIPTOR)NewSecurityDescriptor, &DaclPresent, &Dacl, &DaclDefaulted )) { printf( "Cannot GetSecurityDescriptorDacl for %s subkey %s ", KeyName, Subkey ); PrintStatus( GetLastError() ); goto Cleanup; } if ( !DaclPresent ) { printf( "Cannot GetSecurityDescriptorDacl " ); printf( "Cannot DaclNotPresent for %s subkey %s ", KeyName, Subkey ); goto Cleanup; } if ( !GetAclInformation( Dacl, &AclSizeInfo, sizeof(AclSizeInfo), AclSizeInformation )) { printf( "Cannot GetAclInformation for %s subkey %s ", KeyName, Subkey ); PrintStatus( GetLastError() ); goto Cleanup; } // // Search for an administrators ACE and give it "DesiredAccess" // for ( i=0; iHeader.AceType != ACCESS_ALLOWED_ACE_TYPE ) { continue; } if ( !EqualSid( AdminSid, (PSID)&Ace->SidStart ) ) { continue; } Ace->Mask |= DesiredAccess; break; } if ( i >= AclSizeInfo.AceCount ) { printf( "No Administrators Ace for %s subkey %s\n", KeyName, Subkey ); goto Cleanup; } // // Actually set the new DACL on the key // RegStatus = RegSetKeySecurity( TempHandle, SecurityInformation, (PSECURITY_DESCRIPTOR)NewSecurityDescriptor ); if ( RegStatus != ERROR_SUCCESS ) { printf( "Cannot RegSetKeySecurity for %s subkey %s ", KeyName, Subkey ); PrintStatus( RegStatus ); goto Cleanup; } DaclChanged = TRUE; // // Open the sub-key again with the desired access // SavedStatus = RegOpenKeyExA( ParentHandle, Subkey, 0, //Reserved DesiredAccess, ReturnHandle ); if ( SavedStatus != ERROR_SUCCESS ) { printf( "Cannot RegOpenKeyEx following DACL change for %s subkey %s ", KeyName, Subkey ); PrintStatus( SavedStatus ); goto Cleanup; } Cleanup: if ( TempHandle != NULL ) { // // Restore DACL to original value. // if ( DaclChanged ) { RegStatus = RegSetKeySecurity( TempHandle, SecurityInformation, (PSECURITY_DESCRIPTOR)OldSecurityDescriptor ); if ( RegStatus != ERROR_SUCCESS ) { printf( "Cannot RegSetKeySecurity to restore %s subkey %s ", KeyName, Subkey ); PrintStatus( RegStatus ); goto Cleanup; } } (VOID) RegCloseKey( TempHandle ); } if ( AdminSid != NULL ) { (VOID) FreeSid( AdminSid ); } return SavedStatus; } LONG ForceRegOpenKey( HKEY BaseHandle, LPSTR KeyName, REGSAM DesiredAccess, PHKEY ReturnHandle ) /*++ Routine Description: Open the specified key one subkey at a time defeating access denied by setting the DACL to allow us access. This kludge is needed since the security tree is shipped not allowing Administrators access. Arguments: BaseHandle - Currently open handle KeyName - Registry key to open relative to BaseHandle. DesiredAccess - Desired access to the new key ReturnHandle - Returns an open handle to the newly opened key. Return Value: Return TRUE for success. --*/ { LONG RegStatus; PCHAR StartOfSubkey; PCHAR EndOfSubkey; CHAR Subkey[512]; HKEY ParentHandle; ASSERT( KeyName[0] != '\0' ); // // Loop opening the next subkey. // EndOfSubkey = KeyName; ParentHandle = BaseHandle; for (;;) { // // Compute the name of the next subkey. // StartOfSubkey = EndOfSubkey; for ( ;; ) { if ( *EndOfSubkey == '\0' || *EndOfSubkey == '\\' ) { strncpy( Subkey, StartOfSubkey, EndOfSubkey-StartOfSubkey ); Subkey[EndOfSubkey-StartOfSubkey] = '\0'; if ( *EndOfSubkey == '\\' ) { EndOfSubkey ++; } break; } EndOfSubkey ++; } // // Open the sub-key // RegStatus = ForceRegOpenSubkey( ParentHandle, KeyName, Subkey, DesiredAccess, ReturnHandle ); // // Close the parent handle and return any error condition. // if ( ParentHandle != BaseHandle ) { (VOID) RegCloseKey( ParentHandle ); } if( RegStatus != ERROR_SUCCESS ) { *ReturnHandle = NULL; return RegStatus; } // // If this is the entire key name, // we're done. // if ( *EndOfSubkey == '\0' ) { return ERROR_SUCCESS; } ParentHandle = *ReturnHandle; } } struct { LPSTR Name; enum { UnicodeStringType, HexDataType, LmPasswordType, NtPasswordType } Type; } UserVariableDataTypes[] = { { "SecurityDescriptor" , HexDataType }, { "AccountName" , UnicodeStringType }, { "FullName" , UnicodeStringType }, { "AdminComment" , UnicodeStringType }, { "UserComment" , UnicodeStringType }, { "Parameters" , UnicodeStringType }, { "HomeDirectory" , UnicodeStringType }, { "HomeDirectoryDrive" , UnicodeStringType }, { "ScriptPath" , UnicodeStringType }, { "ProfilePath" , UnicodeStringType }, { "Workstations" , UnicodeStringType }, { "LogonHours" , HexDataType }, { "Groups" , HexDataType }, { "LmOwfPassword" , LmPasswordType }, { "NtOwfPassword" , NtPasswordType }, { "NtPasswordHistory" , HexDataType }, { "LmPasswordHistory" , HexDataType } }; VOID PrintUserInfo( IN LPWSTR ServerName, IN LPSTR UserName ) /*++ Routine Description: Print a user's description from the SAM database Arguments: ServerName - Name of server to query UserName - Name of user to query Return Value: None --*/ { NTSTATUS Status; LONG RegStatus; ULONG i; HKEY BaseHandle = NULL; HKEY UserHandle = NULL; HKEY RidHandle = NULL; CHAR UserKey[200]; CHAR RidKey[200]; LONG Rid; CHAR AnsiRid[20]; CHAR FixedData[1000]; ULONG FixedDataSize; SAMP_V1_0A_FIXED_LENGTH_USER FixedUser1_0A; PSAMP_V1_0A_FIXED_LENGTH_USER f; PSAMP_V1_0_FIXED_LENGTH_USER f1_0; BOOLEAN IsVersion1_0; CHAR VariableData[32768]; ULONG VariableDataSize; PSAMP_VARIABLE_LENGTH_ATTRIBUTE v; LM_OWF_PASSWORD LmOwfPassword; NT_OWF_PASSWORD NtOwfPassword; // // Open the registry // RegStatus = RegConnectRegistryW( ServerName, HKEY_LOCAL_MACHINE, &BaseHandle); if ( RegStatus != ERROR_SUCCESS ) { printf( "Cannot connect to registy on " FORMAT_LPWSTR " ", ServerName ); PrintStatus( RegStatus ); goto Cleanup; } // // Open the key for this user name. // strcpy( UserKey, "SAM\\SAM\\Domains\\Account\\Users\\Names\\" ); strcat( UserKey, UserName ); RegStatus = ForceRegOpenKey( BaseHandle, UserKey, KEY_READ|KEY_QUERY_VALUE, &UserHandle ); if ( RegStatus != ERROR_SUCCESS ) { printf( "Cannot open %s ", UserKey ); PrintStatus( RegStatus ); goto Cleanup; } // // Get the RID of the user // RegStatus = RegQueryValueExW( UserHandle, NULL, // No name NULL, // Reserved &Rid, // Really the type NULL, // Data not needed NULL); // Data not needed if ( RegStatus != ERROR_SUCCESS ) { printf( "Cannot Query %s ", UserKey ); PrintStatus( RegStatus ); goto Cleanup; } printf( "User: %s\nRid: 0x%lx\n", UserName, Rid ); // // Open the key for this user rid. // sprintf( AnsiRid, "%8.8lx", Rid ); strcpy( RidKey, "SAM\\SAM\\Domains\\Account\\Users\\" ); strcat( RidKey, AnsiRid ); RegStatus = ForceRegOpenKey( BaseHandle, RidKey, KEY_READ|KEY_QUERY_VALUE, &RidHandle ); if ( RegStatus != ERROR_SUCCESS ) { printf( "Cannot open %s ", RidKey ); PrintStatus( RegStatus ); goto Cleanup; } // // Get the Fixed Values associated with this RID // FixedDataSize = sizeof(FixedData); RegStatus = RegQueryValueExA( RidHandle, "F", // Fixed value NULL, // Reserved NULL, // Type Not Needed FixedData, &FixedDataSize ); if ( RegStatus != ERROR_SUCCESS ) { printf( "Cannot Query %s ", RidKey ); PrintStatus( RegStatus ); goto Cleanup; } // // If the fixed length data is NT 1.0, // convert it to NT 1.0A format. // if ( IsVersion1_0 = (FixedDataSize == sizeof(*f1_0)) ) { f1_0 = (PSAMP_V1_0_FIXED_LENGTH_USER) &FixedData; FixedUser1_0A.LastLogon = f1_0->LastLogon; FixedUser1_0A.LastLogoff = f1_0->LastLogoff; FixedUser1_0A.PasswordLastSet = f1_0->PasswordLastSet; FixedUser1_0A.AccountExpires = f1_0->AccountExpires; FixedUser1_0A.UserId = f1_0->UserId; FixedUser1_0A.PrimaryGroupId = f1_0->PrimaryGroupId; FixedUser1_0A.UserAccountControl = f1_0->UserAccountControl; FixedUser1_0A.CountryCode = f1_0->CountryCode; FixedUser1_0A.BadPasswordCount = f1_0->BadPasswordCount; FixedUser1_0A.LogonCount = f1_0->LogonCount; FixedUser1_0A.AdminCount = f1_0->AdminCount; RtlCopyMemory( FixedData, &FixedUser1_0A, sizeof(FixedUser1_0A) ); } // // Print the fixed length data. // f = (PSAMP_V1_0A_FIXED_LENGTH_USER) &FixedData; if ( !IsVersion1_0) { printf( "Version: 0x%lx\n", f->Revision ); } PrintTime( "LastLogon: ", f->LastLogon ); PrintTime( "LastLogoff: ", f->LastLogoff ); PrintTime( "PasswordLastSet: ", f->PasswordLastSet ); PrintTime( "AccountExpires: ", f->AccountExpires ); if ( !IsVersion1_0) { PrintTime( "LastBadPasswordTime: ", f->LastBadPasswordTime ); } printf( "PrimaryGroupId: 0x%lx\n", f->PrimaryGroupId ); printf( "UserAccountControl: 0x%lx\n", f->UserAccountControl ); printf( "CountryCode: 0x%lx\n", f->CountryCode ); printf( "CodePage: 0x%lx\n", f->CodePage ); printf( "BadPasswordCount: 0x%lx\n", f->BadPasswordCount ); printf( "LogonCount: 0x%lx\n", f->LogonCount ); printf( "AdminCount: 0x%lx\n", f->AdminCount ); // // Get the Variable Values associated with this RID // VariableDataSize = sizeof(VariableData); RegStatus = RegQueryValueExA( RidHandle, "V", // Variable value NULL, // Reserved NULL, // Type Not Needed VariableData, &VariableDataSize ); if ( RegStatus != ERROR_SUCCESS ) { printf( "Cannot Query %s \n", RidKey ); PrintStatus( RegStatus ); goto Cleanup; } // // Loop printing all the attributes. // v = (PSAMP_VARIABLE_LENGTH_ATTRIBUTE) VariableData; for ( i=0; i ((PCHAR)v)+VariableDataSize ) { printf( "Variable data desc %ld not in variable value.\n", i ); goto Cleanup; } if ( v[i].Offset > (LONG) VariableDataSize || v[i].Offset + v[i].Length > VariableDataSize ) { printf( "Variable data item %ld not in variable value.\n", i ); printf( "Offset: %ld Length: %ld Size: %ld\n", v[i].Offset, v[i].Length, VariableDataSize ); goto Cleanup; } // // Don't print null data. // if ( v[i].Length == 0 ) { continue; } // // Print the various types of data. // switch ( UserVariableDataTypes[i].Type ) { case UnicodeStringType: UnicodeString.Buffer = (PUSHORT)(((PCHAR)v)+v[i].Offset); UnicodeString.Length = (USHORT)v[i].Length; printf( "%s: %wZ\n", UserVariableDataTypes[i].Name, &UnicodeString); break; case LmPasswordType: Status = RtlDecryptLmOwfPwdWithIndex( (PENCRYPTED_LM_OWF_PASSWORD)(((PCHAR)v)+v[i].Offset), &Rid, &LmOwfPassword ); if ( !NT_SUCCESS( Status ) ) { printf( "Cannot decrypt LM password: " ); PrintStatus( Status ); goto Cleanup; } printf( "%s: ", UserVariableDataTypes[i].Name); DumpBuffer( &LmOwfPassword, sizeof(LmOwfPassword )); break; case NtPasswordType: Status = RtlDecryptNtOwfPwdWithIndex( (PENCRYPTED_NT_OWF_PASSWORD)(((PCHAR)v)+v[i].Offset), &Rid, &NtOwfPassword ); if ( !NT_SUCCESS( Status ) ) { printf( "Cannot decrypt NT password: " ); PrintStatus( Status ); goto Cleanup; } printf( "%s: ", UserVariableDataTypes[i].Name); DumpBuffer( &NtOwfPassword, sizeof(NtOwfPassword )); break; case HexDataType: printf( "%s: ", UserVariableDataTypes[i].Name); DumpBuffer( (((PCHAR)v)+v[i].Offset), v[i].Length ); break; } } // // Be tidy. // Cleanup: if ( UserHandle != NULL ) { RegCloseKey( UserHandle ); } if ( RidHandle != NULL ) { RegCloseKey( RidHandle ); } if ( BaseHandle != NULL ) { RegCloseKey( BaseHandle ); } return; } VOID SetDbflagInRegistry( LPWSTR ServerName, ULONG DbFlagValue ) /*++ Routine Description: Set the value DbFlagValue in the Netlogon service portion of the registry. Arguments: ServerName - Name of the server to update DbFlagValue - Value to set dbflag to. Return Value: None. --*/ { LONG RegStatus; UCHAR AnsiDbFlag[20]; DWORD AnsiDbFlagLength; HKEY BaseHandle = NULL; HKEY ParmHandle = NULL; LPSTR KeyName; #define NL_PARAM_KEY "SYSTEM\\CurrentControlSet\\Services\\Netlogon\\Parameters" // // Open the registry // RegStatus = RegConnectRegistryW( ServerName, HKEY_LOCAL_MACHINE, &BaseHandle); if ( RegStatus != ERROR_SUCCESS ) { printf( "Cannot connect to registy on " FORMAT_LPWSTR " ", ServerName ); PrintStatus( RegStatus ); goto Cleanup; } // // Open the key for Netlogon\parameters. // KeyName = NL_PARAM_KEY; RegStatus = ForceRegOpenKey( BaseHandle, KeyName, KEY_SET_VALUE, &ParmHandle ); if ( RegStatus != ERROR_SUCCESS ) { printf( "Cannot open " NL_PARAM_KEY ); PrintStatus( RegStatus ); goto Cleanup; } // // Set the DbFlag value into the registry. // AnsiDbFlagLength = sprintf( AnsiDbFlag, "0x%8.8lx", DbFlagValue ); RegStatus = RegSetValueExA( ParmHandle, "DbFlag", 0, // Reserved REG_SZ, AnsiDbFlag, AnsiDbFlagLength + 1 ); if ( RegStatus != ERROR_SUCCESS ) { printf( "Cannot Set %s:", KeyName ); PrintStatus( RegStatus ); goto Cleanup; } printf( "%s set to %s\n", KeyName, AnsiDbFlag ); // // Be tidy. // Cleanup: if ( ParmHandle != NULL ) { RegCloseKey( ParmHandle ); } if ( BaseHandle != NULL ) { RegCloseKey( BaseHandle ); } return; } NET_API_STATUS UaspGetDomainId( IN LPWSTR ServerName OPTIONAL, OUT PSAM_HANDLE SamServerHandle OPTIONAL, OUT PPOLICY_ACCOUNT_DOMAIN_INFO * AccountDomainInfo ) /*++ Routine Description: Return a domain ID of the account domain of a server. Arguments: ServerName - A pointer to a string containing the name of the Domain Controller (DC) to query. A NULL pointer or string specifies the local machine. SamServerHandle - Returns the SAM connection handle if the caller wants it. DomainId - Receives a pointer to the domain ID. Caller must deallocate buffer using NetpMemoryFree. Return Value: Error code for the operation. --*/ { NET_API_STATUS NetStatus; NTSTATUS Status; SAM_HANDLE LocalSamHandle = NULL; ACCESS_MASK LSADesiredAccess; LSA_HANDLE LSAPolicyHandle = NULL; OBJECT_ATTRIBUTES LSAObjectAttributes; UNICODE_STRING ServerNameString; // // Connect to the SAM server // RtlInitUnicodeString( &ServerNameString, ServerName ); Status = SamConnect( &ServerNameString, &LocalSamHandle, SAM_SERVER_LOOKUP_DOMAIN, NULL); if ( !NT_SUCCESS(Status)) { printf( "UaspGetDomainId: Cannot connect to Sam %lX\n",Status ); LocalSamHandle = NULL; NetStatus = NetpNtStatusToApiStatus( Status ); goto Cleanup; } // // Open LSA to read account domain info. // // // set desired access mask. // LSADesiredAccess = POLICY_VIEW_LOCAL_INFORMATION; InitializeObjectAttributes( &LSAObjectAttributes, NULL, // Name 0, // Attributes NULL, // Root NULL ); // Security Descriptor Status = LsaOpenPolicy( &ServerNameString, &LSAObjectAttributes, LSADesiredAccess, &LSAPolicyHandle ); if( !NT_SUCCESS(Status) ) { printf( "UaspGetDomainId: Cannot open LSA Policy %lX\n", Status ); NetStatus = NetpNtStatusToApiStatus( Status ); goto Cleanup; } // // now read account domain info from LSA. // Status = LsaQueryInformationPolicy( LSAPolicyHandle, PolicyAccountDomainInformation, (PVOID *) AccountDomainInfo ); if( !NT_SUCCESS(Status) ) { printf( "UaspGetDomainId: " "Cannot read LSA %lX\n", Status ); NetStatus = NetpNtStatusToApiStatus( Status ); goto Cleanup; } // // Return the SAM connection handle to the caller if he wants it. // Otherwise, disconnect from SAM. // if ( ARGUMENT_PRESENT( SamServerHandle ) ) { *SamServerHandle = LocalSamHandle; LocalSamHandle = NULL; } NetStatus = NERR_Success; // // Cleanup locally used resources // Cleanup: if ( LocalSamHandle != NULL ) { (VOID) SamCloseHandle( LocalSamHandle ); } if( LSAPolicyHandle != NULL ) { LsaClose( LSAPolicyHandle ); } return NetStatus; } // UaspGetDomainId NET_API_STATUS UaspOpenDomain( IN LPWSTR ServerName OPTIONAL, IN ULONG DesiredAccess, OUT PSAM_HANDLE DomainHandle, OUT PSID *DomainId OPTIONAL ) /*++ Routine Description: Return a SAM Connection handle and a domain handle given the server name and the access desired to the domain. Only a single thread in a process can open a domain at a time. Subsequent threads block in this routine. This exclusive access allows a single SAM connection handle to be cached. The routine UaspCloseDomain closes the domain and allows other threads to proceed. Arguments: ServerName - A pointer to a string containing the name of the remote server containing the SAM database. A NULL pointer or string specifies the local machine. DesiredAccess - Supplies the access mask indicating which access types are desired to the domain. This routine always requests DOMAIN_LOOKUP access in addition to those specified. DomainHandle - Receives the Domain handle to be used on future calls to the SAM server. DomainId - Recieves a pointer to the Sid of the domain. This domain ID must be freed using NetpMemoryFree. Return Value: Error code for the operation. NULL means initialization was successful. --*/ { NET_API_STATUS NetStatus; NTSTATUS Status; PSID LocalDomainId; HANDLE SamServerHandle; PPOLICY_ACCOUNT_DOMAIN_INFO AccountDomainInfo; // // Give everyone DOMAIN_LOOKUP access. // DesiredAccess |= DOMAIN_LOOKUP; if ( ServerName == NULL ) { ServerName = L""; } if ( *ServerName != L'\0' && (ServerName[0] != L'\\' || ServerName[1] != L'\\') ) { return NERR_InvalidComputer; } // // Connect to the SAM server and // Determine the Domain Id of the account domain for this server. // NetStatus = UaspGetDomainId( ServerName, &SamServerHandle, &AccountDomainInfo ); if ( NetStatus != NERR_Success ) { printf( "UaspUpdateCache: Cannot UaspGetDomainId %ld\n", NetStatus ); return ( NetStatus ); } // // Choose the domain ID for the right SAM domain. // LocalDomainId = AccountDomainInfo->DomainSid; // // At this point the domain ID of the account domain of the server is // cached. Open the domain. // Status = SamOpenDomain( SamServerHandle, DesiredAccess, LocalDomainId, DomainHandle ); if ( !NT_SUCCESS( Status ) ) { printf( "UaspOpenDomain: Cannot SamOpenDomain %lX\n", Status ); *DomainHandle = NULL; return NetpNtStatusToApiStatus( Status ); } // // Return the DomainId to the caller in an allocated buffer // if (ARGUMENT_PRESENT( DomainId ) ) { ULONG SidSize; SidSize = RtlLengthSid( LocalDomainId ); *DomainId = NetpMemoryAllocate( SidSize ); if ( *DomainId == NULL ) { (VOID) SamCloseHandle( *DomainHandle ); *DomainHandle = NULL; return ERROR_NOT_ENOUGH_MEMORY; } if ( !NT_SUCCESS( RtlCopySid( SidSize, *DomainId, LocalDomainId) ) ) { (VOID) SamCloseHandle( *DomainHandle ); *DomainHandle = NULL; NetpMemoryFree( *DomainId ); *DomainId = NULL; return NERR_InternalError; } } return NERR_Success; } // UaspOpenDomain VOID SetLockout( IN LPWSTR ServerName, IN ULONG LockoutThreshold, IN ULONG LockoutDuration, IN ULONG LockoutWindow ) /*++ Routine Description: Set the lockout parameter on a domain. Arguments: Return Value: Exit status --*/ { NTSTATUS Status; NET_API_STATUS NetStatus; DOMAIN_LOCKOUT_INFORMATION LockoutInfo; HANDLE DomainHandle; NetStatus = UaspOpenDomain( ServerName, DOMAIN_WRITE_PASSWORD_PARAMS, &DomainHandle, NULL ); if ( NetStatus != NERR_Success ) { printf( "UaspOpenDomain failed %ld\n", NetStatus ); return; } // // Fill in the info level structure // LockoutInfo.LockoutThreshold = (USHORT) LockoutThreshold; // Convert times from seconds to 100ns LockoutInfo.LockoutDuration = RtlEnlargedIntegerMultiply( LockoutDuration, -10000000 ); LockoutInfo.LockoutObservationWindow = RtlEnlargedIntegerMultiply( LockoutWindow, -10000000 ); // // Set the information in SAM // Status = SamSetInformationDomain( DomainHandle, DomainLockoutInformation, &LockoutInfo ); if ( !NT_SUCCESS(Status) ) { printf( "Can't SamSetInformationDomain 0x%lx\n", Status ); } } int _CRTAPI1 main( IN int argc, IN char ** argv ) /*++ Routine Description: Drive the Netlogon service by calling I_NetLogonControl2. Arguments: argc - the number of command-line arguments. argv - an array of pointers to the arguments. Return Value: Exit status --*/ { NTSTATUS Status; NET_API_STATUS NetStatus; LPSTR argument; int i; DWORD FunctionCode = 0; LPSTR AnsiServerName = NULL; CHAR AnsiUncServerName[UNCLEN+1]; LPSTR AnsiDomainName = NULL; LPSTR AnsiTrustedDomainName = NULL; LPWSTR TrustedDomainName = NULL; LPSTR AnsiUserName = NULL; LPSTR AnsiPassword = NULL; LPSTR AnsiSimMachineName = NULL; LPSTR AnsiDeltaFileName = NULL; LPSTR ShutdownReason = NULL; LPWSTR ServerName = NULL; LPWSTR UserName = NULL; PNETLOGON_INFO_1 NetlogonInfo1 = NULL; ULONG Rid = 0; DWORD Level = 1; DWORD ShutdownSeconds; LPBYTE InputDataPtr = NULL; DWORD DbFlagValue; LARGE_INTEGER ConvertTime; ULONG IterationCount; NT_OWF_PASSWORD NtOwfPassword; BOOLEAN NtPasswordPresent = FALSE; LM_OWF_PASSWORD LmOwfPassword; BOOLEAN LmPasswordPresent = FALSE; BOOLEAN GetPdcName = FALSE; BOOLEAN GetTrustedDcName = FALSE; BOOLEAN GetDcList = FALSE; BOOLEAN WhoWill = FALSE; BOOLEAN QuerySync = FALSE; BOOLEAN SimFullSync = FALSE; BOOLEAN QueryUser = FALSE; BOOLEAN ListDeltasFlag = FALSE; BOOLEAN ListRedoFlag = FALSE; BOOLEAN ResetSecureChannelsFlag = FALSE; BOOLEAN ShutdownAbort = FALSE; BOOLEAN TrustedDomainsFlag = FALSE; BOOLEAN DoLockout = FALSE; ULONG LockoutThreshold; ULONG LockoutDuration; ULONG LockoutWindow; #define QUERY_PARAM "/QUERY" #define REPL_PARAM "/REPL" #define SYNC_PARAM "/SYNC" #define PDC_REPL_PARAM "/PDC_REPL" #define SERVER_PARAM "/SERVER:" #define PWD_PARAM "/PWD:" #define RID_PARAM "/RID:" #define USER_PARAM "/USER:" #define BP_PARAM "/BP" #define DBFLAG_PARAM "/DBFLAG:" #define DCLIST_PARAM "/DCLIST:" #define DCNAME_PARAM "/DCNAME:" #define DCTRUST_PARAM "/DCTRUST:" #define TRUNCATE_LOG_PARAM "/TRUNC" #define BACKUP_CHANGE_LOG_PARAM "/BKP_CHK" #define TIME_PARAM "/TIME:" #define WHOWILL_PARAM "/WHOWILL:" #define BDC_QUERY_PARAM "/BDC_QUERY:" #define LOGON_QUERY_PARAM "/LOGON_QUERY" #define SIM_SYNC_PARAM "/SIM_SYNC:" #define LIST_DELTAS_PARAM "/LIST_DELTAS:" #define LIST_REDO_PARAM "/LIST_REDO:" #define SC_RESET_PARAM "/SC_RESET:" #define SC_QUERY_PARAM "/SC_QUERY:" #define SHUTDOWN_PARAM "/SHUTDOWN:" #define SHUTDOWN_ABORT_PARAM "/SHUTDOWN_ABORT" #define LOCKOUT_PARAM "/LOCKOUT:" #define TRANSPORT_PARAM "/TRANSPORT_NOTIFY" #define FINDUSER_PARAM "/FINDUSER:" #define TRUSTED_DOMAINS_PARAM "/TRUSTED_DOMAINS" // // Set the netlib debug flag. // extern DWORD NetlibpTrace; NetlibpTrace |= 0x8000; // NETLIB_DEBUG_LOGON ConvertTime.QuadPart = 0; // // Loop through the arguments handle each in turn // for ( i=1; i= argc ) { goto Usage; } argument = argv[i]; AnsiSimMachineName = argument; SimFullSync = TRUE; // // handle delta listing // } else if (_strnicmp(argument, LIST_DELTAS_PARAM, sizeof(LIST_DELTAS_PARAM)-1 ) == 0 ){ if ( AnsiDeltaFileName != NULL ) { goto Usage; } AnsiDeltaFileName = &argument[sizeof(LIST_DELTAS_PARAM)-1]; ListDeltasFlag = TRUE; // // handle redo listing // } else if (_strnicmp(argument, LIST_REDO_PARAM, sizeof(LIST_REDO_PARAM)-1 ) == 0 ){ if ( AnsiDeltaFileName != NULL ) { goto Usage; } AnsiDeltaFileName = &argument[sizeof(LIST_REDO_PARAM)-1]; ListRedoFlag = TRUE; // // Handle /DCLIST // } else if (_strnicmp(argument, DCLIST_PARAM, sizeof(DCLIST_PARAM)-1 ) == 0 ){ if ( AnsiDomainName != NULL ) { goto Usage; } AnsiDomainName = &argument[sizeof(DCLIST_PARAM)-1]; GetDcList = TRUE; // // Handle /DCNAME // } else if (_strnicmp(argument, DCNAME_PARAM, sizeof(DCNAME_PARAM)-1 ) == 0 ){ if ( AnsiDomainName != NULL ) { goto Usage; } AnsiDomainName = &argument[sizeof(DCNAME_PARAM)-1]; GetPdcName = TRUE; // // Handle /DCTRUST // } else if (_strnicmp(argument, DCTRUST_PARAM, sizeof(DCTRUST_PARAM)-1 ) == 0 ){ if ( AnsiDomainName != NULL ) { goto Usage; } AnsiDomainName = &argument[sizeof(DCTRUST_PARAM)-1]; GetTrustedDcName = TRUE; // // Handle /SERVER:servername // } else if (_strnicmp(argument, SERVER_PARAM, sizeof(SERVER_PARAM)-1 ) == 0 ){ if ( AnsiServerName != NULL ) { goto Usage; } AnsiServerName = &argument[sizeof(SERVER_PARAM)-1]; // // Handle /PWD:password // } else if (_strnicmp(argument, PWD_PARAM, sizeof(PWD_PARAM)-1 ) == 0 ){ if ( AnsiPassword != NULL ) { goto Usage; } AnsiPassword = &argument[sizeof(PWD_PARAM)-1]; // // Handle /USER:username // } else if (_strnicmp(argument, USER_PARAM, sizeof(USER_PARAM)-1 ) == 0 ){ if ( AnsiUserName != NULL ) { goto Usage; } AnsiUserName = &argument[sizeof(USER_PARAM)-1]; QueryUser = TRUE; // // Handle /RID:relative_id // } else if (_strnicmp(argument, RID_PARAM, sizeof(RID_PARAM)-1 ) == 0 ){ char *end; if ( Rid != 0 ) { goto Usage; } Rid = strtol( &argument[sizeof(RID_PARAM)-1], &end, 16 ); // // Handle /SHUTDOWN:Reason seconds // } else if (_strnicmp(argument, SHUTDOWN_PARAM, sizeof(SHUTDOWN_PARAM)-1 ) == 0 ){ if ( ShutdownReason != NULL ) { goto Usage; } ShutdownReason = &argument[sizeof(SHUTDOWN_PARAM)-1]; if ( i+1 < argc ) { char *end; i++; argument = argv[i]; if ( !ISDIGIT(argument[0]) ) { fprintf(stderr, "Second argument to " SHUTDOWN_PARAM " must be a number.\n\n"); goto Usage; } ShutdownSeconds = strtoul( argument, &end, 10 ); } else { ShutdownSeconds = 60; } // // Handle /SHUTDOWN_ABORT // } else if (_stricmp(argument, SHUTDOWN_ABORT_PARAM ) == 0 ){ ShutdownAbort = TRUE; // // Handle /TRUSTED_DOMAINS // } else if (_stricmp(argument, TRUSTED_DOMAINS_PARAM ) == 0 ){ TrustedDomainsFlag = TRUE; // // Handle all other parameters // } else { Usage: fprintf( stderr, "Usage: nltest [/OPTIONS]\n\n" ); fprintf( stderr, "\n" " " SERVER_PARAM " - Specify \n" "\n" " " QUERY_PARAM " - Query netlogon service\n" " " REPL_PARAM " - Force replication on BDC\n" " " SYNC_PARAM " - Force SYNC on BDC\n" " " PDC_REPL_PARAM " - Force UAS change message from PDC\n" "\n" " " SC_QUERY_PARAM " - Query secure channel for on \n" " " SC_RESET_PARAM " - Reset secure channel for on \n" " " DCLIST_PARAM " - Get list of DC's for \n" " " DCNAME_PARAM " - Get the PDC name for \n" " " DCTRUST_PARAM " - Get name of DC is used for trust of \n" " " WHOWILL_PARAM "* [] - See if will log on \n" " " FINDUSER_PARAM " - See which trusted will log on \n" " " TRANSPORT_PARAM " - Notify of netlogon of new transport\n" "\n" " " BP_PARAM " - Force a BreakPoint in Netlogon on \n" " " DBFLAG_PARAM " - New debug flag\n" " " TRUNCATE_LOG_PARAM " - Truncate log file (rename to *.bak)\n" "\n" " " PWD_PARAM " - Specify Password to encrypt\n" " " RID_PARAM " - RID to encrypt Password with\n" " " USER_PARAM " - Query User info on \n" "\n" " " TIME_PARAM " - Convert NT GMT time to ascii\n" " " LOCKOUT_PARAM " - set lockout parameters on a domain\n" " " LOGON_QUERY_PARAM " - Query number of cumulative logon attempts\n" " " TRUSTED_DOMAINS_PARAM " - Query names of domains trusted by workstation\n" "\n" " " BDC_QUERY_PARAM " - Query replication status of BDCs for \n" " " SIM_SYNC_PARAM " - Simulate full sync replication\n" "\n" " " BACKUP_CHANGE_LOG_PARAM " - Backup Change log file (copy to netlogon.bkp)\n" " " LIST_DELTAS_PARAM " - display the content of given change log file \n" " " LIST_REDO_PARAM " - display the content of given redo log file \n" "\n" " " SHUTDOWN_PARAM " [] - Shutdown for \n" " " SHUTDOWN_ABORT_PARAM " - Abort a system shutdown\n" "\n" ); return(1); } } // // Convert the server name to unicode. // if ( AnsiServerName != NULL ) { if ( AnsiServerName[0] == '\\' && AnsiServerName[1] == '\\' ) { ServerName = NetpAllocWStrFromStr( AnsiServerName ); } else { AnsiUncServerName[0] = '\\'; AnsiUncServerName[1] = '\\'; strcpy(AnsiUncServerName+2, AnsiServerName); ServerName = NetpAllocWStrFromStr( AnsiUncServerName ); AnsiServerName = AnsiUncServerName; } } // // Convert the user name to unicode. // if ( AnsiUserName != NULL ) { UserName = NetpAllocWStrFromStr( AnsiUserName ); if ( UserName == NULL ) { fprintf( stderr, "Not enough memory\n" ); return(1); } } // // If we've been asked to contact the Netlogon service, // Do so // if ( FunctionCode != 0 ) { // // The dbflag should be set in the registry as well as in netlogon // proper. // if ( FunctionCode == NETLOGON_CONTROL_SET_DBFLAG ) { SetDbflagInRegistry( ServerName, DbFlagValue ); } NetStatus = I_NetLogonControl2( ServerName, FunctionCode, Level, (LPBYTE) &InputDataPtr, (LPBYTE *)&NetlogonInfo1 ); if ( NetStatus != NERR_Success ) { fprintf( stderr, "I_NetLogonControl failed: " ); PrintStatus( NetStatus ); return(1); } if( (Level == 1) || (Level == 2) ) { // // Print level 1 information // printf( "Flags: %lx", NetlogonInfo1->netlog1_flags ); if ( NetlogonInfo1->netlog1_flags & NETLOGON_REPLICATION_IN_PROGRESS ) { if ( NetlogonInfo1->netlog1_flags & NETLOGON_FULL_SYNC_REPLICATION ) { printf( " FULL_SYNC " ); } else { printf( " PARTIAL_SYNC " ); } printf( " REPLICATION_IN_PROGRESS" ); } else if ( NetlogonInfo1->netlog1_flags & NETLOGON_REPLICATION_NEEDED ) { if ( NetlogonInfo1->netlog1_flags & NETLOGON_FULL_SYNC_REPLICATION ) { printf( " FULL_SYNC " ); } else { printf( " PARTIAL_SYNC " ); } printf( " REPLICATION_NEEDED" ); } if ( NetlogonInfo1->netlog1_flags & NETLOGON_REDO_NEEDED) { printf( " REDO_NEEDED" ); } printf( "\n" ); printf( "Connection "); PrintStatus( NetlogonInfo1->netlog1_pdc_connection_status ); } if( Level == 2 ) { // // Print level 2 only information // PNETLOGON_INFO_2 NetlogonInfo2; NetlogonInfo2 = (PNETLOGON_INFO_2)NetlogonInfo1; printf("Trusted DC Name %ws \n", NetlogonInfo2->netlog2_trusted_dc_name ); printf("Trusted DC Connection Status "); PrintStatus( NetlogonInfo2->netlog2_tc_connection_status ); } if ( Level == 3 ) { printf( "Number of attempted logons: %ld\n", ((PNETLOGON_INFO_3)NetlogonInfo1)->netlog3_logon_attempts ); } if( Level == 4 ) { PNETLOGON_INFO_4 NetlogonInfo4; NetlogonInfo4 = (PNETLOGON_INFO_4)NetlogonInfo1; printf("Domain Name: %ws\n", NetlogonInfo4->netlog4_trusted_domain_name ); printf("Trusted DC Name %ws \n", NetlogonInfo4->netlog4_trusted_dc_name ); } } // // If we've been asked to debug password encryption, // do so. // if ( AnsiPassword != NULL ) { LPWSTR Password = NULL; UNICODE_STRING UnicodePasswordString; STRING AnsiPasswordString; CHAR LmPasswordBuffer[LM20_PWLEN + 1]; Password = NetpAllocWStrFromStr( AnsiPassword ); RtlInitUnicodeString( &UnicodePasswordString, Password ); // // Compute the NT One-Way-Function of the password // Status = RtlCalculateNtOwfPassword( &UnicodePasswordString, &NtOwfPassword ); if ( !NT_SUCCESS(Status) ) { fprintf( stderr, "RtlCalculateNtOwfPassword failed: 0x%lx", Status); return(1); } printf( "NT OWF Password for: %s ", AnsiPassword ); DumpBuffer( &NtOwfPassword, sizeof( NtOwfPassword )); printf("\n"); NtPasswordPresent = TRUE; // // Compute the Ansi version to the Cleartext password. // // The Ansi version of the Cleartext password is at most 14 bytes long, // exists in a trailing zero filled 15 byte buffer, // is uppercased. // AnsiPasswordString.Buffer = LmPasswordBuffer; AnsiPasswordString.MaximumLength = sizeof(LmPasswordBuffer); RtlZeroMemory( LmPasswordBuffer, sizeof(LmPasswordBuffer) ); Status = RtlUpcaseUnicodeStringToOemString( &AnsiPasswordString, &UnicodePasswordString, FALSE ); if ( !NT_SUCCESS(Status) ) { RtlZeroMemory( LmPasswordBuffer, sizeof(LmPasswordBuffer) ); Status = STATUS_SUCCESS; printf( "LM OWF Password for: %s\n", AnsiPassword ); printf( " ----- Password doesn't translate from unicode ----\n"); LmPasswordPresent = FALSE; } else { Status = RtlCalculateLmOwfPassword( LmPasswordBuffer, &LmOwfPassword); printf( "LM OWF Password for: %s ", AnsiPassword ); DumpBuffer( &LmOwfPassword, sizeof( LmOwfPassword )); printf("\n"); LmPasswordPresent = TRUE; } } // // If we've been given a Rid, // use it to further encrypt the password // if ( Rid != 0 ) { ENCRYPTED_LM_OWF_PASSWORD EncryptedLmOwfPassword; ENCRYPTED_NT_OWF_PASSWORD EncryptedNtOwfPassword; if ( NtPasswordPresent ) { Status = RtlEncryptNtOwfPwdWithIndex( &NtOwfPassword, &Rid, &EncryptedNtOwfPassword ); printf( "NT OWF Password encrypted by: 0x%lx ", Rid ); if ( NT_SUCCESS( Status ) ) { DumpBuffer( &EncryptedNtOwfPassword,sizeof(EncryptedNtOwfPassword)); printf("\n"); } else { printf( "RtlEncryptNtOwfPwdWithIndex returns 0x%lx\n", Status ); } } if ( LmPasswordPresent ) { Status = RtlEncryptLmOwfPwdWithIndex( &LmOwfPassword, &Rid, &EncryptedLmOwfPassword ); printf( "LM OWF Password encrypted by: 0x%lx ", Rid ); if ( NT_SUCCESS( Status ) ) { DumpBuffer( &EncryptedLmOwfPassword,sizeof(EncryptedLmOwfPassword)); printf("\n"); } else { printf( "RtlEncryptNtOwfPwdWithIndex returns 0x%lx\n", Status ); } } } // // If we've been asked to query a user, // do so. // if ( QueryUser ) { PrintUserInfo( ServerName, AnsiUserName ); } // // If we've been asked to get the list of domain controllers, // Do so // if ( AnsiDomainName != NULL ) { LPWSTR DomainName; DomainName = NetpAllocWStrFromStr( AnsiDomainName ); if ( DomainName == NULL ) { fprintf( stderr, "Not enough memory\n" ); return(1); } if ( GetPdcName ) { LPWSTR PdcName; NetStatus = NetGetDCName( ServerName, DomainName, (LPBYTE *)&PdcName ); if ( NetStatus != NERR_Success ) { fprintf( stderr, "NetGetDCName failed: " ); PrintStatus( NetStatus ); return(1); } printf( "PDC for Domain " FORMAT_LPWSTR " is " FORMAT_LPWSTR "\n", DomainName, PdcName ); } else if ( GetDcList ) { DWORD DCCount; PUNICODE_STRING DCNames; DWORD i; NetStatus = I_NetGetDCList( ServerName, DomainName, &DCCount, &DCNames ); if ( NetStatus != NERR_Success ) { fprintf( stderr, "I_NetGetDCList failed: "); PrintStatus( NetStatus ); return(1); } printf( "List of DCs in Domain " FORMAT_LPWSTR "\n", DomainName ); for (i=0; i 0 ) { printf(" %wZ", &DCNames[i] ); } else { printf(" NULL"); } if ( i==0 ) { printf( " (PDC)"); } printf("\n"); } } else if ( GetTrustedDcName ) { LPWSTR TrustedDcName; NetStatus = NetGetAnyDCName( ServerName, DomainName, (LPBYTE *)&TrustedDcName ); if ( NetStatus != NERR_Success ) { fprintf( stderr, "NetGetAnyDCName failed: "); PrintStatus( NetStatus ); return(1); } printf( "Trusted DC for Domain " FORMAT_LPWSTR " is " FORMAT_LPWSTR "\n", DomainName, TrustedDcName ); } else if ( WhoWill ) { WhoWillLogMeOn( DomainName, UserName, IterationCount ); } else if( QuerySync ) { DWORD DCCount; PUNICODE_STRING DCNames; DWORD i; PNETLOGON_INFO_1 SyncNetlogonInfo1 = NULL; LPWSTR SyncServerName = NULL; NetStatus = I_NetGetDCList( ServerName, DomainName, &DCCount, &DCNames ); if ( NetStatus != NERR_Success ) { fprintf( stderr, "I_NetGetDCList failed: "); PrintStatus( NetStatus ); return(1); } for (i=1; i 0 ) { SyncServerName = DCNames[i].Buffer; } else { SyncServerName = NULL; } NetStatus = I_NetLogonControl( SyncServerName, NETLOGON_CONTROL_QUERY, 1, (LPBYTE *)&SyncNetlogonInfo1 ); if ( NetStatus != NERR_Success ) { printf( "Server : " FORMAT_LPWSTR "\n", SyncServerName ); printf( "\tI_NetLogonControl failed: "); PrintStatus( NetStatus ); } else { printf( "Server : " FORMAT_LPWSTR "\n", SyncServerName ); printf( "\tSyncState : " ); if ( SyncNetlogonInfo1->netlog1_flags == 0 ) { printf( " IN_SYNC \n" ); } else if ( SyncNetlogonInfo1->netlog1_flags & NETLOGON_REPLICATION_IN_PROGRESS ) { printf( " REPLICATION_IN_PROGRESS \n" ); } else if ( SyncNetlogonInfo1->netlog1_flags & NETLOGON_REPLICATION_NEEDED ) { printf( " REPLICATION_NEEDED \n" ); } else { printf( " UNKNOWN \n" ); } printf( "\tConnectionState : "); PrintStatus( SyncNetlogonInfo1->netlog1_pdc_connection_status ); NetApiBufferFree( SyncNetlogonInfo1 ); } } } else if( SimFullSync ) { LPWSTR MachineName; LPWSTR PdcName; MachineName = NetpAllocWStrFromStr( AnsiSimMachineName ); if ( MachineName == NULL ) { fprintf( stderr, "Not enough memory\n" ); return(1); } NetStatus = NetGetDCName( ServerName, DomainName, (LPBYTE *)&PdcName ); if ( NetStatus != NERR_Success ) { fprintf( stderr, "NetGetDCName failed: " ); PrintStatus( NetStatus ); return(1); } Status = SimulateFullSync( PdcName, MachineName ); if ( !NT_SUCCESS( Status )) { return(1); } } } // // if we are asked to display the change log file. do so. // if( ListDeltasFlag || ListRedoFlag ) { LPWSTR DeltaFileName; DeltaFileName = NetpAllocWStrFromStr( AnsiDeltaFileName ); if ( DeltaFileName == NULL ) { fprintf( stderr, "Not enough memory\n" ); return(1); } ListDeltas( DeltaFileName, ListRedoFlag ); } // // Handle shutting down a system. // if ( ShutdownReason != NULL ) { if ( !InitiateSystemShutdownA( AnsiServerName, ShutdownReason, ShutdownSeconds, FALSE, // Don't lose unsaved changes TRUE ) ) { // Reboot when done fprintf( stderr, "InitiateSystemShutdown failed: "); PrintStatus( GetLastError() ); return 1; } } if ( ShutdownAbort ) { if ( !AbortSystemShutdownA( AnsiServerName ) ) { fprintf( stderr, "AbortSystemShutdown failed: "); PrintStatus( GetLastError() ); return 1; } } // // Print the list of domains trusted by a workstation. // if ( TrustedDomainsFlag ) { ULONG CurrentIndex; ULONG EntryCount; LPWSTR CurrentEntry; LPWSTR TrustedDomainList; Status = NetEnumerateTrustedDomains( ServerName, &TrustedDomainList ); if ( !NT_SUCCESS(Status) ) { fprintf( stderr, "NetEnumerateTrustedDOmains failed: "); PrintStatus( Status ); return 1; } EntryCount = NetpTStrArrayEntryCount( TrustedDomainList ); printf( "Trusted domain list:\n" ); CurrentEntry = TrustedDomainList; for ( CurrentIndex=0; CurrentIndex