Glen D.
(Lurker)
2002-08-25 07:23 PM
Scripting "User cannot change password" option

I'm having some difficulty with a routine I'm working on to update the "User cannot change password" option. It's not a simple flag, but rather an access control entry on the discretionary access control list of security discriptor of the user object. I've modified the code I've found on the MS scriptcenter for KIX, but I'm getting a "member not found error" when I attempt to reassign the security descriptor. If I comment out all the code between where I retrieve the DACL and then try to reassign it so that the code would flow as follows:

$objDACL=$objSD.DiscretionaryAcl
$objSD.DiscretionaryAcl = $objDACL

I still get the error on the assign. Any thoughts?

Here's the code I have.

$ADS_ACETYPE_ACCESS_DENIED_OBJECT=&6
$ADS_ACEFLAG_OBJECT_TYPE_PRESENT=&1
$CHANGE_PASSWORD_GUID = "{AB721A53-1E2F-11D0-9819-00AA0040529B}"
$ADS_RIGHT_DS_CONTROL_ACCESS =&100

$objUser=GetObject("LDAP://CN=TestStudent, OU=test, DC=test, DC=local")
$test=$objUser.Get("SN")
? "User:"+ $test
$objSD=$objUser.Get("ntSecurityDescriptor")
$objDACL=$objSD.DiscretionaryAcl

; This is test code to see if the ACE can be read (this works)
For Each $Ace In $objDACL
?$ACE.ObjectType+" --> "+$Ace.Acetype
If (($Ace.AceType = $ADS_ACETYPE_ACCESS_DENIED_OBJECT) AND (LCase($Ace.ObjectType) = $CHANGE_PASSWORD_GUID))
$blnACEPresent = True
EndIf
Next
If $blnACEPresent
? "ADS_UF_PASSWD_CANT_CHANGE is enabled"
Else
? "ADS_UF_PASSWD_CANT_CHANGE is disabled"
ENDIF

; Set up the new ACE entries
$aryTrustees = "nt authority\self" , "EVERYONE"
FOR EACH $strTrustee IN $aryTrustees
$objACE = CreateObject("AccessControlEntry")
$objACE.Trustee=$strTrustee
$objACEFlags=0
$objACE.AceType= $ADS_ACETYPE_ACCESS_DENIED_OBJECT
$objACE.Flags = $ADS_ACEFLAG_OBJECT_TYPE_PRESENT
$objACE.ObjectType= $CHANGE_PASSWORD_GUID
$objACE.AccessMask = $ADS_RIGHT_DS_CONTROL_ACCESS
$objDACL.AddAce($objACE)
NEXT
$objSD.DiscretionaryAcl = $objDACL
? "1:"+@error+" "+@serror
$objUser.Put("ntSecurityDescriptor", $objSD)
? "2:"+@error+" "+@serror
$objUser.SetInfo
? "3:"+@error+" "+@serror


Howard Bullock
(KiX Supporter)
2002-08-25 07:48 PM
Re: Scripting "User cannot change password" option

I think you are headed in the wrong direction. UF_PASSWD_CANT_CHANGE is part of the UserFlags property of the user account.

See: AllFlags() - Test to see if all the flags tested are set

UsrMustChgPwd() - Force a user to change password at next login This second link will give code very close to what you need. You can also use the WinNT:// provider if you desire.


Glen D.
(Lurker)
2002-08-25 08:04 PM
Re: Scripting "User cannot change password" option

I had hoped that would be the case. Unfortunately for the Cannot Change Password option, this can only be read from the Userflags property--it can't be set(regardless if it's the LDAP or WINNT interface)

To quote the MS Site ADSI reference
http://msdn.microsoft.com/library/default.asp?url=/library/en-us/netdir/adsi/adsi_reference.asp

