Skip to content
Permalink
Browse files
Updating Installer
- UI for providing a cookie at install time

- possibility generating a random cookie

- update existing conf files with fixes for CVE-2022-24706
  • Loading branch information
big-r81 committed Apr 26, 2022
1 parent 7694db8 commit df2d5d983b0b062cd1c688e4407e5fd972cb3c98
Showing 7 changed files with 250 additions and 98 deletions.
@@ -69,9 +69,10 @@ candle -arch x64 -ext WiXUtilExtension couchdb.wxs
candle -arch x64 "-dCouchDir=${CouchDB}" couchdbfiles.wxs
candle -arch x64 -ext WiXUtilExtension couchdb_wixui.wxs
candle -arch x64 -ext WiXUtilExtension adminprompt.wxs
candle -arch x64 -ext WiXUtilExtension cookieprompt.wxs
candle -arch x64 -ext WiXUtilExtension customexit.wxs
candle -arch x64 -ext WiXUtilExtension CouchInstallDirDlg.wxs
light -sw1076 -sice:ICE17 -ext WixUIExtension -ext WiXUtilExtension "-cultures:en-us;en;neutral" adminprompt.wixobj couchdb.wixobj couchdbfiles.wixobj couchdb_wixui.wixobj customexit.wixobj CouchInstallDirDlg.wixobj -out apache-couchdb-${CouchDBVersion}.msi
light -sw1076 -sice:ICE17 -ext WixUIExtension -ext WiXUtilExtension "-cultures:en-us;en;neutral" adminprompt.wixobj cookieprompt.wixobj couchdb.wixobj couchdbfiles.wixobj couchdb_wixui.wixobj customexit.wixobj CouchInstallDirDlg.wixobj -out apache-couchdb-${CouchDBVersion}.msi

Pop-Location

@@ -3,101 +3,194 @@
using System.IO;
using System.Security.Cryptography;
using System.Text;
using System.Text.RegularExpressions;
using Microsoft.Deployment.WindowsInstaller;

