Full Unified support including Linux and Windows (#155 / #154)

* Initial work

* Fix typo

* Fix typo

* Fix stupid issue

* Add comments and fix minor issues

* Add extra information

* Add Linux script for generating keys

* Add circleci

* Add comments

* Add extra option

* Add missing permissions and empty script for now

* Fix line endings

* Add missing mount point

* Simplify patch

* Fix scripts

* Reduce complexity

* Fix circleci

* Remove useless line

* Move to src folder and improve image creation
This commit is contained in:
Michiel Hazelhof
2023-01-16 21:13:43 +01:00
committed by GitHub
parent b819fe0c7d
commit d4abc9e5b7
20 changed files with 784 additions and 732 deletions

View File

@@ -1,11 +1,14 @@
ARG BITWARDEN_TAG
FROM ${BITWARDEN_TAG}
FROM mcr.microsoft.com/dotnet/sdk:6.0 as build
WORKDIR /bitBetter
COPY bin/Debug/netcoreapp6.0/publish/* /bitBetter/
COPY ./.keys/cert.cert /newLicensing.cer
COPY . /bitBetter
COPY cert.cert /app/
RUN set -e; set -x; \
dotnet /bitBetter/bitBetter.dll && \
mv /app/Core.dll /app/Core.orig.dll && \
mv /app/modified.dll /app/Core.dll && \
rm -rf /bitBetter && rm -rf /newLicensing.cer
RUN dotnet restore
RUN dotnet publish -c Release -o /app --no-restore
FROM mcr.microsoft.com/dotnet/sdk:6.0
WORKDIR /app
COPY --from=build /app .
ENTRYPOINT [ "/app/bitBetter" ]

View File

@@ -0,0 +1,4 @@
FROM bitwarden/self-host:beta
COPY ./temp/Api/Core.dll /app/Api/Core.dll
COPY ./temp/Identity/Core.dll /app/Identity/Core.dll

View File

@@ -1,93 +1,75 @@
using System;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Security.Cryptography.X509Certificates;
using Mono.Cecil;
using Mono.Cecil.Cil;
using Mono.Cecil.Rocks;
using dnlib.DotNet;
using dnlib.DotNet.Emit;
using dnlib.DotNet.Writer;
using dnlib.IO;
namespace bitwardenSelfLicensor
namespace bitBetter;
internal class Program
{
class Program
private static Int32 Main(String[] args)
{
static int Main(string[] args)
const String certFile = "/app/cert.cert";
String[] files = Directory.GetFiles("/app/mount", "Core.dll", SearchOption.AllDirectories);
foreach (String file in files)
{
string cerFile;
string corePath;
Console.WriteLine(file);
ModuleDefMD moduleDefMd = ModuleDefMD.Load(file);
Byte[] cert = File.ReadAllBytes(certFile);
if(args.Length >= 2) {
cerFile = args[0];
corePath = args[1];
} else if (args.Length == 1) {
cerFile = args[0];
corePath = "/app/Core.dll";
}
else {
cerFile = "/newLicensing.cer";
corePath = "/app/Core.dll";
}
EmbeddedResource embeddedResourceToRemove = moduleDefMd.Resources
.OfType<EmbeddedResource>()
.First(r => r.Name.Equals("Bit.Core.licensing.cer"));
Console.WriteLine(embeddedResourceToRemove.Name);
var module = ModuleDefinition.ReadModule(new MemoryStream(File.ReadAllBytes(corePath)));
var cert = File.ReadAllBytes(cerFile);
EmbeddedResource embeddedResourceToAdd = new("Bit.Core.licensing.cer", cert) {Attributes = embeddedResourceToRemove.Attributes };
moduleDefMd.Resources.Add(embeddedResourceToAdd);
moduleDefMd.Resources.Remove(embeddedResourceToRemove);
var x = module.Resources.OfType<EmbeddedResource>()
.Where(r => r.Name.Equals("Bit.Core.licensing.cer"))
.First();
Console.WriteLine(x.Name);
var e = new EmbeddedResource("Bit.Core.licensing.cer", x.Attributes, cert);
module.Resources.Add(e);
module.Resources.Remove(x);
var services = module.Types.Where(t => t.Namespace == "Bit.Core.Services");
DataReader reader = embeddedResourceToRemove.CreateReader();
X509Certificate2 existingCert = new(reader.ReadRemainingBytes());
var type = services.First(t => t.Name == "LicensingService");
var licensingType = type.Resolve();
var existingCert = new X509Certificate2(x.GetResourceData());
Console.WriteLine($"Existing Cert Thumbprint: {existingCert.Thumbprint}");
X509Certificate2 certificate = new X509Certificate2(cert);
X509Certificate2 certificate = new(cert);
Console.WriteLine($"New Cert Thumbprint: {certificate.Thumbprint}");
var ctor = licensingType.GetConstructors().Single();
var rewriter = ctor.Body.GetILProcessor();
var instToReplace =
ctor.Body.Instructions.Where(i => i.OpCode == OpCodes.Ldstr
&& string.Equals((string)i.Operand, existingCert.Thumbprint, StringComparison.InvariantCultureIgnoreCase))
.FirstOrDefault();
if(instToReplace != null) {
rewriter.Replace(instToReplace, Instruction.Create(OpCodes.Ldstr, certificate.Thumbprint));
IEnumerable<TypeDef> services = moduleDefMd.Types.Where(t => t.Namespace == "Bit.Core.Services");
TypeDef type = services.First(t => t.Name == "LicensingService");
MethodDef constructor = type.FindConstructors().First();
Instruction instructionToPatch =
constructor.Body.Instructions
.FirstOrDefault(i => i.OpCode == OpCodes.Ldstr
&& String.Equals((String)i.Operand, existingCert.Thumbprint, StringComparison.InvariantCultureIgnoreCase));
if (instructionToPatch != null)
{
instructionToPatch.Operand = certificate.Thumbprint;
}
else {
Console.WriteLine("Cant find inst");
else
{
Console.WriteLine("Can't find constructor to patch");
}
// foreach (var inst in ctor.Body.Instructions)
// {
// Console.Write(inst.OpCode.Name + " " + inst.Operand?.GetType() + " = ");
// if(inst.OpCode.FlowControl == FlowControl.Call) {
// Console.WriteLine(inst.Operand);
// }
// else if(inst.OpCode == OpCodes.Ldstr) {
// Console.WriteLine(inst.Operand);
// }
// else {Console.WriteLine();}
// }
ModuleWriterOptions moduleWriterOptions = new(moduleDefMd);
moduleWriterOptions.MetadataOptions.Flags |= MetadataFlags.KeepOldMaxStack;
moduleWriterOptions.MetadataOptions.Flags |= MetadataFlags.PreserveAll;
moduleWriterOptions.MetadataOptions.Flags |= MetadataFlags.PreserveRids;
module.Write("modified.dll");
return 0;
moduleDefMd.Write(file + ".new");
moduleDefMd.Dispose();
File.Delete(file);
File.Move(file + ".new", file);
}
return 0;
}
}
}

View File

@@ -5,8 +5,8 @@
<TargetFramework>netcoreapp6.0</TargetFramework>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Mono.Cecil" Version="0.11.2" />
<ItemGroup>
<PackageReference Include="dnlib" Version="3.6.0" />
</ItemGroup>
</Project>

View File

@@ -1,7 +0,0 @@
#!/bin/bash
set -e
set -x
dotnet restore
dotnet publish