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())
                });
            }
예제 #2
0
        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());
        }
예제 #5
0
        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);
            }
        }
예제 #6
0
        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());
        }
예제 #7
0
        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());
        }
예제 #8
0
        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);
        }
예제 #9
0
        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());
        }
예제 #10
0
        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());
        }