/// <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);
        }
Beispiel #12
0
        /// <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 });
        }