Skip to content

Commit

Permalink
Added ability to read tickets from LSA
Browse files Browse the repository at this point in the history
  • Loading branch information
SteveSyfuhs committed Jan 13, 2022
1 parent 209b922 commit 3e3410f
Show file tree
Hide file tree
Showing 22 changed files with 821 additions and 206 deletions.
13 changes: 8 additions & 5 deletions Bruce/CommandLine/KerberosDestroyCommand.cs
Expand Up @@ -3,6 +3,7 @@
// The .NET Foundation licenses this file to you under the MIT license.
// -----------------------------------------------------------------------

using Kerberos.NET.Client;
using System;
using System.IO;
using System.Threading.Tasks;
Expand Down Expand Up @@ -34,18 +35,20 @@ public override async Task<bool> Execute()
client.Configuration.Defaults.DefaultCCacheName = this.Cache;
}

this.PurgeTickets(client.Configuration.Defaults.DefaultCCacheName);
this.PurgeTickets(client);

return true;
}

private void PurgeTickets(string cache)
private void PurgeTickets(KerberosClient client)
{
TicketCacheBase.TryParseCacheType(cache, out _, out string path);

try
{
File.Delete(Environment.ExpandEnvironmentVariables(path));
if (client.Cache is ITicketCache2 cache)
{
cache.PurgeTickets();
}

this.WriteLine(SR.Resource("CommandLine_KerberosDestroy_Deleted"));
}
catch (Exception ex)
Expand Down
44 changes: 33 additions & 11 deletions Bruce/CommandLine/KerberosListCommand.cs
Expand Up @@ -3,13 +3,13 @@
// The .NET Foundation licenses this file to you under the MIT license.
// -----------------------------------------------------------------------

using Kerberos.NET.Client;
using Kerberos.NET.Entities;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Security.Cryptography;
using System.Threading.Tasks;
using Kerberos.NET.Client;
using Kerberos.NET.Entities;

namespace Kerberos.NET.CommandLine
{
Expand Down Expand Up @@ -98,7 +98,7 @@ public override async Task<bool> Execute()
await this.RenewServiceTicket(client);
}

this.ListTickets(client.Configuration.Defaults.DefaultCCacheName);
this.ListTickets(client);

if (this.DescribeClient)
{
Expand Down Expand Up @@ -217,13 +217,20 @@ private async Task GetServiceTicket(KerberosClient client)
);
}

