Archive: LsaOpenPolicy Question


LsaOpenPolicy Question
I have been trying to achieve something like this using NSIS without any success. In fact everything that I have tried involving LSA handles has failed because I cannot open one. I suspect that my problem is my definition of the access mask for the LsaOpenHandle function... Here is my non working code:

!define strLSA_UNICODE_STRING '(i,i,w)i'
!define strLSA_OBJECT_ATTRIBUTES '(i,i,w,i,i,i)i'
!define POLICY_CREATE_SECRET 0x00000020L
; open the Lsa handle
System::Call '*${strLSA_OBJECT_ATTRIBUTES}(0,n,n,0,n,n).s'
Pop $R1
System::Call '*$R1${strLSA_OBJECT_ATTRIBUTES}(0,n,n,0,n,n)'
StrCpy $4 ${POLICY_CREATE_SECRET}
System::Call 'advapi32::LsaOpenPolicy(w n, i R1, i r4, *i .R0) ?e'
Pop $R6
System::Call 'advapi32::LsaNtStatusToWinError(R6) ?e'
Pop $R7
;define the key type
StrLen $1 "DefaultPassword"
IntOp $2 $1 * 2
IntOp $3 $1 + 1
IntOp $3 $3 * 2
StrCpy $4 "DefaultPassword"
StrLen $5 $2
IntOp $5 $5 * 2
StrLen $6 $3
IntOp $6 $6 * 2
System::Call '*(&i$5 r2, &i$6 r3, &w$1 r4)i.s'
Pop $R1
System::Call '*$R1(&i$5 r2, &i$6 r3, &w$1 r4)'
;define the secret to be stored
StrLen $1 "$UserPassword"
IntOp $2 $1 * 2
IntOp $3 $1 + 1
IntOp $3 $3 * 2
StrCpy $4 "$UserPassword"
StrLen $5 $2
IntOp $5 $5 * 2
StrLen $6 $3
IntOp $6 $6 * 2
System::Call '*(&i$5 r2, &i$6 r3, &w$1 r4)i.s'
Pop $R2
System::Call '*$R2(&i$5 r2, &i$6 r3, &w$1 r4)'
;store the secret
System::Call 'advapi32::LsaStorePrivateData(i R0, i R1, i R2) ?e'
Pop $R6
System::Call 'advapi32::LsaNtStatusToWinError(R6) ?e'
Pop $R7
System::Call 'advapi32::LsaClose(R0)'
LsaOpenPolicy is defined here and LsaStorePrivateData can be found here

Most likely it is something obvious but I can't seem to find it, and I have tried many different things ... If anyone has an insight, I would really appreciate some input :hang:
Thanks,
CF

There's a stray 'L' in the end of POLICY_CREATE_SECRET. I can't see any other obvious problems from a brief review.


Damn, I missed that L ... But even without it the function does not succeed. I think I am getting a policy handle (at least I am getting an address in $R0) but when I call LsaStorePrivateData using this handle I do not get the expected registry entries in HKLM\SECURITY\Policy\Secrets\DefaultPassword.
The way the code is written in my first post I get error 997 from the LSA calls and this translates into 126 (ERROR_MOD_NOT_FOUND) ... I'll mess around a bit more with it.

Anyway, thanks for the input kitchik :)
CF


I am not sure if this is an issue, but I noticed that the LSA_OBJECT_ATTRIBUTES structure is defined in such a way that its first member is its size. In my example however I initialized the structure using '0' as its size. Is there an automated way to calculate its size and pass it to the system call (other than counting manually) or should I define it as '${NSIS_MAX_STRLEN}' or '0'?

I guess this is a more general question as there are many structures that require their length to be part of their definition. How do we handle those in NSIS?

Also my definition of the above structure is wrong (I think). Should be

!define strLSA_OBJECT_ATTRIBUTES '(i,i,i,i,i,i)i'
although this doesn't work either
CF

There is no way to calculate the size automatically and you must always set the exact size. A structure definition is eventually just a piece of text in a header file. It's no exported by DLLs like API.


I guess what confuses me is that this structure is filled with zeros and nulls before being passed to the Lsa function. Do I just count the bytes for the zeros? For example is this a valid definition?

