View Single Post
Old 15th April 2006, 15:18   #14
Senior Member
Join Date: Apr 2006
Posts: 289
Send a message via ICQ to CancerFace
After a lot of messing around with those netapi32 functions I managed to get them to work.
Needless to say that without kichik's comments I would probably be still trying ...

So here is the full story if anyone wants to use those in the future.

Some background:
I wanted to be able to create a user and specify parameters that were not used in the UserMgr plugin like making the password to not expire or prevent the user from changing it. In order to achieve this I had to use netapi32.dll and its functions (You can get all the info needed for the netapi32 functions through this MSDN page).

Step 0 - define constants and variables
Here are the constants that are used by the NetAPI functions that I am calling, along with some variables that you may or may not want to use:

;Possible user flags
!define UF_SCRIPT 0x000001
!define UF_ACCOUNTDISABLE 0x000002
!define UF_HOMEDIR_REQUIRED 0x000008
!define UF_LOCKOUT 0x000010
!define UF_PASSWD_NOTREQD 0x000020
!define UF_PASSWD_CANT_CHANGE 0x000040
!define UF_NORMAL_ACCOUNT 0x000200
!define UF_SERVER_TRUST_ACCOUNT 0x002000
!define UF_DONT_EXPIRE_PASSWD 0x010000
!define UF_MNS_LOGON_ACCOUNT 0x020000
!define UF_SMARTCARD_REQUIRED 0x040000
!define UF_NOT_DELEGATED 0x100000
!define UF_USE_DES_KEY_ONLY 0x200000
!define UF_DONT_REQUIRE_PREAUTH 0x400000
!define UF_PASSWORD_EXPIRED 0x800000

; User Types
!define USER_TYPE_1 1
!define USER_TYPE_1011 1011

!define USER_INFO_23 23

; Local Group levels

; User level of privilege
!define USER_PRIV_GUEST 0x000000
!define USER_PRIV_USER 0x000001
!define USER_PRIV_ADMIN 0x000002

; Structure Definitions
!define strUSER_INFO_1 '(w,w,i,i,w,w,i,w)i'
!define strUSER_INFO_1011 '(w)i'

Var "TargetServerName"
Var "UserName"
Var "UserPassword"
Var "UserFirstName"
Var "UserLastName"
Var "UserComment"
Var "MakeAdmin"
Var "UnlimitedPass"

Step 1 - Get the computer name
This step may be redundant since you can use a null name and then the local machine will be the target system. Here is a function that will get the computer name and will place it on the $TargetServerName variable (you need to include LogicLib.nsh for this):
Function GetComputerNameAPI
System::Call 'kernel32.dll::GetComputerNameExW(i 4, w .r0, *i ${NSIS_MAX_STRLEN} r1) i.r2'
${If} $2 = 1
StrCpy $TargetServerName "\\$0"
System::Call "kernel32.dll::GetComputerNameW(t .r0, *i ${NSIS_MAX_STRLEN} r1) i.r2"
${If} $2 = 1
StrCpy $TargetServerName "\\$0"
MessageBox MB_OK|MB_ICONSTOP 'Could not determine the computer name!$\nAborting...'
System::Free $0

Step 2 - Create the user
In order to create the user we need to call the NetUserAdd function which requires to generate at least a USER_INFO_1 structure which will hold the user's information (Note that we could use the USER_INFO_3 structure instead which contains a lot more information for the user). Here is the relevant code:
Function CreateUserAPI
StrCpy $1 "$TargetServerName"
StrCpy $2 ${USER_TYPE_1}
StrCpy $R1 "$UserName"
StrCpy $R2 "$UserPassword"
StrCpy $R4 "$UserComment"
Pop $R5
${If} $UnlimitedPass = 1
System::Int64Op $R5 + ${UF_DONT_EXPIRE_PASSWD}
Pop $R5
System::Call '*${strUSER_INFO_1}(R1,R2,n,R3,n,R4,R5,n) i.s'
Pop $R6
System::Call '*$R6${strUSER_INFO_1}(R1,R2,n,R3,n,R4,R5,n)'
System::Call 'netapi32.dll::NetUserAdd(w r1, i r2, i R6, *i.r3) i.r4'
System::Free $R6

Note that we can add more user flags (see Step 0) by adding them incrementally to $R5

Step 3 - Set the user's first and last name
To do this I will be using the NetUserSetInfo function. There are quite a few things we can change with this function, but I will only alter the first and last name of the user (structure USER_TYPE_1011). Here is the function:
Function SetUserInfoAPI
StrCpy $1 "$TargetServerName"
StrCpy $2 "$UserName"
StrCpy $3 ${USER_TYPE_1011}
StrCpy $R1 "$UserFirstName $UserLastName"
System::Call '*${strUSER_INFO_1011}(R1)i.s'
Pop $R2
System::Call '*$R2${strUSER_INFO_1011}(R1)'
System::Call 'netapi32.dll::NetUserSetInfo(w r1, w r2, i r3, i R2, *i.r4) i.r5'
System::Free $R2

Step 4 - Get the user's SID
We need this in order to use it in Step 5 (see bellow)
I will use the NetUserGetInfo function to get the SID of the user that was just created in Step 3. The data will be fed to a USER_INFO_23 structure. The SID will be kept at a buffer pointed to by R9 in the next function:
Function GetUserInfoAPI
StrCpy $1 "$TargetServerName"
StrCpy $2 "$UserName"
StrCpy $3 ${USER_INFO_23}
System::Call 'netapi32::NetUserGetInfo(w r1, w r2, i r3, *i .R8) i.r4'
System::Call '*$R8(w .R1, w .R2, w .R3, i .R4, i .R9)'
System::Free $R8

Step 5 - Add the user to a group
This will be done using the NetLocalGroupAddMembers function using the SID from the previous step. I am using the $MakeAdmin variable (you have to set it to 1 in order to add the user to the admin group) to determine the group I will be adding the user to.
Function AddUserToGroupAPI
StrCpy $1 "$TargetServerName"
${If} $MakeAdmin = 1
StrCpy $2 "Administrators"
StrCpy $2 "Users"
System::Int64Op 1 * 0x000001
Pop $4
System::Call 'netapi32.dll::NetLocalGroupAddMembers(w r1, w r2, i r3, *i R9, i r4) i.r6'

That's it!
You can call the functions from inside your code or make a continuous block of all the commands and get the user created.
Also you can add some error checking for each function. In general if the NetAPIs succeed they return 0 to the relevant variable.

I have to say this was a an excellent chance for me to dive into the system plugin and I really enjoyed the process

Again a big 'thank you' to kichik for the directions ...
CancerFace is offline   Reply With Quote