Пример #1
0
        /// <summary>
        /// Is called by SIP proxy or registrar server when it needs to authenticate user.
        /// </summary>
        /// <param name="auth">Authentication context.</param>
        /// <returns></returns>
        internal SIP_AuthenticateEventArgs OnAuthenticate(Auth_HttpDigest auth)
        {
            SIP_AuthenticateEventArgs eArgs = new SIP_AuthenticateEventArgs(auth);

            if (this.Authenticate != null)
            {
                this.Authenticate(eArgs);
            }

            return(eArgs);
        }
Пример #2
0
        /// <summary>
        /// Gets specified realm SIP proxy credentials. Returns null if none exists for specified realm.
        /// </summary>
        /// <param name="request">SIP reques.</param>
        /// <param name="realm">Realm(domain).</param>
        /// <returns>Returns specified realm credentials or null if none.</returns>
        public static SIP_t_Credentials GetCredentials(SIP_Request request, string realm)
        {
            foreach (SIP_SingleValueHF <SIP_t_Credentials> authorization in request.ProxyAuthorization.HeaderFields)
            {
                if (authorization.ValueX.Method.ToLower() == "digest")
                {
                    Auth_HttpDigest authDigest = new Auth_HttpDigest(authorization.ValueX.AuthData, request.RequestLine.Method);
                    if (authDigest.Realm.ToLower() == realm.ToLower())
                    {
                        return(authorization.ValueX);
                    }
                }
            }

            return(null);
        }
Пример #3
0
        /// <summary>
        /// Default constructor.
        /// </summary>
        /// <param name="stack">Reference to SIP stack.</param>
        /// <exception cref="ArgumentNullException">Is raised when <b>sipStack</b> is null.</exception>
        public SIP_ProxyCore(SIP_Stack stack)
        {
            if (stack == null)
            {
                throw new ArgumentNullException("stack");
            }

            m_pStack = stack;
            m_pStack.RequestReceived  += m_pStack_RequestReceived;
            m_pStack.ResponseReceived += m_pStack_ResponseReceived;

            m_pRegistrar     = new SIP_Registrar(this);
            m_pB2BUA         = new SIP_B2BUA(this);
            m_Opaque         = Auth_HttpDigest.CreateOpaque();
            m_pProxyContexts = new List <SIP_ProxyContext>();
        }
Пример #4
0
        /// <summary>
        /// Authenticates SIP request. This method also sends all needed replys to request sender.
        /// </summary>
        /// <param name="e">Request event arguments.</param>
        /// <param name="userName">If authentication sucessful, then authenticated user name is stored to this variable.</param>
        /// <returns>Returns true if request was authenticated.</returns>
        internal bool AuthenticateRequest(SIP_RequestReceivedEventArgs e, out string userName)
        {
            userName = null;

            SIP_t_Credentials credentials = SIP_Utils.GetCredentials(e.Request, m_pStack.Realm);

            // No credentials for our realm.
            if (credentials == null)
            {
                SIP_Response notAuthenticatedResponse = m_pStack.CreateResponse(SIP_ResponseCodes.x407_Proxy_Authentication_Required, e.Request);
                notAuthenticatedResponse.ProxyAuthenticate.Add(new Auth_HttpDigest(m_pStack.Realm, m_pStack.DigestNonceManager.CreateNonce(), m_Opaque).ToChallenge());

                e.ServerTransaction.SendResponse(notAuthenticatedResponse);
                return(false);
            }

            Auth_HttpDigest auth = new Auth_HttpDigest(credentials.AuthData, e.Request.RequestLine.Method);

            // Check opaque validity.
            if (auth.Opaque != m_Opaque)
            {
                SIP_Response notAuthenticatedResponse = m_pStack.CreateResponse(SIP_ResponseCodes.x407_Proxy_Authentication_Required + ": Opaque value won't match !", e.Request);
                notAuthenticatedResponse.ProxyAuthenticate.Add(new Auth_HttpDigest(m_pStack.Realm, m_pStack.DigestNonceManager.CreateNonce(), m_Opaque).ToChallenge());

                // Send response
                e.ServerTransaction.SendResponse(notAuthenticatedResponse);
                return(false);
            }
            // Check nonce validity.
            if (!m_pStack.DigestNonceManager.NonceExists(auth.Nonce))
            {
                SIP_Response notAuthenticatedResponse = m_pStack.CreateResponse(SIP_ResponseCodes.x407_Proxy_Authentication_Required + ": Invalid nonce value !", e.Request);
                notAuthenticatedResponse.ProxyAuthenticate.Add(new Auth_HttpDigest(m_pStack.Realm, m_pStack.DigestNonceManager.CreateNonce(), m_Opaque).ToChallenge());

                // Send response
                e.ServerTransaction.SendResponse(notAuthenticatedResponse);
                return(false);
            }
            // Valid nonce, consume it so that nonce can't be used any more.
            else
            {
                m_pStack.DigestNonceManager.RemoveNonce(auth.Nonce);
            }

            SIP_AuthenticateEventArgs eArgs = this.OnAuthenticate(auth);

            // Authenticate failed.
            if (!eArgs.Authenticated)
            {
                SIP_Response notAuthenticatedResponse = m_pStack.CreateResponse(SIP_ResponseCodes.x407_Proxy_Authentication_Required + ": Authentication failed.", e.Request);
                notAuthenticatedResponse.ProxyAuthenticate.Add(new Auth_HttpDigest(m_pStack.Realm, m_pStack.DigestNonceManager.CreateNonce(), m_Opaque).ToChallenge());

                // Send response
                e.ServerTransaction.SendResponse(notAuthenticatedResponse);
                return(false);
            }

            userName = auth.UserName;

            return(true);
        }
