protected override async Task <ReadOnlyMemory <byte> > ExecuteCore(IKerberosMessage message) { // 1. check what pre-auth validation is required for user // 2. enumerate all pre-auth handlers that are available // - fail hard if required doesn't intersect available // 3. if pre-auth is required and not present, return error prompting for it // 4. if pre-auth is present, validate it // 5. if pre-auth failed, return error // 6. if some pre-auth succeeded, return error // 7. if all required validation succeeds, generate PAC, TGT, and return it KrbAsReq asReq = (KrbAsReq)message; var principal = await RealmService.Principals.Find(asReq.Body.CName.FullyQualifiedName); try { var preAuthRequests = await ProcessPreAuth(asReq, principal); if (preAuthRequests.Count() > 0) { return(RequirePreAuth(preAuthRequests, principal)); } } catch (KerberosValidationException kex) { Log(kex); return(PreAuthFailed(kex, principal)); } return(await GenerateAsRep(asReq, principal)); }
public override async Task <ReadOnlyMemory <byte> > ExecuteCore(IKerberosMessage message, PreAuthenticationContext context) { // 1. check what pre-auth validation is required for user // 2. enumerate all pre-auth handlers that are available // - fail hard if required doesn't intersect available // 3. if pre-auth is required and not present, return error prompting for it // 4. if pre-auth is present, validate it // 5. if pre-auth failed, return error // 6. if some pre-auth succeeded, return error // 7. if all required validation succeeds, generate PAC, TGT, and return it if (context.Failure != null) { return(PreAuthFailed(context)); } KrbAsReq asReq = (KrbAsReq)message; if (context.Principal == null) { logger.LogInformation("User {User} not found in realm {Realm}", asReq.Body.CName.FullyQualifiedName, RealmService.Name); return(GenerateError(KerberosErrorCode.KDC_ERR_S_PRINCIPAL_UNKNOWN, null, RealmService.Name, asReq.Body.CName.FullyQualifiedName)); } if (!context.PreAuthenticationSatisfied) { return(RequirePreAuth(context)); } return(await GenerateAsRep(context, asReq)); }
protected override async Task <ReadOnlyMemory <byte> > ExecuteCore(IKerberosMessage message) { // 1. check what pre-auth validation is required for user // 2. enumerate all pre-auth handlers that are available // - fail hard if required doesn't intersect available // 3. if pre-auth is required and not present, return error prompting for it // 4. if pre-auth is present, validate it // 5. if pre-auth failed, return error // 6. if some pre-auth succeeded, return error // 7. if all required validation succeeds, generate PAC, TGT, and return it KrbAsReq asReq = (KrbAsReq)message; var username = asReq.Body.CName.FullyQualifiedName; var principal = await RealmService.Principals.Find(username); if (principal == null) { logger.LogInformation("User {User} not found in realm {Realm}", username, RealmService.Name); return(GenerateError(KerberosErrorCode.KDC_ERR_S_PRINCIPAL_UNKNOWN, null, RealmService.Name, username)); } var preauth = new PreAuthenticationContext { Principal = principal }; try { var preAuthRequests = await ProcessPreAuth(preauth, asReq); if (preAuthRequests.Count() > 0) { return(RequirePreAuth(preAuthRequests, principal)); } } catch (KerberosValidationException kex) { logger.LogWarning(kex, "AS-REQ failed processing for principal {Principal}", principal); return(PreAuthFailed(kex, principal)); } return(await GenerateAsRep(preauth, asReq)); }
public override async Task <PreAuthenticationContext> ValidateTicketRequest(IKerberosMessage message) { KrbAsReq asReq = (KrbAsReq)message; await SetRealmContext(asReq.Realm); var username = asReq.Body.CName.FullyQualifiedName; var principal = await RealmService.Principals.Find(username); var preauth = new PreAuthenticationContext { Principal = principal }; if (preauth.Principal == null) { return(preauth); } try { var preauthReq = await ProcessPreAuth(preauth, asReq); if (preauth.PaData == null) { preauth.PaData = Array.Empty <KrbPaData>(); } preauth.PaData = preauth.PaData.Union(preauthReq).ToArray(); } catch (KerberosValidationException kex) { logger.LogWarning(kex, "AS-REQ failed processing for principal {Principal}", principal); preauth.Failure = kex; } return(preauth); }
public override async Task <PreAuthenticationContext> ValidateTicketRequest(IKerberosMessage message) { var tgsReq = (KrbTgsReq)message; await SetRealmContext(tgsReq.Realm); 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); return(new PreAuthenticationContext { Principal = principal, EncryptedPartKey = krbtgtApReqDecrypted.SessionKey, Ticket = krbtgtApReqDecrypted.Ticket }); }
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()); }