diff --git a/Kerberos.NET/Entities/Krb/KrbPrincipalName.cs b/Kerberos.NET/Entities/Krb/KrbPrincipalName.cs index 0539fd6..4d672d8 100644 --- a/Kerberos.NET/Entities/Krb/KrbPrincipalName.cs +++ b/Kerberos.NET/Entities/Krb/KrbPrincipalName.cs @@ -80,20 +80,20 @@ public partial class KrbPrincipalName { "msdtc", HostServiceName } }); - private static readonly ReadOnlyMemory NameTypeSeperator = new[] + private static readonly ReadOnlyMemory NameTypeSeperator = new[] { - "@", // NT_UNKNOWN = 0, - "@", // NT_PRINCIPAL = 1, - "/", // NT_SRV_INST = 2, - "/", // NT_SRV_HST = 3, - "/", // NT_SRV_XHST = 4, - "@", // NT_UID = 5, - ",", // NT_X500_PRINCIPAL = 6, - "@", // NT_SMTP_NAME = 7, - "@", // 8 - "@", // 9 - "@", // NT_ENTERPRISE = 10, - "/" // NT_WELLKNOWN = 11 + '@', // NT_UNKNOWN = 0, + '@', // NT_PRINCIPAL = 1, + '/', // NT_SRV_INST = 2, + '/', // NT_SRV_HST = 3, + '/', // NT_SRV_XHST = 4, + '@', // NT_UID = 5, + ',', // NT_X500_PRINCIPAL = 6, + '@', // NT_SMTP_NAME = 7, + '@', // 8 + '@', // 9 + '@', // NT_ENTERPRISE = 10, + '/' // NT_WELLKNOWN = 11 }; internal PrincipalName ToKeyPrincipal() @@ -125,9 +125,24 @@ public void Canonicalize(string qualifyShortname) this.Name[1] = $"{this.Name[1]}.{qualifyShortname}"; } + private static char GetSeperator(PrincipalNameType type) + { + var index = (int)type; + + if (index < 0 || index >= NameTypeSeperator.Length) + { + // Name types outside the standard range (e.g. the Microsoft-specific + // KRB5_NT_MS_PRINCIPAL = -128 returned by Windows KDCs during S4U2Self) + // are treated like NT_PRINCIPAL and joined with '@'. + return '@'; + } + + return NameTypeSeperator.Span[index]; + } + private static string MakeFullName(IEnumerable names, PrincipalNameType type, bool normalizeAlias = false) { - var seperator = NameTypeSeperator.Span[(int)type]; + var seperator = GetSeperator(type); using (var enumerator = names.GetEnumerator()) { @@ -140,7 +155,7 @@ private static string MakeFullName(IEnumerable names, PrincipalNameType string firstPortion = enumerator.Current; - if (seperator == "/" && normalizeAlias) + if (seperator == '/' && normalizeAlias) { if (ServiceAliases.TryGetValue(firstPortion.ToLowerInvariant(), out string alias)) { @@ -161,9 +176,9 @@ private static string MakeFullName(IEnumerable names, PrincipalNameType { if (enumerator.Current != null) { - if (seperator != ",") + if (seperator != ',') { - sb.Append("@"); + sb.Append('@'); } else { @@ -242,7 +257,7 @@ public static KrbPrincipalName FromString( var actualType = type ?? TryDetectType(principal); - var splitOn = NameTypeSeperator.Span[(int)actualType][0]; + var splitOn = GetSeperator(actualType); if (splitOn == '@') { diff --git a/Kerberos.NET/Entities/PrincipalNameType.cs b/Kerberos.NET/Entities/PrincipalNameType.cs index 5a8843c..f0038d1 100644 --- a/Kerberos.NET/Entities/PrincipalNameType.cs +++ b/Kerberos.NET/Entities/PrincipalNameType.cs @@ -10,6 +10,24 @@ namespace Kerberos.NET.Entities /// public enum PrincipalNameType { + /// + /// A Microsoft-specific principal name (KRB5_NT_MS_PRINCIPAL). Windows KDCs may return this + /// name type (for example in an S4U2Self reply). It is treated like . + /// + NT_MS_PRINCIPAL = -128, + + /// + /// A Microsoft-specific principal name with a SID (KRB5_NT_MS_PRINCIPAL_AND_ID). + /// It is treated like . + /// + NT_MS_PRINCIPAL_AND_ID = -129, + + /// + /// A Microsoft-specific enterprise principal name with a SID (KRB5_NT_ENT_PRINCIPAL_AND_ID). + /// It is treated like . + /// + NT_ENT_PRINCIPAL_AND_ID = -130, + /// /// The principal name format is unknown and will be treated like /// diff --git a/Tests/Tests.Kerberos.NET/KrbApReq/PrincipalNameTests.cs b/Tests/Tests.Kerberos.NET/KrbApReq/PrincipalNameTests.cs index f975f1a..beecfee 100644 --- a/Tests/Tests.Kerberos.NET/KrbApReq/PrincipalNameTests.cs +++ b/Tests/Tests.Kerberos.NET/KrbApReq/PrincipalNameTests.cs @@ -147,14 +147,35 @@ public void PrincipalName_Equality_DifferentServiceTypes() } [TestMethod] - public void PrincipalName_Equality_ServiceTypeAliasesMatch() + public void PrincipalName_MsPrincipal_DoesNotThrow() { - var a = KrbPrincipalName.FromString("host/aaaa"); - var b = KrbPrincipalName.FromString("bbbb/aaaa"); + var principal = new KrbPrincipalName + { + Type = PrincipalNameType.NT_MS_PRINCIPAL, + Name = new[] { "testuser" } + }; - KrbPrincipalName.ServiceAliases["bbbb"] = "host"; + Assert.AreEqual("testuser", principal.FullyQualifiedName); + } - Assert.IsTrue(a.Matches(b)); + [TestMethod] + public void PrincipalName_MsPrincipal_WithUpn() + { + var principal = new KrbPrincipalName + { + Type = PrincipalNameType.NT_MS_PRINCIPAL, + Name = new[] { "testuser@domain.local" } + }; + + Assert.AreEqual("testuser@domain.local", principal.FullyQualifiedName); + } + + [TestMethod] + public void PrincipalName_MsPrincipal_FromString() + { + var principal = KrbPrincipalName.FromString("testuser@domain.local", PrincipalNameType.NT_MS_PRINCIPAL); + + Assert.AreEqual("testuser@domain.local", principal.FullyQualifiedName); } } } \ No newline at end of file