private void ListTickets(string cache)
private void ListTickets(KerberosClient client)
{
TicketCacheBase.TryParseCacheType(cache, out _, out string path);
IEnumerable<KerberosClientCacheEntry> cache;

var ticketCache = new Krb5TicketCache(path);
if (client.Cache is ITicketCache2 cache2)
{
cache = cache2.GetAll().Cast<KerberosClientCacheEntry>();
}
else
{
cache = Array.Empty<KerberosClientCacheEntry>();
}

var tickets = ticketCache.Krb5Cache.Credentials.Where(c => c.EndTime >= DateTimeOffset.UtcNow).ToArray();
var tickets = cache.Where(c => c.EndTime > DateTimeOffset.UtcNow).ToArray();

this.WriteLine(string.Format("{0}: {{TicketCount}}", SR.Resource("CommandLine_KList_Count")), tickets.Length);
this.WriteLine();
Expand All @@ -232,19 +239,34 @@ private void ListTickets(string cache)
{
var ticket = tickets[i];

KrbTicket decodedTicket = TryParseTicket(ticket.Ticket);
KrbTicket decodedTicket = ticket.KdcResponse.Ticket;

var properties = new List<(string, (string, object[]))>
{
(SR.Resource("CommandLine_KList_Client"), ("{CName} @ {Realm}", new[] { ticket.Client.FullyQualifiedName, ticket.Client.Realm })),
(SR.Resource("CommandLine_KList_Server"), ("{SName} @ {Realm}", new[] { ticket.Server.FullyQualifiedName, ticket.Server.Realm })),
(SR.Resource("CommandLine_KList_Client"), ("{CName} @ {Realm}", new[] { ticket.KdcResponse.CName.FullyQualifiedName, ticket.KdcResponse.CRealm })),
(SR.Resource("CommandLine_KList_Server"), ("{SName} @ {Realm}", new[] { ticket.SName.FullyQualifiedName, ticket.KdcResponse.Ticket.Realm })),
(SR.Resource("CommandLine_KList_TicketEType"), ("{EType} ({ETypeInt})", new object[] { decodedTicket?.EncryptedPart?.EType, (int)decodedTicket?.EncryptedPart?.EType })),
(SR.Resource("CommandLine_KList_Flags"), ("{FlagsHex:x} -> {Flags}", new object[] { (uint)ticket.Flags, ticket.Flags })),
(SR.Resource("CommandLine_KList_Start"), ("{StartTime}", new object[] { ticket.AuthTime.ToLocalTime() })),
(SR.Resource("CommandLine_KList_End"), ("{EndTime}", new object[] { ticket.EndTime.ToLocalTime() })),
(SR.Resource("CommandLine_KList_RenewTime"), ("{RenewTime}", new object[] { ticket.RenewTill.ToLocalTime() }))
(SR.Resource("CommandLine_KList_RenewTime"), ("{RenewTime}", new object[] { ticket.RenewTill?.ToLocalTime() })),
};

if (ticket.SessionKey != null)
{
properties.Add((SR.Resource("CommandLine_KList_SessionEType"), ("{EType} ({ETypeInt})", new object[] { ticket.SessionKey.EType, (int)ticket.SessionKey.EType })));
}

if (ticket.BranchId > 0)
{
properties.Add((SR.Resource("CommandLine_KList_BranchId"), ("{BranchHex:x} -> {Branch}", new object[] { (uint)ticket.Flags, ticket.Flags })));
}

if (!string.IsNullOrWhiteSpace(ticket.KdcCalled))
{
properties.Add((SR.Resource("CommandLine_KList_KdcCalled"), ("{Kdc}", new object[] { ticket.KdcCalled })));
}

var ticketEntryNumber = string.Format("#{0}>", i);

var max = properties.Max(p => p.Item1.Length) + 10;
Expand Down
15 changes: 9 additions & 6 deletions Bruce/CommandLine/KerberosWhoAmICommand.cs
Expand Up @@ -3,17 +3,14 @@
// The .NET Foundation licenses this file to you under the MIT license.
// -----------------------------------------------------------------------

using System;
using Kerberos.NET.Client;
using Kerberos.NET.Entities;
using Kerberos.NET.Entities.Pac;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Security.Claims;
using System.Threading.Tasks;
using Humanizer;
using Kerberos.NET.Client;
using Kerberos.NET.Entities;
using Kerberos.NET.Entities.Pac;
using Kerberos.NET.Reflection;

namespace Kerberos.NET.CommandLine
{
Expand Down Expand Up @@ -64,6 +61,12 @@ public override async Task<bool> Execute()

private async Task S4u2Self(KerberosClient client)
{
if (client.Cache is LsaCredentialCache)
{
this.WriteLineError("The whoami command isn't supported with the LSA Cache");
return;
}

var myTgtEntry = client.Cache.GetCacheItem<KerberosClientCacheEntry>($"krbtgt/{client.DefaultDomain}");

var myTgt = myTgtEntry.KdcResponse?.Ticket;
Expand Down
27 changes: 27 additions & 0 deletions Bruce/Strings.Designer.cs

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

9 changes: 9 additions & 0 deletions Bruce/Strings.resx
Expand Up @@ -474,6 +474,9 @@
<data name="CommandLine_KInit_PassPrompt" xml:space="preserve">
<value>Password for {0}: </value>
</data>
<data name="CommandLine_KList_BranchId" xml:space="preserve">
<value>Branch Id</value>
</data>
<data name="CommandLine_KList_Client" xml:space="preserve">
<value>Client</value>
</data>
Expand All @@ -489,12 +492,18 @@
<data name="CommandLine_KList_Flags" xml:space="preserve">
<value>Flags</value>
</data>
<data name="CommandLine_KList_KdcCalled" xml:space="preserve">
<value>KDC Called</value>
</data>
<data name="CommandLine_KList_RenewTime" xml:space="preserve">
<value>Renew Until</value>
</data>
<data name="CommandLine_KList_Server" xml:space="preserve">
<value>Server</value>
</data>
<data name="CommandLine_KList_SessionEType" xml:space="preserve">
<value>Session Key Type</value>
</data>
<data name="CommandLine_KList_Start" xml:space="preserve">
<value>Start Time</value>
</data>
Expand Down
Expand Up @@ -4,10 +4,22 @@
// -----------------------------------------------------------------------

using System;
using System.Collections.Generic;
using System.Threading.Tasks;

namespace Kerberos.NET
{
public interface ITicketCache2 : ITicketCache
{
void PurgeTickets();

Task PurgeTicketsAsync();

IEnumerable<object> GetAll();

Task<IEnumerable<object>> GetAllAsync();
}

public interface ITicketCache
{
bool RefreshTickets { get; set; }
Expand Down
16 changes: 16 additions & 0 deletions Kerberos.NET/Cache/ITicketReplayValidator.cs
@@ -0,0 +1,16 @@
// -----------------------------------------------------------------------
// Licensed to The .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// -----------------------------------------------------------------------

using System.Threading.Tasks;

namespace Kerberos.NET
{
public interface ITicketReplayValidator
{
Task<bool> Add(TicketCacheEntry entry);

Task<bool> Contains(TicketCacheEntry entry);
}
}
Expand Up @@ -5,6 +5,7 @@

using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Threading.Tasks;
Expand Down Expand Up @@ -108,6 +109,11 @@ public override bool Contains(TicketCacheEntry entry)
return this.cache.ContainsKey(entry.Computed);
}

public override IEnumerable<object> GetAll()
{
return this.cache.Values.Select(v => v.Value).AsEnumerable();
}

public override ValueTask<object> GetCacheItemAsync(string key, string container = null)
{
return new ValueTask<object>(this.GetCacheItem(key, container));
Expand Down Expand Up @@ -163,6 +169,11 @@ public override async ValueTask<T> GetCacheItemAsync<T>(string key, string conta
return result != null ? (T)result : default;
}

public override void PurgeTickets()
{
this.cache.Clear();
}

[DebuggerDisplay("{Key}; E: {Expiration}; R: {RenewUntil};")]
internal class CacheEntry
{
Expand Down
File renamed without changes.

0 comments on commit 3e3410f

Please sign in to comment.