Пример #5
0
        /// <summary>
        /// This method is called when new request is received.
        /// </summary>
        /// <param name="e">Request event arguments.</param>
        internal void OnRequestReceived(SIP_RequestReceivedEventArgs e)
        {
            SIP_Request request = e.Request;

            if (request.RequestLine.Method == SIP_Methods.CANCEL)
            {
                /* RFC 3261 9.2.
                 *  If the UAS did not find a matching transaction for the CANCEL
                 *  according to the procedure above, it SHOULD respond to the CANCEL
                 *  with a 481 (Call Leg/Transaction Does Not Exist).
                 *
                 *  Regardless of the method of the original request, as long as the
                 *  CANCEL matched an existing transaction, the UAS answers the CANCEL
                 *  request itself with a 200 (OK) response.
                 */

                SIP_ServerTransaction trToCancel = m_pProxy.Stack.TransactionLayer.MatchCancelToTransaction(e.Request);
                if (trToCancel != null)
                {
                    trToCancel.Cancel();
                    //e.ServerTransaction.SendResponse(request.CreateResponse(SIP_ResponseCodes.x200_Ok));
                }
                else
                {
                    //e.ServerTransaction.SendResponse(request.CreateResponse(SIP_ResponseCodes.x481_Call_Transaction_Does_Not_Exist));
                }
            }
            // We never should ge BYE here, because transport layer must match it to dialog.
            else if (request.RequestLine.Method == SIP_Methods.BYE)
            {
                /* RFC 3261 15.1.2.
                 *  If the BYE does not match an existing dialog, the UAS core SHOULD generate a 481
                 *  (Call/Transaction Does Not Exist) response and pass that to the server transaction.
                 */
                //e.ServerTransaction.SendResponse(request.CreateResponse(SIP_ResponseCodes.x481_Call_Transaction_Does_Not_Exist));
            }
            // We never should ge ACK here, because transport layer must match it to dialog.
            else if (request.RequestLine.Method == SIP_Methods.ACK)
            {
                // ACK is response less request, so we may not return error to it.
            }
            // B2BUA must respond to OPTIONS request, not to forward it.
            else if (request.RequestLine.Method == SIP_Methods.OPTIONS)      /*
                                                                              * SIP_Response response = e.Request.CreateResponse(SIP_ResponseCodes.x200_Ok);
                                                                              * // Add Allow to non ACK response.
                                                                              * if(e.Request.RequestLine.Method != SIP_Methods.ACK){
                                                                              * response.Allow.Add("INVITE,ACK,OPTIONS,CANCEL,BYE,PRACK,MESSAGE,UPDATE");
                                                                              * }
                                                                              * // Add Supported to 2xx non ACK response.
                                                                              * if(response.StatusCodeType == SIP_StatusCodeType.Success && e.Request.RequestLine.Method != SIP_Methods.ACK){
                                                                              * response.Supported.Add("100rel,timer");
                                                                              * }
                                                                              * e.ServerTransaction.SendResponse(response);*/
            {
            }
            // We never should get PRACK here, because transport layer must match it to dialog.
            else if (request.RequestLine.Method == SIP_Methods.PRACK)
            {
                //e.ServerTransaction.SendResponse(request.CreateResponse(SIP_ResponseCodes.x481_Call_Transaction_Does_Not_Exist));
            }
            // We never should get UPDATE here, because transport layer must match it to dialog.
            else if (request.RequestLine.Method == SIP_Methods.UPDATE)
            {
                //e.ServerTransaction.SendResponse(request.CreateResponse(SIP_ResponseCodes.x481_Call_Transaction_Does_Not_Exist));
            }
            else
            {
                /* draft-marjou-sipping-b2bua-00 4.1.3.
                 *  When the UAS of the B2BUA receives an upstream SIP request, its
                 *  associated UAC generates a new downstream SIP request with its new
                 *  Via, Max-Forwards, Call-Id, CSeq, and Contact header fields. Route
                 *  header fields of the upstream request are copied in the downstream
                 *  request, except the first Route header if it is under the
                 *  responsibility of the B2BUA.  Record-Route header fields of the
                 *  upstream request are not copied in the new downstream request, as
                 *  Record-Route is only meaningful for the upstream dialog.  The UAC
                 *  SHOULD copy other header fields and body from the upstream request
                 *  into this downstream request before sending it.
                 */

                SIP_Request b2buaRequest = e.Request.Copy();
                b2buaRequest.Via.RemoveAll();
                b2buaRequest.MaxForwards         = 70;
                b2buaRequest.CallID              = SIP_t_CallID.CreateCallID().CallID;
                b2buaRequest.CSeq.SequenceNumber = 1;
                b2buaRequest.Contact.RemoveAll();
                // b2buaRequest.Contact.Add(m_pProxy.CreateContact(b2buaRequest.To.Address).ToStringValue());
                if (b2buaRequest.Route.Count > 0 && m_pProxy.IsLocalRoute(SIP_Uri.Parse(b2buaRequest.Route.GetTopMostValue().Address.Uri.ToString())))
                {
                    b2buaRequest.Route.RemoveTopMostValue();
                }
                b2buaRequest.RecordRoute.RemoveAll();

                // Remove our Authorization header if it's there.
                foreach (SIP_SingleValueHF <SIP_t_Credentials> header in b2buaRequest.ProxyAuthorization.HeaderFields)
                {
                    try{
                        Auth_HttpDigest digest = new Auth_HttpDigest(header.ValueX.AuthData, b2buaRequest.RequestLine.Method);
                        if (m_pProxy.Stack.Realm == digest.Realm)
                        {
                            b2buaRequest.ProxyAuthorization.Remove(header);
                        }
                    }
                    catch {
                        // We don't care errors here. This can happen if remote server xxx auth method here and
                        // we don't know how to parse it, so we leave it as is.
                    }
                }

                //--- Add/replace default fields. ------------------------------------------
                b2buaRequest.Allow.RemoveAll();
                b2buaRequest.Supported.RemoveAll();
                // Accept to non ACK,BYE request.
                if (request.RequestLine.Method != SIP_Methods.ACK && request.RequestLine.Method != SIP_Methods.BYE)
                {
                    b2buaRequest.Allow.Add("INVITE,ACK,OPTIONS,CANCEL,BYE,PRACK");
                }
                // Supported to non ACK request.
                if (request.RequestLine.Method != SIP_Methods.ACK)
                {
                    b2buaRequest.Supported.Add("100rel,timer");
                }
                // Remove Require: header.
                b2buaRequest.Require.RemoveAll();

                // RFC 4028 7.4. For re-INVITE and UPDATE we need to add Session-Expires and Min-SE: headers.
                if (request.RequestLine.Method == SIP_Methods.INVITE || request.RequestLine.Method == SIP_Methods.UPDATE)
                {
                    b2buaRequest.SessionExpires = new SIP_t_SessionExpires(m_pProxy.Stack.SessionExpries, "uac");
                    b2buaRequest.MinSE          = new SIP_t_MinSE(m_pProxy.Stack.MinimumSessionExpries);
                }

                // Forward request.
                //m_pProxy.ForwardRequest(true,e,b2buaRequest,false);
            }
        }