To determine if the user has been granted permission to change his or her password, read the ADS_UF_PASSWD_CANT_CHANGE (0x0040) bit on the userFlags attribute of the user object. This flag is defined in the ADS_USER_FLAG_ENUM enumeration. This flag cannot be set directly.

To prevent a user from changing the password, set two ACEs in the security descriptor DACL of the user object. One ACE denies the permission to the user and another ACE denies the permission to the Everyone group. Both ACEs are object-specific deny ACEs that specify the GUID of the extended permission for changing passwords.


Howard Bullock
(KiX Supporter)
2002-08-25 09:32 PM
Re: Scripting "User cannot change password" option

I just set the UF_PASSWD_CANT_CHANGE flag on NT4 using Perl by performing a Bitwise OR of UserFlags and ($UF_PASSWD_CANT_CHANGE = 0x0040;). I will check KiXtart shortly.

{edit}

This also worked on a W2K AD DC.

[ 25. August 2002, 21:38: Message edited by: Howard Bullock ]


Glen D.
(Lurker)
2002-08-26 12:08 AM
Re: Scripting "User cannot change password" option

While I can set other flags (for example the ADS_UF_DONT_EXPIRE_PASSWD &10000) using the method you specify, I can't set the ADS_UF_PASSWD_CANT_CHANGE.

If I try to set an ADS_UF_PASSWD_CANT_CHANGE(&40), the script will "work" insofar as it won't report any errors; however, checking the user account will show that the flag is not set. Rerunning the script and just displaying the flag will also show that the value is not "saved" after the put.

Since I forgot to post it in the original message, I am using an AD under win2k and kixtart 4.10.

Here's the script I used to test that out on an account with no flags enabled:

$Ouser=GetObject("LDAP://CN=Test, OU=Test, DC=TASD, DC=LOCAL")
if @error<>0 ? @error+" "+@serror endif

If $oUser
; Print Base
$Flags = $oUser.Get("UserAccountControl")
? "Base: "+$Flags

? "ADS_UF_DONT_EXPIRE_PASSWD"
$Flags = $oUser.Get("UserAccountControl")
$Flags = $Flags | &10000
? " Before Put:"+$Flags
$oUser.Put("UserAccountControl",$Flags)
if @error<>0 ? @error+" "+@serror endif
$oUser.SetInfo
if @error<>0 ? @error+" "+@serror endif

;Check the value again
$Flags = $oUser.Get("UserAccountControl")
?" After Put: "+$Flags

?"ADS_UF_PASSWD_CANT_CHANGE"
$Flags = $oUser.Get("UserAccountControl")
$Flags = $Flags | &40
? " Before Put:"+$Flags
$oUser.Put("UserAccountControl",$Flags)
if @error<>0 ? @error+" "+@serror endif
$oUser.SetInfo
if @error<>0 ? @error+" "+@serror endif

;Check the value again
$Flags = $oUser.Get("UserAccountControl")
?" After Put: "+$Flags

Else
? "User not found"
EndIf
$oUser=0

And here's the output:

Base: 544
ADS_UF_DONT_EXPIRE_PASSWD
Before Put:66080
After Put: 66080
ADS_UF_PASSWD_CANT_CHANGE
Before Put:66144
After Put: 66080

This seems to jive with what the Microsoft docs say.

Thanks
-Glen


Howard Bullock
(KiX Supporter)
2002-08-26 01:31 AM
Re: Scripting "User cannot change password" option

This code works. I have yet to test the LDAP:// provider.

Notice the difference of my use of "&40". I believe that you need to use the "VAL" function. Test and let me know.

code:
$Domain = "us-tyco-e"
$Account = "habtest1"

? "WinNT://$Domain/$Account,user"
$oAccount=getobject("WinNT://$Domain/$Account,user")
? @serror + " " +vartypename($oAccount)

$val=$oAccount.UserFlags
? @serror
? $Val

$Val = $val | val("&40")
? $Val

$oAccount.Put ("UserFlags", $Val )
? "setinfo " +@serror


