Securing Computers and Active Directory

In this third part to a four-part series on how Active Directory handles computers, you'll learn how to test a secure channel for a computer, reset a computer account, and more. It is excerpted from chapter eight of the Active Directory Cookbook, Second Edition, written by Robbie Allen and Laura E. Hunter (O'Reilly; ISBN: 059610202X). Copyright © 2006 O'Reilly Media, Inc. All rights reserved. Used with permission from the publisher. Available from booksellers or direct from O'Reilly Media.

Contributed by
Rating: 5 stars5 stars5 stars5 stars5 stars / 2
January 24, 2008
Rate this Article:
MEH MEH++


SEARCH ASP FREE
TOOLS YOU CAN USE

advertisement

Testing the Secure Channel for a Computer

Problem

You want to test the secure channel of a computer.

Solution

Using a command-line interface

  > nltest /server:<ComputerName> /sc_query:<DomainName>

Discussion

Every member computer in an Active Directory domain establishes a secure channel with a domain controller. The computer’s password is stored locally in the form of an LSA secret and in Active Directory. This password is used by the NetLogon service to establish the secure channel with a domain controller. If for some reason the LSA secret and computer password become out of sync, the computer will no longer be able to authenticate in the domain. The nltest /sc_query command can query a computer to verify its secure channel is working. Here is sample output from the command when things are working:

  Flags: 30 HAS_IP HAS_TIMESERV
  Trusted DC Name \\dc1.rallencorp.com
  Trusted DC Connection Status Status = 0 0x0 NERR_Success
  The command completed successfully

If a secure channel is failing, you’ll need to reset the computer as described in Recipe 8.9. Here is sample output when things are not working:

  Flags: 0
  Trusted DC Name
  Trusted DC Connection Status Status = 1311 0x51f ERROR_NO_LOGON_SERVERS
  The command completed successfully

See Also

Recipe 8.9 for resetting a computer and MS KB 216393 (Resetting Computer Accounts in Windows 2000 and Windows XP)

Resetting a Computer Account

Problem

You want to reset a computer because its secure channel is failing.

Solution

Using a graphical user interface

  1. Open the ADUC snap-in.
  2. If you need to change domains, right-click on Active Directory Users and Computers in the left pane, select “Connect to Domain,” enter the domain name, and click OK.
  3. In the left pane, right-click on the domain and select Find.
  4. Beside Find, select Computers.
  5. Type the name of the computer and click Find Now.
  6. In the Search Results, right-click on the computer and select Reset Account.
  7. Click Yes to verify.
  8. Click OK.
  9. Rejoin the computer to the domain.

Using a command-line interface

You can use the DSMod utility to reset a computer’s password. You will need to rejoin the computer to the domain after doing this.

  > dsmod computer "<ComputerDN>" -reset

Another option is to use thenetdomcommand, which can reset the secure channel between the computer and the domain controller without affecting the computer’s password, so that you do not need to rejoin it to the domain:

  > netdom reset <ComputerName> /Domain <DomainName> /UserO <UserUPN> /PasswordO *

You can also use thenltest command to reset a secure channel using the following syntax:

  > nltest /sc_reset:<DomainName>\<DCName>