Пример #6
0
        /// <summary>
        /// This method is called when new request is received.
        /// </summary>
        /// <param name="e">Request event arguments.</param>
        internal void OnRequestReceived(SIP_RequestReceivedEventArgs e)
        {
            SIP_Request request = e.Request;

            if (request.RequestLine.Method == SIP_Methods.CANCEL)
            {
                /* RFC 3261 9.2.
                    If the UAS did not find a matching transaction for the CANCEL
                    according to the procedure above, it SHOULD respond to the CANCEL
                    with a 481 (Call Leg/Transaction Does Not Exist).
                  
                    Regardless of the method of the original request, as long as the
                    CANCEL matched an existing transaction, the UAS answers the CANCEL
                    request itself with a 200 (OK) response.
                */

                SIP_ServerTransaction trToCancel =
                    m_pProxy.Stack.TransactionLayer.MatchCancelToTransaction(e.Request);
                if (trToCancel != null)
                {
                    trToCancel.Cancel();
                    //e.ServerTransaction.SendResponse(request.CreateResponse(SIP_ResponseCodes.x200_Ok));
                }
                else
                {
                    //e.ServerTransaction.SendResponse(request.CreateResponse(SIP_ResponseCodes.x481_Call_Transaction_Does_Not_Exist));
                }
            }
                // We never should ge BYE here, because transport layer must match it to dialog.
            else if (request.RequestLine.Method == SIP_Methods.BYE)
            {
                /* RFC 3261 15.1.2.
                    If the BYE does not match an existing dialog, the UAS core SHOULD generate a 481
                    (Call/Transaction Does Not Exist) response and pass that to the server transaction.
                */
                //e.ServerTransaction.SendResponse(request.CreateResponse(SIP_ResponseCodes.x481_Call_Transaction_Does_Not_Exist));
            }
                // We never should ge ACK here, because transport layer must match it to dialog.
            else if (request.RequestLine.Method == SIP_Methods.ACK)
            {
                // ACK is response less request, so we may not return error to it.
            }
                // B2BUA must respond to OPTIONS request, not to forward it.
            else if (request.RequestLine.Method == SIP_Methods.OPTIONS)
            {
                /*          
                SIP_Response response = e.Request.CreateResponse(SIP_ResponseCodes.x200_Ok);                
                // Add Allow to non ACK response.
                if(e.Request.RequestLine.Method != SIP_Methods.ACK){
                    response.Allow.Add("INVITE,ACK,OPTIONS,CANCEL,BYE,PRACK,MESSAGE,UPDATE");
                }
                // Add Supported to 2xx non ACK response.
                if(response.StatusCodeType == SIP_StatusCodeType.Success && e.Request.RequestLine.Method != SIP_Methods.ACK){
                    response.Supported.Add("100rel,timer");
                }
                e.ServerTransaction.SendResponse(response);*/
            }
                // We never should get PRACK here, because transport layer must match it to dialog.
            else if (request.RequestLine.Method == SIP_Methods.PRACK)
            {
                //e.ServerTransaction.SendResponse(request.CreateResponse(SIP_ResponseCodes.x481_Call_Transaction_Does_Not_Exist));
            }
                // We never should get UPDATE here, because transport layer must match it to dialog.
            else if (request.RequestLine.Method == SIP_Methods.UPDATE)
            {
                //e.ServerTransaction.SendResponse(request.CreateResponse(SIP_ResponseCodes.x481_Call_Transaction_Does_Not_Exist));
            }
            else
            {
                /* draft-marjou-sipping-b2bua-00 4.1.3.
                    When the UAS of the B2BUA receives an upstream SIP request, its
                    associated UAC generates a new downstream SIP request with its new
                    Via, Max-Forwards, Call-Id, CSeq, and Contact header fields. Route
                    header fields of the upstream request are copied in the downstream
                    request, except the first Route header if it is under the
                    responsibility of the B2BUA.  Record-Route header fields of the
                    upstream request are not copied in the new downstream request, as
                    Record-Route is only meaningful for the upstream dialog.  The UAC
                    SHOULD copy other header fields and body from the upstream request
                    into this downstream request before sending it.
                */

                SIP_Request b2buaRequest = e.Request.Copy();
                b2buaRequest.Via.RemoveAll();
                b2buaRequest.MaxForwards = 70;
                b2buaRequest.CallID = SIP_t_CallID.CreateCallID().CallID;
                b2buaRequest.CSeq.SequenceNumber = 1;
                b2buaRequest.Contact.RemoveAll();
                // b2buaRequest.Contact.Add(m_pProxy.CreateContact(b2buaRequest.To.Address).ToStringValue());
                if (b2buaRequest.Route.Count > 0 &&
                    m_pProxy.IsLocalRoute(
                        SIP_Uri.Parse(b2buaRequest.Route.GetTopMostValue().Address.Uri.ToString())))
                {
                    b2buaRequest.Route.RemoveTopMostValue();
                }
                b2buaRequest.RecordRoute.RemoveAll();

                // Remove our Authorization header if it's there.
                foreach (
                    SIP_SingleValueHF<SIP_t_Credentials> header in
                        b2buaRequest.ProxyAuthorization.HeaderFields)
                {
                    try
                    {
                        Auth_HttpDigest digest = new Auth_HttpDigest(header.ValueX.AuthData,
                                                                     b2buaRequest.RequestLine.Method);
                        if (m_pProxy.Stack.Realm == digest.Realm)
                        {
                            b2buaRequest.ProxyAuthorization.Remove(header);
                        }
                    }
                    catch
                    {
                        // We don't care errors here. This can happen if remote server xxx auth method here and
                        // we don't know how to parse it, so we leave it as is.
                    }
                }

                //--- Add/replace default fields. ------------------------------------------
                b2buaRequest.Allow.RemoveAll();
                b2buaRequest.Supported.RemoveAll();
                // Accept to non ACK,BYE request.
                if (request.RequestLine.Method != SIP_Methods.ACK &&
                    request.RequestLine.Method != SIP_Methods.BYE)
                {
                    b2buaRequest.Allow.Add("INVITE,ACK,OPTIONS,CANCEL,BYE,PRACK");
                }
                // Supported to non ACK request. 
                if (request.RequestLine.Method != SIP_Methods.ACK)
                {
                    b2buaRequest.Supported.Add("100rel,timer");
                }
                // Remove Require: header.
                b2buaRequest.Require.RemoveAll();

                // RFC 4028 7.4. For re-INVITE and UPDATE we need to add Session-Expires and Min-SE: headers.
                if (request.RequestLine.Method == SIP_Methods.INVITE ||
                    request.RequestLine.Method == SIP_Methods.UPDATE)
                {
                    b2buaRequest.SessionExpires = new SIP_t_SessionExpires(m_pProxy.Stack.SessionExpries,
                                                                           "uac");
                    b2buaRequest.MinSE = new SIP_t_MinSE(m_pProxy.Stack.MinimumSessionExpries);
                }

                // Forward request.
                m_pProxy.ForwardRequest(true, e, b2buaRequest, false);
            }
        }
