- NSIS Discussion
- Create folder share using system plugin
Archive: Create folder share using system plugin
CancerFace
6th April 2007 13:12 UTC
Create folder share using system plugin
I am trying to create a network share with NSIS using the system plugin.
I do not want to call net share from a command prompt since I want to give access only to a specific user for the share.
I am trying to implement this idea in nsis:
...
!define NO_MULTIPLE_TRUSTEE 0
!define TRUSTEE_IS_SID 0
!define TRUSTEE_IS_USER 1
!define GENERIC_READ 8
!define GENERIC_EXECUTE 2
!define SET_ACCESS 2
!define STYPE_DISKTREE 0
!define SECURITY_DESCRIPTOR_REVISION 0
!define NO_INHERITANCE 0
!define ACCESS_READ 0x1
!define ACCESS_EXEC 0x8
!define strEXPLICIT_ACCESS '(i,i,i,i)i'
!define strTRUSTEE '(i,i,i,i,t)i'
!define strSHARE_INFO_502 '(w,i,w,i,i,i,w,w,i,i)i'
...
System::Call '*${strTRUSTEE}(0,${NO_MULTIPLE_TRUSTEE},${TRUSTEE_IS_SID},${TRUSTEE_IS_USER},$R8).R7'
System::Call '*${strEXPLICIT_ACCESS}(${GENERIC_READ}|${GENERIC_EXECUTE},${SET_ACCESS},${NO_INHERITANCE},R7).R6'
System::Call 'advapi32::SetEntriesInAclA(i 1,i R6,i n,*i .R5)i.r1'
System::Call 'advapi32::InitializeSecurityDescriptor(*i.R4,i ${SECURITY_DESCRIPTOR_REVISION})i.r1'
System::Call 'advapi32::SetSecurityDescriptorDacl(i R4,i 1,i R5,i 0)i.r1'
System::Call '*${strSHARE_INFO_502}("Some Name",${STYPE_DISKTREE},"Comment",${ACCESS_READ}|${ACCESS_EXEC},-1,0,"F:\NewShare","",0,R4).R0'
System::Call 'netapi32::NetShareAdd(,i 502,i R0,*i .R1)i.r1'
The code breaks at the
SetEntriesInAclA call with error 87 (invalid parameter). I tried to define the EXPLICIT_ACCESS structure as:
System::Call '*${strEXPLICIT_ACCESS}(${GENERIC_READ}|${GENERIC_EXECUTE},${SET_ACCESS},${NO_INHERITANCE},0,${NO_MULTIPLE_TRUSTEE},${TRUSTEE_IS_SID},${TRUSTEE_IS_USER},$R8).R6'
including the TRUSTEE information as well, where $R8 is a valid user SID which I get using the
LookupAccountName and
ConvertSidToStringSid calls of advapi32.dll, but I am still getting error 87.
Any insights?
CF
:(
kichik
8th April 2007 17:31 UTC
strEXPLICIT_ACCESS is not defined properly. In your definition it contains four integers, but there's no mention of the TRUSTEE structure.
CancerFace
8th April 2007 20:00 UTC
Hi kichik,
thanks for the input!
I realize that the second example I used actually is wrong (blame it on copy/paste) as I redefined the strEXPLICIT_ACCESS in order to contain also the TRUSTEE structure.
Here is the correct 2nd part of the post:
...
!define strEXPLICIT_ACCESS '(i,i,i,i,i,i,i,t)i
System::Call '*${strEXPLICIT_ACCESS}(${GENERIC_READ}|${GENERIC_EXECUTE},${SET_ACCESS},${NO_INHERITANCE},0,${NO_MULTIPLE_TRUSTEE},${TRUSTEE_IS_SID},${TRUSTEE_IS_USER},$R8).R6'
System::Call 'advapi32::SetEntriesInAclA(i 1,i R6,i n,*i .R5)i.r1'
I am still getting error 87 and I am not sure why ...
CF
kichik
8th April 2007 20:06 UTC
Can you attach a complete example I can test?
CancerFace
8th April 2007 20:26 UTC
Hi kichik,
I am attaching the nsi file that I am working on.
Thanks for taking the time to look at this :)
CF
kichik
8th April 2007 21:00 UTC
When passing TRUSTEE_IS_SID, you must pass a real SID, not a string representation of the SID or a string representation of a pointer to it. Use `i R9` instead of `t R8`.
BTW, it crashes right away if the provided user name doesn't exist. A little error handling won't hurt :)
Also, $R5 should be freed using LocalFree as documented by MSDN and not by System::Free.
CancerFace
8th April 2007 21:21 UTC
Hmmm ...
I tried it with the SID buffer (R9) but it still gives me error 87 ...
This is a test build hence the lack of error checking ... A good idea nonetheless :)
Thanks for the help as always!
CF
kichik
8th April 2007 21:27 UTC
Did you change `t` to `i` in the definition of strEXPLICIT_ACCESS?
CancerFace
8th April 2007 21:33 UTC
:)
Yes (I get error 87 with 'i R9' or 't R8')
kichik
8th April 2007 21:40 UTC
Weird, it worked fine for me as attached... Make sure the SID came out right. Though I don't get 87 even with an invalid user (but the SID pointer still points somewhere).
CancerFace
8th April 2007 21:47 UTC
Hmmm...
Both the SID buffer (R9) and the string SID (R8) look OK and I still get error 87 ($1 on the message box) ...
:weird:
Even worse, if I pass an invalid username then it works ($1 is 0 and $R5 gets an address)
?
:weird:
Anders
9th April 2007 02:09 UTC
87=The parameter is incorrect
and
0x87=An attempt was made to use a JOIN or SUBST command on a drive that has already been substituted.
not sure if its in hex or not, but i would guess the parameter is incorrect
CancerFace
24th April 2007 13:48 UTC
Finally got it!
I was freeing the SID buffer ($R4) inside the LookupAccountName macro and as such the SetEntriesInAcl call would fail with error 87 (where did you find the 0x87 error Anders? It is not hex, it is a standard error code after all).
So for anyone who is interested here is a macro that will create a share giving read permission to a specified user. I am including more definitions in case someone wants to modify this in any way:
!include "LogicLib.nsh"
# Definitions and user flags
!define STYPE_DISKTREE 0
!define ACCESS_READ 0x01
!define ACCESS_WRITE 0x02
!define ACCESS_CREATE 0x04
!define ACCESS_EXEC 0x08
!define ACCESS_DELETE 0x10
!define ACCESS_ATRIB 0x20
!define ACCESS_PERM 0x40
!define ACCESS_ALL 0x7F
# Permissions
!define GENERIC_READ 0x80000000
!define GENERIC_WRITE 0x40000000
!define GENERIC_EXECUTE 0x20000000
!define GENERIC_ALL 0x10000000
!define NO_INHERITANCE 0x0
!define SECURITY_DESCRIPTOR_REVISION 1
# ACCESS_MODE values
!define NOT_USED_ACCESS 0
!define GRANT_ACCESS 1
!define SET_ACCESS 2
!define DENY_ACCESS 3
!define REVOKE_ACCESS 4
!define SET_AUDIT_SUCCESS 5
!define SET_AUDIT_FAILURE 6
# MULTIPLE_TRUSTEE_OPERATION values
!define NO_MULTIPLE_TRUSTEE 0
!define TRUSTEE_IS_IMPERSONATE 1
# TRUSTEE_FORM values
!define TRUSTEE_IS_SID 0
!define TRUSTEE_IS_NAME 1
!define TRUSTEE_BAD_FORM 2
!define TRUSTEE_IS_OBJECTS_AND_SID 3
!define TRUSTEE_IS_OBJECTS_AND_NAME 4
# TRUSTEE_TYPE values
!define TRUSTEE_IS_UNKNOWN 0
!define TRUSTEE_IS_USER 1
!define TRUSTEE_IS_GROUP 2
!define TRUSTEE_IS_DOMAIN 3
!define TRUSTEE_IS_ALIAS 4
!define TRUSTEE_IS_WELL_KNOWN_GROUP 5
!define TRUSTEE_IS_DELETED 6
!define TRUSTEE_IS_INVALID 7
!define TRUSTEE_IS_COMPUTER 8
# Structure Definitions
!define strSHARE_INFO_2 '(w,i,w,i,i,i,w,w)i'
!define strSHARE_INFO_502 '(w,i,w,i,i,i,w,w,i,i)i'
!define strEXPLICIT_ACCESS '(i,i,i,i,i,i,i,i)i'
!macro CreateNewShare USERNAME SHARENAME SHARE_TYPE SHARE_COMMENT SHARE_PERMISSIONS ACL_ACCESS MAX_USERS CURRENT_USES SHARE_PATH SHARE_PASS
# Get the user's SID from the username
System::Call /NOUNLOAD '*(&w${NSIS_MAX_STRLEN})i.R9'
System::Call /NOUNLOAD 'advapi32::LookupAccountNameA(,t "${USERNAME}",i R9,*i ${NSIS_MAX_STRLEN},w .R8,*i ${NSIS_MAX_STRLEN},*i .r4)i.r5'
System::Call /NOUNLOAD 'advapi32::ConvertSidToStringSid(i R9,*t .R8)i.r5'
${If} $R8 == ''
MessageBox MB_OK|MB_ICONSTOP|MB_TOPMOST 'User "${USERNAME}" does not exist!$\nAborting...'
System::Free $R9
Quit
${EndIf}
# Create an EXPLICIT_ACCESS structure and place it on $R6
System::Call /NOUNLOAD '*${strEXPLICIT_ACCESS}(${ACL_ACCESS},${SET_ACCESS},${NO_INHERITANCE},0, ${NO_MULTIPLE_TRUSTEE},${TRUSTEE_IS_SID},${TRUSTEE_IS_USER},$R9).R6'
System::Call /NOUNLOAD 'advapi32::SetEntriesInAclA(i 1,i R6,,*i .R5)i.r1'
${If} $1 <> 0
System::Free $R9
System::Free $R6
Quit
${EndIf}
# Create an empty security descriptor and place it in R4.
System::Alloc ${NSIS_MAX_STRLEN}
Pop $R4
System::Call /NOUNLOAD 'advapi32::InitializeSecurityDescriptor(i R4,i ${SECURITY_DESCRIPTOR_REVISION})i.r1'
${If} $1 == 0
System::Free $R9
System::Free $R6
System::Call 'kernel32::LocalFree(i R5)i.r0'
Quit
${EndIf}
# Add the ACL to the security descriptor
System::Call /NOUNLOAD 'advapi32::SetSecurityDescriptorDacl(i R4,i 1,i R5,i 0)i.r1'
${If} $1 == 0
System::Free $R9
System::Free $R6
System::Call 'kernel32::LocalFree(i R5)i.r0'
Quit
${EndIf}
# Generate the structure that holds the share info and place it on $R0
System::Call /NOUNLOAD '*${strSHARE_INFO_502}("${SHARENAME}",${SHARE_TYPE},"${SHARE_COMMENT}",${SHARE_PERMISSIONS},${MAX_USERS},${CURRENT_USES},"${SHARE_PATH}","${SHARE_PASS}",0,R4).R0'
System::Call /NOUNLOAD 'netapi32::NetShareAdd(, i 502, i R0, *i .R1) i .r1'
${If} $1 <> 0
MessageBox MB_OK|MB_ICONSTOP|MB_TOPMOST 'There was an error creating the share!'
${EndIf}
# Cleanup
System::Free $R9
System::Free $R6
System::Call 'kernel32::LocalFree(i R5)i.r0'
System::Free $R0
!macroend
In order to create a share and give it read access to the user jdoe call the macro like this:
!insertmacro CreateNewShare "jdoe" "Name for the share" ${STYPE_DISKTREE} "share description" ${ACCESS_ALL} ${GENERIC_READ}|${GENERIC_EXECUTE} -1 0 "C:\<some_folder_to_share>" ""
In order to create a share with full access for the user jdoe call the macro like this:
!insertmacro CreateNewShare "jdoe" "Name for the share" ${STYPE_DISKTREE} "share description" ${ACCESS_ALL} ${GENERIC_ALL} -1 0 "C:\<some_folder_to_share>" ""
and so on.
Thanks for the help guys!
CF
CancerFace
24th April 2007 15:36 UTC
Sometimes we may want to create a share with full access for everyone, without specifying extra permissions.
This can be done with the above code by using the SHARE_INFO_2 structure. Here it is in a macro:
!define strSHARE_INFO_2 '(w,i,w,i,i,i,w,w)i'
!macro CreateNewFullShare SHARENAME SHARE_TYPE SHARE_COMMENT SHARE_PERMISSIONS MAX_USERS CURRENT_USES SHARE_PATH SHARE_PASS
System::Call /NOUNLOAD '*${strSHARE_INFO_2}("${SHARENAME}",${SHARE_TYPE},"${SHARE_COMMENT}",${SHARE_PERMISSIONS},${MAX_USERS},${CURRENT_USES},"${SHARE_PATH}","${SHARE_PASS}")i.R0'
System::Call /NOUNLOAD 'netapi32::NetShareAdd(, i 2, i R0, *i .R1) i .r1'
${If} $1 <> 0
MessageBox MB_OK|MB_ICONSTOP|MB_TOPMOST 'There was an error creating the share!'
${EndIf}
System::Free $R0
!macroend
To use it call it from within your code:
!insertmacro CreateNewFullShare "Share Name" ${STYPE_DISKTREE} "Share Description" 0 -1 0 "X:\<folder_to_share>" ""
Also added a
wiki page for all the above :D
CF
fantrs91
2nd April 2008 09:54 UTC
Hi,
Your macros are very useful :-)
Does any one have another one which allows to unshare a directory ? I need this for my uninstaller script.
Thanks
CancerFace
3rd April 2008 01:46 UTC
You can use NetShareDel to remove a share from a computer, assuming that you have admin/power user rights. Note however that this will also close any active connections to the share ...
Here is a very simple macro version of the above without any error checking:
!macro RemoveShare SHARENAME
System::Call /NOUNLOAD 'netapi32::NetShareDel(, *t ${SHARENAME}, i 0) i .r1'
${If} $1 <> 0
MessageBox MB_OK|MB_ICONSTOP|MB_TOPMOST 'There was an error removing the share!'
${EndIf}
!macroend
To use it call it from within your code:
!insertmacro RemoveShare "ShareName"
Let me know if this works (untested)
CF
fantrs91
14th April 2008 13:58 UTC
Hi,
thanks for your reply !
I just tried your RemoveShare macro, and it does not work :'-(
Let me explain:
In my installation procedure I use the following:
!insertmacro CreateNewFullShare "MY_SHARE" ${STYPE_DISKTREE} "blablabla" 0 -1 0 "$INSTDIR" ""
And in my uninstaller:
!insertmacro RemoveShare "MY_SHARE"
But while uninstallation I get the message from the macro: 'There was an error removing the share!'
I will try to investigate.
CancerFace
15th April 2008 22:21 UTC
My bad, it should be a unicode string ...
This one works:
!macro RemoveShare SHARENAME
System::Call /NOUNLOAD 'netapi32::NetShareDel(, w "${SHARENAME}",i 0) i .r1'
${If} $1 <> 0
MessageBox MB_OK|MB_ICONSTOP|MB_TOPMOST 'There was an error removing the share!"'
${EndIf}
!macroend
CF
fantrs91
25th April 2008 16:43 UTC
This one works flawlessly.
Thanks a lot!
deinename
21st April 2011 11:47 UTC
I don't know why but none of above scripts worked for me.
I managed to share a folder using standard windows command line like this:
nsExec::ExecToStack 'net share "name"="dir_to_share" /remark:"share description"'
pop $NET_SHARE_RETURN_CODE
${If} $NET_SHARE_RETURN_CODE = 0
MessageBox MB_OK|MB_ICONSTOP|MB_TOPMOST 'Share created'
${EndIf}
if return code is "2" than a share with this name already exists