namespace CustomAction
{
public class CustomActions
{
[CustomAction] public static ActionResult WriteAdminIniFile(Session session)
{
try
{
if (!File.Exists(session.CustomActionData["ADMINCONFIGFILE"])) {
using (StreamWriter writer = new StreamWriter(session.CustomActionData["ADMINCONFIGFILE"]))
{
writer.WriteLine("; CouchDB Windows installer-generated admin user");
writer.WriteLine("[admins]");
writer.WriteLine($"{session.CustomActionData["ADMINUSER"]} = {session.CustomActionData["ADMINPASSWORD"]}");
}
public class CustomActions
{
[CustomAction]
public static ActionResult InitCookieValue(Session session)
{
try
{
byte[] buffer = new byte[16];
RandomNumberGenerator rng = RNGCryptoServiceProvider.Create();
rng.GetBytes(buffer);
session["COOKIEVALUE"] = BitConverter.ToString(buffer).Replace("-", String.Empty);
}
catch (Exception ex)
{
session.Log("ERROR in custom action InitCookieValue {0}",
ex.ToString());
return ActionResult.Failure;
}

return ActionResult.Success;
}
[CustomAction]
public static ActionResult WriteAdminIniFile(Session session)
{
try
{
if (!File.Exists(session.CustomActionData["ADMINCONFIGFILE"]))
{
using (StreamWriter writer = new StreamWriter(session.CustomActionData["ADMINCONFIGFILE"]))
{
writer.WriteLine("; CouchDB Windows installer-generated admin user");
writer.WriteLine("[admins]");
writer.WriteLine($"{session.CustomActionData["ADMINUSER"]} = {session.CustomActionData["ADMINPASSWORD"]}");
}
}
}
}
catch (Exception ex)
{
session.Log("ERROR in custom action WriteAdminIniFile {0}",
ex.ToString());
return ActionResult.Failure;
}

return ActionResult.Success;
}

[CustomAction] public static ActionResult MaybeCopyIniFiles(Session session)
{
try
{
string[] files = new string[2];
files[0] = "vm.args";
files[1] = "local.ini";

foreach (string file in files)
catch (Exception ex)
{
if (!File.Exists(Path.Combine(session.CustomActionData["ETCDIR"], file)))
{
File.Copy(
Path.Combine(session.CustomActionData["ETCDIR"], file + ".dist"),
Path.Combine(session.CustomActionData["ETCDIR"], file)
);
}
session.Log("ERROR in custom action WriteAdminIniFile {0}",
ex.ToString());
return ActionResult.Failure;
}
}
catch (Exception ex)
{
session.Log("ERROR in custom action MaybeCopyIniFiles {0}",
ex.ToString());
return ActionResult.Failure;
}
return ActionResult.Success;
}

[CustomAction] public static ActionResult MaybeRemoveUserConfig(Session session)
{
try
{
string[] files = new string[2];
files[0] = "vm.args";
files[1] = "local.ini";

foreach (string file in files)

return ActionResult.Success;
}

[CustomAction]
public static ActionResult WriteCookieToVmArgs(Session session)
{
try
{
string VMARGSFILE = Path.Combine(session.CustomActionData["ETCDIR"], "vm.args");
if (File.Exists(VMARGSFILE))
{
session.Log("Patching erlang cookie in existing vm.args file");
PatchErlangCookie(session, VMARGSFILE);
}

string VMFile = Path.Combine(session.CustomActionData["ETCDIR"], "vm.args.dist");
byte[] VMBuffer = File.ReadAllBytes(VMFile);
string VMText = Regex.Replace(Encoding.UTF8.GetString(VMBuffer), @"# -setcookie", $"-setcookie {session.CustomActionData["COOKIEVALUE"]}");
File.WriteAllBytes(VMFile, Encoding.UTF8.GetBytes(VMText));
}
catch (Exception ex)
{
session.Log("ERROR in custom action WriteCookieToVmArgs {0}",
ex.ToString());
return ActionResult.Failure;
}

return ActionResult.Success;
}

private static void PatchErlangCookie(Session session, string file)
{
//Patching erlang cookie
byte[] VMBuffer = File.ReadAllBytes(file);
string VMText = Regex.Replace(Encoding.UTF8.GetString(VMBuffer), @"-setcookie \S*", $"-setcookie {session.CustomActionData["COOKIEVALUE"]}");
File.WriteAllBytes(file, Encoding.UTF8.GetBytes(VMText));
}

private static void PatchErlangInterface(Session session, string file)
{
byte[] VMBuffer = File.ReadAllBytes(file);

//Patching erlang interface
string pattern = @"-kernel inet_dist_use_interface";
string input = Encoding.UTF8.GetString(VMBuffer);
Match m = Regex.Match(input, pattern, RegexOptions.IgnoreCase);
if (!m.Success)
{
session.Log("Pattern \"inet_dist_use_interface\" not found, appending fix.");

using (StreamWriter sw = File.AppendText(file))
{
sw.WriteLine();
sw.WriteLine("# Which interfaces should the node listen on?");
sw.WriteLine("-kernel inet_dist_use_interface {127,0,0,1}");
sw.Close();
}
}
else
{
session.Log("Pattern \"inet_dist_use_interface\" found, skipping.");
}
}

[CustomAction]
public static ActionResult MaybeCopyIniFiles(Session session)
{
try
{
string[] files = new string[2];
files[0] = "vm.args";
files[1] = "local.ini";

string VMARGSFILE = Path.Combine(session.CustomActionData["ETCDIR"], files[0]);

if (File.Exists(VMARGSFILE))
{
session.Log("Patching erlang interface in existing vm.args file");
PatchErlangInterface(session, VMARGSFILE);
}

foreach (string file in files)
{
if (!File.Exists(Path.Combine(session.CustomActionData["ETCDIR"], file)))
{
File.Copy(
Path.Combine(session.CustomActionData["ETCDIR"], file + ".dist"),
Path.Combine(session.CustomActionData["ETCDIR"], file)
);
}
}
}
catch (Exception ex)
{
session.Log("ERROR in custom action MaybeCopyIniFiles {0}",
ex.ToString());
return ActionResult.Failure;
}
return ActionResult.Success;
}

[CustomAction]
public static ActionResult MaybeRemoveUserConfig(Session session)
{
try
{
string[] files = new string[2];
files[0] = "vm.args";
files[1] = "local.ini";

foreach (string file in files)
{
if (File.Exists(Path.Combine(session.CustomActionData["ETCDIR"], file)) &&
File.Exists(Path.Combine(session.CustomActionData["ETCDIR"], file + ".dist")))
{
if (GetChecksum(Path.Combine(session.CustomActionData["ETCDIR"], file)) ==
GetChecksum(Path.Combine(session.CustomActionData["ETCDIR"], file + ".dist")))
{
File.Delete(Path.Combine(session.CustomActionData["ETCDIR"], file));
}
}
}
}
catch (Exception ex)
{
session.Log("ERROR in custom action MaybeRemoveUserConfig {0}",
ex.ToString());
return ActionResult.Failure;
}
return ActionResult.Success;
}

private static string GetChecksum(string file)
{
using (FileStream stream = File.OpenRead(file))
{
if (File.Exists(Path.Combine(session.CustomActionData["ETCDIR"], file)) &&
File.Exists(Path.Combine(session.CustomActionData["ETCDIR"], file + ".dist")))
{
if (GetChecksum(Path.Combine(session.CustomActionData["ETCDIR"], file)) ==
GetChecksum(Path.Combine(session.CustomActionData["ETCDIR"], file + ".dist")))
{
File.Delete(Path.Combine(session.CustomActionData["ETCDIR"], file));
}
}
SHA256Managed sha = new SHA256Managed();
byte[] checksum = sha.ComputeHash(stream);
return BitConverter.ToString(checksum).Replace("-", String.Empty);
}
}
catch (Exception ex)
{
session.Log("ERROR in custom action MaybeRemoveUserConfig {0}",
ex.ToString());
return ActionResult.Failure;
}
return ActionResult.Success;
}

private static string GetChecksum(string file)
{
using (FileStream stream = File.OpenRead(file))
{
SHA256Managed sha = new SHA256Managed();
byte[] checksum = sha.ComputeHash(stream);
return BitConverter.ToString(checksum).Replace("-", String.Empty);
}
}
}
}
}
}
@@ -7,6 +7,6 @@
-->
<configuration>
<startup useLegacyV2RuntimeActivationPolicy="true">
<supportedRuntime version="v2.0.50727" />
<supportedRuntime version="v4.0" />
</startup>
</configuration>
@@ -10,7 +10,7 @@
<AppDesignerFolder>Properties</AppDesignerFolder>
<RootNamespace>CustomAction</RootNamespace>
<AssemblyName>CouchIniAction</AssemblyName>
<TargetFrameworkVersion>v3.5</TargetFrameworkVersion>
<TargetFrameworkVersion>v4.8</TargetFrameworkVersion>
<FileAlignment>512</FileAlignment>
<WixCATargetsPath Condition=" '$(WixCATargetsPath)' == '' ">C:\Program Files (x86)\MSBuild\Microsoft\WiX\v3.x\wix.ca.targets</WixCATargetsPath>
</PropertyGroup>
@@ -0,0 +1,32 @@
<?xml version="1.0" encoding="UTF-8"?>

<Wix xmlns="http://schemas.microsoft.com/wix/2006/wi">
<Fragment>
<Property Id="COOKIEVALUE" Hidden="yes" />
<UI>
<Dialog Id="CookiePromptDlg" Width="370" Height="270" Title="Set Cookie value">
<Control Id="Title" Type="Text" X="20" Y="20" Width="220" Height="30" Transparent="yes" NoPrefix="yes" Text="{\WixUI_Font_Emphasized}Set Cookie value" />
<Control Id="Description" Type="Text" X="20" Y="50" Width="220" Height="40" Transparent="yes" NoPrefix="yes" Text="{\WixUI_Font_Emphasized}For security reasons, the cookie value that CouchDB instances use to communicate between each other needs to be set even in standalone mode." />
<Control Id="Label" Type="Text" Width="322" Height="10" X="20" Y="110" Text="Cookie value:" />
<Control Id="Textbox" Type="Edit" Width="200" Height="15" X="20" Y="123" Property="COOKIEVALUE" />
<Control Id="Validate" Type="PushButton" Width="100" Height="15" X="140" Y="180" Text="Validate Cookie" />
<Control Id="Random" Type="PushButton" Width="100" Height="15" X="20" Y="180" Text="Random Cookie">
<Publish Event="DoAction" Value="InitCookieValue">1</Publish>
<Publish Property="COOKIEVALUE" Value="[COOKIEVALUE]">1</Publish>
</Control>
<Control Id="BottomLine" Type="Line" X="0" Y="234" Width="370" Height="0" />
<Control Id="Back" Type="PushButton" X="180" Y="243" Width="56" Height="17" Text="!(loc.WixUIBack)" />
<Control Id="Next" Type="PushButton" X="236" Y="243" Width="56" Height="17" Default="yes" Text="!(loc.WixUINext)">
<Condition Action="disable"><![CDATA[(COOKIEVALUE = "") OR (COOKIEVALUE >< " ")]]></Condition>
<Condition Action="enable"><![CDATA[(COOKIEVALUE <> "") AND NOT (COOKIEVALUE >< " ")]]></Condition>

<Publish Property="COOKIEVALUE" Value="[COOKIEVALUE]">1</Publish>
</Control>
<Control Id="Cancel" Type="PushButton" X="304" Y="243" Width="56" Height="17" Cancel="yes" Text="!(loc.WixUICancel)">
<Publish Event="SpawnDialog" Value="CancelDlg">1</Publish>
</Control>
</Dialog>
</UI>
</Fragment>
</Wix>

0 comments on commit df2d5d9

Please sign in to comment.