Пример #7
0
 /// <summary>
 /// Default constructor.
 /// </summary>
 /// <param name="auth">Authentication context.</param>
 public SIP_AuthenticateEventArgs(Auth_HttpDigest auth)
 {
     m_pAuth = auth;
 }
Пример #8
0
        /// <summary>
        /// Is called by SIP proxy or registrar server when it needs to authenticate user.
        /// </summary>
        /// <param name="auth">Authentication context.</param>
        /// <returns></returns>
        internal SIP_AuthenticateEventArgs OnAuthenticate(Auth_HttpDigest auth)
        {
            SIP_AuthenticateEventArgs eArgs = new SIP_AuthenticateEventArgs(auth);
            if (Authenticate != null)
            {
                Authenticate(eArgs);
            }

            return eArgs;
        }
        /// <summary>
        /// Creates authorization for each challange in <b>response</b>.
        /// </summary>
        /// <param name="request">SIP request where to add authorization values.</param>
        /// <param name="response">SIP response which challanges to authorize.</param>
        /// <param name="credentials">Credentials for authorization.</param>
        /// <returns>Returns true if all challanges were authorized. If any of the challanges was not authorized, returns false.</returns>
        private bool Authorize(SIP_Request request, SIP_Response response, NetworkCredential[] credentials)
        {
            if (request == null)
            {
                throw new ArgumentNullException("request");
            }
            if (response == null)
            {
                throw new ArgumentNullException("response");
            }
            if (credentials == null)
            {
                throw new ArgumentNullException("credentials");
            }

            bool allAuthorized = true;

            #region WWWAuthenticate

            foreach (SIP_t_Challenge challange in response.WWWAuthenticate.GetAllValues())
            {
                Auth_HttpDigest authDigest = new Auth_HttpDigest(challange.AuthData,
                                                                 request.RequestLine.Method);

                // Serach credential for the specified challange.
                NetworkCredential credential = null;
                foreach (NetworkCredential c in credentials)
                {
                    if (c.Domain.ToLower() == authDigest.Realm.ToLower())
                    {
                        credential = c;
                        break;
                    }
                }
                // We don't have credential for this challange.
                if (credential == null)
                {
                    allAuthorized = false;
                }
                // Authorize challange.
                else
                {
                    authDigest.UserName = credential.UserName;
                    authDigest.Password = credential.Password;
                    authDigest.CNonce   = Auth_HttpDigest.CreateNonce();

                    request.Authorization.Add(authDigest.ToAuthorization());
                }
            }

            #endregion

            #region ProxyAuthenticate

            foreach (SIP_t_Challenge challange in response.ProxyAuthenticate.GetAllValues())
            {
                Auth_HttpDigest authDigest = new Auth_HttpDigest(challange.AuthData,
                                                                 request.RequestLine.Method);

                // Serach credential for the specified challange.
                NetworkCredential credential = null;
                foreach (NetworkCredential c in credentials)
                {
                    if (c.Domain.ToLower() == authDigest.Realm.ToLower())
                    {
                        credential = c;
                        break;
                    }
                }
                // We don't have credential for this challange.
                if (credential == null)
                {
                    allAuthorized = false;
                }
                // Authorize challange.
                else
                {
                    authDigest.UserName = credential.UserName;
                    authDigest.Password = credential.Password;
                    authDigest.CNonce   = Auth_HttpDigest.CreateNonce();

                    request.ProxyAuthorization.Add(authDigest.ToAuthorization());
                }
            }

            #endregion

            return(allAuthorized);
        }
