/// <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; }
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); }
/// <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; }