/// <summary>
        /// Updated context based on Kerberos pdu
        /// </summary>
        /// <param name="pdu">Kerberos pdu</param>
        public override void UpdateContext(KerberosPdu pdu)
        {
            if (pdu is KerberosAsRequest)
            {
                KerberosAsRequest request = (KerberosAsRequest)pdu;
                UpdateContext(request);
            }
            if (pdu is KerberosKrbError)
            {
                KerberosKrbError error = (KerberosKrbError)pdu;
                UpdateContext(error);
            }
            if (pdu is KerberosAsResponse)
            {
                KerberosAsResponse response = (KerberosAsResponse)pdu;
                UpdateContext(response);
            }

            if (pdu is KerberosTgsRequest)
            {
                KerberosTgsRequest request = pdu as KerberosTgsRequest;
                UpdateContext(request);
            }

            if (pdu is KerberosTgsResponse)
            {
                KerberosTgsResponse response = pdu as KerberosTgsResponse;
                UpdateContext(response);
            }

            this.expectedPduType = null;
        }
Exemple #2
0
        private KerberosAsResponse ExpectAsResponse()
        {
            KerberosPdu responsePdu = this.client.ExpectPdu(KerberosConstValue.TIMEOUT_DEFAULT, typeof(KerberosAsResponse));

            if (responsePdu == null)
            {
                throw new Exception("Expected KerberosAsResponse data is null");
            }

            if (responsePdu is KerberosKrbError)
            {
                KerberosKrbError errorResponse = responsePdu as KerberosKrbError;
                throw new Exception($"Expected KerberosAsResponse failed, Error Code:{errorResponse.ErrorCode}");
            }
            else if (!(responsePdu is KerberosAsResponse))
            {
                throw new Exception($"Expected KerberosAsResponse failed, reponse type is not a valid KerberosAsResponse, Response Type:{responsePdu.GetType().ToString()}");
            }

            KerberosAsResponse response = responsePdu as KerberosAsResponse;

            response.Decrypt(this.Context.ReplyKey.keyvalue.ByteArrayValue);
            return(response);
        }