Пример #10
0
        /// <summary>
        /// Authenticates SIP request. This method also sends all needed replys to request sender.
        /// </summary>
        /// <param name="e">Request event arguments.</param>
        /// <param name="userName">If authentication sucessful, then authenticated user name is stored to this variable.</param>
        /// <returns>Returns true if request was authenticated.</returns>
        internal bool AuthenticateRequest(SIP_RequestReceivedEventArgs e, out string userName)
        {
            userName = null;

            SIP_t_Credentials credentials = SIP_Utils.GetCredentials(e.Request, m_pStack.Realm);
            // No credentials for our realm.
            if (credentials == null)
            {
                SIP_Response notAuthenticatedResponse =
                    m_pStack.CreateResponse(SIP_ResponseCodes.x407_Proxy_Authentication_Required, e.Request);
                notAuthenticatedResponse.ProxyAuthenticate.Add(
                    new Auth_HttpDigest(m_pStack.Realm, m_pStack.DigestNonceManager.CreateNonce(), m_Opaque).
                        ToChallange());

                e.ServerTransaction.SendResponse(notAuthenticatedResponse);
                return false;
            }

            Auth_HttpDigest auth = new Auth_HttpDigest(credentials.AuthData, e.Request.RequestLine.Method);
            // Check opaque validity.
            if (auth.Opaque != m_Opaque)
            {
                SIP_Response notAuthenticatedResponse =
                    m_pStack.CreateResponse(
                        SIP_ResponseCodes.x407_Proxy_Authentication_Required + ": Opaque value won't match !",
                        e.Request);
                notAuthenticatedResponse.ProxyAuthenticate.Add(
                    new Auth_HttpDigest(m_pStack.Realm, m_pStack.DigestNonceManager.CreateNonce(), m_Opaque).
                        ToChallange());

                // Send response
                e.ServerTransaction.SendResponse(notAuthenticatedResponse);
                return false;
            }
            // Check nonce validity.
            if (!m_pStack.DigestNonceManager.NonceExists(auth.Nonce))
            {
                SIP_Response notAuthenticatedResponse =
                    m_pStack.CreateResponse(
                        SIP_ResponseCodes.x407_Proxy_Authentication_Required + ": Invalid nonce value !",
                        e.Request);
                notAuthenticatedResponse.ProxyAuthenticate.Add(
                    new Auth_HttpDigest(m_pStack.Realm, m_pStack.DigestNonceManager.CreateNonce(), m_Opaque).
                        ToChallange());

                // Send response
                e.ServerTransaction.SendResponse(notAuthenticatedResponse);
                return false;
            }
                // Valid nonce, consume it so that nonce can't be used any more. 
            else
            {
                m_pStack.DigestNonceManager.RemoveNonce(auth.Nonce);
            }

            SIP_AuthenticateEventArgs eArgs = OnAuthenticate(auth);
            // Authenticate failed.
            if (!eArgs.Authenticated)
            {
                SIP_Response notAuthenticatedResponse =
                    m_pStack.CreateResponse(
                        SIP_ResponseCodes.x407_Proxy_Authentication_Required + ": Authentication failed.",
                        e.Request);
                notAuthenticatedResponse.ProxyAuthenticate.Add(
                    new Auth_HttpDigest(m_pStack.Realm, m_pStack.DigestNonceManager.CreateNonce(), m_Opaque).
                        ToChallange());

                // Send response
                e.ServerTransaction.SendResponse(notAuthenticatedResponse);
                return false;
            }

            userName = auth.UserName;

            return true;
        }