Using VBScript

  ' This resets an existing computer object's password to initial default.
  ' You'll need to rejoin the computer after doing this.
  set objComputer = GetObject(LDAP://<ComputerDN>")
  objComputer.SetPassword "<ComputerName>"

Discussion

When you’ve identified that a computer’s secure channel has failed, you’ll need to reset the computer object, which consists of setting the computer object password to the name of the computer. This is the default initial password for new computers. Every 30 days, Windows 2000 and newer systems automatically change their passwords in the domain. After you’ve set the password, you’ll need to rejoin the computer to the domain since it will no longer be able to communicate with a domain controller due to unsynchronized passwords. However, the netdom reset command will try to reset the password on both the computer and in Active Directory, which will not necessitate rejoining it to the domain if successful.

From a practical standpoint, you should first attempt to reset the secure channel between the computer and the domain using thenetdomornltestsyntaxes, since doing so will not require you to unjoin and rejoin the computer to the domain; in particular, this will save you from performing the associated reboots involved with rejoining the domain. If resetting the secure channel does not correct the issue you’re facing, you can then resort to resetting the computer’s password.

See Also

Recipe 8.3 for joining a computer to a domain, Recipe 8.8 for testing a secure channel, MS KB 216393 (Resetting Computer Accounts in Windows 2000 and Windows XP), and MS KB 325850 (How to Use Netdom.exe to Reset Machine Account Passwords of a Windows Server 2003 Domain Controller)

Finding Inactive or Unused Computers

Problem

You want to find inactive computer accounts in a domain.

Solution

These solutions might only apply to Windows-based machines. Other types of machines—e.g., Unix, Mac, Network Attached Storage (NAS)—that have accounts in Active Directory might not update their login timestamps or passwords, which are used to determine inactivity.

Using a command-line interface

The following query will locate all inactive computers in the current forest:

  > dsquery computer forestroot -inactive <NumWeeks>

You can also usedomainrootin combination with the
-doption to query a specific domain:

  > dsquery computer domainroot -d <DomainName> -inactive <NumWeeks>

or you can target your query at a specific container:

  > dsquery computer ou=MyComputers,dc=rallencorp,dc=com
-inactive <NumWeeks>

These commands can only be run against a Windows Server 2003 domain functional level or higher domain.

You can also use the OldCmp joeware utility to create a report of all computer accounts whose passwords are older than a certain number of days (90 by default) by using the following syntax:

  > oldcmp -report

To specify an alternate password age witholdcmp, use the–age x switch. You can also use the–lltsswitch to use thelastLogonTimeStampattribute to perform the age calculations. (Without this switch,oldcmpwill usepwdLastSetby default.)

Using Perl

  #!perl

  #----------------------
  # Script Configuration
  #----------------------
  # Domain and container/OU to check for inactive computer accounts
  my $domain        = 'amer.rallencorp.com';

  # set to empty string to query entire domain
  my $computer_cont = 'cn=Computers,';
  # Number of weeks used to find inactive computers
  my $weeks_ago = 30;
  #----------------------
  # End Configuration
  #----------------------

  use strict;
  use Win32::OLE;
     $Win32::OLE::Warn = 3;
  use Math::BigInt;

  # Must convert the number of seconds since $weeks_ago
  # to a large integer for comparison against lastLogonTimestamp
  my $sixmonth_secs = time - 60*60*24*7*$weeks_ago;
  my $intObj = Math::BigInt->new($sixmonth_secs);
     $intObj = Math::BigInt->new($intObj->bmul('10 000 000'));
  my $sixmonth_int = Math::BigInt->new(
                          $intObj->badd('116 444 736 000 000 000'));
     $sixmonth_int =~ s/^[+-]//;

  # Set up the ADO connections
  my $connObj                         = Win32::OLE->new('ADODB.Connection');
  $connObj->{Provider}                = "ADsDSOObject";
  $connObj->Open;
  my $commObj                         = Win32::OLE->new('ADODB.Command');
  $commObj->{ActiveConnection}        = $connObj;
 
$commObj->Properties->{'Page Size'} = 1000;

  # Grab the default root domain name
  my $rootDSE = Win32::OLE->GetObject(LDAP://$domain/RootDSE);
  my $rootNC = $rootDSE->Get("defaultNamingContext");

  # Run ADO query and print results
  my $query = "<LDAP://$domain/$computer_cont$rootNC>;";
  $query .= "(&(objectclass=computer)";
  $query .=   "(objectcategory=computer)";
  $query .= "(lastlogontimestamp<=$sixmonth_int));";
  $query .= "cn,distinguishedName;";
  $query .= "subtree";
  $commObj->{CommandText} = $query;
  my $resObj = $commObj->Execute($query);
  die "Could not query $domain: ",$Win32::OLE::LastError,"\n"
   
unless ref $resObj;

  print "\nComputers that have been inactive for $weeks_ago weeks or more:\n";
  my $total = 0;
  while (!($resObj->EOF)) {
     
my $cn = $resObj->Fields(0)->value;
      print "\t",$resObj->Fields("distinguishedName")->value,"\n";
      $total++;
      $resObj->MoveNext;
  }
  print "Total: $total\n";

Discussion

Using a command-line interface

The dsquery computer command is very handy for finding inactive computers that have not logged in to the domain for a number of weeks or months. You can pipe the results of the query to the dsrm command-line utility if you want to remove the inactive computer objects from Active Directory in a single command. Here is an example that would delete all computers in the current domain that have been inactive for 12 weeks or longer:

  > for /F "usebackq" %i in (`dsquery computer domainroot -inactive 12`) do dsrm
%i

You can also use OldCmp to disable inactive accounts, and then either delete them or move them to an alternate OU. OldCmp has a number of safeties built into the utility to prevent you from deleting a large number of computer accounts without meaning to. For example, OldCmp will not delete an account unless it has first been disabled, it will not modify more than 10 objects at a time unless you manually specify a higher limit, and it simply will not do anything at all to a domain controller computer account under any circumstances. Unless you have a requirement for quickly removing unusedcomputer objects, we’d recommend allowing them to remain inactive for at least three months before removing them. If you don’t really care when the objects get removed, use a year (i.e., 52 weeks) to be on the safe side.

Using Perl

With Windows 2000 Active Directory, the only way you can determine if a computer is inactive is to query either the pwdLastSet or lastLogon attributes. The pwdLastSet attribute is a 64-bit integer that translates into the date and time the computer last updated its password. Since computers are supposed to change their password every 30 days, you could run a query that finds the computers that have not changed their password in several months. This is difficult with VBScript because it does not handle 64-bit integer manipulation very well. There are third-party add-ons that provide 64-bit functions, but none of the built-in VBScript functions can do it and it is nontrivial to implement without an add-on.

ThelastLogonattribute can also be used to find inactive computers because that attribute contains a 64-bit integer representing the last time the computer logged into the domain. The problem with thelastLogonattribute is that it is not replicated. Since it is not replicated, you have to query every domain controller in the domain to find the most recentlastLogonvalue. As you can imagine, this is less than ideal, especially if you have a lot of domain controllers.

Fortunately, in Windows Server 2003, Microsoft added a new attribute calledlastLogonTimestamptouserandcomputerobjects. This attribute contains the approximate last logon timestamp (again in a 64-bit, large-integer format) for the user or computer and is replicated to all domain controllers. It is the approximate last logon because the domain controllers will update the value only if it hasn’t been updated for a certain period of time (such as a week). This prevents the attribute from being updated constantly and causing a lot of unnecessary replication traffic.

Since VBScript was out of the question, we turned to our first love—Perl. It is very rare to find a problem that you can’t solve with Perl and this is no exception. The biggest issue is manipulating a number to a 64-bit integer, which we can do with theMath::BigIntmodule.

First, we determine the time in seconds from 1970 for the date that we want to query computer inactivity against. That is, take the current time and subtract the number of weeks we want to go back. Then we have to convert that number to a big integer. The last step is simply to perform an ADO query for all computers that have alastLogonTimestampless than or equal to the value we just calculated.

See Also

Recipe 6.31 for finding users whose accounts are about to expire

Changing the Maximum Number of Computers a User Can Join to the Domain

Problem

You want to grant users the ability to join more or fewer than 10 computers to a domain. This limit is called the machine account quota.

Solution

Using a graphical user interface

  1. Open the ADSI Edit MMC snap-in and connect to the Domain Naming Context.
  2. Right-click on the domainDNS object for the domain you want to change and select Properties.
  3. Edit the ms-DS-MachineAccountQuota attribute and enter the new quota value.
  4. Click OK twice.

Using a command-line interface

In the following LDIF code replace <DomainDN> with the distinguished name of the domain you want to change and replace <Quota> with the new machine account quota:

  dn: <DomainDN>
  changetype: modify
  replace: ms-DS-MachineAccountQuota
  ms-DS-MachineAccountQuota: <Quota>
  -

If the LDIF file was named change_computer_quota.ldf, you would then run the following command:

  > ldifde -v -i -f change_computer_quota.ldf

You can also make this change using AdMod, as follows:

  > admod –b <DomainDN> ms-DS-MachineAccountQuota::<Quota>

Using VBScript

  ' This code sets the machine account quota for a domain.
  ' ------ SCRIPT CONFIGURATION ------
  intQuota  = <Quota>
  strDomain = "<DomainDNSName>" ' e.g. emea.rallencorp.com
  ' ------ END CONFIGURATION ---------

  set objRootDSE = GetObject("LDAP://" & strDomain & "/RootDSE")
  set objDomain = GetObject("LDAP://" & objRootDSE.Get("defaultNamingContext"))
  objDomain.Put "ms-DS-MachineAccountQuota", intQuota
  objDomain.SetInfo
  WScript.Echo "Updated user quota to " & intQuota

Discussion

In a default Active Directory installation, members of the Authenticated Users group can add and join up to 10 computer accounts in the default Computers container. The number of computer accounts that can be created is defined in the
ms-DS-MachineAccountQuota attribute on the domainDNS object for a domain. The default setting is artificially set to 10, but you can easily change that to whatever number you want, including 0, via the methods described in the Solution section. If you set it to 0, users have to be granted explicit permissions in Active Directory to join computers; refer to Recipe 8.3 for instructions on granting these permissions.

Another method for granting users the right to addcomputerobjects, although not recommended, is via Group Policy. If you grant the “Add workstation to domain” right via Computer Configuration -> Windows Settings -> Security Settings -> Local Policies -> User Rights Assignment on a GPO that’s been linked to the Domain Controllers OU, then users will be able to create computer accounts even if they do not have create child permissions on the defaultComputerscontainer. This is a holdover from Windows NT to maintain backward compatibility and should not be used unless absolutely necessary. In fact, a good security best practice would be to remove this user right from any user or group objects that do not require it.

See Also

Recipe 8.3 for permissions needed to join computers to a domain, MS KB 251335 (Domain Users Cannot Join Workstation or Server to a Domain), and MS KB 314462 (“You Have Exceeded the Maximum Number of Computer Accounts” Error Message When You Try to Join a Windows XP Computer to a Windows 2000 Domain).

Please check back next week for the conclusion to this article.

blog comments powered by Disqus
WINDOWS SCRIPTING ARTICLES

- More Windows Scripting Workarounds from Nilpo
- Overloading Methods and More in VBScript
- Improving MFC for Windows Vista
- Regular Expressions in VBScript
- Working with Dates in WMI
- Completing Calendars with VBScript Date Func...
- Building Calendars with VBScript Date Functi...
- Working With Dates and Times in VBScript
- Designing WCF DataContract Classes Using the...
- Understanding Dates and Times in VBScript
- Working With Arrays in VBScript
- Compressed Folders in WSH
- Using .NET Interops in VBScript
- Nilpo`s Scripting Secrets, Vol I
- Database operations using Silverlight 2.0 WC...

ASP Web Hosting ASP.Net Web Hosting Windows Web Hosting
 
 
 

ASP Free Forums 
 RSS  Tutorials RSS
 RSS  Forums RSS
 RSS  All Feeds
Site Map 
Request Media Kit
Write For Us Get Paid 
Weekly Newsletter
 
Developer Updates  
Free Website Content 
Privacy Policy 
Support 


© 2003-2012 by Developer Shed. All rights reserved. DS Cluster 7 - Follow our Sitemap
Most Popular Topics
All ASP.Net Tutorials