Skip to content

Commit 56fde0c

Browse files
committed
Added basic querying of kerberos ticket cache.
1 parent fe5eacc commit 56fde0c

8 files changed

Lines changed: 619 additions & 6 deletions

File tree

NtApiDotNet/NtApiDotNet.csproj

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -340,8 +340,10 @@
340340
<Compile Include="Win32\Security\Authentication\Kerberos\KerberosChecksum.cs" />
341341
<Compile Include="Win32\Security\Authentication\Kerberos\KerberosChecksumGSSApi.cs" />
342342
<Compile Include="Win32\Security\Authentication\Kerberos\KerberosCredential.cs" />
343+
<Compile Include="Win32\Security\Authentication\Kerberos\KerberosExternalTicket.cs" />
343344
<Compile Include="Win32\Security\Authentication\Kerberos\KerberosHostAddress.cs" />
344345
<Compile Include="Win32\Security\Authentication\Kerberos\KerberosPreAuthenticationType.cs" />
346+
<Compile Include="Win32\Security\Authentication\Kerberos\KerberosTicketCache.cs" />
345347
<Compile Include="Win32\Security\Authentication\Kerberos\KerberosTicketDecrypted.cs" />
346348
<Compile Include="Win32\Security\Authentication\Kerberos\KerberosKeySet.cs" />
347349
<Compile Include="Win32\Security\Authentication\Kerberos\KerberosTransitedEncoding.cs" />
@@ -365,7 +367,7 @@
365367
<Compile Include="Win32\Security\Authentication\Kerberos\KerberosMessageType.cs" />
366368
<Compile Include="Win32\Security\Authentication\Kerberos\KerberosNameType.cs" />
367369
<Compile Include="Win32\Security\Authentication\Kerberos\Ndr\PacDeviceInfoParser.cs" />
368-
<Compile Include="Win32\Security\Authentication\Kerberos\PrincipalName.cs" />
370+
<Compile Include="Win32\Security\Authentication\Kerberos\KerberosPrincipalName.cs" />
369371
<Compile Include="Win32\Security\Authentication\Negotiate\NegotiateInitAuthenticationToken.cs" />
370372
<Compile Include="Win32\Security\Authentication\Negotiate\NegotiateAuthenticationToken.cs" />
371373
<Compile Include="Win32\Security\Authentication\Negotiate\NegotiateResponseAuthenticationToken.cs" />

NtApiDotNet/Win32/SafeHandles/SafeLsaReturnBufferHandle.cs

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,8 @@
1414

1515
using NtApiDotNet.Win32.Security.Native;
1616
using System;
17+
using System.Runtime.CompilerServices;
18+
using System.Runtime.ConstrainedExecution;
1719
using System.Runtime.InteropServices;
1820

