public override ReadOnlyMemory <byte> ExecuteCore(PreAuthenticationContext context) { // Now that we know who is requesting the ticket we can issue the ticket // // 3. Find the requested service principal // 4. Determine if the requested service principal is in another realm and if so refer them // 5. Evaluate whether the client identity should get a ticket to the service // 6. Evaluate whether it should do U2U and if so extract that key instead // 7. Generate a service ticket for the calling client to the service // 8. return to client var tgsReq = (KrbTgsReq)context.Message; if (context.ServicePrincipal == null) { // we can't find what they're asking for, but maybe it's in a realm we can transit? context.ServicePrincipal = ProposeTransitedRealm(tgsReq, context); } if (context.ServicePrincipal == null) { // we have no idea what service they're asking for and // there isn't a realm we can refer them to that can issue a ticket return(GenerateError( KerberosErrorCode.KDC_ERR_S_PRINCIPAL_UNKNOWN, "", RealmService.Name, tgsReq.Body.SName.FullyQualifiedName )); } // renewal is an odd case here because the SName will be krbtgt // does this need to be validated more than the Decrypt call? EvaluateSecurityPolicy(context.Principal, context.ServicePrincipal); KerberosKey serviceKey; if (tgsReq.Body.KdcOptions.HasFlag(KdcOptions.EncTktInSkey)) { serviceKey = GetUserToUserTicketKey(tgsReq.Body.AdditionalTickets, context); } else { serviceKey = context.ServicePrincipal.RetrieveLongTermCredential(); } var now = RealmService.Now(); TicketFlags flags = 0; if (tgsReq.Body.KdcOptions.HasFlag(KdcOptions.Forwardable)) { flags |= TicketFlags.Forwardable; } if (context.Ticket.Flags.HasFlag(TicketFlags.PreAuthenticated)) { flags |= TicketFlags.PreAuthenticated; } var includePac = DetectPacRequirement(tgsReq); if (includePac == null) { includePac = context.Ticket?.AuthorizationData?.Any(a => a.Type == AuthorizationDataType.AdIfRelevant) ?? false; } var tgsRep = KrbKdcRep.GenerateServiceTicket <KrbTgsRep>( new ServiceTicketRequest { KdcAuthorizationKey = context.EvidenceTicketKey, Principal = context.Principal, EncryptedPartKey = context.EncryptedPartKey, ServicePrincipal = context.ServicePrincipal, ServicePrincipalKey = serviceKey, RealmName = RealmService.Name, Addresses = tgsReq.Body.Addresses, RenewTill = context.Ticket.RenewTill, StartTime = now - RealmService.Settings.MaximumSkew, EndTime = now + RealmService.Settings.SessionLifetime, Flags = flags, Now = now, Nonce = tgsReq.Body.Nonce, IncludePac = includePac ?? false } ); return(tgsRep.EncodeApplication()); }
public override async Task <ReadOnlyMemory <byte> > ExecuteCore(IKerberosMessage message, PreAuthenticationContext context) { // the logic for a TGS-REQ is relatively simple in the primary case where you have a TGT and want // to get a ticket to another service. It gets a bit more complicated when you need to do something // like a U2U exchange, renew, or get a referral to another realm. Realm referral isn't supported yet. // 1. Get the ApReq (TGT) from the PA-Data of the request // 2. Decrypt the TGT and extract the client calling identity // 3. Find the requested service principal // 4. Evaluate whether the client identity should get a ticket to the service // 5. Evaluate whether it should do U2U and if so extract that key instead // 6. Generate a service ticket for the calling client to the service // 7. return to client var tgsReq = (KrbTgsReq)message; logger.LogInformation("TGS-REQ incoming. SPN = {SPN}", tgsReq.Body.SName.FullyQualifiedName); var krbtgtIdentity = await RealmService.Principals.RetrieveKrbtgt(); var krbtgtKey = await krbtgtIdentity.RetrieveLongTermCredential(); var servicePrincipal = await RealmService.Principals.Find(tgsReq.Body.SName.FullyQualifiedName); // renewal is an odd case here because the SName will be krbtgt // does this need to be validated more than the Decrypt call? await EvaluateSecurityPolicy(context.Principal, servicePrincipal); KerberosKey serviceKey; if (tgsReq.Body.KdcOptions.HasFlag(KdcOptions.EncTktInSkey)) { serviceKey = GetUserToUserTicketKey(tgsReq.Body.AdditionalTickets, krbtgtKey); } else { serviceKey = await servicePrincipal.RetrieveLongTermCredential(); } var now = RealmService.Now(); TicketFlags flags = 0; if (tgsReq.Body.KdcOptions.HasFlag(KdcOptions.Forwardable)) { flags |= TicketFlags.Forwardable; } if (context.Ticket.Flags.HasFlag(TicketFlags.PreAuthenticated)) { flags |= TicketFlags.PreAuthenticated; } var tgsRep = await KrbKdcRep.GenerateServiceTicket <KrbTgsRep>( new ServiceTicketRequest { Principal = context.Principal, EncryptedPartKey = context.EncryptedPartKey, ServicePrincipal = servicePrincipal, ServicePrincipalKey = serviceKey, RealmName = RealmService.Name, Addresses = tgsReq.Body.Addresses, RenewTill = context.Ticket.RenewTill, StartTime = now - RealmService.Settings.MaximumSkew, EndTime = now + RealmService.Settings.SessionLifetime, Flags = flags, Now = now, Nonce = tgsReq.Body.Nonce, IncludePac = context.Ticket.AuthorizationData.Any(a => a.Type == AuthorizationDataType.AdIfRelevant) } ); return(tgsRep.EncodeApplication()); }
protected override async Task <ReadOnlyMemory <byte> > ExecuteCore(IKerberosMessage message) { // the logic for a TGS-REQ is relatively simple in the primary case where you have a TGT and want // to get a ticket to another service. It gets a bit more complicated when you need to do something // like a U2U exchange, renew, or get a referral to another realm. Realm referral isn't supported yet. // 1. Get the ApReq (TGT) from the PA-Data of the request // 2. Decrypt the TGT and extract the client calling identity // 3. Find the requested service principal // 4. Evaluate whether the client identity should get a ticket to the service // 5. Evaluate whether it should do U2U and if so extract that key instead // 6. Generate a service ticket for the calling client to the service // 7. return to client var tgsReq = (KrbTgsReq)message; var apReq = ExtractApReq(tgsReq); var krbtgtIdentity = await RealmService.Principals.RetrieveKrbtgt(); var krbtgtKey = await krbtgtIdentity.RetrieveLongTermCredential(); var krbtgtApReqDecrypted = DecryptApReq(apReq, krbtgtKey); var principal = await RealmService.Principals.Find(krbtgtApReqDecrypted.Ticket.CName.FullyQualifiedName); var servicePrincipal = await RealmService.Principals.Find(tgsReq.Body.SName.FullyQualifiedName); // renewal is an odd case here because the SName will be krbtgt // does this need to be validated more than the Decrypt call? await EvaluateSecurityPolicy(principal, servicePrincipal, krbtgtApReqDecrypted); KerberosKey serviceKey; if (tgsReq.Body.KdcOptions.HasFlag(KdcOptions.EncTktInSkey)) { serviceKey = GetUserToUserTicketKey(tgsReq.Body.AdditionalTickets, krbtgtKey); } else { serviceKey = await servicePrincipal.RetrieveLongTermCredential(); } var now = RealmService.Now(); var tgsRep = await KrbKdcRep.GenerateServiceTicket <KrbTgsRep>( new ServiceTicketRequest { Principal = principal, EncryptedPartKey = krbtgtApReqDecrypted.SessionKey, ServicePrincipal = servicePrincipal, ServicePrincipalKey = serviceKey, RealmName = RealmService.Name, Addresses = tgsReq.Body.Addresses, RenewTill = krbtgtApReqDecrypted.Ticket.RenewTill, StartTime = now - RealmService.Settings.MaximumSkew, EndTime = now + RealmService.Settings.SessionLifetime, Flags = KrbKdcRep.DefaultFlags, Now = now } ); return(tgsRep.EncodeApplication()); }