Пример #11
0
        /// <summary>
        /// Authenticates user. Authenticate method chooses strongest possible authentication method supported by server, 
        /// preference order DIGEST-MD5 -> CRAM-MD5 -> LOGIN.
        /// </summary>
        /// <param name="userName">User login name.</param>
        /// <param name="password">Password.</param>
        /// <exception cref="ObjectDisposedException">Is raised when this object is disposed and this method is accessed.</exception>
        /// <exception cref="InvalidOperationException">Is raised when SMTP client is not connected or is already authenticated.</exception>
        /// <exception cref="ArgumentNullException">Is raised when <b>userName</b> is null.</exception>
        /// <exception cref="SMTP_ClientException">Is raised when SMTP server returns error.</exception>
        public void Authenticate(string userName, string password)
        {
            if (IsDisposed)
            {
                throw new ObjectDisposedException(GetType().Name);
            }
            if (!IsConnected)
            {
                throw new InvalidOperationException("You must connect first.");
            }
            if (IsAuthenticated)
            {
                throw new InvalidOperationException("Session is already authenticated.");
            }
            if (string.IsNullOrEmpty(userName))
            {
                throw new ArgumentNullException("userName");
            }
            if (password == null)
            {
                password = "";
            }

            // Choose authentication method, we consider LOGIN as default.
            string authMethod = "LOGIN";
            List<string> authMethods = new List<string>(SaslAuthMethods);
            if (authMethods.Contains("DIGEST-MD5"))
            {
                authMethod = "DIGEST-MD5";
            }
            else if (authMethods.Contains("CRAM-MD5"))
            {
                authMethod = "CRAM-MD5";
            }

            #region AUTH LOGIN

            if (authMethod == "LOGIN")
            {
                /* LOGIN
			          Example:
			            C: AUTH LOGIN<CRLF>
			            S: 334 VXNlcm5hbWU6<CRLF>   VXNlcm5hbWU6 = base64("USERNAME")
			            C: base64(username)<CRLF>
			            S: 334 UGFzc3dvcmQ6<CRLF>   UGFzc3dvcmQ6 = base64("PASSWORD")
			            C: base64(password)<CRLF>
			            S: 235 Ok<CRLF>
			    */

                WriteLine("AUTH LOGIN");

                // Read server response.
                string line = ReadLine();
                // Response line must start with 334 or otherwise it's error response.
                if (!line.StartsWith("334"))
                {
                    throw new SMTP_ClientException(line);
                }

                // Send user name to server.
                WriteLine(Convert.ToBase64String(Encoding.ASCII.GetBytes(userName)));

                // Read server response.
                line = ReadLine();
                // Response line must start with 334 or otherwise it's error response.
                if (!line.StartsWith("334"))
                {
                    throw new SMTP_ClientException(line);
                }

                // Send password to server.
                WriteLine(Convert.ToBase64String(Encoding.ASCII.GetBytes(password)));

                // Read server response.
                line = ReadLine();
                // Response line must start with 334 or otherwise it's error response.
                if (!line.StartsWith("235"))
                {
                    throw new SMTP_ClientException(line);
                }

                m_pAuthdUserIdentity = new GenericIdentity(userName, "LOGIN");
            }

                #endregion

                #region AUTH CRAM-MD5

            else if (authMethod == "CRAM-MD5")
            {
                /* CRAM-M5
                    Description:
                        HMACMD5 key is "password".
                 
			        Example:
					    C: AUTH CRAM-MD5<CRLF>
					    S: 334 base64(md5_calculation_hash)<CRLF>
					    C: base64(username password_hash)<CRLF>
					    S: 235 Ok<CRLF>
			    */

                WriteLine("AUTH CRAM-MD5");

                // Read server response.
                string line = ReadLine();
                // Response line must start with 334 or otherwise it's error response.
                if (!line.StartsWith("334"))
                {
                    throw new SMTP_ClientException(line);
                }

                HMACMD5 kMd5 = new HMACMD5(Encoding.ASCII.GetBytes(password));
                string passwordHash =
                    Core.ToHexString(kMd5.ComputeHash(Convert.FromBase64String(line.Split(' ')[1]))).ToLower();

                // Send authentication info to server.
                WriteLine(Convert.ToBase64String(Encoding.ASCII.GetBytes(userName + " " + passwordHash)));

                // Read server response.
                line = ReadLine();
                // Response line must start with 235 or otherwise it's error response
                if (!line.StartsWith("235"))
                {
                    throw new SMTP_ClientException(line);
                }

                m_pAuthdUserIdentity = new GenericIdentity(userName, "CRAM-MD5");
            }

                #endregion

                #region AUTH DIGEST-MD5

            else if (authMethod == "DIGEST-MD5")
            {
                /*
                    Example:
					    C: AUTH DIGEST-MD5<CRLF>
					    S: 334 base64(digestChallange)<CRLF>
					    C: base64(digestAuthorization)<CRLF>
                        S: 334 base64(serverResponse)<CRLF>
                        C: <CRLF>
					    S: 235 Ok<CRLF>
                */

                WriteLine("AUTH DIGEST-MD5");

                // Read server response.
                string line = ReadLine();
                // Response line must start with 334 or otherwise it's error response.
                if (!line.StartsWith("334"))
                {
                    throw new SMTP_ClientException(line);
                }

                Auth_HttpDigest digestmd5 =
                    new Auth_HttpDigest(
                        Encoding.Default.GetString(Convert.FromBase64String(line.Split(' ')[1])),
                        "AUTHENTICATE");
                digestmd5.CNonce = Auth_HttpDigest.CreateNonce();
                digestmd5.Uri = "smtp/" + digestmd5.Realm;
                digestmd5.UserName = userName;
                digestmd5.Password = password;

                // Send authentication info to server.
                WriteLine(Convert.ToBase64String(Encoding.ASCII.GetBytes(digestmd5.ToAuthorization(false))));

                // Read server response.
                line = ReadLine();
                // Response line must start with 334 or otherwise it's error response.
                if (!line.StartsWith("334"))
                {
                    throw new SMTP_ClientException(line);
                }

                // Send empty line.
                WriteLine("");

                // Read server response.
                line = ReadLine();
                // Response line must start with 235 or otherwise it's error response.
                if (!line.StartsWith("235"))
                {
                    throw new SMTP_ClientException(line);
                }

                m_pAuthdUserIdentity = new GenericIdentity(userName, "DIGEST-MD5");
            }

            #endregion
        }
