diff options
Diffstat (limited to 'embeddable-dll-service/csharp')
-rw-r--r-- | embeddable-dll-service/csharp/Keypair.cs | 65 | ||||
-rw-r--r-- | embeddable-dll-service/csharp/Program.cs | 56 | ||||
-rw-r--r-- | embeddable-dll-service/csharp/Ringlogger.cs | 46 | ||||
-rw-r--r-- | embeddable-dll-service/csharp/Service.cs | 131 | ||||
-rw-r--r-- | embeddable-dll-service/csharp/Win32.cs | 245 |
5 files changed, 543 insertions, 0 deletions
diff --git a/embeddable-dll-service/csharp/Keypair.cs b/embeddable-dll-service/csharp/Keypair.cs new file mode 100644 index 00000000..98a00a30 --- /dev/null +++ b/embeddable-dll-service/csharp/Keypair.cs @@ -0,0 +1,65 @@ +/* SPDX-License-Identifier: MIT + * + * Copyright (C) 2019 WireGuard LLC. All Rights Reserved. + */ + +using System; +using System.ComponentModel; +using System.Runtime.InteropServices; + +namespace Tunnel +{ + public class Keypair + { + public readonly string Public; + public readonly string Private; + + private Keypair(string pub, string priv) + { + Public = pub; + Private = priv; + } + + public static Keypair Generate() + { + var algoHandle = new IntPtr(); + var statusCode = Win32.BCryptOpenAlgorithmProvider(ref algoHandle, Win32.BCRYPT_ECDH_ALGORITHM, null, 0); + if (statusCode > 0) + throw new Win32Exception((int)statusCode); + + try + { + var curveType = Win32.BCRYPT_ECC_CURVE_25519 + Char.MinValue; + statusCode = Win32.BCryptSetProperty(algoHandle, Win32.BCRYPT_ECC_CURVE_NAME, curveType, curveType.Length * sizeof(char), 0); + if (statusCode > 0) + throw new Win32Exception((int)statusCode); + var key = new IntPtr(); + statusCode = Win32.BCryptGenerateKeyPair(algoHandle, ref key, 255, 0); + if (statusCode > 0) + throw new Win32Exception((int)statusCode); + try + { + statusCode = Win32.BCryptFinalizeKeyPair(key, 0); + if (statusCode > 0) + throw new Win32Exception((int)statusCode); + + var keyBlob = new Win32.KeyBlob(); + int exportedKeySize = 0; + statusCode = Win32.BCryptExportKey(key, IntPtr.Zero, Win32.BCRYPT_ECCPRIVATE_BLOB, keyBlob, Marshal.SizeOf(typeof(Win32.KeyBlob)), out exportedKeySize); + if (statusCode > 0) + throw new Win32Exception((int)statusCode); + + return new Keypair(Convert.ToBase64String(keyBlob.Public), Convert.ToBase64String(keyBlob.Private)); + } + finally + { + Win32.BCryptDestroyKey(key); + } + } + finally + { + Win32.BCryptCloseAlgorithmProvider(algoHandle, 0); + } + } + } +} diff --git a/embeddable-dll-service/csharp/Program.cs b/embeddable-dll-service/csharp/Program.cs new file mode 100644 index 00000000..27cefdc9 --- /dev/null +++ b/embeddable-dll-service/csharp/Program.cs @@ -0,0 +1,56 @@ +/* SPDX-License-Identifier: MIT + * + * Copyright (C) 2019 WireGuard LLC. All Rights Reserved. + */ + +using System; +using System.Net.Sockets; +using System.IO; +using System.Text; +using System.Diagnostics; + +namespace Tunnel +{ + class Program + { + static void Main(string[] args) + { + if (args.Length == 2 && args[0] == "/service") + { + Service.Run(args[1]); + return; + } + var keys = Keypair.Generate(); + var client = new TcpClient("demo.wireguard.com", 42912); + var stream = client.GetStream(); + var reader = new StreamReader(stream, Encoding.UTF8); + var pubKeyBytes = Encoding.UTF8.GetBytes(keys.Public + "\n"); + stream.Write(pubKeyBytes, 0, pubKeyBytes.Length); + stream.Flush(); + var ret = reader.ReadLine().Split(':'); + client.Close(); + var status = ret.Length >= 1 ? ret[0] : ""; + var serverPubkey = ret.Length >= 2 ? ret[1] : ""; + var serverPort = ret.Length >= 3 ? ret[2] : ""; + var internalIP = ret.Length >= 4 ? ret[3] : ""; + + if (status != "OK") + throw new InvalidOperationException(String.Format("Server status is {0}", status)); + + var configFileContents = String.Format("[Interface]\nPrivateKey = {0}\nAddress = {1}/24\nDNS = 8.8.8.8, 8.8.4.4\n\n[Peer]\nPublicKey = {2}\nEndpoint = demo.wireguard.com:{3}\nAllowedIPs = 0.0.0.0/0\n", keys.Private, internalIP, serverPubkey, serverPort); + var configFile = Path.Combine(Path.GetDirectoryName(Process.GetCurrentProcess().MainModule.FileName), "demobox.conf"); + File.WriteAllText(configFile, configFileContents); + + try + { + Service.Add(configFile); + Console.WriteLine("=== Press enter to exit ==="); + Console.ReadLine(); + } + finally + { + Service.Remove(configFile); + } + } + } +} diff --git a/embeddable-dll-service/csharp/Ringlogger.cs b/embeddable-dll-service/csharp/Ringlogger.cs new file mode 100644 index 00000000..711c22e7 --- /dev/null +++ b/embeddable-dll-service/csharp/Ringlogger.cs @@ -0,0 +1,46 @@ +/* SPDX-License-Identifier: MIT + * + * Copyright (C) 2019 WireGuard LLC. All Rights Reserved. + */ + +using System; +using System.IO; +using System.IO.MemoryMappedFiles; +using System.Text; + +namespace Tunnel +{ + public class Ringlogger + { + private readonly MemoryMappedViewAccessor _viewAccessor; + + public Ringlogger(string filename) + { + var file = File.Open(filename, FileMode.Open, FileAccess.Read, FileShare.ReadWrite | FileShare.Delete); + var mmap = MemoryMappedFile.CreateFromFile(file, null, 0, MemoryMappedFileAccess.Read, HandleInheritability.None, false); + _viewAccessor = mmap.CreateViewAccessor(0, 8 + 2048 * (512 + 8), MemoryMappedFileAccess.Read); + if (_viewAccessor.ReadUInt32(0) != 0xbadbabe) + throw new InvalidDataException("The provided file is missing the magic number."); + } + + public void WriteTo(TextWriter writer) + { + var start = _viewAccessor.ReadUInt32(4); + for (var i = 0; i < 2048; ++i) + { + var lineOffset = 8 + (8 + 512) * ((i + start) % 2048); + var timeNs = _viewAccessor.ReadInt64(lineOffset); + if (timeNs == 0) + continue; + var textBytes = new byte[512]; + _viewAccessor.ReadArray<byte>(lineOffset + 8, textBytes, 0, textBytes.Length); + var nullByte = Array.IndexOf<byte>(textBytes, 0); + if (nullByte <= 0) + continue; + var text = Encoding.UTF8.GetString(textBytes, 0, nullByte); + var time = DateTimeOffset.FromUnixTimeMilliseconds(timeNs / 1000000).ToString("yyyy'-'MM'-'dd HH':'mm':'ss'.'ffffff"); + writer.WriteLine(String.Format("{0}: {1}", time, text)); + } + } + } +} diff --git a/embeddable-dll-service/csharp/Service.cs b/embeddable-dll-service/csharp/Service.cs new file mode 100644 index 00000000..1375a3cb --- /dev/null +++ b/embeddable-dll-service/csharp/Service.cs @@ -0,0 +1,131 @@ +/* SPDX-License-Identifier: MIT + * + * Copyright (C) 2019 WireGuard LLC. All Rights Reserved. + */ + +using System; +using System.IO; +using System.IO.Pipes; +using System.Runtime.InteropServices; +using System.ComponentModel; +using System.Diagnostics; +using System.Security.Principal; +using System.Threading; + +namespace Tunnel +{ + public class Service + { + private const string LongName = "Example WireGuard Tunnel Client"; + private const string Description = "A WireGuard tunnel created by example code."; + + [StructLayout(LayoutKind.Sequential)] + private struct GoString + { + public string str; + public long len; + } + + [DllImport("tunnel.dll", EntryPoint = "WireGuardTunnelService", CallingConvention = CallingConvention.Cdecl)] + private static extern bool WireGuardTunnelService(GoString configFile); + + public static bool Run(string configFile) + { + return WireGuardTunnelService(new GoString { str = configFile, len = configFile.Length }); + } + + public static NamedPipeClientStream GetPipe(string configFile) + { + var pipepath = "ProtectedPrefix\\Administrators\\WireGuard\\" + Path.GetFileNameWithoutExtension(configFile); + return new NamedPipeClientStream(pipepath); + } + + public static void Add(string configFile) + { + var tunnelName = Path.GetFileNameWithoutExtension(configFile); + var shortName = String.Format("WireGuardTunnel${0}", tunnelName); + var longName = String.Format("{0}: {1}", LongName, tunnelName); + var exeName = Process.GetCurrentProcess().MainModule.FileName; + var pathAndArgs = String.Format("\"{0}\" /service \"{1}\"", exeName, configFile); //TODO: This is not the proper way to escape file args. + + var accessControl = File.GetAccessControl(configFile); //TODO: TOCTOU! + accessControl.SetOwner(new NTAccount(Environment.UserDomainName, Environment.UserName)); + File.SetAccessControl(configFile, accessControl); + + var scm = Win32.OpenSCManager(null, null, Win32.ScmAccessRights.AllAccess); + if (scm == IntPtr.Zero) + throw new Win32Exception(Marshal.GetLastWin32Error()); + try + { + var service = Win32.OpenService(scm, shortName, Win32.ServiceAccessRights.AllAccess); + if (service != IntPtr.Zero) + { + Win32.CloseServiceHandle(service); + Remove(configFile); + } + service = Win32.CreateService(scm, shortName, longName, Win32.ServiceAccessRights.AllAccess, Win32.ServiceType.Win32OwnProcess, Win32.ServiceStartType.Auto, Win32.ServiceError.Normal, pathAndArgs, null, IntPtr.Zero, "Nsi", null, null); + if (service == IntPtr.Zero) + throw new Win32Exception(Marshal.GetLastWin32Error()); + try + { + var sidType = Win32.ServiceSidType.Unrestricted; + if (!Win32.ChangeServiceConfig2(service, Win32.ServiceConfigType.SidInfo, ref sidType)) + throw new Win32Exception(Marshal.GetLastWin32Error()); + + var description = new Win32.ServiceDescription { lpDescription = Description }; + if (!Win32.ChangeServiceConfig2(service, Win32.ServiceConfigType.Description, ref description)) + throw new Win32Exception(Marshal.GetLastWin32Error()); + + if (!Win32.StartService(service, 0, null)) + throw new Win32Exception(Marshal.GetLastWin32Error()); + } + finally + { + Win32.CloseServiceHandle(service); + } + } + finally + { + Win32.CloseServiceHandle(scm); + } + } + + public static void Remove(string configFile) + { + var tunnelName = Path.GetFileNameWithoutExtension(configFile); + var shortName = String.Format("WireGuardTunnel${0}", tunnelName); + + var scm = Win32.OpenSCManager(null, null, Win32.ScmAccessRights.AllAccess); + if (scm == IntPtr.Zero) + throw new Win32Exception(Marshal.GetLastWin32Error()); + try + { + var service = Win32.OpenService(scm, shortName, Win32.ServiceAccessRights.AllAccess); + if (service == IntPtr.Zero) + { + Win32.CloseServiceHandle(service); + return; + } + try + { + var serviceStatus = new Win32.ServiceStatus(); + Win32.ControlService(service, Win32.ServiceControl.Stop, serviceStatus); + + for (int i = 0; i < 180 && Win32.QueryServiceStatus(service, serviceStatus) && serviceStatus.dwCurrentState != Win32.ServiceState.Stopped; ++i) + Thread.Sleep(1000); + + if (!Win32.DeleteService(service)) + throw new Win32Exception(Marshal.GetLastWin32Error()); + } + finally + { + Win32.CloseServiceHandle(service); + } + } + finally + { + Win32.CloseServiceHandle(scm); + } + } + } +} diff --git a/embeddable-dll-service/csharp/Win32.cs b/embeddable-dll-service/csharp/Win32.cs new file mode 100644 index 00000000..3dd9cfca --- /dev/null +++ b/embeddable-dll-service/csharp/Win32.cs @@ -0,0 +1,245 @@ +/* SPDX-License-Identifier: MIT + * + * Copyright (C) 2019 WireGuard LLC. All Rights Reserved. + */ + +using System; +using System.Runtime.InteropServices; + +namespace Tunnel +{ + static class Win32 + { + [Flags] + public enum ScmAccessRights + { + Connect = 0x0001, + CreateService = 0x0002, + EnumerateService = 0x0004, + Lock = 0x0008, + QueryLockStatus = 0x0010, + ModifyBootConfig = 0x0020, + StandardRightsRequired = 0xF0000, + AllAccess = (StandardRightsRequired | Connect | CreateService | EnumerateService | Lock | QueryLockStatus | ModifyBootConfig) + } + + [Flags] + public enum ServiceAccessRights + { + QueryConfig = 0x1, + ChangeConfig = 0x2, + QueryStatus = 0x4, + EnumerateDependants = 0x8, + Start = 0x10, + Stop = 0x20, + PauseContinue = 0x40, + Interrogate = 0x80, + UserDefinedControl = 0x100, + Delete = 0x00010000, + StandardRightsRequired = 0xF0000, + AllAccess = (StandardRightsRequired | QueryConfig | ChangeConfig | QueryStatus | EnumerateDependants | Start | Stop | PauseContinue | Interrogate | UserDefinedControl) + } + + [Flags] + public enum ServiceStartType + { + Boot = 0x00000000, + System = 0x00000001, + Auto = 0x00000002, + Demand = 0x00000003, + Disabled = 0x00000004 + } + + [Flags] + public enum ServiceControl + { + Stop = 0x00000001, + Pause = 0x00000002, + Continue = 0x00000003, + Interrogate = 0x00000004, + Shutdown = 0x00000005, + ParamChange = 0x00000006, + NetBindAdd = 0x00000007, + NetBindRemove = 0x00000008, + NetBindEnable = 0x00000009, + NetBindDisable = 0x0000000A + } + + [Flags] + public enum ServiceError + { + Ignore = 0x00000000, + Normal = 0x00000001, + Severe = 0x00000002, + Critical = 0x00000003 + } + + [Flags] + public enum ServiceSidType + { + None = 0x00000000, + Unrestricted = 0x00000001, + Restricted = 0x00000003 + } + + [Flags] + public enum ServiceType + { + KernelDriver = 0x00000001, + FileSystemDriver = 0x00000002, + Win32OwnProcess = 0x00000010, + Win32ShareProcess = 0x00000020, + InteractiveProcess = 0x00000100 + } + + [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode, Size = 8192), ComVisible(false)] + public struct ServiceSidInfo + { + public ServiceSidType serviceSidType; + }; + + public enum ServiceState + { + Unknown = -1, + NotFound = 0, + Stopped = 1, + StartPending = 2, + StopPending = 3, + Running = 4, + ContinuePending = 5, + PausePending = 6, + Paused = 7 + } + + [StructLayout(LayoutKind.Sequential)] + public class ServiceStatus + { + public int dwServiceType = 0; + public ServiceState dwCurrentState = 0; + public int dwControlsAccepted = 0; + public int dwWin32ExitCode = 0; + public int dwServiceSpecificExitCode = 0; + public int dwCheckPoint = 0; + public int dwWaitHint = 0; + } + + [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode, Size = 8192), ComVisible(false)] + public struct ServiceDescription + { + public String lpDescription; + }; + + public enum ServiceConfigType + { + Description = 1, + SidInfo = 5 + } + + [DllImport("advapi32.dll", EntryPoint = "OpenSCManagerW", ExactSpelling = true, CharSet = CharSet.Unicode, SetLastError = true)] + public static extern IntPtr OpenSCManager(string machineName, string databaseName, ScmAccessRights dwDesiredAccess); + + [DllImport("advapi32.dll", SetLastError = true, CharSet = CharSet.Auto)] + public static extern IntPtr OpenService(IntPtr hSCManager, string lpServiceName, ServiceAccessRights dwDesiredAccess); + + [DllImport("advapi32.dll", SetLastError = true)] + [return: MarshalAs(UnmanagedType.Bool)] + public static extern bool CloseServiceHandle(IntPtr hSCObject); + + [DllImport("advapi32.dll", SetLastError = true)] + [return: MarshalAs(UnmanagedType.Bool)] + public static extern bool StartService(IntPtr hService, int dwNumServiceArgs, string[] lpServiceArgVectors); + + [DllImport("advapi32.dll", SetLastError = true, CharSet = CharSet.Auto)] + public static extern IntPtr CreateService(IntPtr hSCManager, string lpServiceName, string lpDisplayName, ServiceAccessRights dwDesiredAccess, ServiceType dwServiceType, ServiceStartType dwStartType, ServiceError dwErrorControl, string lpBinaryPathName, string lpLoadOrderGroup, IntPtr lpdwTagId, string lpDependencies, string lp, string lpPassword); + + [DllImport("advapi32.dll", SetLastError = true)] + [return: MarshalAs(UnmanagedType.Bool)] + public static extern bool DeleteService(IntPtr hService); + + [DllImport("advapi32.dll", SetLastError = true)] + [return: MarshalAs(UnmanagedType.Bool)] + public static extern bool ControlService(IntPtr hService, ServiceControl dwControl, ServiceStatus lpServiceStatus); + + [DllImport("advapi32.dll", SetLastError = true)] + [return: MarshalAs(UnmanagedType.Bool)] + public static extern bool QueryServiceStatus(IntPtr hService, ServiceStatus lpServiceStatus); + + [DllImport("advapi32.dll", EntryPoint = "ChangeServiceConfig2", CharSet = CharSet.Unicode, SetLastError = true, CallingConvention = CallingConvention.StdCall)] + [return: MarshalAs(UnmanagedType.Bool)] + public static extern bool ChangeServiceConfig2(IntPtr hService, ServiceConfigType dwInfoLevel, ref ServiceSidType lpInfo); + + [DllImport("advapi32.dll", EntryPoint = "ChangeServiceConfig2", CharSet = CharSet.Unicode, SetLastError = true, CallingConvention = CallingConvention.StdCall)] + [return: MarshalAs(UnmanagedType.Bool)] + public static extern bool ChangeServiceConfig2(IntPtr hService, ServiceConfigType dwInfoLevel, ref ServiceDescription lpInfo); + + [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)] + public class KeyBlob + { + BCRYPT_ECCKEY_BLOB Header; + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 32)] + public byte[] Public; + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 32)] + public byte[] Unused; + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 32)] + public byte[] Private; + } + + public const string BCRYPT_ECC_CURVE_NAME = "ECCCurveName"; + public const string BCRYPT_ECDH_ALGORITHM = "ECDH"; + public const string BCRYPT_ECC_CURVE_25519 = "curve25519"; + public const string BCRYPT_ECCPRIVATE_BLOB = "ECCPRIVATEBLOB"; + + [DllImport("bcrypt.dll", SetLastError = true, ExactSpelling = true, CharSet = CharSet.Unicode)] + public static extern uint BCryptSetProperty(IntPtr hObject, string property, string input, int inputSize, uint Flags = 0); + + [DllImport("bcrypt.dll", CharSet = CharSet.Auto, SetLastError = true)] + public static extern uint BCryptOpenAlgorithmProvider(ref IntPtr hAlgorithm, string AlgId, string Implementation, uint Flags); + + [DllImport("bcrypt.dll", SetLastError = true)] + public static extern uint BCryptGenerateKeyPair(IntPtr hObject, ref IntPtr hKey, uint length, uint Flags); + + [DllImport("bcrypt.dll", SetLastError = true)] + public static extern uint BCryptFinalizeKeyPair(IntPtr hKey, uint Flags); + + [DllImport("bcrypt.dll", SetLastError = true, ExactSpelling = true, CharSet = CharSet.Unicode)] + public static extern uint BCryptExportKey(IntPtr hKey, IntPtr hExportKey, [MarshalAs(UnmanagedType.LPWStr)] string pszBlobType, [Out] KeyBlob pbOutput, int cbOutput, out int pcbResult, uint Flags = 0); + + [DllImport("bcrypt.dll", SetLastError = true)] + public static extern uint BCryptDestroyKey(IntPtr hKey); + + [DllImport("bcrypt.dll", SetLastError = true)] + public static extern uint BCryptCloseAlgorithmProvider(IntPtr hAlgorithm, uint Flags); + + [DllImport("bcrypt.dll", SetLastError = true)] + public static extern uint BCryptDestroySecret(IntPtr hSecretAgreement); + + [DllImport("bcrypt.dll", CharSet = CharSet.Auto, SetLastError = true)] + public static extern uint BCryptImportKeyPair(IntPtr hAlgorithm, IntPtr hImportKey, string BlobType, ref IntPtr hPublicKey, byte[] Input, uint InputByteLength, uint Flags); + + [DllImport("bcrypt.dll", CharSet = CharSet.Auto, SetLastError = true)] + public static extern uint BCryptSecretAgreement(IntPtr hPrivKey, IntPtr hPublicKey, ref IntPtr phSecret, uint Flags); + + [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)] + public class BCryptBufferDesc + { + public uint ulVersion; + public uint cBuffers; + public IntPtr pBuffers; + } + + [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)] + public class BCryptBuffer + { + public uint cbBuffer; + public uint bufferType; + public IntPtr pvBuffer; + } + + [StructLayout(LayoutKind.Sequential)] + public class BCRYPT_ECCKEY_BLOB + { + uint magic; + uint cbKey; + } + } +} |