6.19.2 Solution If you have MIT Kerberos 5 client installed and configured properly, you can run the following commands, which will change your password in Active Directory: $ kinit Pa
Trang 1The value for the unicodePwd attribute must be a Unicode string that is surrounded by quotes and Base64 encoded See Recipe 10.4 for more on encoding text with Base64
6.18.3 Discussion
The unicodePwd attribute can be directly modified over a SSL or TLS connection, but it can never be read
6.18.4 See Also
Recipe 10.4 for more on Base64 encoding, Recipe 14.1 for enabling SSL/TLS, MS KB 263991 (How to Set a User's Password with Ldifde), MS KB 264480 (Description of Password-Change Protocols in Windows 2000), and MS KB 269190 (HOWTO: Change a Windows 2000 User's Password Through LDAP)
Recipe 6.19 Setting a User's Password via Kerberos
6.19.1 Problem
You want to change a password using Kerberos from a Unix machine
6.19.2 Solution
If you have MIT Kerberos 5 client installed and configured properly, you can run the following commands, which will change your password in Active Directory:
$ kinit
Password for jsmith@RALLENCORP.COM: ****
$ kpasswd
Password for jsmith@RALLENCORP.COM: ****
Enter new password: ******
Enter it again: ******
Password changed
6.19.3 Discussion
See Recipe 18.7 for more information on Kerberos
6.19.4 See Also
MS KB 264480 (Description of Password-Change Protocols in Windows 2000), RFC 3244 (Microsoft Windows 2000 Kerberos Change Password and Set Password Protocols), and IETF draft-ietf-cat-kerb-chg-password-02.txt
Trang 2Recipe 6.20 Preventing a User from Changing His
Password
6.20.1 Problem
You want to disable a user's ability to change his password
6.20.2 Solution
6.20.2.1 Using a graphical user interface
1 Open the Active Directory Users and Computers snap-in
2 In the left pane, right-click on the domain and select Find
3 Select the appropriate domain beside In
4 Beside Name, type the name of the user you want to modify and click Find Now
5 In the Search Results, double-click on the user
6 Click the Account tab
7 Under Account options, check the box beside User cannot change password
8 Click OK
6.20.2.2 Using a command-line interface
> dsmod user <UserDN> -canchpwd no
6.20.2.3 Using VBScript
' This code disables a user's ability to change password
' - SCRIPT CONFIGURATION -
strUserDN = "<UserDN>" ' e.g cn=rallen,ou=Sales,dc=rallencorp,dc=com
' - END CONFIGURATION -
Const ACETYPE_ACCESS_DENIED_OBJECT = 6
Const ACEFLAG_OBJECT_TYPE_PRESENT = 1
Const RIGHT_DS_CONTROL_ACCESS = 256
Const CHANGE_PASSWORD_GUID = "{ab721a53-1e2f-11d0-9819-00aa0040529b}"
set objUser = GetObject("LDAP://" & strUserDN)
set objSD = objUser.Get("ntSecurityDescriptor")
set objDACL = objSD.DiscretionaryAcl
' Add a deny ACE for Everyone
set objACE = CreateObject("AccessControlEntry")
objACE.Trustee = "Everyone"
objACE.AceFlags = 0
objACE.AceType = ACETYPE_ACCESS_DENIED_OBJECT
objACE.Flags = ACEFLAG_OBJECT_TYPE_PRESENT
objACE.ObjectType = CHANGE_PASSWORD_GUID
objACE.AccessMask = RIGHT_DS_CONTROL_ACCESS
objDACL.AddAce objACE
' Add a deny ACE for Self
set objACE = CreateObject("AccessControlEntry")
Trang 3objACE.Trustee = "Self"
objACE.AceFlags = 0
objACE.AceType = ACETYPE_ACCESS_DENIED_OBJECT
objACE.Flags = ACEFLAG_OBJECT_TYPE_PRESENT
objACE.ObjectType = CHANGE_PASSWORD_GUID
objACE.AccessMask = RIGHT_DS_CONTROL_ACCESS
objDACL.AddAce objACE
objSD.DiscretionaryAcl = objDACL
objUser.Put "nTSecurityDescriptor", objSD
objUser.SetInfo
WScript.Echo "Enabled no password changing for " & strUserDN
6.20.3 Discussion
Even though in the GUI solution you check and uncheck the "User cannot change password" setting, actually making the change in Active Directory is a little more complicated as is evident
in the VBScript solution Not allowing a user to change her password consists of setting two deny Change Password ACEs on the target user object One deny ACE is for the Everyone
account and the other is for Self
The VBScript solution should work as is, but it is not very robust in terms of checking to see if the ACEs already exist and making sure they are in the proper order If you need to make the code more robust, I suggest checking out MS KB 269159 for more information on setting ACEs properly
6.20.4 See Also
MS KB 269159 (HOWTO: Use Visual Basic and ADsSecurity.dll to Properly Order ACEs in an ACL)
Recipe 6.21 Requiring a User to Change Her Password
at Next Logon
6.21.1 Problem
You want to require a user to change her password the next time she logs on to the domain
6.21.2 Solution
6.21.2.1 Using a graphical user interface
1 Open the Active Directory Users and Computers snap-in
2 In the left pane, right-click on the domain and select Find
3 Select the appropriate domain beside In
4 Beside Name, type the name of the user you want to modify and click Find Now
5 In the Search Results, double-click on the user
Trang 46 Click the Account tab
7 Under Account options, check the box beside User must change password at next logon
8 Click OK
6.21.2.2 Using a command-line interface
> dsmod user "<UserDN>" -mustchpwd yes
6.21.2.3 Using VBScript
' This code sets the flag that requires a user to change their password
' - SCRIPT CONFIGURATION -
strUserDN = "<UserDN>" ' e.g cn=rallen,ou=Sales,dc=rallencorp,dc=com
' - END CONFIGURATION -
set objUser = GetObject("LDAP://" & strUserDN)
objUser.Put "pwdLastSet", 0
objUser.SetInfo
WScript.Echo "User must change password at next logon: " & strUserDN
6.21.3 Discussion
When a user changes her password, a timestamp is written to the pwdLastSet attribute of the
user object When the user logs in to the domain, this timestamp is compared to the maximum password age that is defined by the Domain Security Policy to determine if the password has expired To force a user to change her password at next logon, set the pwdLastSet attribute of the target user to and verify that the user's account doesn't have the never expire password option enabled
To disable this option so that a user does not have to change her password, set pwdLastSet to -1 These two values (0 and -1) are the only ones that can be set on the pwdLastSet attribute
Recipe 6.22 Preventing a User's Password from
Expiring
6.22.1 Problem
You want to prevent a user's password from expiring
6.22.2 Solution
6.22.2.1 Using a graphical user interface
1 Open the Active Directory Users and Computers snap-in
2 In the left pane, right-click on the domain and select Find
3 Select the appropriate domain beside In
4 Beside Name, type the name of the user you want to modify and click Find Now
5 In the Search Results, double-click on the user
6 Click the Account tab
Trang 57 Under Account options, check the box beside Password never expires
8 Click OK
6.22.2.2 Using a command-line interface
> dsmod user "<UserDN>" -pwdneverexpires yes
6.22.2.3 Using VBScript
' This code sets a users password to never expire
' See Recipe 4.12 for the code for the CalcBit function
' - SCRIPT CONFIGURATION -
strUserDN = "<UserDN>" ' e.g cn=rallen,ou=Sales,dc=rallencorp,dc=com
' - END CONFIGURATION -
intBit = 65536
strAttr = "userAccountControl"
set objUser = GetObject("LDAP://" & strUserDN)
intBitsOrig = objUser.Get(strAttr)
intBitsCalc = CalcBit(intBitsOrig, intBit, TRUE)
if intBitsOrig <> intBitsCalc then
objUser.Put strAttr, intBitsCalc
objUser.SetInfo
WScript.Echo "Changed " & strAttr & " from " & _
intBitsOrig & " to " & intBitsCalc
else
WScript.Echo "Did not need to change " & strAttr & " (" & _
intBitsOrig & ")"
end if
6.22.3 Discussion
Setting a user's password to never expire overrides any password aging policy you've defined in the domain To disable password expiration, you need to set the bit equivalent of 65536 (i.e., 10000000000000000) in the userAccountControl attribute of the target user
6.22.4 See Also
Recipe 4.12 for more on modifying a bit-flag attribute and Recipe 6.24 for more on setting the
userAccountControl attribute
Recipe 6.23 Finding Users Whose Passwords Are
About to Expire
6.23.1 Problem
You want to find the users whose passwords are about to expire
Trang 66.23.2 Solution
6.23.2.1 Using a command-line interface
> dsquery user -stalepwd <NumDaysSinceLastPwdChange>
6.23.2.2 Using Perl
#!perl
# This code finds the user accounts whose password is about to expire
# - SCRIPT CONFIGURATION -
# Domain and container/OU to check for accounts that are about to expire
my $domain = '<DomainDNSName>';
my $cont = ''; # set to empty string to query entire domain
# Or set to a relative path in the domain, e.g cn=Users
# Days since password change
my $days_ago = <NumDaysSinceLastPwdChange> # e.g 60;
# - END CONFIGURATION -
use strict;
use Win32::OLE;
$Win32::OLE::Warn = 3;
use Math::BigInt;
# Need to convert the number of seconds from $day_ago
# to a large integer for comparison against pwdLastSet
my $past_secs = time - 60*60*24*$days_ago;
my $intObj = Math::BigInt->new($past_secs);
$intObj = Math::BigInt->new($intObj->bmul('10 000 000'));
my $past_largeint = Math::BigInt->new(
$intObj->badd('116 444 736 000 000 000')); $past_largeint =~ s/^[+-]//;
# Setup the ADO connections
my $connObj = Win32::OLE->new('ADODB.Connection');
$connObj->{Provider} = "ADsDSOObject";
# Set these next two if you need to authenticate
# $connObj->Properties->{'User ID'} = '<User>';
# $connObj->Properties->{'Password'} = '<Password>';
$connObj->Open;
my $commObj = Win32::OLE->new('ADODB.Command');
$commObj->{ActiveConnection} = $connObj;
$commObj->Properties->{'Page Size'} = 1000;
# Grab the default domain naming context
my $rootDSE = Win32::OLE->GetObject("LDAP://$domain/RootDSE");
my $rootNC = $rootDSE->Get("defaultNamingContext");
# Run ADO query and print results
$cont = "," if $cont and not $cont =~ /,$/;
my $query = "<LDAP://$domain/$cont$rootNC>;";
$query = "(&(objectclass=user)";
$query = "(objectcategory=Person)";
$query = "(!useraccountcontrol:1.2.840.113556.1.4.803:=2)";
$query = "(pwdLastSet<=$past_largeint)";
$query = "(!pwdLastSet=0));";
$query = "cn,distinguishedName;";
$query = "subtree";
$commObj->{CommandText} = $query;
Trang 7my $resObj = $commObj->Execute($query);
die "Could not query $domain: ",$Win32::OLE::LastError,"\n"
unless ref $resObj;
print "\nUsers who haven't set their passwd in $days_ago days or longer:\n";
my $total = 0;
while (!($resObj->EOF)) {
print "\t",$resObj->Fields("distinguishedName")->value,"\n";
$total++;
$resObj->MoveNext;
}
print "Total: $total\n";
6.23.3 Discussion
When a Windows-based client logs on to Active Directory, a check is done against the domain password policy and the user's pwdLastSet attribute to determine if the user's password has expired If it has, the user is prompted to change it In a pure Windows-based environment, this notification process may be adequate, but if you have a lot of non-Windows-based computers that are joined to an Active Directory domain (e.g., Kerberos-enabled Unix clients), or you have
a lot of application and service accounts, you'll need to develop your own user password
expiration notification process Even in a pure Windows environment, cached logins present a problem because when a user logs into the domain with cached credentials (i.e., when the client
is not able to reach a domain controller), this password expiration notification check is not done The process of finding users whose passwords are about to expire is a little complicated
Fortunately, the new dsquery user command helps by providing an option for searching for users that haven't changed their password for a number of days (-stalepwd) The downside to the dsquery user command is that it will not only find users whose password is about to expire, but also users that must change their password at next logon (i.e., pwdLastSet = 0) The Perl solution does not suffer from this limitation
The Perl solution consists of a two-step process First, we need to calculate a time in the past at which we would consider a password "old" or "about" to expire The pwdLastSet attribute is a replicated attribute on user objects that contain the timestamp (as a large integer) of when the user last set her password If today is May 31 and we want to find all users who have not set their password for 30 days, we need to query for user's who have a pwdLastSet timestamp older than May 1
First, a brief word on timestamps stored as large integers It may seem odd, but large integer timestamps are represented as the number of 100-nanosecond intervals since January 1, 1601 To convert the current time to a large integer, we have to find the current time in seconds since the epoch (January 1, 1970) multiply that times 10,000,000 and then add 116,444,736,000,000,000
to it This will give you an approximate time (in 100-nanosecond intervals) as a large integer It
is only an approximate time because when dealing with big numbers like this, a degree of
accuracy is lost during the arithmetic
Trang 8I chose to use Perl over VBScript because VBScript doesn't handle computing large integers given the current time and date very well
All right, now that you know how to calculate the current time, we need to calculate a time in the past as a large integer Remember, we need to find the time at which passwords are considered close to expiring In the Perl solution, you can configure the number of days since users changed their password Once we've calculated this value, all we need is to come up with a search filter that we can use in ADO to find the matching users
The first part of the filter will match all user objects
$query = "(&(objectclass=user)";
$query = "(objectcategory=Person)";
But we really only want to find all enabled user objects (do you care if a disabled user object's password is about to expire?) This next bit-wise filter will match only enabled user objects See Recipe 6.13 for more information on finding disabled and enabled users
$query = "(!useraccountcontrol:1.2.840.113556.1.4.803:=2)";
The next part of the filter is the important part This is where we use the derived last password change timestamp to compare against pwdLastSet
$query = "(pwdLastSet<=$past_largeint)";
Finally, we exclude all users that are required to change their password at next logon
(pwdLastSet equal to zero)
$query = "(!pwdLastSet=0));";
6.23.4 See Also
Recipe 6.11 for more on the password policy for a domain, Recipe 6.17 for how to set a user's password, and Recipe 6.22 for how to set a user's password to never expire
Recipe 6.24 Setting a User's Account Options
(userAccountControl)
6.24.1 Problem
You want to view or update the userAccountControl attribute for a user This attribute controls various account options, such as if the user must change their password at next logon and if the account is disabled
Trang 96.24.2 Solution
6.24.2.1 Using a graphical user interface
1 Open the Active Directory Users and Computers snap-in
2 In the left pane, right-click on the domain and select Find
3 Select the appropriate domain beside In
4 Beside Name, type the name of the user and click Find Now
5 In the Search Results, double-click on the user
6 Select the Account tab
7 Many of the userAccountControl flags can be set under Account options
8 Click OK after you're done
6.24.2.2 Using a command-line interface
The dsmod user command has several options for setting various userAccountControl flags, as shown in Table 6-2 Each switch accepts yes or no as a parameter to either enable or disable the setting
Table 6-2 dsmod user options for setting userAccountControl
-mustchpwd Sets whether the user must change password at next logon
-canchpwd Sets whether the user can change his password
-disabled Set account status to enabled or disabled
-reversiblepwd Sets whether the user's password is stored using reversible encryption
-pwdneverexpires Sets whether the user's password never expires
6.24.2.3 Using VBScript
' This code enables or disables a bit value in the userAccountControl attr ' See Recipe 4.12 for the code for the CalcBit function
' - SCRIPT CONFIGURATION -
strUserDN = "<UserDN>" ' e.g cn=rallen,ou=Sales,dc=rallencorp,dc=com intBit = <BitValue> ' e.g 65536
boolEnable = <TrueOrFalse> ' e.g TRUE
' - END CONFIGURATION -
strAttr = "userAccountControl"
set objUser = GetObject("LDAP://" & strUserDN)
intBitsOrig = objUser.Get(strAttr)
intBitsCalc = CalcBit(intBitsOrig, intBit, boolEnable)
if intBitsOrig <> intBitsCalc then
objUser.Put strAttr, intBitsCalc
objUser.SetInfo
WScript.Echo "Changed " & strAttr & " from " & _
Trang 10intBitsOrig & " to " & intBitsCalc
else
WScript.Echo "Did not need to change " & strAttr & " (" & _
intBitsOrig & ")"
end if
6.24.3 Discussion
The userAccountControl attribute on user (and computer) objects could be considered the
kitchen sink of miscellaneous and sometimes completely unrelated user account properties If
you have to work with creating and managing user objects very much, you'll need to become
intimately familiar with this attribute
The userAccountControl attribute is a bit flag, which means you have to take a couple extra
steps to search against it or modify it See Recipe 4.9 for more on searching with a bit-wise filter
and Recipe 4.12 for modifying a bit-flag attribute
The dsmod user command can be used to modify a subset of userAccountControl properties,
as shown in Table 6-2 Table 6-3 contains the complete list userAccountControl properties as
defined in the ADS_USER_FLAG_ENUM enumeration
Table 6-3 ADS_USER_FLAG_ENUM values
cannot change their password ADS_UF_ENCRYPTED_TEXT_PASSWORD_ALLOWED 128 Store password using reversib
no other domain that trusts th
ADS_UF_INTERDOMAIN_TRUST_ACCOUNT 2048 A permit to trust account for
domain that trusts other doma