$oAccount.SetInfo
? "setinfo " +@serror

? "WinNT://$Domain/$Account,user"
$oAccount=getobject("WinNT://$Domain/$Account,user")

$val=$oAccount.UserFlags
? @serror
? $Val



[ 26. August 2002, 01:33: Message edited by: Howard Bullock ]


Howard Bullock
(KiX Supporter)
2002-08-26 01:50 AM
Re: Scripting "User cannot change password" option

My statement about the VAL function was incorrect. I spoke before I tested.
code:
$Val = $val | &40  

worked too.

[ 26. August 2002, 01:51: Message edited by: Howard Bullock ]


Glen D.
(Lurker)
2002-08-26 02:56 AM
Re: Scripting "User cannot change password" option

Thanks! It does seem to work properly under the WINNT provider. I was working primarily under the LDAP provider since this was part of a user creation script and I was using LDAP to get the users into the correct OUs. I guess next time I should just try it before reading the documentation from MS [Big Grin]

Thanks again. (Heck, I'll even buy AMP connectors next time I put in an order [Wink] )


Bonji
(Starting to like KiXtart)
2002-09-12 08:53 PM
Re: Scripting "User cannot change password" option

I've got the hard part of this issue done, the only problem is that my OR operation is returning the same value. I quickly wrote up a test script to make sure I was doing it properly. Here it is...

code:
BREAK ON
$VAR1 = 577
$VAR1 = $VAR1 | &40
? $VAR1

$VAR1 is returning 577. If I'm not mistaken the operation should look something like this...

code:
10-01000001     (577)
| (OR)
00-01000000 (64)
RESULT (Equals)
10-00000001 (513)

Can someone shed some light on this for me?

Thanks,
Ben

[ 12. September 2002, 20:58: Message edited by: Ben Dulaney ]


LonkeroAdministrator
(KiX Master Guru)
2002-09-12 09:36 PM
Re: Scripting "User cannot change password" option

I have thought that too and shed some tears with it.
but, I think the or is returning the first true value.

can't test now (on linux machine), but what happens if you put it like:
$VAR1 = 0 | &40

or like:
$VAR1 = 1 | &40

just like and (&) checks if both are true...
 


Bonji
(Starting to like KiXtart)
2002-09-12 10:21 PM
Re: Scripting "User cannot change password" option

$VAR1 = 0 | &40 returned 64
$VAR1 = 1 | &40 returned 65

I would expect these result based on my logic posted earlier. Do you agree?


LonkeroAdministrator
(KiX Master Guru)
2002-09-12 10:35 PM
Re: Scripting "User cannot change password" option

according your input, your first result (577 | 64)
should have been:
641

I don't know...
and as the [american]guru's aren't responding, I think you could ask this directly from ruud...
you know, to get direct answer.
then also let us know what he has to say.

you cathced surely thing that has bothered me but I've been too lazy to ask about it.

cheers.

 


Sealeopard
(KiX Master)
2002-09-12 11:40 PM
Re: Scripting "User cannot change password" option

Ben: OR sets a bit to 1 if either of the bits is 1 like
code:
    010
OR
101
EQV
111

Thus
code:
    10-01000001
OR
00-01000000
EQV
10-01000001



Bonji
(Starting to like KiXtart)
2002-09-13 02:35 PM
Re: Scripting "User cannot change password" option

Apparantly I need an exclusive OR to do what I need to. I'm not aware of such a function in KiX. Is there anyway to get the result I'm looking for?

Thanks,
Ben


Howard Bullock
(KiX Supporter)
2002-09-13 02:47 PM
Re: Scripting "User cannot change password" option

Does
code:
$VAR1 = $Var1 - ($VAR1 & &40)

get you where you need to go?

[ 13. September 2002, 14:48: Message edited by: Howard Bullock ]


Bonji
(Starting to like KiXtart)
2002-09-13 02:58 PM
Re: Scripting "User cannot change password" option

That works when it is set to 577. However if I wanted to go from 513 back to 577 (turn the bit back on) that would not work. I need an operation that can flip-flop and flop-flip a bit in a binary number. [Smile]

Thanks,
Ben

[ 13. September 2002, 14:59: Message edited by: Ben Dulaney ]


Bonji
(Starting to like KiXtart)
2002-09-13 03:04 PM
Re: Scripting "User cannot change password" option

After a little creative thinking I think this may do the trick...

code:
BREAK ON
$VAR1=577
? $VAR1
$VAR2=$VAR1 - ($VAR1 & &40)
IF $VAR1 = $VAR2 ;The bit was off
$VAR1 = $VAR1 + &40
ELSE ;The bit was on
$VAR1 = $VAR1 - &40
ENDIF
? $VAR1

Thanks for your input Howard. It put me on the road to binary bliss.

This could be setup as a UDF
code:
Function XOR($BinaryVal,$Bit)  ;&BinaryVal = Value of binary number // $Bit = Value of bit to swap
Dim $TstBit
$TstBit = $Bit
WHILE $TstBit MOD 2 = 0 AND $TstBit <> 1
$TstBit = $TstBit / 2
LOOP
IF $TstBit <> 1
? "The bit is not a power of 2!"
RETURN
ENDIF
Dim $TmpVar
$TmpVar=$BinaryVal - ($BinaryVal & $Bit)
IF $TmpVar = $BinaryVal ;The bit was off
$XOR = $BinaryVal + $Bit
ELSE ;The bit was on
$XOR = $BinaryVal - $Bit
ENDIF
EndFunction

Some method of checking $Bit to be sure it's a power of 2 would be good also.
*UPDATE* I went ahead and decided to be proactive and added a routine to check the $Bit value. It's inlcuded in the above UDF.

Thanks,
Ben

[ 13. September 2002, 15:49: Message edited by: Ben Dulaney ]


Chris S.
(MM club member)
2002-09-13 03:59 PM
Re: Scripting "User cannot change password" option

Ben, I'd encourage you to write that function up and post it to the UDF forum. It will be harder to find if you just leave it in here.

Bonji
(Starting to like KiXtart)
2002-09-13 04:36 PM
Re: Scripting "User cannot change password" option

The above UDF has been posted to the UDF forum. I also made a few small changes to it.
XOR()


Ruud van Velsen
(Hey THIS is FUN)
2002-10-18 11:46 AM
Re: Scripting "User cannot change password" option

I've put XOR on the todo-list... ;-)

Ruud


Sealeopard
(KiX Master)
2002-10-18 04:10 PM
Re: Scripting "User cannot change password" option

Thanks, Ruud!

Joost
(Lurker)
2005-01-11 12:20 PM
Re: Scripting "User cannot change password" option

Hi there,

New on the forum! How's everybody doing?

I'm looking for a way to set the "User cannot change password". I can't get the code in this topic to work, but I have something interesting:

You al know the UserAccountControl option. With ADSI edit (Can be found in the support kit) you can view the value(Integer) of the UserAccountControl. With only one value set (Password never expires) the value = 66080. Changing the "User cannot change password option in de AD User and Computers value, the UserAccountControl value doensn't change.

My conclusion is: You can't change the User Cannot Change Password option by setting the UserAccountControl. It must be ACL! Correct me if i'm totaly wrong please...

Anyone more succes?


Howard Bullock
(KiX Supporter)
2005-01-11 05:03 PM
Re: Scripting "User cannot change password" option

You can set this flag. See my web page for a description of the bits of the flag and what they mean.

http://home.comcast.net/~habullock/Win32Admin.htm#User

Did you read the rest of the this thread? There is posted code that works using the WinNT:// ADSI provider.

UserAccountControl would be used for LDAP and should also work. My guess is that you are not viewing the the same server with ADSIEDIT that you had changed.