예제 #1
0
        private Boolean UpdateChallenges(TSIP_Response response, Boolean acceptNewVector)
        {
            Boolean ret = true;
            TSIP_HeaderWWWAuthenticate WWW_Authenticate;

            // TSIP_HeaderProxyAuthenticate Proxy_Authenticate;

            /* RFC 2617 - HTTP Digest Session
             *
             *	(A) The client response to a WWW-Authenticate challenge for a protection
             *          space starts an authentication session with that protection space.
             *          The authentication session lasts until the client receives another
             *          WWW-Authenticate challenge from any server in the protection space.
             *
             *          (B) The server may return a 401 response with a new nonce value, causing the client
             *          to retry the request; by specifying stale=TRUE with this response,
             *          the server tells the client to retry with the new nonce, but without
             *          prompting for a new username and password.
             */
            /* RFC 2617 - 1.2 Access Authentication Framework
             *  The realm directive (case-insensitive) is required for all authentication schemes that issue a challenge.
             */

            /* FIXME: As we perform the same task ==> Use only one loop.
             */

            for (int i = 0; (WWW_Authenticate = (TSIP_HeaderWWWAuthenticate)response.GetHeaderAtIndex(TSIP_Header.tsip_header_type_t.WWW_Authenticate, i)) != null; i++)
            {
                Boolean isnew = true;

                foreach (TSIP_Challenge challenge in mChallenges)
                {
                    //if(challenge.IProxy)
                    //{
                    //    continue;
                    //}

                    if (String.Equals(challenge.Realm, WWW_Authenticate.Realm, StringComparison.InvariantCultureIgnoreCase) && (WWW_Authenticate.Stale || acceptNewVector))
                    {
                        /*== (B) ==*/
                        if (!(ret = challenge.Update(WWW_Authenticate.Scheme,
                                                     WWW_Authenticate.Realm,
                                                     WWW_Authenticate.Nonce,
                                                     WWW_Authenticate.Opaque,
                                                     WWW_Authenticate.Algorithm,
                                                     WWW_Authenticate.Qop)))
                        {
                            return(ret);
                        }
                        else
                        {
                            isnew = false;
                            continue;
                        }
                    }
                    else
                    {
                        TSK_Debug.Error("Failed to handle new challenge");
                        return(false);
                    }
                }

                if (isnew)
                {
                    TSIP_Challenge challenge;
                    if ((challenge = new TSIP_Challenge(this.Stack,
                                                        false,
                                                        WWW_Authenticate.Scheme,
                                                        WWW_Authenticate.Realm,
                                                        WWW_Authenticate.Nonce,
                                                        WWW_Authenticate.Opaque,
                                                        WWW_Authenticate.Algorithm,
                                                        WWW_Authenticate.Qop)) != null)
                    {
                        mChallenges.Add(challenge);
                    }
                    else
                    {
                        TSK_Debug.Error("Failed to handle new challenge");
                        return(false);
                    }
                }
            }

            return(ret);
        }