1921
namespace NtApiDotNet.Win32.SafeHandles
@@ -44,5 +46,27 @@ public override bool IsInvalid
4446
return handle == IntPtr.Zero;
4547
}
4648
}
49+
50+
/// <summary>
51+
/// Detaches the current buffer and allocates a new one.
52+
/// </summary>
53+
/// <returns>The detached buffer.</returns>
54+
/// <remarks>The original buffer will become invalid after this call.</remarks>
55+
[ReliabilityContract(Consistency.MayCorruptInstance, Cer.MayFail)]
56+
public SafeLsaReturnBufferHandle Detach()
57+
{
58+
RuntimeHelpers.PrepareConstrainedRegions();
59+
try // Needed for constrained region.
60+
{
61+
IntPtr handle = DangerousGetHandle();
62+
SetHandleAsInvalid();
63+
var ret = new SafeLsaReturnBufferHandle(handle, true);
64+
ret.Initialize(ByteLength);
65+
return ret;
66+
}
67+
finally
68+
{
69+
}
70+
}
4771
}
4872
}
Lines changed: 146 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,146 @@
1+
// Copyright 2020 Google Inc. All Rights Reserved.
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
using NtApiDotNet.Utilities.ASN1;
16+
using System;
17+
using System.IO;
18+
using System.Linq;
19+
using System.Runtime.InteropServices;
20+
21+
namespace NtApiDotNet.Win32.Security.Authentication.Kerberos
22+
{
23+
/// <summary>
24+
/// Class to represent a cached external ticket.
25+
/// </summary>
26+
public sealed class KerberosExternalTicket
27+
{
28+
private static KerberosPrincipalName ParseName(IntPtr ptr)
29+
{
30+
if (ptr == IntPtr.Zero)
31+
return new KerberosPrincipalName();
32+
KerberosNameType name_type = (KerberosNameType)Marshal.ReadInt16(ptr, 0);
33+
int count = Marshal.ReadInt16(ptr, 2);
34+
if (count == 0)
35+
return new KerberosPrincipalName(name_type, new string[0]);
36+
37+
var name = new SafeStructureInOutBuffer<KERB_EXTERNAL_NAME>(ptr, Marshal.SizeOf(typeof(KERB_EXTERNAL_NAME))
38+
+ Marshal.SizeOf(typeof(UnicodeStringOut)) * count, false);
39+
UnicodeStringOut[] names = new UnicodeStringOut[count];
40+
name.Data.ReadArray(0, names, 0, count);
41+
return new KerberosPrincipalName(name_type, names.Select(u => u.ToString()));
42+
}
43+
44+
private static KerberosAuthenticationKey ParseKey(KerberosPrincipalName server_name, string realm, KERB_CRYPTO_KEY key)
45+
{
46+
byte[] key_data = new byte[key.Length];
47+
Marshal.Copy(key.Value, key_data, 0, key.Length);
48+
return new KerberosAuthenticationKey(key.KeyType, key_data, server_name.NameType, realm, server_name.Names, DateTime.Now, 0);
49+
}
50+
51+
/// <summary>
52+
/// Service name.
53+
/// </summary>
54+
public KerberosPrincipalName ServiceName { get; private set; }
55+
/// <summary>
56+
/// Target name.
57+
/// </summary>
58+
public KerberosPrincipalName TargetName { get; private set; }
59+
/// <summary>
60+
/// Client name.
61+
/// </summary>
62+
public KerberosPrincipalName ClientName { get; private set; }
63+
/// <summary>
64+
/// Domain name.
65+
/// </summary>
66+
public string DomainName { get; private set; }
67+
/// <summary>
68+
/// Target domain name.
69+
/// </summary>
70+
public string TargetDomainName { get; private set; }
71+
/// <summary>
72+
/// Alt target domain name.
73+
/// </summary>
74+
public string AltTargetDomainName { get; private set; }
75+
/// <summary>
76+
/// Session key for ticket.
77+
/// </summary>
78+
public KerberosAuthenticationKey SessionKey { get; private set; }
79+
/// <summary>
80+
/// Ticket flags.
81+
/// </summary>
82+
public KerberosTicketFlags TicketFlags { get; private set; }
83+
/// <summary>
84+
/// Additional reserved flags.
85+
/// </summary>
86+
public int Flags { get; private set; }
87+
/// <summary>
88+
/// Key expiration time.
89+
/// </summary>
90+
public DateTime KeyExpirationTime { get; private set; }
91+
/// <summary>
92+
/// Ticket start time.
93+
/// </summary>
94+
public DateTime StartTime { get; private set; }
95+
/// <summary>
96+
/// Ticket end time.
97+
/// </summary>
98+
public DateTime EndTime { get; private set; }
99+
/// <summary>
100+
/// Ticket renew time.
101+
/// </summary>
102+
public DateTime RenewUntil { get; private set; }
103+
/// <summary>
104+
/// Time skew.
105+
/// </summary>
106+
public TimeSpan TimeSkew { get; private set; }
107+
/// <summary>
108+
/// Ticket.
109+
/// </summary>
110+
public KerberosTicket Ticket { get; private set; }
111+
112+
internal static bool TryParse(KERB_EXTERNAL_TICKET ticket, out KerberosExternalTicket result)
113+
{
114+
result = null;
115+
try
116+
{
117+
var ret = new KerberosExternalTicket();
118+
ret.ServiceName = ParseName(ticket.ServiceName);
119+
ret.TargetName = ParseName(ticket.TargetName);
120+
ret.ClientName = ParseName(ticket.ClientName);
121+
ret.DomainName = ticket.DomainName.ToString();
122+
ret.TargetDomainName = ticket.TargetDomainName.ToString();
123+
ret.AltTargetDomainName = ticket.AltTargetDomainName.ToString();
124+
ret.SessionKey = ParseKey(ret.ServiceName, ret.DomainName, ticket.SessionKey);
125+
ret.TicketFlags = (KerberosTicketFlags)ticket.TicketFlags.SwapEndian();
126+
ret.Flags = ticket.Flags;
127+
ret.KeyExpirationTime = ticket.KeyExpirationTime.ToDateTime();
128+
ret.StartTime = ticket.StartTime.ToDateTime();
129+
ret.EndTime = ticket.EndTime.ToDateTime();
130+
ret.RenewUntil = ticket.RenewUntil.ToDateTime();
131+
ret.TimeSkew = new TimeSpan(ticket.TimeSkew.QuadPart);
132+
byte[] ticket_data = ticket.ReadTicket();
133+
DERValue[] values = DERParser.ParseData(ticket_data, 0);
134+
if (values.Length != 1)
135+
return false;
136+
ret.Ticket = KerberosTicket.Parse(values[0], ticket_data);
137+
result = ret;
138+
return true;
139+
}
140+
catch (InvalidDataException)
141+
{
142+
return false;
143+
}
144+
}
145+
}
146+
}

NtApiDotNet/Win32/Security/Authentication/Kerberos/PrincipalName.cs renamed to NtApiDotNet/Win32/Security/Authentication/Kerberos/KerberosPrincipalName.cs

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -55,10 +55,16 @@ public string GetPrincipal(string realm)
5555
return $"{FullName}@{realm}";
5656
}
5757

58-
internal KerberosPrincipalName()
58+
internal KerberosPrincipalName()
59+
: this(KerberosNameType.UNKNOWN, new string[0])
5960
{
60-
NameType = KerberosNameType.UNKNOWN;
61-
Names = new List<string>().AsReadOnly();
61+
}
62+
63+
internal KerberosPrincipalName(KerberosNameType name_type,
64+
IEnumerable<string> names)
65+
{
66+
NameType = name_type;
67+
Names = new List<string>(names).AsReadOnly();
6268
}
6369

6470
internal static KerberosPrincipalName Parse(DERValue value)

0 commit comments

Comments
 (0)