-
Notifications
You must be signed in to change notification settings - Fork 106
Expand file tree
/
Copy pathPaDataTgsTicketHandler.cs
More file actions
151 lines (116 loc) · 6.07 KB
/
PaDataTgsTicketHandler.cs
File metadata and controls
151 lines (116 loc) · 6.07 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
// -----------------------------------------------------------------------
// Licensed to The .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// -----------------------------------------------------------------------
using System;
using System.Linq;
using Kerberos.NET.Crypto;
using Kerberos.NET.Entities;
namespace Kerberos.NET.Server
{
public class PaDataTgsTicketHandler : KdcPreAuthenticationHandlerBase
{
public PaDataTgsTicketHandler(IRealmService service)
: base(service)
{
}
public ValidationActions Validation { get; set; } = ValidationActions.All & ~ValidationActions.Replay;
/// <summary>
/// Executes before the validation stage and can be used for initial decoding of the message.
/// </summary>
/// <param name="preauth">The current context of the request</param>
public override void PreValidate(PreAuthenticationContext preauth)
{
if (preauth == null)
{
throw new ArgumentNullException(nameof(preauth));
}
ExtractApReq(preauth);
if (preauth.EvidenceTicketKey == null)
{
return;
}
var state = preauth.GetState<TgsState>(PaDataType.PA_TGS_REQ);
state.DecryptedApReq = this.DecryptApReq(state.ApReq, preauth.EvidenceTicketKey, preauth.ExpectedChannelBindings);
}
/// <summary>
/// Executes the primary validation process for the pre-auth data.
/// </summary>
/// <param name="asReq">The message to validate</param>
/// <param name="context">The current context of the request</param>
/// <returns>Optionally returns PA-Data that needs to be sent to the client otherwise returns null.</returns>
public override KrbPaData Validate(KrbKdcReq asReq, PreAuthenticationContext context)
{
if (context == null)
{
throw new ArgumentNullException(nameof(context));
}
// First we authenticate the incoming request
//
// 1. Get the ApReq (TGT) from the PA-Data of the request
// 2. Decrypt the TGT and extract the client calling identity
if (context.EvidenceTicketIdentity == null)
{
// we wouldn't ever hit this in the normal case, but we could hit it
// if a referral came in from a realm we don't recognize or don't trust
throw new KerberosProtocolException(KerberosErrorCode.KDC_ERR_S_PRINCIPAL_UNKNOWN);
}
var evidenceSName = KrbPrincipalName.FromPrincipal(context.EvidenceTicketIdentity, PrincipalNameType.NT_SRV_INST);
if (!evidenceSName.IsKrbtgt())
{
// spec-wise this isn't exactly correct as the authz ticket might be for renewal
// we will deviate from the spec because that's how other KDCs operate today
// KDC_ERR_PADATA_TYPE_NOSUPP is the closest error to indicate the way you
// authenticated the request is not something we're willing to accept
throw new KerberosProtocolException(KerberosErrorCode.KDC_ERR_PADATA_TYPE_NOSUPP);
}
// we need to validate that the evidence ticket is the KDC service (krbtgt)
// it might either be our KDC that issued it, or a KDC from another realm (referral)
// if it's ours it'll match our service name (krbtgt), and it'll decrypt with our key
// if it's a referral it'll match a trusted realm's name and decrypt with their key
// if it's a referral then the incoming identity will also need to be transposed
// no matter what we only ever want the TGS service ticket
// it might belong to another realm, but that's ok because it could be a referral
// it is a krbtgt service identity we recognize
// it could be ours, or a referral from a trusted realm
// in either case we can and will validate the ticket and
// extract the user principal from within the krbtgt ticket
var krbtgtKey = context.EvidenceTicketIdentity.RetrieveLongTermCredential()
?? throw new KerberosProtocolException(KerberosErrorCode.KDC_ERR_ETYPE_NOSUPP);
context.EvidenceTicketKey ??= krbtgtKey;
var state = context.GetState<TgsState>(PaDataType.PA_TGS_REQ);
state.DecryptedApReq ??= this.DecryptApReq(state.ApReq, context.EvidenceTicketKey, context.ExpectedChannelBindings);
context.EncryptedPartKey = state.DecryptedApReq.SessionKey;
context.Ticket = state.DecryptedApReq.Ticket;
return null;
}
/// <summary>
/// Locate the AP-REQ in the PA-Data of a TGS-REQ.
/// </summary>
/// <param name="context">The current contex of the request.</param>
/// <returns>Returns the AP-REQ message within the TGS-REQ PA-Data.</returns>
public static KrbApReq ExtractApReq(PreAuthenticationContext context)
{
if (context == null)
{
throw new ArgumentNullException(nameof(context));
}
var state = context.GetState<TgsState>(PaDataType.PA_TGS_REQ);
if (state.ApReq == null)
{
var tgsReq = (KrbTgsReq)context.Message;
var paData = tgsReq.PaData.First(p => p.Type == PaDataType.PA_TGS_REQ);
state.ApReq = paData.DecodeApReq();
}
return state.ApReq;
}
private DecryptedKrbApReq DecryptApReq(KrbApReq apReq, KerberosKey krbtgtKey, GssChannelBindings expectedChannelBindings)
{
var apReqDecrypted = new DecryptedKrbApReq(apReq, MessageType.KRB_TGS_REQ);
apReqDecrypted.Decrypt(krbtgtKey);
apReqDecrypted.ExpectedChannelBindings = expectedChannelBindings;
apReqDecrypted.Validate(this.Validation);
return apReqDecrypted;
}
}
}