예제 #2
0
        protected TSIP_Request CreateRequest(String method)
        {
            TSIP_Request request = null;
            TSIP_Uri     to_uri, from_uri, request_uri;
            String       call_id;
            int          copy_routes_start = -1; /* NONE */

            /*
             *  RFC 3261 - 12.2.1.1 Generating the Request
             *
             *  The Call-ID of the request MUST be set to the Call-ID of the dialog.
             */
            call_id = mCallId;

            /*
             *  RFC 3261 - 12.2.1.1 Generating the Request
             *
             *  Requests within a dialog MUST contain strictly monotonically
             *  increasing and contiguous CSeq sequence numbers (increasing-by-one)
             *  in each direction (excepting ACK and CANCEL of course, whose numbers
             *  equal the requests being acknowledged or cancelled).  Therefore, if
             *  the local sequence number is not empty, the value of the local
             *  sequence number MUST be incremented by one, and this value MUST be
             *  placed into the CSeq header field.
             */
            /*if(!tsk_striequals(method, "ACK") && !tsk_striequals(method, "CANCEL"))
             * {
             *  TSIP_DIALOG(self)->cseq_value +=1;
             * }
             * ===> See send method (cseq will be incremented before sending the request)
             */


            /*
             * RFC 3261 - 12.2.1.1 Generating the Request
             *
             * The URI in the To field of the request MUST be set to the remote URI
             * from the dialog state.  The tag in the To header field of the request
             * MUST be set to the remote tag of the dialog ID.  The From URI of the
             * request MUST be set to the local URI from the dialog state.  The tag
             * in the From header field of the request MUST be set to the local tag
             * of the dialog ID.  If the value of the remote or local tags is null,
             * the tag parameter MUST be omitted from the To or From header fields,
             * respectively.
             */
            to_uri   = mUriRemote;
            from_uri = mUriLocal;

            /*
             *  RFC 3261 - 12.2.1.1 Generating the Request
             *
             *  If the route set is empty, the UAC MUST place the remote target URI
             *  into the Request-URI.  The UAC MUST NOT add a Route header field to
             *  the request.
             */
            if (mRecordRoutes == null || mRecordRoutes.Count == 0)
            {
                request_uri = mUriRemoteTarget;
            }

            /*
             *  RFC 3261 - 12.2.1.1 Generating the Request
             *
             *  If the route set is not empty, and the first URI in the route set
             *  contains the lr parameter (see Section 19.1.1), the UAC MUST place
             *  the remote target URI into the Request-URI and MUST include a Route
             *  header field containing the route set values in order, including all
             *  parameters.
             *
             *  If the route set is not empty, and its first URI does not contain the
             *  lr parameter, the UAC MUST place the first URI from the route set
             *  into the Request-URI, stripping any parameters that are not allowed
             *  in a Request-URI.  The UAC MUST add a Route header field containing
             *  the remainder of the route set values in order, including all
             *  parameters.  The UAC MUST then place the remote target URI into the
             *  Route header field as the last value.
             *
             *  For example, if the remote target is sip:user@remoteua and the route
             *  set contains:
             *
             *  <sip:proxy1>,<sip:proxy2>,<sip:proxy3;lr>,<sip:proxy4>
             */
            else
            {
                TSIP_Uri first_route = mRecordRoutes[0].Uri;
                if (TSK_Param.HasParam(first_route.Params, "lr"))
                {
                    request_uri       = mUriRemoteTarget;
                    copy_routes_start = 0;             /* Copy all */
                }
                else
                {
                    request_uri       = first_route;
                    copy_routes_start = 1;             /* Copy starting at index 1. */
                }
            }

            /*=====================================================================
             */
            request              = new TSIP_Request(method, request_uri, from_uri, to_uri, call_id, (Int32)mCSeqValue);
            request.To.Tag       = mTagRemote;
            request.From.Tag     = mTagLocal;
            request.ShouldUpdate = true; /* Now signal that the message should be updated by the transport layer (Contact, SigComp, IPSec, ...) */

            /*
             *  RFC 3261 - 12.2.1.1 Generating the Request
             *
             *  A UAC SHOULD include a Contact header field in any target refresh
             *  requests within a dialog, and unless there is a need to change it,
             *  the URI SHOULD be the same as used in previous requests within the
             *  dialog.  If the "secure" flag is true, that URI MUST be a SIPS URI.
             *  As discussed in Section 12.2.2, a Contact header field in a target
             *  refresh request updates the remote target URI.  This allows a UA to
             *  provide a new contact address, should its address change during the
             *  duration of the dialog.
             */
            switch (request.RequestType)
            {
            case TSIP_Message.tsip_request_type_t.MESSAGE:
            case TSIP_Message.tsip_request_type_t.PUBLISH:
            case TSIP_Message.tsip_request_type_t.BYE:
            {
                if (request.RequestType == TSIP_Message.tsip_request_type_t.PUBLISH)
                {
                    request.AddHeader(new TSIP_HeaderExpires(mExpires));
                }
                /* add caps in Accept-Contact headers */
                foreach (TSK_Param param in mSipSession.Caps)
                {
                    request.AddHeader(new TSIP_HeaderDummy("Accept-Contact",
                                                           String.Format("*;{0}{1}{2}",
                                                                         param.Name,
                                                                         !String.IsNullOrEmpty(param.Value) ? "=" : String.Empty,
                                                                         !String.IsNullOrEmpty(param.Value) ? param.Value : String.Empty)
                                                           ));
                }
                break;
            }

            default:
            {
                String contact = null;
                List <TSIP_HeaderContact> hdr_contacts = null;
                if (request.RequestType == TSIP_Message.tsip_request_type_t.OPTIONS ||
                    request.RequestType == TSIP_Message.tsip_request_type_t.PUBLISH ||
                    request.RequestType == TSIP_Message.tsip_request_type_t.REGISTER)
                {
                    /**** with expires */
                    contact = String.Format("m: <{0}:{1}@{2}:{3}>;expires={4}\r\n",
                                            "sip",
                                            from_uri.UserName,
                                            "127.0.0.1",
                                            5060,
                                            TSK_Time.Milliseconds2Seconds(mExpires));
                }
                else
                {
                    /**** without expires */
                    if (request.RequestType == TSIP_Message.tsip_request_type_t.SUBSCRIBE)
                    {
                        /* RFC 3265 - 3.1.1. Subscription Duration
                         *                              An "expires" parameter on the "Contact" header has no semantics for SUBSCRIBE and is explicitly
                         *                              not equivalent to an "Expires" header in a SUBSCRIBE request or response.
                         */
                        request.AddHeader(new TSIP_HeaderExpires(TSK_Time.Milliseconds2Seconds(mExpires)));
                    }
                    contact = String.Format("m: <{0}:{1}@{2}:{3}>\r\n",
                                            "sip",
                                            from_uri.UserName,
                                            "127.0.0.1",
                                            5060);
                }

                hdr_contacts = TSIP_HeaderContact.Parse(contact);
                if (hdr_contacts != null && hdr_contacts.Count > 0)
                {
                    request.Contact = hdr_contacts[0];
                }

                /* Add capabilities as per RFC 3840 */
                if (request.Contact != null)
                {
                    foreach (TSK_Param param in mSipSession.Caps)
                    {
                        request.Contact.Params = TSK_Param.AddParam(request.Contact.Params, param.Name, param.Value);
                    }
                }

                break;
            }
            }

            /* Update authorizations */
            if (mState == tsip_dialog_state_t.Initial && mChallenges == null || mChallenges.Count == 0)
            {
                /* 3GPP TS 33.978 6.2.3.1 Procedures at the UE
                 *              On sending a REGISTER request in order to indicate support for early IMS security procedures, the UE shall not
                 *              include an Authorization header field and not include header fields or header field values as required by RFC3329.
                 */
                if (request.IsREGISTER && !this.Stack.EarlyIMS)
                {
                    /*	3GPP TS 24.229 - 5.1.1.2.2 Initial registration using IMS AKA
                     *                  On sending a REGISTER request, the UE shall populate the header fields as follows:
                     *                          a) an Authorization header field, with:
                     *                          - the "username" header field parameter, set to the value of the private user identity;
                     *                          - the "realm" header field parameter, set to the domain name of the home network;
                     *                          - the "uri" header field parameter, set to the SIP URI of the domain name of the home network;
                     *                          - the "nonce" header field parameter, set to an empty value; and
                     *                          - the "response" header field parameter, set to an empty value;
                     */
                    String      realm        = this.Stack.Realm != null ? this.Stack.Realm.Host : "(null)";
                    String      request_uri_ = TSIP_Uri.ToString(request.Uri, false, false);
                    TSIP_Header auth_hdr     = TSIP_Challenge.CreateEmptyAuthorization(this.Stack.PrivateIdentity, realm, request_uri_);
                    if (auth_hdr != null)
                    {
                        request.AddHeader(auth_hdr);
                    }
                }
            }
            else if (mChallenges != null && mChallenges.Count > 0)
            {
                TSIP_Header auth_hdr;
                foreach (TSIP_Challenge challenge in mChallenges)
                {
                    auth_hdr = challenge.CreateHeaderAuthorization(request);
                    if (auth_hdr != null)
                    {
                        request.AddHeader(auth_hdr);
                    }
                }
            }

            /* Update CSeq */

            /*	RFC 3261 - 13.2.2.4 2xx Responses
             * Generating ACK: The sequence number of the CSeq header field MUST be
             * the same as the INVITE being acknowledged, but the CSeq method MUST
             * be ACK.  The ACK MUST contain the same credentials as the INVITE.  If
             * the 2xx contains an offer (based on the rules above), the ACK MUST
             * carry an answer in its body.
             * ==> CSeq number will be added/updated by the caller of this function,
             * credentials were added above.
             */
            if (!request.IsACK && !request.IsCANCEL)
            {
                request.CSeq.CSeq = ++mCSeqValue;
            }

            /* Route generation
             *	==> http://betelco.blogspot.com/2008/11/proxy-and-service-route-discovery-in.html
             * The dialog Routes have been copied above.
             *
             *          3GPP TS 24.229 - 5.1.2A.1 UE-originating case
             *
             *          The UE shall build a proper preloaded Route header field value for all new dialogs and standalone transactions. The UE
             *          shall build a list of Route header field values made out of the following, in this order:
             *          a) the P-CSCF URI containing the IP address or the FQDN learnt through the P-CSCF discovery procedures; and
             *          b) the P-CSCF port based on the security mechanism in use:
             *
             *          - if IMS AKA or SIP digest with TLS is in use as a security mechanism, the protected server port learnt during
             *          the registration procedure;
             *          - if SIP digest without TLS, NASS-IMS bundled authentciation or GPRS-IMS-Bundled authentication is in
             *          use as a security mechanism, the unprotected server port used during the registration procedure;
             *          c) and the values received in the Service-Route header field saved from the 200 (OK) response to the last
             *          registration or re-registration of the public user identity with associated contact address.
             */
            if (!request.IsREGISTER)
            {     // According to the above link ==> Initial/Re/De registration do not have routes.
                if (copy_routes_start != -1)
                { /* The dialog already have routes ==> copy them. */
                    if (mState == tsip_dialog_state_t.Early || mState == tsip_dialog_state_t.Established)
                    {
                        Int32 index = -1;
                        foreach (TSIP_HeaderRecordRoute record_route in mRecordRoutes)
                        {
                            TSIP_Uri uri = record_route.Uri;
                            if (++index < copy_routes_start || uri == null)
                            {
                                continue;
                            }
                            TSIP_HeaderRoute route = new TSIP_HeaderRoute(uri);
                            // copy parameters: see http://code.google.com/p/imsdroid/issues/detail?id=52
                            route.Params.AddRange(record_route.Params);
                            request.AddHeader(route);
                        }
                    }
                }
            }
            else
            {/* No routes associated to this dialog. */
                if (mState == tsip_dialog_state_t.Initial || mState == tsip_dialog_state_t.Early)
                {
                    /*	GPP TS 24.229 section 5.1.2A [Generic procedures applicable to all methods excluding the REGISTER method]:
                     *                      The UE shall build a proper preloaded Route header field value for all new dialogs and standalone transactions. The UE
                     *                      shall build a list of Route header field values made out of the following, in this order:
                     *                      a) the P-CSCF URI containing the IP address or the FQDN learnt through the P-CSCF discovery procedures; and
                     *                      b) the P-CSCF port based on the security mechanism in use:
                     *                              - if IMS AKA or SIP digest with TLS is in use as a security mechanism, the protected server port learnt during
                     *                              the registration procedure;
                     *                              - if SIP digest without TLS, NASS-IMS bundled authentciation or GPRS-IMS-Bundled authentication is in
                     *                              use as a security mechanism, the unprotected server port used during the registration procedure;
                     *                      c) and the values received in the Service-Route header field saved from the 200 (OK) response to the last
                     *                      registration or re-registration of the public user identity with associated contact address.
                     */
#if _DEBUG && SDS_HACK
                    /* Ericsson SDS hack (INVITE with Proxy-CSCF as First route fail) */
#else
                    TSIP_Uri uri = this.Stack.GetProxyCSCFUri(true);
                    // Proxy-CSCF as first route
                    if (uri != null)
                    {
                        request.AddHeader(new TSIP_HeaderRoute(uri));
                    }
#endif
                    // Service routes
                    foreach (TSIP_Uri uriServiceRoute in mServiceRoutes)
                    {
                        request.AddHeader(new TSIP_HeaderRoute(uriServiceRoute));
                    }
                }
            }

            /* Add headers associated to the dialog's session */
            foreach (TSK_Param param in mSipSession.Headers)
            {
                request.AddHeader(new TSIP_HeaderDummy(param.Name, param.Value));
            }

            /* Add headers associated to the dialog's stack */
            foreach (TSK_Param param in this.Stack.Headers)
            {
                request.AddHeader(new TSIP_HeaderDummy(param.Name, param.Value));
            }

            /* Add common headers */
            this.AddCommonHeaders(request);

            /* SigComp */


            return(request);
        }