0

We need our MSI to store the password in the registry, so users can use our installer to change connection strings, etc.

We found MsiExt for its cryptography DLL and are trying to configure the custom actions.

Our users will need to be able to install both through the UI dialogs and silently -- so we need to able to execute in both sequences.

Here is a sample of our Product.wxs

    <Property Id="DB_PASSWORD" Secure="yes"/>
    <Property Id="P.DB_PASSWORD">
      <RegistrySearch Id="S.DB_PASSWORD" Root="HKLM" Key="SOFTWARE\$(var.Manufacturer)\$(var.ProductName)" Name="DB_PASSWORD" Type="raw" Win64="$(var.Win64)"/>
    </Property>

    <Component Id="c.RegistryEntries" Guid="XXXXX-XXXXX-XXXXX-XXXXX" Directory="INSTALLDIR">
      <RegistryKey Root="HKLM" Key="SOFTWARE\$(var.Manufacturer)\$(var.ProductName)" Action="createAndRemoveOnUninstall">
        <RegistryValue Id="R.DB_PASSWORD" Name="DB_PASSWORD" Value="[ENCRYPTED_DBPASSWORD]" Type="string" />
      </RegistryKey>
    </Component>

    <!--For encrypting the database password on the way to the registry-->
    <Binary Id="Cryptography" SourceFile="..\..\lib\msiext-1.4\CustomActions\Cryptography.dll"/>

    <!--This property will receive the encrypted DB_PASSWORD that the user enters and will be encrypted-->
    <Property Id="CRYPTPROTECT_DATA" Hidden="yes" />

    <Property Id="CRYPTPROTECT_FLAGS" Value="CRYPTPROTECT_LOCAL_MACHINE|CRYPTPROTECT_UI_FORBIDDEN" />
    <CustomAction Id="EncryptPassword" BinaryKey="Cryptography" DllEntry="CryptProtectDataHex" Execute="immediate" />
    <CustomAction Id="SetDBUSERsPASSWORDForEncryption" Property="CRYPTPROTECT_DATA" Value="[DB_PASSWORD]" />
    <Property Id="ENCRYPTED_DBPASSWORD" Hidden="yes" />
    <SetProperty Id="ENCRYPTED_DBPASSWORD" Value="[CRYPTPROTECT_RESULT]" Sequence="execute" After="SetDBUSERsPASSWORDForEncryption" />

    <!--This is for decrypting the registry value-->
    <Property Id="CRYPTUNPROTECT_DATA" Hidden="yes" />
    <Property Id="CRYPTUNPROTECT_FLAGS" Value="CRYPTPROTECT_LOCAL_MACHINE|CRYPTPROTECT_UI_FORBIDDEN" />
    <SetProperty Id="CRYPTUNPROTECT_DATA" Value="[P.DB_PASSWORD]" Before="DecryptPassword" >NOT(DB_PASSWORD)</SetProperty>
    <CustomAction Id="DecryptPassword" BinaryKey="Cryptography" DllEntry="CryptUnprotectDataHex" Execute="firstSequence" />
    <CustomAction Id="SetDBUSERsDecryptedPASSWORD" Property="DB_PASSWORD" Value="[CRYPTUNPROTECT_RESULT]"/>

    <CustomAction Id="LaunchApplication" BinaryKey="WixCA" DllEntry="WixShellExec" Impersonate="yes" />
    <InstallUISequence>
      <Custom Action="DecryptPassword" After="CostFinalize"><![CDATA[NOT(DB_PASSWORD)]]></Custom>
      <Custom Action="SetDBUSERsDecryptedPASSWORD" After="DecryptPassword" ><![CDATA[NOT(DB_PASSWORD)]]></Custom>
    </InstallUISequence>
    <InstallExecuteSequence>
      <Custom Action="SchedXmlConfig" After="InstallFiles"><![CDATA[(NOT REMOVE~="All")]]></Custom>
      <Custom Action="SetDBUSERsPASSWORDForEncryption" Before="InstallInitialize" />
      <Custom Action="EncryptPassword" After="SetDBUSERsPASSWORDForEncryption" />

      <Custom Action="DecryptPassword" Before="InstallFiles"><![CDATA[CRYPTUNPROTECT_RESULT]]></Custom>
      <Custom Action="SetDBUSERsDecryptedPASSWORD" After="DecryptPassword" ><![CDATA[CRYPTUNPROTECT_RESULT]]></Custom>

    </InstallExecuteSequence>

The install log shows that the DecryptPassword is firing during the UISequence. The decryption custom action fires, then the DecryptPassword works.

