diff --git a/.editorconfig b/.editorconfig index 7c5c5e2c..ec2660d3 100644 --- a/.editorconfig +++ b/.editorconfig @@ -58,6 +58,9 @@ end_of_line = crlf [*.cs] +[*.generated.cs] +generated_code = true + # IDE0063: Use simple 'using' statement csharp_prefer_simple_using_statement = true:silent diff --git a/.gitignore b/.gitignore index fbd71724..036c443f 100644 --- a/.gitignore +++ b/.gitignore @@ -26,6 +26,8 @@ bld/ # Visual Studio 2015 cache/options directory .vs/ +# Visual Studio Code directory +.vscode/ # Uncomment if you have tasks that create the project's static files in wwwroot #wwwroot/ @@ -285,4 +287,4 @@ __pycache__/ *.btp.cs *.btm.cs *.odx.cs -*.xsd.cs \ No newline at end of file +*.xsd.cs diff --git a/Bruce/Bruce.csproj b/Bruce/Bruce.csproj index 8a795643..ea86f0de 100644 --- a/Bruce/Bruce.csproj +++ b/Bruce/Bruce.csproj @@ -1,8 +1,15 @@ - + + + + net8.0-windows + + + + net8.0 + Exe - netcoreapp3.1 Kerberos.NET.CommandLine Major @@ -33,13 +40,21 @@ - + - + + + + + + + + diff --git a/Bruce/CommandLine/KerberosDumpCommand.cs b/Bruce/CommandLine/KerberosDumpCommand.cs index 142f17b5..68183124 100644 --- a/Bruce/CommandLine/KerberosDumpCommand.cs +++ b/Bruce/CommandLine/KerberosDumpCommand.cs @@ -4,8 +4,10 @@ // ----------------------------------------------------------------------- using System.Threading.Tasks; +#if WINDOWS using System.Windows.Forms; using KerbDump; +#endif namespace Kerberos.NET.CommandLine { @@ -14,8 +16,10 @@ public class KerberosDumpCommand : BaseCommand { static KerberosDumpCommand() { +#if WINDOWS Application.EnableVisualStyles(); Application.SetCompatibleTextRenderingDefault(false); +#endif } public KerberosDumpCommand(CommandLineParameters parameters) @@ -29,11 +33,7 @@ public KerberosDumpCommand(CommandLineParameters parameters) public override Task Execute() { - if (!OSPlatform.IsWindows) - { - return Task.FromResult(false); - } - +#if WINDOWS using (var form = new DecoderForm() { Ticket = this.Ticket, @@ -44,6 +44,10 @@ public override Task Execute() } return Task.FromResult(true); +#else + return Task.FromResult(false); +#endif } } } + diff --git a/Bruce/CommandLine/KerberosHelpCommand.cs b/Bruce/CommandLine/KerberosHelpCommand.cs index 90248e6e..d134f178 100644 --- a/Bruce/CommandLine/KerberosHelpCommand.cs +++ b/Bruce/CommandLine/KerberosHelpCommand.cs @@ -123,11 +123,11 @@ private void WriteCommandLabel(Type type, int max = 0) if (string.Equals(descName, desc, StringComparison.OrdinalIgnoreCase)) { - this.WriteLine(string.Format(format, label), attr.Description, commands.Skip(1)); + this.WriteLine(string.Format(format, label), attr.Description, string.Join(", ", commands.Skip(1))); } else { - this.WriteLine(string.Format(format, label), desc, commands.Skip(1)); + this.WriteLine(string.Format(format, label), desc, string.Join(", ", commands.Skip(1))); } } } diff --git a/Bruce/CommandLine/KerberosPasswordCommand.cs b/Bruce/CommandLine/KerberosPasswordCommand.cs index c007b6da..83ccf59c 100644 --- a/Bruce/CommandLine/KerberosPasswordCommand.cs +++ b/Bruce/CommandLine/KerberosPasswordCommand.cs @@ -3,17 +3,12 @@ // The .NET Foundation licenses this file to you under the MIT license. // ----------------------------------------------------------------------- -using Kerberos.NET.Configuration; using Kerberos.NET.Credentials; -using Kerberos.NET.Crypto; using Kerberos.NET.Entities; using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging.Abstractions; using System; -using System.Linq; -using System.Security.Cryptography.X509Certificates; using System.Threading.Tasks; -using System.Windows.Forms; namespace Kerberos.NET.CommandLine { diff --git a/Bruce/Program.cs b/Bruce/Program.cs index 538fd174..31ce2829 100644 --- a/Bruce/Program.cs +++ b/Bruce/Program.cs @@ -1,9 +1,8 @@ -using System; +using Kerberos.NET.PortableDns; +using System; using System.Diagnostics; using System.IO; using System.Linq; -using Kerberos.NET.CommandLine.Dns; -using Kerberos.NET.Dns; namespace Kerberos.NET.CommandLine { @@ -12,7 +11,10 @@ class Program [STAThread] static void Main(string[] args) { - DnsQuery.RegisterImplementation(new PlatformIndependentDnsClient()); + if (!OSPlatform.IsWindows) + { + PortableDnsClient.Configure(); + } var assembly = Path.GetFileNameWithoutExtension(Process.GetCurrentProcess().ProcessName); diff --git a/Kerberos.NET.PortableDns/Kerberos.NET.PortableDns.csproj b/Kerberos.NET.PortableDns/Kerberos.NET.PortableDns.csproj new file mode 100644 index 00000000..308a1e7a --- /dev/null +++ b/Kerberos.NET.PortableDns/Kerberos.NET.PortableDns.csproj @@ -0,0 +1,15 @@ + + + + netstandard2.0 + + + + + + + + + + + diff --git a/Kerberos.NET.PortableDns/PortableDnsClient.cs b/Kerberos.NET.PortableDns/PortableDnsClient.cs new file mode 100644 index 00000000..17a90b75 --- /dev/null +++ b/Kerberos.NET.PortableDns/PortableDnsClient.cs @@ -0,0 +1,51 @@ +using DnsClient; +using System.Linq; +using System.Net; + +namespace Kerberos.NET.PortableDns +{ + public static class PortableDnsClient + { + public static void Configure() + { + PortableDnsImplementation.Options = new LookupClientOptions(); + } + + public static void Configure(params NameServer[] nameServers) + { + PortableDnsImplementation.Options = new LookupClientOptions(nameServers); + } + + public static void Configure(params IPEndPoint[] nameServers) + { + PortableDnsImplementation.Options = new LookupClientOptions(nameServers); + } + + public static void Configure(params IPAddress[] nameServers) + { + PortableDnsImplementation.Options = new LookupClientOptions(nameServers); + } + + public static void Configure(params string[] nameServers) + { + PortableDnsImplementation.Options = new LookupClientOptions(nameServers.Select(n => { + var parts = n.Split(':'); + IPAddress address; + switch (parts.Length) + { + case 1: + address = IPAddress.Parse(parts[0]); + return new IPEndPoint(address, 53); + case 2: + address = IPAddress.Parse(parts[0]); + var port = int.Parse(parts[1]); + return new IPEndPoint(address, port); + default: + throw new System.FormatException($"{n} is not in the correct format 'IPaddress:Port'"); + } + }).ToArray()); + } + + public static LookupClientOptions Options => PortableDnsImplementation.Options; + } +} diff --git a/Bruce/Dns/PlatformIndependentDnsClient.cs b/Kerberos.NET.PortableDns/PortableDnsImplementation.cs similarity index 67% rename from Bruce/Dns/PlatformIndependentDnsClient.cs rename to Kerberos.NET.PortableDns/PortableDnsImplementation.cs index 765b3448..f2c5f9cb 100644 --- a/Bruce/Dns/PlatformIndependentDnsClient.cs +++ b/Kerberos.NET.PortableDns/PortableDnsImplementation.cs @@ -1,27 +1,30 @@ -using System; +using DnsClient; +using Kerberos.NET.Dns; +using System; using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; -using DnsClient; -using Kerberos.NET.Dns; -namespace Kerberos.NET.CommandLine.Dns +namespace Kerberos.NET.PortableDns { - internal class PlatformIndependentDnsClient : IKerberosDnsQuery + internal class PortableDnsImplementation : IKerberosDnsQuery { - private static readonly WindowsDnsQuery WindowsDns = new WindowsDnsQuery(); - - public async Task> Query(string query, DnsRecordType type) + static PortableDnsImplementation() { - if (WindowsDns.IsSupported) - { - return await WindowsDns.Query(query, type); - } + DnsQuery.RegisterImplementation(new PortableDnsImplementation()); + } - var client = new LookupClient(); + public static LookupClientOptions Options { get; set; } = new LookupClientOptions(); - var response = await client.QueryAsync(query, (QueryType)type); + private static LookupClient Create() + { + return new LookupClient(Options); + } + public async Task> Query(string query, DnsRecordType type) + { + var client = Create(); + var response = await client.QueryAsync(query, (QueryType)type); var srvRecords = response.Answers.SrvRecords().Select(a => new DnsRecord { Name = a.DomainName, @@ -38,9 +41,7 @@ public async Task> Query(string query, DnsRecordT foreach (var srv in srvRecords) { var c1 = merged.Where(m => m.Key.Equals(srv.Target, StringComparison.InvariantCultureIgnoreCase)); - var canon = c1.SelectMany(r => r); - srv.Canonical = canon.ToList(); } diff --git a/Kerberos.NET.sln b/Kerberos.NET.sln index edae1732..01e7acff 100644 --- a/Kerberos.NET.sln +++ b/Kerberos.NET.sln @@ -1,7 +1,7 @@  Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio Version 16 -VisualStudioVersion = 16.0.29006.145 +# Visual Studio Version 17 +VisualStudioVersion = 17.11.35327.3 MinimumVisualStudioVersion = 10.0.40219.1 Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Kerberos.NET", "Kerberos.NET\Kerberos.NET.csproj", "{3066D890-0544-4E13-95FD-1DDCC72FEDA1}" EndProject @@ -29,6 +29,8 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Client Tools", "Client Tool EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Bruce", "Bruce\Bruce.csproj", "{D12B0644-0D57-45ED-AA0A-AB18D593CCA3}" EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Kerberos.NET.PortableDns", "Kerberos.NET.PortableDns\Kerberos.NET.PortableDns.csproj", "{3085F7D7-B384-4EB6-B5F4-CAEDC7C1C0E6}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -55,6 +57,10 @@ Global {D12B0644-0D57-45ED-AA0A-AB18D593CCA3}.Debug|Any CPU.Build.0 = Debug|Any CPU {D12B0644-0D57-45ED-AA0A-AB18D593CCA3}.Release|Any CPU.ActiveCfg = Release|Any CPU {D12B0644-0D57-45ED-AA0A-AB18D593CCA3}.Release|Any CPU.Build.0 = Release|Any CPU + {3085F7D7-B384-4EB6-B5F4-CAEDC7C1C0E6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {3085F7D7-B384-4EB6-B5F4-CAEDC7C1C0E6}.Debug|Any CPU.Build.0 = Debug|Any CPU + {3085F7D7-B384-4EB6-B5F4-CAEDC7C1C0E6}.Release|Any CPU.ActiveCfg = Release|Any CPU + {3085F7D7-B384-4EB6-B5F4-CAEDC7C1C0E6}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -65,6 +71,7 @@ Global {046122A3-9C6F-42E8-A21E-E4F2CD4DBCF8} = {8BD43321-3C92-4D6F-B965-783F2CC4CEE1} {5115DFE1-AD08-4AF5-B88C-F436744D7A3A} = {8F0C1D56-CBBB-4B8B-81D1-5D1544AD5C72} {D12B0644-0D57-45ED-AA0A-AB18D593CCA3} = {8BD43321-3C92-4D6F-B965-783F2CC4CEE1} + {3085F7D7-B384-4EB6-B5F4-CAEDC7C1C0E6} = {E3EE549C-8245-45E7-A964-E38C78DC9FD3} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {17150968-CFF9-4183-989D-C93E19033096} diff --git a/Kerberos.NET/Asn1/AsnXml.targets b/Kerberos.NET/Asn1/AsnXml.targets index 2cce5696..ef5f99b2 100644 --- a/Kerberos.NET/Asn1/AsnXml.targets +++ b/Kerberos.NET/Asn1/AsnXml.targets @@ -13,7 +13,6 @@ - @@ -32,6 +31,13 @@ + + + + - \ No newline at end of file + diff --git a/Kerberos.NET/Cache/FileHandle.cs b/Kerberos.NET/Cache/FileHandle.cs index 061451ed..53da0d04 100644 --- a/Kerberos.NET/Cache/FileHandle.cs +++ b/Kerberos.NET/Cache/FileHandle.cs @@ -20,8 +20,9 @@ internal class FileHandle : IDisposable private readonly FileMode mode; private readonly FileAccess access; private readonly FileShare share; +#nullable enable private static readonly MethodInfo? SetUnixFileMode = TryGetSetUnixFileMode(); - +#nullable disable private static readonly TimeSpan LockWaitTimeout = TimeSpan.FromMilliseconds(5000); public FileHandle(string file, FileMode mode, FileAccess access, FileShare share) @@ -78,6 +79,7 @@ private static string GetObjectName(string file, string type) .Replace(Path.VolumeSeparatorChar, '_'); } +#nullable enable private static MethodInfo? TryGetSetUnixFileMode() { MethodInfo? mi = null; @@ -90,11 +92,15 @@ private static string GetObjectName(string file, string type) { mi = typeof(File).GetMethod("SetUnixFileMode", new Type[] { typeof(SafeFileHandle), Type.GetType("System.IO.UnixFileMode") }); } - catch { } + catch + { + // ignored + } } return mi; } +#nullable disable private class FileLock : IDisposable { diff --git a/Kerberos.NET/Configuration/Krb5Config.cs b/Kerberos.NET/Configuration/Krb5Config.cs index c4b6842c..f6a12fad 100644 --- a/Kerberos.NET/Configuration/Krb5Config.cs +++ b/Kerberos.NET/Configuration/Krb5Config.cs @@ -74,14 +74,18 @@ public Krb5Config() public Krb5Logging Logging { get; private set; } public static string UserConfigurationPath => GetFilePath( - envVar: "%KRB5_CONFIG%", + envVar: "%KRB5_CONFIG%", // %KRB_CONFIG% is being used to specify the user + // configuration path, but it should be the path to krb5.conf itself + // (see https://web.mit.edu/kerberos/krb5-1.12/doc/admin/env_variables.html) winPath: "%APPDATA%\\Kerberos.NET\\", osxPath: "Library/Preferences/Kerberos.NET/", - linuxPath: "/etc/" + linuxPath: "%HOME%/.config/Kerberos.NET/" // use XDG_CONFIG_HOME default ); public static string ServiceConfigurationPath => GetFilePath( - envVar: "%KRB5_KDC_PROFILE%", + envVar: "%KRB5_KDC_PROFILE%", // %KRB5_KDC_PROFILE% is being used to specify the service + // configuration path, but it should be the path to kdc.conf itself + // (see https://web.mit.edu/kerberos/krb5-1.12/doc/admin/env_variables.html) winPath: "%APPDATA%\\Kerberos.NET\\", osxPath: "Library/Preferences/Kerberos.NET/", linuxPath: "/var/krb5kdc" @@ -117,9 +121,11 @@ public static Krb5Config CurrentUser(string path = null) path = DefaultUserConfigurationPath; } - if (File.Exists(path)) + // Expansion allows the use of environment variables in krb5.conf, but they must be in %VAR% format + var expandedPath = Environment.ExpandEnvironmentVariables(path); + if (File.Exists(expandedPath)) { - return Krb5ConfigurationSerializer.Deserialize(File.ReadAllText(path)).ToConfigObject(); + return Krb5ConfigurationSerializer.Deserialize(File.ReadAllText(expandedPath)).ToConfigObject(); } return Default(); @@ -180,7 +186,9 @@ private static string GetFilePath(string envVar, string winPath, string osxPath, } else if (OSPlatform.IsLinux) { - return linuxPath; + // Environment variables use %VAR% format + // (see https://github.com/dotnet/runtime/issues/25792) + return Environment.ExpandEnvironmentVariables(linuxPath); } return string.Empty; diff --git a/Kerberos.NET/Configuration/Krb5ConfigDefaults.cs b/Kerberos.NET/Configuration/Krb5ConfigDefaults.cs index ec948aa5..886d4da1 100644 --- a/Kerberos.NET/Configuration/Krb5ConfigDefaults.cs +++ b/Kerberos.NET/Configuration/Krb5ConfigDefaults.cs @@ -49,21 +49,45 @@ public class Krb5ConfigDefaults : Krb5ConfigObject /// /// This relation specifies the name of the default credential cache. The default is "FILE:%APPDATA%\Kerberos.NET\.krb5cc". /// +#if WINDOWS [DefaultValue("FILE:%APPDATA%\\Kerberos.NET\\.krb5cc")] +#elif LINUX + [DefaultValue("FILE:%HOME%/.config/Kerberos.NET/.krb5cc")] +#elif MACOS + [DefaultValue("FILE:%HOME%/Library/Preferences/Kerberos.NET/.krb5cc")] +#else + #error Unknown platform +#endif [DisplayName("default_ccache_name")] public string DefaultCCacheName { get; set; } /// /// This relation specifies the name of the default keytab for obtaining client credentials. The default is "%APPDATA%\\Kerberos.NET\\client.keytab". /// - [DefaultValue("%APPDATA%\\Kerberos.NET\\.keytab")] +#if WINDOWS + [DefaultValue("%APPDATA%\\Kerberos.NET\\client.keytab")] +#elif LINUX + [DefaultValue("%HOME%/.config/Kerberos.NET/client.keytab")] +#elif MACOS + [DefaultValue("%HOME%/Library/Preferences/Kerberos.NET/client.keytab")] +#else + #error Unknown platform +#endif [DisplayName("default_client_keytab_name")] public string DefaultClientKeytabName { get; set; } /// /// This relation specifies the default keytab name to be used by application servers such as sshd. The default is "%APPDATA%\\Kerberos.NET\\server.keytab". /// - [DefaultValue("%APPDATA%\\Kerberos.NET\\.keytab")] +#if WINDOWS + [DefaultValue("%APPDATA%\\Kerberos.NET\\server.keytab")] +#elif LINUX + [DefaultValue("%HOME%/.config/Kerberos.NET/server.keytab")] +#elif MACOS + [DefaultValue("%HOME%/Library/Preferences/Kerberos.NET/server.keytab")] +#else + #error Unknown platform +#endif [DisplayName("default_keytab_name")] public string DefaultKeytabName { get; set; } diff --git a/Kerberos.NET/Kerberos.NET.csproj b/Kerberos.NET/Kerberos.NET.csproj index 654aed17..781b0f23 100644 --- a/Kerberos.NET/Kerberos.NET.csproj +++ b/Kerberos.NET/Kerberos.NET.csproj @@ -1,16 +1,30 @@  netstandard2.0 + 9.0 Kerberos.NET library A cross-platform, managed-code Kerberos Ticket parsing, validation, and authentication library. security kerberos true + disable WEAKCRYPTO + + $(DefineConstants);WINDOWS + + + + $(DefineConstants);LINUX + + + + $(DefineConstants);MACOS + + diff --git a/Kerberos.NET/OSPlatform.cs b/Kerberos.NET/OSPlatform.cs index e77447ae..52434b92 100644 --- a/Kerberos.NET/OSPlatform.cs +++ b/Kerberos.NET/OSPlatform.cs @@ -1,16 +1,14 @@ using System; +using RtIs = System.Runtime.InteropServices; namespace Kerberos.NET { public static class OSPlatform { - public static readonly bool IsWindows = Environment.OSVersion.Platform == PlatformID.Win32S - || Environment.OSVersion.Platform == PlatformID.Win32Windows - || Environment.OSVersion.Platform == PlatformID.Win32NT - || Environment.OSVersion.Platform == PlatformID.WinCE; + public static readonly bool IsWindows = RtIs.RuntimeInformation.IsOSPlatform(RtIs.OSPlatform.Windows); - public static readonly bool IsLinux = Environment.OSVersion.Platform == PlatformID.Unix; + public static readonly bool IsLinux = RtIs.RuntimeInformation.IsOSPlatform(RtIs.OSPlatform.Linux); - public static readonly bool IsOsX = Environment.OSVersion.Platform == PlatformID.MacOSX; + public static readonly bool IsOsX = RtIs.RuntimeInformation.IsOSPlatform(RtIs.OSPlatform.OSX); } } diff --git a/Samples/KerbDumpCore/KerbDumpCore.csproj b/Samples/KerbDumpCore/KerbDumpCore.csproj index 46869fbe..30357231 100644 --- a/Samples/KerbDumpCore/KerbDumpCore.csproj +++ b/Samples/KerbDumpCore/KerbDumpCore.csproj @@ -1,11 +1,12 @@ - + Library - netcoreapp3.1 + net8.0-windows + disable true - false + true KerbDumpCore KerbDump