- NSIS Discussion
- Check a user has a certain permission
Archive: Check a user has a certain permission
RobGrant
4th April 2006 12:17 UTC
Check a user has a certain permission
Hi guys
Wondered if there's a way to see whether a user has the Logon As Service NT privilege.
I want to grant this privilege if they don't have it (which is easy - if I perform the operation and the user already has the permission the plugin I use - UserMgr - just ignores it). The issue is that I only want to remove that permission in uninstall if they didn't not already have it.
So basically I want to record in the Registry to remove the user privilege only if I added it in the first place!
Any ideas?
Thanks
kichik
4th April 2006 15:20 UTC
You can call LsaEnumerateAccountRights or you can try using this privilege in the installer to see if it's granted already. I don't know of any plug-in that already does that.
RobGrant
26th April 2006 10:35 UTC
Hm, yes I see. That seems reasonably involved, is there anyone with more experience of calling Windows API functions that could have a look at this for me?
This is a useful url.
Thanks
CancerFace
26th April 2006 10:53 UTC
I am also interested in this. It involves allocation of an Lsa policy handle, something which I have been trying to do for a while now without any success, as stated in this thread.
CF
[Edit]
In general this is what you are looking for:
# get the policy handle and place it on R0
!define POLICY_LOOKUP_NAMES 0x00000800
!define strLSA_OBJECT_ATTRIBUTES '(i,i,i,i,i,i)i'
System::Call '*${strLSA_OBJECT_ATTRIBUTES}(0,n,n,0,n,n).s'
Pop $0
System::Call '*$0${strLSA_OBJECT_ATTRIBUTES}(0,n,n,0,n,n)'
; Fill the structure with zeros
System::Call 'kernel32::ZeroMemory(i r0, i 24)'
StrCpy $1 ${POLICY_LOOKUP_NAMES}
; Initiallize the policy handle as null
StrCpy $R5 ""
; Open the handle and place it in $R0
System::Call 'advapi32::LsaOpenPolicy(w n, i r0, i r1, *i .R0)'
# Get the user SID
; Define the UserName
StrCpy $2 "$UserName"
StrCpy $3 ${NSIS_MAX_STRLEN}
; Allocate memory to accept the SID
System::Call '*(&w${NSIS_MAX_STRLEN})i.R4'
; Call the function and place the SID to R1
System::Call 'Advapi32::LookupAccountNameW(w n, w r2, i R1, *i r3, w .R8, *i r3, *i .r4)'
# Enumerate the rights
; R2 is the pointer to an array of LSA_UNICODE_STRING structures
; R3 is a variable that receives the number of privileges in the UserRights array
System::Call 'advapi32::LsaEnumerateAccountRights(i R0, i R1, *i R2, *i R3)'
# close the policy handle
System::Call 'advapi32::LsaClose(R0)'
The above code will not work for the LsaOpenPolicy handle and I do not know why. The LsaEnumerateAccountRights function will give you an array of structures that have the format listed
here and the trick is to pay attention that the structures accept the sizes in bytes.
Hope this helps as a starting point ...
CancerFace
28th April 2006 23:09 UTC
If it were not for kitchik's input in this thread I wouldn't be able to write this :)
Here is what you are looking for:
!define POLICY_LOOKUP_NAMES 0x00000800
!define strLSA_OBJECT_ATTRIBUTES '(i,i,w,i,i,i)i'
!define strLSA_UNICODE_STRING '(i,i,w)i'
# get the policy handle and place it on R0
System::Call '*${strLSA_OBJECT_ATTRIBUTES}(24,n,n,0,n,n).s'
Pop $0
System::Call 'kernel32::ZeroMemory(i r0, i 24)'
StrCpy $1 ${POLICY_LOOKUP_NAMES}
System::Call 'advapi32::LsaOpenPolicy(w n, i r0, i r1, *i .R0) i.R6'
System::Call 'advapi32::LsaNtStatusToWinError(i R6) i.R7' ; R7 should be zero if the call was succesful
# Get the user SID and place it on R1
StrCpy $2 "$UserName" ; define this somewhere
StrCpy $3 ${NSIS_MAX_STRLEN}
System::Call '*(&w${NSIS_MAX_STRLEN})i.R1'
System::Call 'Advapi32::LookupAccountNameW(w n, w r2, i R1, *i r3, w .R8, *i r3, *i .r4)'
# Enumerate the rights
; R2 is the pointer to an array of LSA_UNICODE_STRING structures
; R3 is a variable that receives the number of privileges in the R2 array
System::Call 'advapi32::LsaEnumerateAccountRights(i R0, i R1, *i .R2, *i .R3)i.R6'
System::Call 'advapi32::LsaNtStatusToWinError(i R6) i.R7' ; R7 should be zero if the call was succesful
# close the policy handle
System::Call 'advapi32::LsaClose(i R0)'
$R3 in the above code will hold the number of enumerated privileges in the array found in $R2. You can pull the privilleges out by calling
System::Call '*$R2${strLSA_UNICODE_STRING}(.r1,.r2,.r3)'
for the first structure, then calculate its size, advance to the next buffer ($R2 + size of first structure) and call it again. Repeat $R3 times ... The names of the privilleges are listed in NTSecAPI.h
CF
RobGrant
5th May 2006 09:35 UTC
Hmm, thanks for that code, it looks ace, but I'm not sure how to carry on in the way you say. I'm not too au fait with how to finish this off to find the SE_SERVICE_LOGON_NAME privilege.
CancerFace
5th May 2006 10:34 UTC
Between the code that I posted on my previous reply and this MSDN page you should get an idea of what you're after. The definitions of the privileges are found in NTSecAPI.h of the windows SDK. Here are the basic ones straight out of that file:
#define SE_INTERACTIVE_LOGON_NAME TEXT("SeInteractiveLogonRight")
#define SE_NETWORK_LOGON_NAME TEXT("SeNetworkLogonRight")
#define SE_BATCH_LOGON_NAME TEXT("SeBatchLogonRight")
#define SE_SERVICE_LOGON_NAME TEXT("SeServiceLogonRight")
#define SE_DENY_INTERACTIVE_LOGON_NAME TEXT("SeDenyInteractiveLogonRight")
#define SE_DENY_NETWORK_LOGON_NAME TEXT("SeDenyNetworkLogonRight")
#define SE_DENY_BATCH_LOGON_NAME TEXT("SeDenyBatchLogonRight")
#define SE_DENY_SERVICE_LOGON_NAME TEXT("SeDenyServiceLogonRight")
#define SE_REMOTE_INTERACTIVE_LOGON_NAME TEXT("SeRemoteInteractiveLogonRight")
#define SE_DENY_REMOTE_INTERACTIVE_LOGON_NAME TEXT("SeDenyRemoteInteractiveLogonRight")
so if I am reading this right, when you enumerate the privileges, you should end up with an array of
LSA_UNICODE_STRING structures and each one contains one privilege, such as
SeServiceLogonRight (which is the SE_SERVICE_LOGON_NAME privilege that you're after).
In your case, one of the elements of the array will contain the
SeServiceLogonRight privilege so $3 on my last piece of code will be equal to
SeServiceLogonRight:
System::Call '*$R2${strLSA_UNICODE_STRING}(.r1,.r2,.r3)'
I'll try to write this up for you as soon as I get some time.
CF
RobGrant
5th May 2006 10:39 UTC
Yeah, I'm seeing what you're saying, although I don't think I can yet translate that into what I need for myself. I tried seeing what was in r1, r2 and r3, I think r3 was "1" when the privilege was enabled, and "0" otherwise...don't know if that helps.
Thanks for your help man, you're obviously good at this :)
CancerFace
8th May 2006 21:05 UTC
Thanks for your help man, you're obviously good at this
Hey I am glad you think so but unfortunately that's not true :)
I played around a bit with this today but I was wrong to assume that the LsaEnumerateAccountRights function gives an array of LSA_UNICODE_STRING structures that you could call as I suggested on my previous post (although this is what M$ suggests in its
MSDN page)
According to
this page the function gives an array but in order to unmarshal it you need to do this:
// QUESTION: Why can't we just use an array of LSAInter.LSA_UNICODE_STRING ???
LSA_UNICODE_STRING right = (LSA_UNICODE_STRING)Marshal.PtrToStructure(
(IntPtr)((int)rights + i * Marshal.SizeOf(typeof(LSA_UNICODE_STRING))),
typeof(LSA_UNICODE_STRING)
);
privileges[i] = Marshal.PtrToStringAuto(right.Buffer,(int)right.Length);
Apparently I was not the only one who thought that we could get the data out using an LSA_UNICODE_STRING array as I suggested :)
However, I failed to translate the above code to a working NSIS system call. This is where the
real pros have to jump into this discussion ... :tinfoil:
CF
RobGrant
10th May 2006 08:57 UTC
Ah mate, thanks for all your help. Hopefully someone even more experienced than you can help :)
kichik
10th May 2006 10:00 UTC
CancerFace, that page is talking about C#, not C. A few problems:
- ZeroMemory is a macro, not a function in kernel32.dll. There's no need for it anyway. On the contrary, the first member must contain the size and not zero.
- You should free the array returned by LsaEnumerateAccountRights using LsaFreeMemory.
- A SID structure is not really a 1024 characters wide string, thought allocating one using AllocateAndInitializeSid doesn't seem to change a thing.
- Your definition of UNICODE_STRING is incorrect. The first two members should be shorts, not ints. Use `&i2` instead of `i`.
RobGrant, r3 should be the name of the privilege, not a number. In my tests, the problem comes earlier where LsaEnumerateAccountRights fails and returns FILE_NOT_FOUND (check $R7 for 2). This error, according to MSDN, could mean anything.
If I keep the lookup call with the following SID generation code, I get meaningful results, 5 of them. This hints there's a problem with the user's SID, or that I don't have any privileges (?!). The following code generates the SID for the Users group.
System::Call "*(&i1 0, &i4 0, &i1 5) i.r0"
System::Call "advapi32::AllocateAndInitializeSid(i r0, i 2, i 32, i 545, i 0, i 0, i 0, \
i 0, i 0, i 0, *i .R1)"
System::Free $0
Maybe I inherit all of my privileges from the groups that contain my user.
CancerFace
10th May 2006 20:20 UTC
@kichik
Your definition of UNICODE_STRING is incorrect. The first two members should be shorts, not ints. Use `&i2` instead of `i`.
This is the real problem since I was not getting anything out on $3 when I was calling the structure from $R2.
If I create a new user and add some privilleges (logon as a service and/or anything else) then the LsaEnumerateAccountRights function returns the correct number of privileges and I am able to get the out in $3. If however I try to get the privileges for an account that has no extra privileges added, then I get the error that you mention.
CF
RobGrant
11th May 2006 15:59 UTC
Is it possible to pull all of that into one script? :)
Thanks
CancerFace
11th May 2006 17:51 UTC
I knew there was something I forgot to post here :)
!define POLICY_LOOKUP_NAMES 0x00000800
!define strLSA_OBJECT_ATTRIBUTES '(i,i,w,i,i,i)i'
!define strLSA_UNICODE_STRING '(&i2,&i2,w)i'
System::Call '*${strLSA_OBJECT_ATTRIBUTES}(24,n,n,0,n,n).s'
Pop $0
StrCpy $1 ${POLICY_LOOKUP_NAMES}
System::Call 'advapi32::LsaOpenPolicy(w n, i r0, i r1, *i .R0) i.R8'
# you can add some error checking here. If the call is succesful then $R8=0
StrCpy $2 "$UserName" ; define this somewhere
StrCpy $3 ${NSIS_MAX_STRLEN}
System::Call '*(&w${NSIS_MAX_STRLEN})i.R1'
System::Call 'Advapi32::LookupAccountNameW(w n, w r2, i R1, *i r3, w .R8, *i r3, *i .r4) i .R8'
# you can add some error checking here. If the call is succesful then $R8 <> 0
# Enumerate the rights
; R2 is the pointer to an array of LSA_UNICODE_STRING structures
; R3 is a variable that receives the number of privileges in the R2 array
System::Call 'advapi32::LsaEnumerateAccountRights(i R0, i R1, *i .R2, *i .R3)i.R8'
# you can add some error checking here. If the call is succesful then $R8 = 0
# Get the rights out to $4
StrCpy $9 0
loop:
StrCmp $9 $R3 stop
System::Call '*$R2${strLSA_UNICODE_STRING}(.r2,.r3,.r4)'
DetailPrint 'Got $4'
IntOp $R2 $R2 + 8
IntOp $9 $9 + 1
Goto loop
stop:
# Free the LSA memory
System::Call 'advapi32::LsaFreeMemory(i R2) i .R8'
# close the policy handle
System::Call 'advapi32::LsaClose(i R0) i .R8'
Note that if your user has no extra rights then the LsaEnumerateAccountRights gives a wierd error on $R8 which you can convert to a windows error using
System::Call 'advapi32::LsaNtStatusToWinError(i R8) i.R9'
and in that case $R9 is equal to 2 (check what kitchik was talking about a few posts up)
The rights will have the format that I posted before (for example
SeInteractiveLogonRight)
The above works for me :)
CF
RobGrant
18th May 2006 15:26 UTC
Perfect! Thank you so much guys, that's it exactly!
CancerFace
2nd June 2006 23:07 UTC
Added a WIKI page for this thread :D
CF