Action ended 17:52:43: CostFinalize. Return value 1.
MSI (c) (CC:A8) [17:52:43:936]: Doing action: SetCRYPTUNPROTECT_DATA
Action 17:52:43: SetCRYPTUNPROTECT_DATA. 
Action start 17:52:43: SetCRYPTUNPROTECT_DATA.
MSI (c) (CC:A8) [17:52:43:937]: PROPERTY CHANGE: Adding CRYPTUNPROTECT_DATA property. Its value is '**********'.
Action ended 17:52:43: SetCRYPTUNPROTECT_DATA. Return value 1.
MSI (c) (CC:A8) [17:52:43:937]: Doing action: DecryptPassword
Action 17:52:43: DecryptPassword. 
Action start 17:52:43: DecryptPassword.
MSI (c) (CC:7C) [17:52:49:129]: Invoking remote custom action. DLL: C:\Users\kujotx\AppData\Local\Temp\MSI9904.tmp, Entrypoint: CryptUnprotectDataHex
MSI (c) (CC:54) [17:52:49:130]: Cloaking enabled.
MSI (c) (CC:54) [17:52:49:130]: Attempting to enable all disabled privileges before calling Install on Server
MSI (c) (CC:54) [17:52:49:130]: Connected to service for CA interface.
CryptUnprotectDataHex: MSI Extensions 1.4.1114.0
MSI (c) (CC!94) [17:52:49:236]: PROPERTY CHANGE: Adding CRYPTUNPROTECT_RESULT property. Its value is 'password'.
Action ended 17:52:49: DecryptPassword. Return value 1.
MSI (c) (CC:A8) [17:52:49:238]: Doing action: SetDBUSERsDecryptedPASSWORD
Action 17:52:49: SetDBUSERsDecryptedPASSWORD. 
Action start 17:52:49: SetDBUSERsDecryptedPASSWORD.
MSI (c) (CC:A8) [17:52:49:239]: PROPERTY CHANGE: Adding DB_PASSWORD property. Its value is '**********'.
Action ended 17:52:49: SetDBUSERsDecryptedPASSWORD. Return value 1.

My problem is that SetCRYPTUNPROTECT_DATA is not executing during ExecuteSequence, so DecryptPassword fails:

Action ended 17:53:47: AppSearch. Return value 1.
MSI (s) (28:1C) [17:53:47:206]: Doing action: DecryptPassword
Action 17:53:47: DecryptPassword. 
Action start 17:53:47: DecryptPassword.
MSI (s) (28:98) [17:53:47:217]: Invoking remote custom action. DLL: C:\Windows\Installer\MSI9020.tmp, Entrypoint: CryptUnprotectDataHex
CryptUnprotectDataHex: MSI Extensions 1.4.1114.0
CryptUnprotectDataHex: [CryptUnprotectDataHex] std::exception: 0x80070057 - Error in CryptUnprotectData: The parameter is incorrect.
MSI (s) (28!DC) [17:53:47:237]: PROPERTY CHANGE: Adding CA_ERROR property. Its value is '0x80070057 - Error in CryptUnprotectData: The parameter is incorrect.'.
CustomAction DecryptPassword returned actual error code 1603 (note this may not be 100% accurate if translation happened inside sandbox)
Action ended 17:53:47: DecryptPassword. Return value 3.
Action ended 17:53:47: INSTALL. Return value 3.

Can you point out how to schedule our custom actions to get this to decrypt properly?

1 Answer 1

1

Your problem is likely due to one of the following:

  1. The <SetProperty> element that sets the CRYPTUNPROTECT_DATA property is being conditionally run based on the value of DB_PASSWORD not being set, but we can't see from the logs that this property is indeed not set. The property action won't run if this evaluates to false.

  2. The default setting for the Sequence attribute on the <SetProperty> element is not working correctly. Try setting the Sequence attribute to first, which will schedule it to be run in the first of either InstallUISequence or InstallExecuteSequence (will be first in silent mode). If that doesn't work, try setting it to both explicitly.

  3. Try changing the Before attribute on the custom action for DecryptPassword in the InstallExecuteSequence to After="InstallInitialize", which is much earlier on in the install sequence than InstallFiles. This will get you behavior that is similar to running with the UI Sequence, which computes and modifies your properties and runs your actions prior to anything actually being installed.

See this page for suggested sequencing (and see relative ordering of events)

Sign up to request clarification or add additional context in comments.

5 Comments

Per 1., I removed the condition entirely. For 2., I assign Sequence to first. It failed again, so I changed it to both. For 3., I did set it to after InstallInitialize and it still failed. Again, the UI Sequence shows that SetCRYPTUNPROTECT_DATA has a PROPERTY CHANGE event. But, the Execute Sequence shows SetCRYPTUNPROTECT_DATA but NO PROPERTY CHANGE event inside.
What does a tool like Orca show as the sequence of script events when you generate the MSI? It will allow you to look at the MSI itself and see how it translates the definition to a sequence of script actions. I think SetProperty is custom action type 51... You can look at it to see if the even is even scheduled to be run at that time. Based on your syntax, there should be no reason why it doesn't run, unless there's something inside one of your actions that is causing it not to work properly. Also, one more thing to try would be to add execute="immediate" on your CA's.
Orca shows the SetProperty immediately precedes DecryptPassword that consumes it in all sequences -- good deal there. The log is showing that the SetProperty fires, but reports no property change. The property name is P.DB_PASSWORD and is marked secure and I can see a log entry with the encrypted value after Execute Sequence begins. I also see the DecryptPassword is running twice in ExecuteSequence -- so I need a condition to remove it from running during RemoveExistingProducts. Making those changes to see how close I am. (thanks, Ryan!)
Good to hear it's sort of working. You can add the Not Installed AND Not UPGRADINGPRODUCTCODE condition to fire only when it's installed fresh, not during a remove/upgrade if that's what you're after. Also, if the value of the property you're using to set your other property doesn't result in a change, it won't show up. I'm gathering from what you said in your comment that you checked that. Good luck...
Marking as answer. Right now, I my CAs are without any conditions to prove it can decrypt and answer my original question.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.