![]() |
#1 |
Senior Member
|
![]()
I have created a small tool that provides a GUI for gathering information in order to create a user account in Windows. I figured it would be a good idea to make direct API calls and get to understand how the system plugin works ... After going over the source of the UserManager plugin as well as several MSDN pages I am having trouble getting this to work.
In the first part of my code I am using InstallOptionsEx in order to create the GUI then I gather all the info into variables (working part, not shown here). The second part however that is handling the user creation is not working: code: The above gives me error 997 (ERROR_IO_PENDING) and $4 gives me error 87 (INVALID_PARAMETER) If I make the call like this: code: then I get error 997 (ERROR_IO_PENDING) and $4 gives me error 2202 (ERROR_BAD_USERNAME) Note that all the variables used have been declared. Also I am using some error checking to make sure that the username and password have correct lengths. Any help will be much appreciated ![]() CF More info about the USER_INFO_1 structure can be found here The NetUserAdd function is described here Last edited by CancerFace; 12th April 2006 at 22:05. |
![]() |
![]() |
![]() |
#2 |
Senior Member
|
Right ...
Although I am allocating space for the structure I am not putting any data into it, silly me. However I am still getting the same error (997 - ERROR_IO_PENDING) when I use this code: code: As for the string values in the structure, they have to be unicode and I am not sure if setting them as 'w Rx' is the way to go ... Any ideas? Thanks in advance CF Last edited by CancerFace; 13th April 2006 at 14:29. |
![]() |
![]() |
![]() |
#3 |
M.I.A.
[NSIS Dev, Mod] Join Date: Oct 2001
Location: Israel
Posts: 11,343
|
$R6 already contains the pointer to your buffer. You shouldn't pass it as a pointer or you'll get a pointer to the pointer. Use just `i` instead of `*i`. On the same issue, in the fourth parameter, you should use `*i` instead of `i`.
NSIS FAQ | NSIS Home Page | Donate $ "I hear and I forget. I see and I remember. I do and I understand." -- Confucius |
![]() |
![]() |
![]() |
#4 |
Senior Member
|
Thanks for the info kichik
![]() Is it sufficient to pass the LPWSTR values to the structure as 'w Rx' since they should be unicode? or should I create a buffer for each parameter, then feed the value in and then point to that buffer from within the structure?code: code: Thanks in advance CF |
![]() |
![]() |
![]() |
#5 |
M.I.A.
[NSIS Dev, Mod] Join Date: Oct 2001
Location: Israel
Posts: 11,343
|
'w' should be good enough.
NSIS FAQ | NSIS Home Page | Donate $ "I hear and I forget. I see and I remember. I do and I understand." -- Confucius |
![]() |
![]() |
![]() |
#6 |
Senior Member
|
Hmmm ... Still error 997.
Here is the part of the code that I am running: code: ? ![]() CF |
![]() |
![]() |
![]() |
#7 |
M.I.A.
[NSIS Dev, Mod] Join Date: Oct 2001
Location: Israel
Posts: 11,343
|
According to MSDN, NetUserAdd doesn't set the last error. The documentation doesn't mention GetLastError. 997 means nothing. The real error is returned by the function itself and set to $4 in your script. 87, which is the real error code, is ERROR_INVALID_PARAMETER.
The real problems is with the `n` in the structure definition. `n` is not a valid type, it's only a valid value. Replace it with `i` and it'll work. NSIS FAQ | NSIS Home Page | Donate $ "I hear and I forget. I see and I remember. I do and I understand." -- Confucius |
![]() |
![]() |
![]() |
#8 |
Senior Member
|
![]() Thanks a lot kichik! Please add me to the (long) list of people that you made happy in this forum ![]() CF |
![]() |
![]() |
![]() |
#9 |
Senior Member
|
Hmmm ...
Adding the user and changing some data works but I am stuck when I try to add the user to a group. Here is the part that works: code: I tried using the NetGroupAddUser function I get error 2220 on $4 (Group Name Cannot be found) and I suspect that this function is only for domain groups (although you would expect a fancier name like AddUserToDomainGroup or something ![]() code: Then I tried to use the NetLocalGroupAddMembers function. If I understand correctly, I need to allocate a buffer that will contain the LOCALGROUP_MEMBERS_INFO_0 structure, which in turn should contain the SID structure of the user-account ... To get the SID I would have to call the NetUserGetInfo function and allocate a buffer for the User_Info_23 structure which in turn contains a pointer to the SID structure (and this should have been pre-allocated) ![]() Something like this: code: However R1-6 are filled with gibberish after running the above code... ![]() Any ideas? CF |
![]() |
![]() |
![]() |
#10 |
M.I.A.
[NSIS Dev, Mod] Join Date: Oct 2001
Location: Israel
Posts: 11,343
|
Your call to NetApiBufferAllocate is invalid. The ampersand prefix is only available on structures and should it be an integer anyway and not a wide string. The allocation of the USER_INFO_23 structure is redundant. NetUserGetInfo allocates it for you. For this, its last parameter must be a pointer and not just a simple integer pointing to your allocated structure.
BTW, according to MSDN, USER_INFO_23 is only available on XP and above. You might want to use LOCALGROUP_MEMBERS_INFO_3 instead.code: NSIS FAQ | NSIS Home Page | Donate $ "I hear and I forget. I see and I remember. I do and I understand." -- Confucius |
![]() |
![]() |
![]() |
#11 | |
Senior Member
|
Makes sense
![]() However I am still a bit confused. The reason I am using the USER_INFO_23 structure for the NetUserGetInfo is that this one points to an SID. The other option would be the USER_INFO_20 structure which points to an RID. In both cases your code gives me in R5 the buffer where the SID/RID is stored so I have to call to get the actual SID/RID out to $R7. In the case of the RIDs, I get the 4 last digits of a user's SID, and this number is different for different users.code: In the case of the SIDs, if I make the call as an 'i' the output is always the same for any user (1281). If I make the call as an 'l' then the output is different for different users. Which brings me to the real question (which explains in part why I was trying to allocate buffers with NetApiBufferAllocate as w) what is the format of the SID that I should expect? Does it matter, since I will feed it to the NetLocalGroupAddMembers function or should I first transform it somehow to a readable quantity? Isn't LOCAL_GROUPMEMBERS_INFO_3 supposed to be available only to XP etc clients? The only reason I was not going to use it is that it contains the information in the format Quote:
Thanks for all the help by the way, without your comments I would have spent way much more time in this, probably to no good end ![]() CF |
|
![]() |
![]() |
![]() |
#12 |
M.I.A.
[NSIS Dev, Mod] Join Date: Oct 2001
Location: Israel
Posts: 11,343
|
$R5 is a pointer to the SID, it's what you need. You don't want to dereference it. LOCALGROUP_MEMBERS_INFO_0 has only one member which is a pointer to a SID. You don't need to mess with the internals of the SID. All you need is the pointer.
LOCAL_GROUPMEMBERS_INFO_3 is available on NT and 2000 as well as XP. It does seem to require a domain name, so it's probably not good enough. You find another way to get the user's SID. You can get the RID using USER_INFO_20 and assemble the SID on your own with GetSidIdentifierAuthority, AllocateAndInitializeSid, GetSidSubAuthority and GetSidSubAuthorityCount. Count the sub-authorities using GetSidSubAuthorityCount, get all of them using GetSidSubAuthority, get the authority identifier using GetSidIdentifierAuthority and allocate your new SID with the those authorities and the RID using AllocateAndInitializeSid. NSIS FAQ | NSIS Home Page | Donate $ "I hear and I forget. I see and I remember. I do and I understand." -- Confucius |
![]() |
![]() |
![]() |
#13 |
Senior Member
|
I just saw your reply while I was going to write that I finally figured it out
![]() I'll get the code shaped up a bit and I'll post it here. kichik I appreciate all your help ![]() Thank you very very very much ![]() CF |
![]() |
![]() |
![]() |
#14 |
Senior Member
|
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: code: 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): code: 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: code: 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: code: 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: code: 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. code: 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 ... CF |
![]() |
![]() |
![]() |
#15 |
Moderator
Join Date: Jun 2002
Location: ${NSISDIR}
Posts: 5,456
|
This is very handy info, would be great if you added a page in the wiki.
One question; is it ok to use the strings "Administrators" and "Users" on non english systems? IntOp $PostCount $PostCount + 1 |
![]() |
![]() |
![]() |
#16 |
Senior Member
|
I'll gladly post a page in the wiki
![]() I am pretty sure that the strings "Administrators" and "Users" apply only to english systems. Where can I find their definitions for other languages? CF [Edit] I guess we could use NetLocalGroupEnum to get all the groups, place them in an array then use NetGroupGetInfo for each member of the array to get the name of the group along with the RID. Then based on the RID decide which is the Admin and which is the guest group ... Last edited by CancerFace; 15th April 2006 at 15:16. |
![]() |
![]() |
![]() |
#17 |
Senior Member
|
Right ... Stuck again.
I am trying to enumerate the groups on the local machine using the NetLocalGroupEnum function: code: According to MSDN the handle should be 0 when we first call the function and then remain unchanged (?). Calling a second time the function gives the same group name every time (since the handle doesn't change?)... And both $R2 and $R3 are the same, as if the function goes directly to the last member of the allocated array. ![]() CF [Edit] Or, if R1 points to an array of LOCAL_GROUP_INFO_0 structures, then would it be logical to get the first value out, count its length, advance to the next memory buffer ($R1 + lenght of value in $R1) then get out the second value etc? Last edited by CancerFace; 15th April 2006 at 18:50. |
![]() |
![]() |
![]() |
#18 |
M.I.A.
[NSIS Dev, Mod] Join Date: Oct 2001
Location: Israel
Posts: 11,343
|
It's better to use a well-known SID. There may be a lot of groups with the administrator in them.
NSIS FAQ | NSIS Home Page | Donate $ "I hear and I forget. I see and I remember. I do and I understand." -- Confucius |
![]() |
![]() |
![]() |
#19 |
Senior Member
|
True.
Which brings me back to my previous question: How do I supply the SID? What format do I have to use? Basically I am looking at WinBuiltinUsersSid, WinBuiltinGuestsSid, WinBuiltinAdministratorsSid etc but I am not sure how to tell the netapi32 which one it is ... CF [Edit] What puzzles me is that NetLocalGroupAddMembers accepts the group name as LPCWSTR so I am not sure how to pass an SID instead or how to get a groupname if I know its SID Last edited by CancerFace; 15th April 2006 at 21:00. |
![]() |
![]() |
![]() |
#20 | |
Senior Member
|
Quote:
I can't believe that pre-XP software devellopers had to go through ALL that just to figure out a user's SID! Anyway here are some more netapi32 functions: Use the NetUserDel API function to delete a user. More info here code: Use the NetLocalGroupDelMembers API function to delete a user from a group. More info here code: Use the NetLocalGroupAdd function to add a new group. More info here code: Use the NetLocalGroupDel API function to delete a group. More info here code: Use the NetLocalGroupGetInfo API function to get info for a group. More infohere code: Use the NetLocalGroupSetInfo function to set the info for a group. More info here code: This IS addictive ![]() CF Last edited by CancerFace; 16th April 2006 at 22:53. |
|
![]() |
![]() |
![]() |
#21 |
Senior Member
|
Right ...
I am trying to add the user that I created to a group without providing the actual group name, in order to avoid the localization of the name (assuming for example that a German version of windows will not have 'Administrators' but something else), I thought about enumerating the groups on the local machine and then try to get their SID. Once I have the SID I can compare it to the well-known ones and decide which group to use. In this way I can also avoid the problem of unicode characters as the group name will always be stored in a memory buffer and I can feed it directly to a netapi32 function. However I hit (yet) another obstacle trying to get the SIDs: The above code is not working. The Lsa handle is allocated but the LsaLookupNames call returns error 126 (NTSTATUS) and 317 (Windows) both of which don't make sense.code: I suspect that the problem is in the way I define the LSA_UNICODE_STRING array but playing around with that didn't get me anywhere ... Any ideas? CF |
![]() |
![]() |
![]() |
#22 |
M.I.A.
[NSIS Dev, Mod] Join Date: Oct 2001
Location: Israel
Posts: 11,343
|
You can use LookupAccountSid to get the name from a SID. Here's an example. Don't forget to allocate a buffer, call the wide-string (Unicode) version and pass that buffer instead of putting the result in $0.
code: NSIS FAQ | NSIS Home Page | Donate $ "I hear and I forget. I see and I remember. I do and I understand." -- Confucius |
![]() |
![]() |
![]() |
#23 |
Senior Member
|
Much faster/smarter than my approach, as usual
![]() Thanks kichik! I am still interested however in understanding why the previous code does not work ... Any clues? CF |
![]() |
![]() |
![]() |
#24 |
M.I.A.
[NSIS Dev, Mod] Join Date: Oct 2001
Location: Israel
Posts: 11,343
|
I can't tell right now why the previous code wasn't working. Although I can give you a hint and say that 126 is not the error you're looking for. First off, it's not an NTSTATUS, it's a simple Windows error which means a module can't be found. The System plug-in always causes it while looking for the right module to load. You probably got 317, which means a message can't be found, because you tried converting the invalid NTSTATUS code.
NSIS FAQ | NSIS Home Page | Donate $ "I hear and I forget. I see and I remember. I do and I understand." -- Confucius |
![]() |
![]() |
![]() |
#25 |
Senior Member
|
Allright, I'll play around a bit more and try to figure it out Thanks again kichik
![]() CF |
![]() |
![]() |
![]() |
#26 |
Senior Member
|
Here are some more netapi32 functions:
Group enumeration using NetLocalGroup Enum code: User enumeration using NetUser Enum code: Following the previous suggestions by kichik here is a better way to add users to groups: instead of adding them to 'GroupName' which may not be localized, we can first grab the group's SID using a well-known SID and then add the user to that group. So 'Step 5' of the above instructions would become: Step 5 - Add user to a group using NetLocalGroupAddMembers code: Note that since we are manipulating users on the local machine, $1 in all the above can be set to null (n) and then the localhost will be the target. ![]() CF |
![]() |
![]() |
![]() |
#28 |
Senior Member
Join Date: Oct 2006
Posts: 106
|
I'm trying to use the EnumerateUsers macro, but keep getting the following error:
"Could not place all the user accounts into an array!" On closer inspection the macro does succeed in enumerating users, but NSISArray::SizeOf returns an empty string. What do you think could be wrong? This is how I use it (this is the full installer script): code: |
![]() |
![]() |
![]() |
#29 |
Senior Member
Join Date: Oct 2006
Posts: 106
|
It looks like the problem is caused by a couple of bugs in the EnumerateUsers code.
First the array is created without specifying any sizes. This is wrong. I propose the following instead: code: Secondly, when getting the size of the array it's important to call 'pop' three times to get the number of elements in the array. Like this: code: |
![]() |
![]() |
![]() |
#30 |
Senior Member
|
Hi TobbeSweden,
The API calls and the array code on the Wiki are quite old (2+ years now!). AfroUK has updated his plugin and shuffled things around, so the options that I was using probably don't work on the latest version. I haven't really changed anything since then so feel free to alter the code on the Wiki as you see fit, as long as it works ![]() CF |
![]() |
![]() |
![]() |
#31 |
Senior Member
Join Date: Oct 2006
Posts: 106
|
Heh. I don't know if my changes work with the latest version of NSIS or the plugins either... But it works with the versions I had installed when I wrote that
![]() |
![]() |
![]() |
![]() |
|
Thread Tools | Search this Thread |
Display Modes | |
|
|