protected override async Task <HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken) { var realmService = new FakeRealmService(Realm); var principal = await realmService.Principals.Find(UserUpn); var principalKey = await principal.RetrieveLongTermCredential(); var rst = new ServiceTicketRequest { Principal = principal, EncryptedPartKey = principalKey, ServicePrincipalKey = new KerberosKey(key: TgtKey, etype: EncryptionType.AES256_CTS_HMAC_SHA1_96) }; var tgt = await KrbAsRep.GenerateTgt(rst, realmService); var encoded = tgt.EncodeApplication(); var response = new Memory <byte>(new byte[encoded.Length + 4]); Endian.ConvertToBigEndian(encoded.Length, response.Slice(0, 4)); encoded.CopyTo(response.Slice(4)); var kdcMessage = new KdcProxyMessage { KerbMessage = response }; return(new HttpResponseMessage(HttpStatusCode.OK) { Content = new ByteArrayContent(kdcMessage.Encode().ToArray()) }); }
private ReadOnlyMemory <byte> GenerateAsRep(KrbAsReq asReq, PreAuthenticationContext context) { // 1. detect if specific PAC contents are requested (claims) // 2. if requested generate PAC for user // 3. stuff PAC into ad-if-relevant pa-data of krbtgt ticket // 4. look up krbtgt account // 5. encrypt against krbtgt // 6. done var rst = new ServiceTicketRequest { Principal = context.Principal, EncryptedPartKey = context.EncryptedPartKey, ServicePrincipal = context.ServicePrincipal, Addresses = asReq.Body.Addresses, Nonce = asReq.Body.Nonce, IncludePac = true, Flags = TicketFlags.Initial | KrbKdcRep.DefaultFlags }; if (!asReq.Body.KdcOptions.HasFlag(KdcOptions.Canonicalize)) { rst.SamAccountName = asReq.Body.CName.FullyQualifiedName; } if (context.ClientAuthority != PaDataType.PA_NONE) { rst.Flags |= TicketFlags.PreAuthenticated; } if (rst.EncryptedPartKey == null) { rst.EncryptedPartKey = rst.Principal.RetrieveLongTermCredential(); } rst.IncludePac = DetectPacRequirement(asReq) ?? false; var asRep = KrbAsRep.GenerateTgt(rst, RealmService); if (context.PaData != null) { asRep.PaData = context.PaData.ToArray(); } return(asRep.EncodeApplication()); }
private async Task <ReadOnlyMemory <byte> > GenerateAsRep(KrbAsReq asReq, IKerberosPrincipal principal) { // 1. detect if specific PAC contents are requested (claims) // 2. if requested generate PAC for user // 3. stuff PAC into ad-if-relevant pa-data of krbtgt ticket // 4. look up krbtgt account // 5. encrypt against krbtgt // 6. done var requirements = new List <KrbPaData>(); foreach (var handler in postProcessAuthHandlers) { await InvokePreAuthHandler(null, principal, requirements, handler.Value); } var rst = new ServiceTicketRequest { Principal = principal, Addresses = asReq.Body.Addresses, Nonce = asReq.Body.Nonce, IncludePac = true, Flags = TicketFlags.Initial | KrbKdcRep.DefaultFlags }; rst.EncryptedPartKey = await principal.RetrieveLongTermCredential(); var pacRequest = asReq.PaData.FirstOrDefault(pa => pa.Type == PaDataType.PA_PAC_REQUEST); if (pacRequest != null) { var paPacRequest = KrbPaPacRequest.Decode(pacRequest.Value); rst.IncludePac = paPacRequest.IncludePac; } var asRep = await KrbAsRep.GenerateTgt(rst, RealmService); asRep.PaData = requirements.ToArray(); return(asRep.EncodeApplication()); }
private async Task <ReadOnlyMemory <byte> > GenerateAsRep(PreAuthenticationContext preauth, KrbAsReq asReq) { // 1. detect if specific PAC contents are requested (claims) // 2. if requested generate PAC for user // 3. stuff PAC into ad-if-relevant pa-data of krbtgt ticket // 4. look up krbtgt account // 5. encrypt against krbtgt // 6. done var rst = new ServiceTicketRequest { Principal = preauth.Principal, EncryptedPartKey = preauth.EncryptedPartKey, Addresses = asReq.Body.Addresses, Nonce = asReq.Body.Nonce, IncludePac = true, Flags = TicketFlags.Initial | KrbKdcRep.DefaultFlags }; if (rst.EncryptedPartKey == null) { rst.EncryptedPartKey = await rst.Principal.RetrieveLongTermCredential(); } var pacRequest = asReq.PaData.FirstOrDefault(pa => pa.Type == PaDataType.PA_PAC_REQUEST); if (pacRequest != null) { var paPacRequest = KrbPaPacRequest.Decode(pacRequest.Value); rst.IncludePac = paPacRequest.IncludePac; } var asRep = await KrbAsRep.GenerateTgt(rst, RealmService); if (preauth.PaData != null) { asRep.PaData = preauth.PaData.ToArray(); } return(asRep.EncodeApplication()); }
public void GenerateTgt() { var realmService = new FakeRealmService(Realm); var principal = realmService.Principals.Find(KrbPrincipalName.FromString(UserUpn)); var principalKey = principal.RetrieveLongTermCredential(); var rst = new ServiceTicketRequest { Flags = TicketFlags.EncryptedPreAuthentication | TicketFlags.Renewable | TicketFlags.Forwardable, Principal = principal, EncryptedPartKey = principalKey, ServicePrincipalKey = new KerberosKey(key: TgtKey, etype: etype, kvno: 123) }; for (var i = 0; i < AuthenticationAttempts; i++) { var tgt = KrbAsRep.GenerateTgt(rst, realmService); Assert.IsNotNull(tgt); } }
public void GeneratedTgtMatchesActiveDirectory() { var realmService = new FakeRealmService(Realm); var principal = realmService.Principals.Find(KrbPrincipalName.FromString(UserUpn)); var principalKey = principal.RetrieveLongTermCredential(); var rst = new ServiceTicketRequest { Flags = ExpectedFlags, Principal = principal, EncryptedPartKey = principalKey, ServicePrincipalKey = new KerberosKey(key: TgtKey, etype: EncryptionType.AES256_CTS_HMAC_SHA1_96) }; var tgt = KrbAsRep.GenerateTgt(rst, realmService); Assert.IsNotNull(tgt); var encoded = tgt.EncodeApplication(); AssertIsExpectedKrbtgt(principalKey, rst.ServicePrincipalKey, encoded.ToArray()); }
public async Task GeneratedTgtMatchesWithOnPremisesSamAccountName() { var realmService = new FakeRealmService(Realm); var principal = await realmService.Principals.Find(UserUpn); var principalKey = await principal.RetrieveLongTermCredential(); var rst = new ServiceTicketRequest { SamAccountName = TestSamAccountName, Flags = ExpectedFlags, Principal = principal, EncryptedPartKey = principalKey, ServicePrincipalKey = new KerberosKey(key: TgtKey, etype: EncryptionType.AES256_CTS_HMAC_SHA1_96) }; var tgt = await KrbAsRep.GenerateTgt(rst, realmService); Assert.IsNotNull(tgt); var encoded = tgt.EncodeApplication(); AssertIsExpectedKrbtgtWithOnPremisesSamAccountName(principalKey, rst.ServicePrincipalKey, encoded.ToArray()); }
public void GeneratedTgtMatchesWithOnPremisesSamAccountName(string realm, KerberosCompatibilityFlags compatibilityFlags, string expectedRealm) { var realmService = new FakeRealmService(realm, compatibilityFlags: compatibilityFlags); var principal = realmService.Principals.Find(KrbPrincipalName.FromString(UserUpn)); var principalKey = principal.RetrieveLongTermCredential(); var rst = new ServiceTicketRequest { SamAccountName = TestSamAccountName, Flags = ExpectedFlags, Principal = principal, EncryptedPartKey = principalKey, ServicePrincipalKey = new KerberosKey(key: TgtKey, etype: EncryptionType.AES256_CTS_HMAC_SHA1_96) }; var tgt = KrbAsRep.GenerateTgt(rst, realmService); Assert.IsNotNull(tgt); var encoded = tgt.EncodeApplication(); AssertIsExpectedKrbtgtWithOnPremisesSamAccountName(principalKey, rst.ServicePrincipalKey, encoded.ToArray(), expectedRealm); }
private ReadOnlyMemory <byte> GenerateAsRep(KrbAsReq asReq, PreAuthenticationContext context) { // 1. detect if specific PAC contents are requested (claims) // 2. if requested generate PAC for user // 3. stuff PAC into ad-if-relevant pa-data of krbtgt ticket // 4. look up krbtgt account // 5. encrypt against krbtgt // 6. done var rst = new ServiceTicketRequest { Principal = context.Principal, EncryptedPartKey = context.EncryptedPartKey, EncryptedPartEType = context.EncryptedPartEType, ServicePrincipal = context.ServicePrincipal, Addresses = asReq.Body.Addresses, Nonce = asReq.Body.Nonce, Now = this.RealmService.Now(), StartTime = asReq.Body.From ?? DateTimeOffset.MinValue, EndTime = asReq.Body.Till, MaximumTicketLifetime = this.RealmService.Settings.SessionLifetime, Flags = TicketFlags.Initial | KrbKdcRep.DefaultFlags, PreferredClientEType = GetPreferredEType( asReq.Body.EType, this.RealmService.Configuration.Defaults.PermittedEncryptionTypes, this.RealmService.Configuration.Defaults.AllowWeakCrypto ), Compatibility = this.RealmService.Settings.Compatibility, }; if (context.ClientAuthority != PaDataType.PA_NONE) { rst.Flags |= TicketFlags.PreAuthenticated; } // Canonicalize means the CName in the reply is allowed to be different from the CName in the request. // If this is not allowed, then we must use the CName from the request. Otherwise, we will set the CName // to what we have in our realm, i.e. user@realm. if (!asReq.Body.KdcOptions.HasFlag(KdcOptions.Canonicalize)) { rst.SamAccountName = asReq.Body.CName.FullyQualifiedName; } if (rst.EncryptedPartKey == null) { rst.EncryptedPartKey = rst.Principal.RetrieveLongTermCredential(); } if (context.IncludePac == null) { context.IncludePac = DetectPacRequirement(asReq); } rst.IncludePac = context.IncludePac ?? false; // 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 asRep = KrbAsRep.GenerateTgt(rst, this.RealmService); if (context.PaData != null) { asRep.PaData = context.PaData.ToArray(); } return(asRep.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()); }