Пример #12
0
        /// <summary>
        /// Gets specified realm SIP proxy credentials. Returns null if none exists for specified realm.
        /// </summary>
        /// <param name="request">SIP reques.</param>
        /// <param name="realm">Realm(domain).</param>
        /// <returns>Returns specified realm credentials or null if none.</returns>
        public static SIP_t_Credentials GetCredentials(SIP_Request request, string realm)
        {
            foreach (
                SIP_SingleValueHF<SIP_t_Credentials> authorization in request.ProxyAuthorization.HeaderFields)
            {
                if (authorization.ValueX.Method.ToLower() == "digest")
                {
                    Auth_HttpDigest authDigest = new Auth_HttpDigest(authorization.ValueX.AuthData,
                                                                     request.RequestLine.Method);
                    if (authDigest.Realm.ToLower() == realm.ToLower())
                    {
                        return authorization.ValueX;
                    }
                }
            }

            return null;
        }
 /// <summary>
 /// Default constructor.
 /// </summary>
 /// <param name="auth">Authentication context.</param>
 public SIP_AuthenticateEventArgs(Auth_HttpDigest auth)
 {
     m_pAuth = auth;
 }
Пример #14
0
        /// <summary>
        /// Creates authorization for each challange in <b>response</b>.
        /// </summary>
        /// <param name="request">SIP request where to add authorization values.</param>
        /// <param name="response">SIP response which challanges to authorize.</param>
        /// <param name="credentials">Credentials for authorization.</param>
        /// <returns>Returns true if all challanges were authorized. If any of the challanges was not authorized, returns false.</returns>
        private bool Authorize(SIP_Request request, SIP_Response response, NetworkCredential[] credentials)
        {
            if (request == null)
            {
                throw new ArgumentNullException("request");
            }
            if (response == null)
            {
                throw new ArgumentNullException("response");
            }
            if (credentials == null)
            {
                throw new ArgumentNullException("credentials");
            }

            bool allAuthorized = true;

            #region WWWAuthenticate

            foreach (SIP_t_Challenge challange in response.WWWAuthenticate.GetAllValues())
            {
                Auth_HttpDigest authDigest = new Auth_HttpDigest(challange.AuthData,
                                                                 request.RequestLine.Method);

                // Serach credential for the specified challange.
                NetworkCredential credential = null;
                foreach (NetworkCredential c in credentials)
                {
                    if (c.Domain.ToLower() == authDigest.Realm.ToLower())
                    {
                        credential = c;
                        break;
                    }
                }
                // We don't have credential for this challange.
                if (credential == null)
                {
                    allAuthorized = false;
                }
                    // Authorize challange.
                else
                {
                    authDigest.UserName = credential.UserName;
                    authDigest.Password = credential.Password;
                    authDigest.CNonce = Auth_HttpDigest.CreateNonce();

                    request.Authorization.Add(authDigest.ToAuthorization());
                }
            }

            #endregion

            #region ProxyAuthenticate

            foreach (SIP_t_Challenge challange in response.ProxyAuthenticate.GetAllValues())
            {
                Auth_HttpDigest authDigest = new Auth_HttpDigest(challange.AuthData,
                                                                 request.RequestLine.Method);

                // Serach credential for the specified challange.
                NetworkCredential credential = null;
                foreach (NetworkCredential c in credentials)
                {
                    if (c.Domain.ToLower() == authDigest.Realm.ToLower())
                    {
                        credential = c;
                        break;
                    }
                }
                // We don't have credential for this challange.
                if (credential == null)
                {
                    allAuthorized = false;
                }
                    // Authorize challange.
                else
                {
                    authDigest.UserName = credential.UserName;
                    authDigest.Password = credential.Password;
                    authDigest.CNonce = Auth_HttpDigest.CreateNonce();

                    request.ProxyAuthorization.Add(authDigest.ToAuthorization());
                }
            }

            #endregion

            return allAuthorized;
        }