!define strLSA_OBJECT_ATTRIBUTES '(i,i,i,i,i,i)i'
System::Call '*${strLSA_OBJECT_ATTRIBUTES}(24,n,n,0,n,n).s'
Pop $R1
System::Call '*$R1${strLSA_OBJECT_ATTRIBUTES}(24,n,n,0,n,n)'

(doesn't work by the way)

How do I calculate the size of a structure if I don't know in advance what the call will fill it up with?

CF

You must count the bytes and align it on a 4 byte boundary. The easiest method is compiling a simple C program which prints sizeof(YOUR_STRUCTURE).

Your definition is valid, but the last line is redundant. The first one already fills the first structure member with 24.

Once again you've used GetLastError where you shouldn't. The LSA API functions don't use SetLastError. MSDN says they return the error code:

System::Call '*${strLSA_OBJECT_ATTRIBUTES}(24,n,n,0,n,n).s'
Pop $R1
StrCpy $4 ${POLICY_CREATE_SECRET}
System::Call 'advapi32::LsaOpenPolicy(w n, i R1, i r4, *i .R0) i .R6'
System::Call 'advapi32::LsaNtStatusToWinError(i R6) i.R7'
Other problems in the code include a missing parameter type in LsaClose, string in structure instead of a pointer to the string and incorrect calculation of the LSA_UNICODE_STRING sizes.
# macros

!macro CreateLsaUnicodeString VAR STRING
StrLen ${VAR} "${STRING}"
IntOp ${VAR} ${VAR} * 2
System::Call '*(&i2 ${VAR}, &i2 ${VAR}, w `${STRING}`) i .s'
Pop ${VAR}
!macroend

# constants

!define strLSA_UNICODE_STRING '(i,i,w)i'
!define strLSA_OBJECT_ATTRIBUTES '(i,i,w,i,i,i)i'
!define POLICY_CREATE_SECRET 0x00000020

# open lsa handle

System::Call '*${strLSA_OBJECT_ATTRIBUTES}(24,n,n,0,n,n).s'
Pop $R1
StrCpy $4 ${POLICY_CREATE_SECRET}
System::Call 'advapi32::LsaOpenPolicy(w n, i R1, i r4, *i .R0) i .R6'
System::Call 'advapi32::LsaNtStatusToWinError(i R6) i .R6'
DetailPrint $R6

!insertmacro CreateLsaUnicodeString $R2 MyTestKey
!insertmacro CreateLsaUnicodeString $R3 MyTestPassword

# create private data

System::Call 'advapi32::LsaStorePrivateData(i R0, i R2, i R3) i .R6'
System::Call 'advapi32::LsaNtStatusToWinError(i R6) i .R6'
DetailPrint $R6

# delete private data

System::Call 'advapi32::LsaStorePrivateData(i R0, i R2, i n) i .R6'
System::Call 'advapi32::LsaNtStatusToWinError(i R6) i .R6'
DetailPrint $R6

# close handle

System::Call 'advapi32::LsaClose(i R0)'
BTW, why not use CryptProtectData as suggested in the MSDN page for LsaStorePrivateData?

Once again you've used GetLastError where you shouldn't.
True :(
...incorrect calculation of the LSA_UNICODE_STRING sizes
Here is where I am confused: The link that I gave on my first post from MSDN uses for the max length of the string in the LSA_Unicode strucutre, (length+1)*2:
lusSecretName.Buffer = L"DefaultPassword";
lusSecretName.Length = SecretNameLength * sizeof(WCHAR);
lusSecretName.MaximumLength = (SecretNameLength+1) * sizeof(WCHAR);
Now this obviously did not work.
In your macro you defined length=max_length (ie the same), and it works (?)

Still I should give a biiiiiig :) for getting your help ....

BTW, why not use CryptProtectData as suggested in the MSDN page for LsaStorePrivateData?
I thought about starting from the easy part and then going to the more complicated one... That's my next step :)

Again thanks for the help kichik ...

CF

Actually, my bad. The string sizes are fine. It's the calculation of the string size parameter size ($5 and $6) which is incorrect. It should always be 2 bytes. Unsigned short and short are both 2 bytes and their size doesn't change according to the size of the number they contain.


:)
Again thanks for the help!

CF