private static DecryptedKrbApReq CreateDecryptedApReq(DateTimeOffset now, DateTimeOffset notBefore, DateTimeOffset notAfter, DateTimeOffset renewUntil) { var key = new KerberosKey(key: new byte[16], etype: EncryptionType.AES128_CTS_HMAC_SHA1_96); var tgsRep = KrbKdcRep.GenerateServiceTicket <KrbTgsRep>(new ServiceTicketRequest { EncryptedPartKey = key, Principal = new FakeKerberosPrincipal("*****@*****.**"), ServicePrincipal = new FakeKerberosPrincipal("host/test.com"), ServicePrincipalKey = key, IncludePac = false, RealmName = "test.com", Now = now, StartTime = notBefore, EndTime = notAfter, RenewTill = renewUntil, Flags = TicketFlags.Renewable }); var encKdcRepPart = tgsRep.EncPart.Decrypt( key, KeyUsage.EncTgsRepPartSessionKey, d => KrbEncTgsRepPart.DecodeApplication(d) ); var apReq = KrbApReq.CreateApReq(tgsRep, encKdcRepPart.Key.AsKey(), default, out KrbAuthenticator authenticator);
public async Task CreateServiceTicket_NullEncPartKey() { await KrbKdcRep.GenerateServiceTicket <KrbTgsRep>(new ServiceTicketRequest { EncryptedPartKey = null }); }
public void CreateServiceTicket_NullEncPartKey() { KrbKdcRep.GenerateServiceTicket <KrbTgsRep>(new ServiceTicketRequest { EncryptedPartKey = null }); }
public async Task CreateServiceTicket_NullServicePrincipalKey() { var key = KrbEncryptionKey.Generate(EncryptionType.AES128_CTS_HMAC_SHA1_96).AsKey(); await KrbKdcRep.GenerateServiceTicket <KrbTgsRep>(new ServiceTicketRequest { EncryptedPartKey = key, ServicePrincipal = new FakeKerberosPrincipal("*****@*****.**") }); }
public void CreateServiceTicket_NullServicePrincipal() { var key = KrbEncryptionKey.Generate(EncryptionType.AES128_CTS_HMAC_SHA1_96).AsKey(); KrbKdcRep.GenerateServiceTicket <KrbTgsRep>(new ServiceTicketRequest { EncryptedPartKey = key, ServicePrincipal = null }); }
public async Task CreateServiceTicket() { var key = KrbEncryptionKey.Generate(EncryptionType.AES128_CTS_HMAC_SHA1_96).AsKey(); var ticket = await KrbKdcRep.GenerateServiceTicket <KrbTgsRep>(new ServiceTicketRequest { EncryptedPartKey = key, ServicePrincipal = new FakeKerberosPrincipal("*****@*****.**"), ServicePrincipalKey = key, Principal = new FakeKerberosPrincipal("*****@*****.**"), RealmName = "blah.com" }); Assert.IsNotNull(ticket); }
public void CreateServiceTicketOnCompatibilitySetting(string realm, KerberosCompatibilityFlags compatibilityFlags, string expectedRealm) { var key = KrbEncryptionKey.Generate(EncryptionType.AES128_CTS_HMAC_SHA1_96).AsKey(); var ticket = KrbKdcRep.GenerateServiceTicket <KrbTgsRep>(new ServiceTicketRequest { EncryptedPartKey = key, ServicePrincipal = new FakeKerberosPrincipal("*****@*****.**"), ServicePrincipalKey = key, Principal = new FakeKerberosPrincipal("*****@*****.**"), RealmName = realm, Compatibility = compatibilityFlags, }); Assert.IsNotNull(ticket); Assert.AreEqual(expectedRealm, ticket.CRealm); }
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()); }
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 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 if (context == null) { throw new ArgumentNullException(nameof(context)); } 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 = this.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, string.Empty, tgsReq.Body.Realm, 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? this.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 = this.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; } if (context.IncludePac == null) { context.IncludePac = DetectPacRequirement(tgsReq); if (context.IncludePac == null) { context.IncludePac = context.Ticket?.AuthorizationData?.Any(a => a.Type == AuthorizationDataType.AdIfRelevant) ?? false; } } var rst = new ServiceTicketRequest { KdcAuthorizationKey = context.EvidenceTicketKey, Principal = context.Principal, EncryptedPartKey = context.EncryptedPartKey, EncryptedPartEType = context.EncryptedPartEType, ServicePrincipal = context.ServicePrincipal, ServicePrincipalKey = serviceKey, RealmName = tgsReq.Body.Realm, Addresses = tgsReq.Body.Addresses, RenewTill = context.Ticket.RenewTill, StartTime = tgsReq.Body.From ?? DateTimeOffset.MinValue, EndTime = tgsReq.Body.Till, MaximumTicketLifetime = this.RealmService.Settings.SessionLifetime, Flags = flags, Now = now, Nonce = tgsReq.Body.Nonce, IncludePac = context.IncludePac ?? false, PreferredClientEType = GetPreferredEType( tgsReq.Body.EType, this.RealmService.Configuration.Defaults.PermittedEncryptionTypes, this.RealmService.Configuration.Defaults.AllowWeakCrypto ), Compatibility = this.RealmService.Settings.Compatibility, }; if (tgsReq.Body.KdcOptions.HasFlag(KdcOptions.Canonicalize)) { rst.SamAccountName = context.GetState <TgsState>(PaDataType.PA_TGS_REQ).DecryptedApReq.Ticket.CName.FullyQualifiedName; } // this is set here instead of in GenerateServiceTicket because GST is used by unit tests to // generate tickets with weird lifetimes for scenario testing and we don't want to break that rst.ClampLifetime(); var tgsRep = KrbKdcRep.GenerateServiceTicket <KrbTgsRep>(rst); 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()); }