Exemple #3
0
        /// <summary>
        /// Kerberos Client Initialize without server token
        /// </summary>
        private void ClientInitialize()
        {
            this.ApRequestAuthenticator = null;
            // Create and send AS request for pre-authentication
            KdcOptions options = KdcOptions.FORWARDABLE | KdcOptions.CANONICALIZE | KdcOptions.RENEWABLE;

            KerberosTicket ticket = this.GetTGTCachedToken(this.credential, this.serverName);

            if (ticket == null)
            {
                this.SendAsRequest(options, null);

                // Expect recieve preauthentication required error
                METHOD_DATA methodData;
                this.ExpectPreauthRequiredError(out methodData);

                // Create sequence of PA data
                string         timeStamp      = KerberosUtility.CurrentKerberosTime.Value;
                PaEncTimeStamp paEncTimeStamp = new PaEncTimeStamp(timeStamp,
                                                                   0,
                                                                   this.Context.SelectedEType,
                                                                   this.Context.CName.Password,
                                                                   this.Context.CName.Salt);
                PaPacRequest             paPacRequest   = new PaPacRequest(true);
                PaPacOptions             paPacOptions   = new PaPacOptions(PacOptions.Claims | PacOptions.ForwardToFullDc);
                Asn1SequenceOf <PA_DATA> seqOfPaData_AS = new Asn1SequenceOf <PA_DATA>(new PA_DATA[] { paEncTimeStamp.Data, paPacRequest.Data, paPacOptions.Data });
                // Create and send AS request for TGT
                KerberosAsRequest asRequest = this.SendAsRequest(options, seqOfPaData_AS);

                // Expect TGT(AS) Response from KDC
                KerberosAsResponse asResponse = this.ExpectAsResponse();

                // Create and send TGS request
                Asn1SequenceOf <PA_DATA> seqOfPaData_TGS = new Asn1SequenceOf <PA_DATA>(new PA_DATA[] { paPacRequest.Data, paPacOptions.Data });
                this.SendTgsRequest(this.serverName, options, seqOfPaData_TGS);

                // Expect TGS Response from KDC
                KerberosTgsResponse tgsResponse = this.ExpectTgsResponse();
                this.UpdateTGTCachedToken(this.Context.Ticket);
            }
            else
            {
                // Restore SessionKey and Ticket from cache
                this.Context.SessionKey    = ticket.SessionKey;
                this.Context.ApSessionKey  = ticket.SessionKey;
                this.Context.Ticket        = ticket;
                this.Context.SelectedEType = (EncryptionType)Context.Ticket.Ticket.enc_part.etype.Value;
            }

            // cache this.Context.Ticket;
            ApOptions apOption;

            GetFlagsByContextAttribute(out apOption);

            AuthorizationData data   = null;
            EncryptionKey     subkey = KerberosUtility.GenerateKey(this.client.Context.ContextKey);

            this.token = this.CreateGssApiToken(apOption,
                                                data,
                                                subkey,
                                                this.Context.ChecksumFlag,
                                                KerberosConstValue.GSSToken.GSSAPI);

            bool isMutualAuth = (contextAttribute & ClientSecurityContextAttribute.MutualAuth)
                                == ClientSecurityContextAttribute.MutualAuth;
            bool isDceStyle = (contextAttribute & ClientSecurityContextAttribute.DceStyle)
                              == ClientSecurityContextAttribute.DceStyle;

            if (isMutualAuth || isDceStyle)
            {
                this.needContinueProcessing = true;
            }
            else
            {
                this.needContinueProcessing = false;
            }
        }
        private void UpdateContext(KerberosAsResponse response)
        {
            KerberosFastResponse kerbFastRep = null;

            if (response.Response.padata != null && response.Response.padata.Elements != null)
            {
                foreach (PA_DATA paData in response.Response.padata.Elements)
                {
                    var parsedPaData = PaDataParser.ParseRepPaData(paData);
                    if (parsedPaData is PaETypeInfo2)
                    {
                        Asn1DecodingBuffer buffer     = new Asn1DecodingBuffer(paData.padata_value.ByteArrayValue);
                        ETYPE_INFO2        eTypeInfo2 = new ETYPE_INFO2();
                        eTypeInfo2.BerDecode(buffer);
                        if (eTypeInfo2.Elements != null && eTypeInfo2.Elements.Length > 0)
                        {
                            // the salt is received from KDC
                            if (eTypeInfo2.Elements[0].salt != null)
                            {
                                Context.CName.Salt = eTypeInfo2.Elements[0].salt.Value;
                            }
                            continue;
                        }
                    }
                    if (parsedPaData is PaFxFastRep)
                    {
                        var armoredRep = ((PaFxFastRep)parsedPaData).GetArmoredRep();
                        kerbFastRep = ((PaFxFastRep)parsedPaData).GetKerberosFastRep(Context.FastArmorkey);
                        var strKey = kerbFastRep.FastResponse.strengthen_key;
                        Context.ReplyKey = KerberosUtility.KrbFxCf2(
                            strKey,
                            //Fix me: should be Context.ReplyKey
                            KerberosUtility.MakeKey(Context.SelectedEType, Context.CName.Password, Context.CName.Salt),
                            "strengthenkey",
                            "replykey");
                    }
                }
            }

            if (Context.ReplyKey != null)
            {
                response.Decrypt(Context.ReplyKey.keyvalue.ByteArrayValue);
            }
            else
            {
                var encryptType = (EncryptionType)response.Response.enc_part.etype.Value;
                var key         = KeyGenerator.MakeKey(encryptType, Context.CName.Password, Context.CName.Salt);
                Context.ReplyKey = new EncryptionKey(new KerbInt32((long)encryptType), new Asn1OctetString(key));
                response.Decrypt(key);
            }

            if (response.EncPart != null)
            {
                Context.SessionKey = response.EncPart.key;
            }

            if (response.Response != null)
            {
                //Response.Response.cname is not the real CName of the ticket when hide-client-names=1
                if (kerbFastRep != null && kerbFastRep.FastResponse != null && kerbFastRep.FastResponse.finished != null)
                {
                    // Windows DC is case insensitive. It may change the cname in the response, e.g. administrator -> Administrator
                    Context.CName.Name = kerbFastRep.FastResponse.finished.cname;
                    Context.Ticket     = new KerberosTicket(response.Response.ticket, kerbFastRep.FastResponse.finished.cname, response.EncPart.key);
                }
                else
                {
                    // Windows DC is case insensitive. It may change the cname in the response, e.g. administrator -> Administrator
                    Context.CName.Name = response.Response.cname;
                    Context.Ticket     = new KerberosTicket(response.Response.ticket, response.Response.cname, response.EncPart.key);
                }
                Context.SelectedEType = (EncryptionType)Context.Ticket.SessionKey.keytype.Value;
                if (Context.Ticket != null && Context.Ticket.Ticket.sname != null &&
                    Context.Ticket.Ticket.sname.name_string != null &&
                    Context.Ticket.Ticket.sname.name_string.Elements != null &&
                    Context.Ticket.Ticket.sname.name_string.Elements.Length > 1)
                {
                    int count = Context.Ticket.Ticket.sname.name_string.Elements.Length;
                    Context.Realm = new Realm(Context.Ticket.Ticket.sname.name_string.Elements[count - 1].Value);
                }
            }
        }
        //Get the expected Kerberos PDU from byte array
        private KerberosPdu getExpectedPduFromBytes(
            byte[] receivedBytes,
            out int consumedLength,
            out int expectedLength)
        {
            // initialize lengths
            consumedLength = 0;
            expectedLength = 0;
            if (null == receivedBytes || 0 == receivedBytes.Length)
            {
                return(null);
            }

            // TCP has a 4 bytes length header, while UDP has not
            byte[] pduBytes = receivedBytes;

            if ((this.Context.TransportType == TransportType.TCP))
            {
                // insufficient data, needs to receive more
                if (receivedBytes.Length < sizeof(int))
                {
                    return(null);
                }

                // get pdu data length
                byte[] lengthBytes = ArrayUtility.SubArray(receivedBytes, 0, sizeof(int));
                Array.Reverse(lengthBytes);
                int pduLength = BitConverter.ToInt32(lengthBytes, 0);

                // insufficient data, needs to receive more
                expectedLength = sizeof(int) + pduLength;
                if (receivedBytes.Length < expectedLength)
                {
                    return(null);
                }

                // remove length header from pdu bytes
                pduBytes = ArrayUtility.SubArray <byte>(receivedBytes, sizeof(int), pduLength);
            }
            else
            {
                // UDP has no length header
                expectedLength = pduBytes.Length;
            }

            // decode according to message type
            consumedLength = expectedLength;

            // get message type
            // (the lower 5 bits indicates its kile message type)
            MsgType     kileMessageType = (MsgType)(pduBytes[0] & 0x1f);
            KerberosPdu pdu             = null;

            switch (kileMessageType)
            {
            case MsgType.KRB_AS_REQ:
                pdu = new KerberosAsRequest();
                break;

            case MsgType.KRB_AS_RESP:
                pdu = new KerberosAsResponse();
                break;

            case MsgType.KRB_TGS_REQ:
                pdu = new KerberosTgsRequest();
                break;

            case MsgType.KRB_TGS_RESP:
                pdu = new KerberosTgsResponse();
                break;

            case MsgType.KRB_ERROR:
                pdu = new KerberosKrbError();
                break;

            default:
                throw new FormatException(
                          "Unsupported Message Type: " + kileMessageType.ToString());
            }
            pdu.FromBytes(pduBytes);
            // update context
            if (this.expectedPduType == null || this.expectedPduType == pdu.GetType())
            {
                this.UpdateContext(pdu);
            }
            return(pdu);
        }
        private void UpdateContext(KerberosAsResponse response)
        {
            KerberosFastResponse kerbFastRep = null;
            if (response.Response.padata != null && response.Response.padata.Elements != null)
            {
                foreach (PA_DATA paData in response.Response.padata.Elements)
                {
                    var parsedPaData = PaDataParser.ParseRepPaData(paData);
                    if (parsedPaData is PaETypeInfo2)
                    {
                        Asn1DecodingBuffer buffer = new Asn1DecodingBuffer(paData.padata_value.ByteArrayValue);
                        ETYPE_INFO2 eTypeInfo2 = new ETYPE_INFO2();
                        eTypeInfo2.BerDecode(buffer);
                        if (eTypeInfo2.Elements != null && eTypeInfo2.Elements.Length > 0)
                        {
                            // the salt is received from KDC
                            if (eTypeInfo2.Elements[0].salt != null)
                                Context.CName.Salt = eTypeInfo2.Elements[0].salt.Value;
                            continue;
                        }
                    }
                    if (parsedPaData is PaFxFastRep)
                    {
                        var armoredRep = ((PaFxFastRep)parsedPaData).GetArmoredRep();
                        kerbFastRep = ((PaFxFastRep)parsedPaData).GetKerberosFastRep(Context.FastArmorkey);
                        var strKey = kerbFastRep.FastResponse.strengthen_key;
                        Context.ReplyKey = KerberosUtility.KrbFxCf2(
                            strKey,
                            //Fix me: should be Context.ReplyKey
                            KerberosUtility.MakeKey(Context.SelectedEType, Context.CName.Password, Context.CName.Salt),
                            "strengthenkey",
                            "replykey");
                    }
                }
            }

            if (Context.ReplyKey != null)
            {
                response.Decrypt(Context.ReplyKey.keyvalue.ByteArrayValue);
            }
            else
            {
                var encryptType = (EncryptionType)response.Response.enc_part.etype.Value;
                var key = KeyGenerator.MakeKey(encryptType, Context.CName.Password, Context.CName.Salt);
                Context.ReplyKey = new EncryptionKey(new KerbInt32((long)encryptType), new Asn1OctetString(key));
                response.Decrypt(key);
            }

            if (response.EncPart != null)
            {
                Context.SessionKey = response.EncPart.key;
            }

            if (response.Response != null)
            {
                //Response.Response.cname is not the real CName of the ticket when hide-client-names=1
                if (kerbFastRep != null && kerbFastRep.FastResponse != null && kerbFastRep.FastResponse.finished != null)
                {
                    // Windows DC is case insensitive. It may change the cname in the response, e.g. administrator -> Administrator
                    Context.CName.Name = kerbFastRep.FastResponse.finished.cname;
                    Context.Ticket = new KerberosTicket(response.Response.ticket, kerbFastRep.FastResponse.finished.cname, response.EncPart.key);
                }
                else
                {
                    // Windows DC is case insensitive. It may change the cname in the response, e.g. administrator -> Administrator
                    Context.CName.Name = response.Response.cname;
                    Context.Ticket = new KerberosTicket(response.Response.ticket, response.Response.cname, response.EncPart.key);
                }
                Context.SelectedEType = (EncryptionType)Context.Ticket.SessionKey.keytype.Value;
                if (Context.Ticket != null && Context.Ticket.Ticket.sname != null
                    && Context.Ticket.Ticket.sname.name_string != null
                    && Context.Ticket.Ticket.sname.name_string.Elements != null
                    && Context.Ticket.Ticket.sname.name_string.Elements.Length > 1)
                {
                    int count = Context.Ticket.Ticket.sname.name_string.Elements.Length;
                    Context.Realm = new Realm(Context.Ticket.Ticket.sname.name_string.Elements[count - 1].Value);
                }
            }
        }
        //Get the expected Kerberos PDU from byte array
        private KerberosPdu getExpectedPduFromBytes(
            byte[] receivedBytes,
            out int consumedLength,
            out int expectedLength)
        {
            // initialize lengths
            consumedLength = 0;
            expectedLength = 0;
            if (null == receivedBytes || 0 == receivedBytes.Length)
            {
                return null;
            }

            // TCP has a 4 bytes length header, while UDP has not
            byte[] pduBytes = receivedBytes;

            if ((this.Context.TransportType == TransportType.TCP))
            {
                // insufficient data, needs to receive more
                if (receivedBytes.Length < sizeof(int))
                {
                    return null;
                }

                // get pdu data length
                byte[] lengthBytes = ArrayUtility.SubArray(receivedBytes, 0, sizeof(int));
                Array.Reverse(lengthBytes);
                int pduLength = BitConverter.ToInt32(lengthBytes, 0);

                // insufficient data, needs to receive more
                expectedLength = sizeof(int) + pduLength;
                if (receivedBytes.Length < expectedLength)
                {
                    return null;
                }

                // remove length header from pdu bytes
                pduBytes = ArrayUtility.SubArray<byte>(receivedBytes, sizeof(int), pduLength);
            }
            else
            {
                // UDP has no length header
                expectedLength = pduBytes.Length;
            }

            // decode according to message type
            consumedLength = expectedLength;

            // get message type
            // (the lower 5 bits indicates its kile message type)
            MsgType kileMessageType = (MsgType)(pduBytes[0] & 0x1f);
            KerberosPdu pdu = null;

            switch (kileMessageType)
            {
                case MsgType.KRB_AS_REQ:
                    pdu = new KerberosAsRequest();
                    break;

                case MsgType.KRB_AS_RESP:
                    pdu = new KerberosAsResponse();
                    break;

                case MsgType.KRB_TGS_REQ:
                    pdu = new KerberosTgsRequest();
                    break;

                case MsgType.KRB_TGS_RESP:
                    pdu = new KerberosTgsResponse();
                    break;

                case MsgType.KRB_ERROR:
                    pdu = new KerberosKrbError();
                    break;

                default:
                    throw new FormatException(
                        "Unsupported Message Type: " + kileMessageType.ToString());
            }
            pdu.FromBytes(pduBytes);
            // update context
            if (this.expectedPduType == null || this.expectedPduType == pdu.GetType())
                this.UpdateContext(pdu);
            return pdu;
        }