/// <summary> /// Create KRB_ERROR response /// </summary> /// <param name="kileConnection">Maintain a connection with a target client. This argument cannot be null.</param> /// <param name="errorCode">Error code returned by Kerberos or the server when a request fails</param> /// <param name="errorText">Additional text to help explain the error code. This argument could be null.</param> /// <param name="errorData">Additional data about the error. This argument could be null.</param> /// <returns>The created Krb Error response</returns> /// <exception cref="System.ArgumentNullException">Thrown when the input parameter is null.</exception> public KileKrbError CreateKrbErrorResponse( KileConnection kileConnection, KRB_ERROR_CODE errorCode, string errorText, byte[] errorData) { KileServerContext serverContext = GetServerContextByKileConnection(kileConnection); KileKrbError response = new KileKrbError(serverContext); // Set KRB_ERROR response.KerberosError.pvno = new Asn1Integer(ConstValue.KERBEROSV5); response.KerberosError.msg_type = new Asn1Integer((int)MsgType.KRB_ERROR); response.KerberosError.stime = KileUtility.CurrentKerberosTime; response.KerberosError.susec = new Microseconds(0); response.KerberosError.sname = serverContext.SName; response.KerberosError.realm = new Realm(domain); response.KerberosError.error_code = new Int32((int)errorCode); if (errorText != null) { response.KerberosError.e_text = new KerberosString(errorText); } if (errorData != null) { response.KerberosError.e_data = new Asn1OctetString(errorData); } return(response); }
/// <summary> /// Get a server context for a specified client's connection. /// </summary> /// <param name="kileConnection">The specified connection. /// Null will be returned if this parameter is null.</param> /// <returns>The specified server context</returns> public KileServerContext GetServerContextByConnection(KileConnection kileConnection) { KileServerContext serverContext = null; if (kileConnection != null && contextList.ContainsKey(kileConnection)) { serverContext = contextList[kileConnection]; } return(serverContext); }
/// <summary> /// Create AP response. This method is used for mutual authentication. /// Then use KilePdu.ToBytes() to get the byte array. /// This method is used to create ApResponse on server side. /// </summary> /// <param name="kileConnection">Maintain a connection with a target client. This argument cannot be null.</param> /// <param name="subkey"> /// Specify the new subkey used in the following exchange. /// If this argument is null, no subkey will be sent.</param> /// <returns>The created AP response.</returns> /// <exception cref="System.ArgumentNullException">Thrown when the input parameter is null.</exception> /// <exception cref="System.InvalidOperationException">Thrown when no kileConnection related server context /// is found </exception> public KileApResponse CreateApResponse(KileConnection kileConnection, EncryptionKey subkey) { context = GetServerContextByKileConnection(kileConnection); KileApResponse apResponse = CreateApResponse(subkey); // Set a random sequence number Random randomNumber = new Random(); apResponse.ApEncPart.seq_number = new UInt32(randomNumber.Next()); context.currentLocalSequenceNumber = (ulong)apResponse.ApEncPart.seq_number.mValue; return(apResponse); }
/// <summary> /// Encode a PDU to a binary stream. Then send the stream. /// </summary> /// <param name="pdu">A specified type of a PDU. This argument cannot be null.</param> /// <param name="kileConnection">Maintain a connection with a target client</param> /// <exception cref="System.ArgumentNullException">Thrown when the input parameter is null.</exception> public void SendPdu(KilePdu pdu, KileConnection kileConnection) { if (kileConnection == null) { throw new ArgumentNullException("kileConnection"); } if (pdu == null) { throw new ArgumentNullException("pdu"); } KileServerContext serverContext = GetServerContextByKileConnection(kileConnection); transport.SendPacket(kileConnection.TargetEndPoint, pdu); serverContext.UpdateContext(pdu); }
/// <summary> /// Get a server context for a specified client's connection. /// </summary> /// <param name="kileConnection">The specified connection.</param> /// <returns>Specified server context if existed and not null</returns> private KileServerContext GetServerContextByKileConnection(KileConnection kileConnection) { if (kileConnection == null) { throw new ArgumentNullException("kileConnection"); } if (!contextList.ContainsKey(kileConnection)) { throw new InvalidOperationException("The specified kileConnection does not exist."); } KileServerContext serverContext = contextList[kileConnection]; if (serverContext == null) { throw new InvalidOperationException("The kileConnection related context does not exist."); } return(serverContext); }
/// <summary> /// Decode and verify a Gss_GetMic token. /// </summary> /// <param name="kileConnection">Maintain a connection with a target client</param> /// <param name="token">The token got from an application message. /// If this argument is null, null will be returned.</param> /// <param name="message">The message to be computed signature. /// If this argument is null, null will be returned.</param> /// <param name="pdu">The decoded Gss_GetMic token.</param> /// <returns>If verifying mic token is successful.</returns> /// <exception cref="System.NotSupportedException">Thrown when the encryption is not supported.</exception> public bool GssVerifyMic(KileConnection kileConnection, byte[] token, byte[] message, out KilePdu pdu) { context = GetServerContextByKileConnection(kileConnection); return(GssVerifyMic(token, message, out pdu)); }
/// <summary> /// Decode a Gss_Wrap token. /// </summary> /// <param name="kileConnection">Maintain a connection with a target client</param> /// <param name="token">The token got from an application message. /// If this argument is null, null will be returned.</param> /// <returns>The decoded Gss_Wrap token.</returns> /// <exception cref="System.NotSupportedException">Thrown when the encryption is not supported.</exception> public KilePdu GssUnWrap(KileConnection kileConnection, byte[] token) { context = GetServerContextByKileConnection(kileConnection); return(GssUnWrap(token)); }
/// <summary> /// Create a Gss_GetMic token. Then use KilePdu.ToBytes() to get the byte array. /// </summary> /// <param name="kileConnection">Maintain a connection with a target client</param> /// <param name="signAlgorithm">Specify the checksum type. /// This is only used for encryption types DES and RC4.</param> /// <param name="message">The message to be computed signature. This argument can be null.</param> /// <returns>The created Gss_GetMic token, NotSupportedException.</returns> /// <exception cref="System.NotSupportedException">Thrown when the encryption is not supported.</exception> public KilePdu GssGetMic(KileConnection kileConnection, SGN_ALG signAlgorithm, byte[] message) { context = GetServerContextByKileConnection(kileConnection); return(GssGetMic(signAlgorithm, message)); }
/// <summary> /// Create a Gss_Wrap token. Then use KilePdu.ToBytes() to get the byte array. /// </summary> /// <param name="kileConnection">Maintain a connection with a target client</param> /// <param name="isEncrypted">If encrypt the message.</param> /// <param name="signAlgorithm">Specify the checksum type. /// This is only used for encryption types DES and RC4.</param> /// <param name="message">The message to be wrapped. This argument can be null.</param> /// <returns>The created Gss_Wrap token.</returns> /// <exception cref="System.NotSupportedException">Thrown when the encryption is not supported.</exception> public KilePdu GssWrap(KileConnection kileConnection, bool isEncrypted, SGN_ALG signAlgorithm, byte[] message) { context = GetServerContextByKileConnection(kileConnection); return(GssWrap(isEncrypted, signAlgorithm, message)); }
/// <summary> /// Create TGS response. /// </summary> /// <param name="kileConnection">Maintain a connection with a target client. This argument cannot be null.</param> /// <param name="seqOfPaData">The pre-authentication data. /// This argument can be generated by method ConstructPaData. This argument could be null.</param> /// <param name="encTicketFlags">Ticket Flags</param> /// <param name="ticketEncryptKey">Encryption key used to encrypt ticket. This parameter cannot be null /// In User-User Authentication mode, use session key in additional ticket in KileTgsRequest. /// Otherwise use service's secret key.</param> /// <param name="ticketAuthorizationData">The authorization-data field is used to pass authorization data from /// the principal on whose behalf a ticket was issued to the application service. This parameter could be null. /// </param> /// <returns>The created TGS response.</returns> /// <exception cref="System.ArgumentNullException">Thrown when the input parameter is null.</exception> /// <exception cref="System.InvalidOperationException">Thrown when no kileConnection related server context /// is found </exception> public KileTgsResponse CreateTgsResponse( KileConnection kileConnection, _SeqOfPA_DATA seqOfPaData, EncTicketFlags encTicketFlags, EncryptionKey ticketEncryptKey, AuthorizationData ticketAuthorizationData) { KileServerContext serverContext = GetServerContextByKileConnection(kileConnection); if (ticketEncryptKey == null) { throw new ArgumentNullException("ticketEncryptKey"); } else { serverContext.TicketEncryptKey = ticketEncryptKey; } KileTgsResponse response = new KileTgsResponse(serverContext); // Construct a Ticket Ticket ticket = new Ticket(); ticket.tkt_vno = new Asn1Integer(ConstValue.KERBEROSV5); ticket.realm = new Realm(domain); ticket.sname = serverContext.SName; // Set EncTicketPart EncTicketPart encTicketPart = new EncTicketPart(); EncryptionType encryptionType = (EncryptionType)serverContext.EncryptType.elements[0].mValue; encTicketPart.key = new EncryptionKey((int)encryptionType, GetEncryptionKeyByType(encryptionType)); encTicketPart.flags = new TicketFlags(KileUtility.ConvertInt2Flags((int)encTicketFlags)); encTicketPart.crealm = serverContext.TgsTicket.crealm; encTicketPart.cname = serverContext.TgsTicket.cname; encTicketPart.transited = serverContext.TgsTicket.transited; encTicketPart.authtime = KileUtility.CurrentKerberosTime; encTicketPart.starttime = KileUtility.CurrentKerberosTime; encTicketPart.endtime = serverContext.TgsTicket.endtime; encTicketPart.renew_till = serverContext.TgsTicket.renew_till; encTicketPart.caddr = serverContext.Addresses; encTicketPart.authorization_data = ticketAuthorizationData; response.TicketEncPart = encTicketPart; // Set AS_REP response.Response.pvno = new Asn1Integer(ConstValue.KERBEROSV5); response.Response.msg_type = new Asn1Integer((int)MsgType.KRB_TGS_RESP); response.Response.padata = seqOfPaData; response.Response.crealm = serverContext.UserRealm; response.Response.cname = serverContext.UserName; response.Response.ticket = ticket; // Set EncASRepPart EncTGSRepPart encTGSRepPart = new EncTGSRepPart(); encTGSRepPart.key = encTicketPart.key; LastReq_element element = new LastReq_element(new Int32(0), KileUtility.CurrentKerberosTime); encTGSRepPart.last_req = new LastReq(new LastReq_element[] { element }); encTGSRepPart.nonce = serverContext.Nonce; encTGSRepPart.flags = encTicketPart.flags; encTGSRepPart.authtime = encTicketPart.authtime; encTGSRepPart.starttime = encTicketPart.starttime; encTGSRepPart.endtime = encTicketPart.endtime; encTGSRepPart.renew_till = encTicketPart.renew_till; encTGSRepPart.srealm = ticket.realm; encTGSRepPart.sname = ticket.sname; encTGSRepPart.caddr = encTicketPart.caddr; response.EncPart = encTGSRepPart; return(response); }
/// <summary> /// Create AS response. /// </summary> /// <param name="kileConnection">Maintain a connection with a target client. This argument cannot be null.</param> /// <param name="accountType">The type of the logoned account. User or Computer</param> /// <param name="password">Password of the user who logon the system. This argument cannot be null.</param> /// <param name="SeqofPaData">The pre-authentication data in AS request. /// This argument can be generated by method ConstructPaData. This argument could be null.</param> /// <param name="encTicketFlags">Ticket Flags</param> /// <param name="ticketAuthorizationData">The authorization-data field is used to pass authorization data from /// the principal on whose behalf a ticket was issued to the application service. This parameter could be null. /// </param> /// <returns>The created AS response.</returns> /// <exception cref="System.ArgumentNullException">Thrown when the input parameter is null.</exception> /// <exception cref="System.InvalidOperationException">Thrown when no kileConnection related server context /// is found </exception> public KileAsResponse CreateAsResponse( KileConnection kileConnection, KerberosAccountType accountType, string password, _SeqOfPA_DATA SeqofPaData, EncTicketFlags encTicketFlags, AuthorizationData ticketAuthorizationData) { KileServerContext serverContext = GetServerContextByKileConnection(kileConnection); string cName = serverContext.UserName.name_string.elements[0].mValue; string cRealm = serverContext.UserRealm.mValue; serverContext.Salt = GenerateSalt(cRealm, cName, accountType); serverContext.TicketEncryptKey = new EncryptionKey((int)EncryptionType.RC4_HMAC, GetEncryptionKeyByType(EncryptionType.RC4_HMAC)); if (password == null) { throw new ArgumentNullException("password"); } else { serverContext.Password = password; } KileAsResponse response = new KileAsResponse(serverContext); // Construct a Ticket Ticket ticket = new Ticket(); ticket.tkt_vno = new Asn1Integer(ConstValue.KERBEROSV5); ticket.realm = new Realm(domain); ticket.sname = serverContext.SName; // Set EncTicketPart EncTicketPart encTicketPart = new EncTicketPart(); EncryptionType encryptionType = (EncryptionType)serverContext.EncryptType.elements[0].mValue; encTicketPart.key = new EncryptionKey((int)encryptionType, GetEncryptionKeyByType(encryptionType)); encTicketPart.flags = new TicketFlags(KileUtility.ConvertInt2Flags((int)encTicketFlags)); encTicketPart.crealm = serverContext.UserRealm; encTicketPart.cname = serverContext.UserName; encTicketPart.transited = new TransitedEncoding(4, null); encTicketPart.authtime = KileUtility.CurrentKerberosTime; encTicketPart.starttime = KileUtility.CurrentKerberosTime; encTicketPart.endtime = serverContext.endTime; encTicketPart.renew_till = serverContext.rtime ?? encTicketPart.endtime; encTicketPart.caddr = serverContext.Addresses; encTicketPart.authorization_data = ticketAuthorizationData; response.TicketEncPart = encTicketPart; // Set AS_REP response.Response.pvno = new Asn1Integer(ConstValue.KERBEROSV5); response.Response.msg_type = new Asn1Integer((int)MsgType.KRB_AS_RESP); response.Response.padata = SeqofPaData; response.Response.crealm = serverContext.UserRealm; response.Response.cname = serverContext.UserName; response.Response.ticket = ticket; // Set EncASRepPart EncASRepPart encASRepPart = new EncASRepPart(); encASRepPart.key = encTicketPart.key; LastReq_element element = new LastReq_element(new Int32(0), KileUtility.CurrentKerberosTime); encASRepPart.last_req = new LastReq(new LastReq_element[] { element }); encASRepPart.nonce = serverContext.Nonce; encASRepPart.flags = encTicketPart.flags; encASRepPart.authtime = encTicketPart.authtime; encASRepPart.starttime = encTicketPart.starttime; encASRepPart.endtime = encTicketPart.endtime; encASRepPart.renew_till = encTicketPart.renew_till; encASRepPart.srealm = ticket.realm; encASRepPart.sname = ticket.sname; encASRepPart.caddr = encTicketPart.caddr; response.EncPart = encASRepPart; return(response); }
/// <summary> /// Decode KILE PDUs from received message bytes /// </summary> /// <param name="endPoint">An endpoint from which the message bytes are received</param> /// <param name="receivedBytes">The received bytes to be decoded</param> /// <param name="consumedLength">Length of message bytes consumed by decoder</param> /// <param name="expectedLength">Length of message bytes the decoder expects to receive</param> /// <returns>The decoded KILE PDUs</returns> /// <exception cref="System.FormatException">thrown when a kile message type is unsupported</exception> internal KilePdu[] DecodePacketCallback(object endPoint, byte[] receivedBytes, out int consumedLength, out int expectedLength) { // initialize lengths consumedLength = 0; expectedLength = 0; if (null == receivedBytes || 0 == receivedBytes.Length) { return(null); } if (!isClientRole) { serverContext = null; if (serverContextList != null) { KileConnection kileConnection = new KileConnection((IPEndPoint)endPoint); if (!serverContextList.ContainsKey(kileConnection)) { serverContext = new KileServerContext(); serverContext.TransportType = connectionType; serverContextList.Add(kileConnection, serverContext); } else { serverContext = serverContextList[kileConnection]; } } if (serverContext == null) { throw new InvalidOperationException("The kileConnection related context does not exist."); } } // TCP has a 4 bytes length header, while UDP has not byte[] pduBytes = receivedBytes; if ((isClientRole && clientContext.TransportType == KileConnectionType.TCP) || (!isClientRole && serverContext.TransportType == KileConnectionType.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); } // check if it is a krb zero message if (pduLength == 0 && receivedBytes.Length == sizeof(int)) { consumedLength = sizeof(int); KrbZero krbZeroPdu = new KrbZero(clientContext); return(new KilePdu[] { krbZeroPdu }); } // remove length header from pdu bytes pduBytes = ArrayUtility.SubArray <byte>(receivedBytes, sizeof(int), pduLength); } else { // UDP has no length header expectedLength = pduBytes.Length; } // get message type // (the lower 5 bits indicates its kile message type) MsgType kileMessageType = (MsgType)(pduBytes[0] & 0x1f); // decode according to message type consumedLength = expectedLength; KilePdu pdu = null; switch (kileMessageType) { case MsgType.KRB_AS_REQ: pdu = new KileAsRequest(serverContext); break; case MsgType.KRB_AS_RESP: pdu = new KileAsResponse(clientContext); break; case MsgType.KRB_TGS_REQ: pdu = new KileTgsRequest(serverContext); break; case MsgType.KRB_TGS_RESP: pdu = new KileTgsResponse(clientContext); break; case MsgType.KRB_ERROR: pdu = new KileKrbError(); break; default: throw new FormatException( "Unsupported Message Type: " + kileMessageType.ToString()); } pdu.FromBytes(pduBytes); // update context if (isClientRole) { clientContext.UpdateContext(pdu); } else { serverContext.UpdateContext(pdu); } return(new KilePdu[] { pdu }); }