void SignedUrl::sign( Url& urlToSign ) { UtlString existingUrlSignature; UtlString urlString; urlToSign.toString( urlString ); if( urlToSign.getUrlParameter( SignatureUrlParamName, existingUrlSignature ) == TRUE ) { OsSysLog::add(FAC_SIP, PRI_DEBUG, "SignedUrl::sign URL '%s' already signed - updating signature", urlString.data() ); } UtlString userInfo; UtlString hostPort; UtlString strSignature; urlToSign.getUserId( userInfo ); urlToSign.getHostWithPort( hostPort ); computeSignature( userInfo, hostPort, strSignature ); urlToSign.setUrlParameter( SignatureUrlParamName, strSignature.data() ); OsSysLog::add(FAC_SIP, PRI_DEBUG, "SignedUrl::sign URL signed: '%s' with signature '%s'", urlString.data(), strSignature.data() ); }
UtlBoolean SignedUrl::isUrlSigned( Url& signedUrl ) { UtlBoolean bUrlProperlySigned; UtlString existingUrlSignature; UtlString urlString; signedUrl.toString( urlString ); if( signedUrl.getUrlParameter( SignatureUrlParamName, existingUrlSignature ) == FALSE ) { bUrlProperlySigned = FALSE; OsSysLog::add(FAC_SIP, PRI_DEBUG, "SignedUrl::isUrlSigned URL '%s' not signed", urlString.data() ); } else { UtlString userInfo; UtlString hostPort; UtlString strReferenceSignature; signedUrl.getUserId( userInfo ); signedUrl.getHostWithPort( hostPort ); computeSignature( userInfo, hostPort, strReferenceSignature ); if( strReferenceSignature.compareTo( existingUrlSignature ) == 0 ) { bUrlProperlySigned = TRUE; OsSysLog::add(FAC_SIP, PRI_DEBUG, "SignedUrl::isUrlSigned URL '%s' is properly signed", urlString.data() ); } else { bUrlProperlySigned = FALSE; OsSysLog::add(FAC_SIP, PRI_DEBUG, "SignedUrl::isUrlSigned URL '%s' does not have a valid signature. " "Expected signature: '%s'", urlString.data(), strReferenceSignature.data() ); } } return bUrlProperlySigned; }
AuthPlugin::AuthResult MSFT_ExchangeTransferHack::authorizeAndModify(const UtlString& id, const Url& requestUri, RouteState& routeState, const UtlString& method, AuthResult priorResult, SipMessage& request, bool bSpiralingRequest, UtlString& reason ) { AuthResult result = CONTINUE; // we modify, but never make an authorization decision if (mUserAgentRegEx) // if not configured, do nothing { // get the call-id to use in logging UtlString callId; request.getCallIdField(&callId); /* * Note: a REFER from Exchange, with the bug we are hunting here, will always look like * it is tranferring to some foreign domain. This will cause the TransferControl AuthPlugin * to ALLOW it. If TransferControl starts challenging REFER from Exchange, that may mean * that MSFT has fixed the bug this plugin is meant to compensate for. */ if (DENY != priorResult) // ignore anything some other plugin has already nixed { if (method.compareTo(SIP_REFER_METHOD) == 0) // we only care about REFER { UtlString userAgent; request.getUserAgentField( &userAgent ); if (mUserAgentRegEx->Search(userAgent)) // does this look like Exchange? { UtlString targetStr; if (request.getReferToField(targetStr)) // get the address of the transfer target { Url target(targetStr); if (Url::SipUrlScheme == target.getScheme()) // target address parsed ok? { // check whether or not this is REFER with Replaces UtlString targetDialog; if (!target.getHeaderParameter(SIP_REPLACES_FIELD, targetDialog)) { /* * This is a REFER without Replaces from Exchange * so check the domain parts of the two URIs to see if they match. */ // Get the domain part of the transfer-target URI UtlString targetDomain; target.getHostWithPort(targetDomain); // Get the domain part of the request URI UtlString requestDomain; requestUri.getHostWithPort(requestDomain); if (targetDomain.compareTo(requestDomain, UtlString::ignoreCase) == 0) { // The domains are the same; this is the bug we're looking for... UtlString correctDomain; mpSipRouter->getDomain(correctDomain); target.setHostAddress(correctDomain); target.setHostPort(PORT_NONE); UtlString modifiedTarget; target.toString(modifiedTarget); request.setReferToField(modifiedTarget.data()); Os::Logger::instance().log(FAC_AUTH, PRI_INFO, "MSFT_ExchangeTransferHack[%s]::authorizeAndModify " "corrected transfer target domain in call '%s'\n" "changed '@%s' -> '@%s'", mInstanceName.data(), callId.data(), targetDomain.data(), correctDomain.data() ); } else { // oh my god... did MSFT fix Exchange? Os::Logger::instance().log(FAC_AUTH, PRI_DEBUG, "MSFT_ExchangeTransferHack[%s]::authorizeAndModify " "request and target domain differ in '%s'; not modified", mInstanceName.data(), callId.data() ); } } else { // This is a REFER with Replaces from Exchange // I don't expect this to happen, but if it does then don't mess with it. Os::Logger::instance().log(FAC_AUTH, PRI_INFO, "MSFT_ExchangeTransferHack[%s]::authorizeAndModify " "allowing REFER with Replaces in call '%s' to '%s'; no action", mInstanceName.data(), callId.data(), targetDialog.data() ); } } else { Os::Logger::instance().log(FAC_AUTH, PRI_WARNING, "MSFT_ExchangeTransferHack[%s]::authorizeAndModify " "unrecognized refer target '%s' for call '%s'", mInstanceName.data(), targetStr.data(), callId.data() ); } } else { // REFER without a Refer-To header... incorrect, but just ignore it. Os::Logger::instance().log(FAC_AUTH, PRI_WARNING, "MSFT_ExchangeTransferHack[%s]::authorizeAndModify " "REFER method without Refer-To in call '%s'", mInstanceName.data(), callId.data() ); } } else { Os::Logger::instance().log(FAC_AUTH, PRI_DEBUG, "MSFT_ExchangeTransferHack[%s]::authorizeAndModify " "User-Agent '%s' does not match recognizer in %s", mInstanceName.data(), userAgent.data(), callId.data() ); } } else { // not a REFER - ignore it. } } else { // Some earlier plugin already decided on this - don't waste time figuring it out. Os::Logger::instance().log(FAC_AUTH, PRI_DEBUG, "MSFT_ExchangeTransferHack[%s]::authorizeAndModify " "prior authorization result %s for call %s", mInstanceName.data(), AuthResultStr(priorResult), callId.data() ); } } return result; }
AuthPlugin::AuthResult CallerAlias::authorizeAndModify(const UtlString& id, /**< The authenticated identity of the * request originator, if any (the null * string if not). * This is in the form of a SIP uri * identity value as used in the * credentials database (user@domain) * without the scheme or any parameters. */ const Url& requestUri, ///< parsed target Uri RouteState& routeState, ///< the state for this request. const UtlString& method,///< the request method AuthResult priorResult,///< results from earlier plugins. SipMessage& request, ///< see AuthPlugin regarding modifying bool bSpiralingRequest, ///< spiraling indication UtlString& reason ///< rejection reason ) { // get the call-id to use in logging UtlString callId; request.getCallIdField(&callId); if ( (priorResult != DENY) // no point in modifying a request that won't be sent ) { UtlString callerFrom; UtlString callerFromTagOffsetStr; UtlString aliasFrom; UtlString aliasFromTagOffsetStr; UtlString originalFromTag; if ( !routeState.getParameter(mInstanceName.data(), CALLER_FROM_PARAM, callerFrom) || !routeState.getParameter(mInstanceName.data(), CALLER_TAG_OFFSET_PARAM, callerFromTagOffsetStr) || !routeState.getParameter(mInstanceName.data(), ALIAS_FROM_PARAM, aliasFrom) || !routeState.getParameter(mInstanceName.data(), ALIAS_TAG_OFFSET_PARAM, aliasFromTagOffsetStr) || !routeState.originalCallerFromTagValue(mInstanceName.data(), originalFromTag) ) { if ( routeState.isMutable() && routeState.directionIsCallerToCalled(mInstanceName.data()) ) // a new dialog? { /* * Get the callers identity by getting the caller URI and: * remove all parameters * remove the scheme name */ UtlString callerIdentity; UtlString originalFromField; request.getFromField(&originalFromField); Url originalFromUrl(originalFromField); /* * Extract the from identity as a key for the caller alias table * Start with the From header field (someday we should use the Identity if present) */ Url fromUrl(originalFromUrl); fromUrl.removeParameters(); // parameters are not relevant for this Url::Scheme fromUrlScheme = fromUrl.getScheme(); switch (fromUrlScheme) { case Url::SipsUrlScheme: // sips and sip are equivalent for identity purposes, // so just set to sip fromUrl.setScheme(Url::SipUrlScheme); // and fall through to extract the identity... case Url::SipUrlScheme: // case Url::TelUrlScheme: will go here, since 'tel' and 'sip' are the same length fromUrl.getUri(callerIdentity); callerIdentity.remove(0,4 /* strlen("sip:") */); // strip off the scheme name break; default: // for all other schemes, treat identity as null Os::Logger::instance().log(FAC_SIP, PRI_WARNING, "CallerAlias[%s]::check4andApplyAlias From uses unsupported scheme '%s'" " - using null identity", mInstanceName.data(), Url::schemeName(fromUrlScheme) ); break; } /* * Determine whether the identity is one for which this proxy * is authoritative; if not, we will not use wildcard matches. */ bool identityIsLocal = mpSipRouter->isLocalDomain(fromUrl); // now we have callerIdentity set; use for looking up each contact. Os::Logger::instance().log(FAC_SIP, PRI_DEBUG, "CallerAlias[%s]::check4andApplyAlias " "\n caller '%s' %s", mInstanceName.data(), callerIdentity.data(), identityIsLocal ? "is local" : "is not local" ); /* * Examine the request URI, * checking for a caller alias set for its domain(including asssociated gateway sipxecsLineid) with callerIdentity */ UtlString sipxecsLineIdField; requestUri.getUrlParameter(SIPX_SIPXECS_LINEID_URI_PARAM, sipxecsLineIdField); Os::Logger::instance().log(FAC_SIP, PRI_DEBUG, "getUrlParameter: sipxecsLineid[%s]" " in CallerAlias", sipxecsLineIdField.data() ); UtlString targetDomain; requestUri.getHostWithPort(targetDomain); if (!(sipxecsLineIdField.isNull())) { targetDomain.append(";").append(SIPX_SIPXECS_LINEID_URI_PARAM).append("=").append(sipxecsLineIdField.data()); } Os::Logger::instance().log(FAC_SIP, PRI_DEBUG, "CallerAlias::targetDomain [%s]", targetDomain.data() ); // look up any caller alias for this identity and contact domain UtlString callerAlias; if (identityIsLocal && getCallerAlias(callerIdentity, targetDomain, callerAlias) ) { // found a caller alias, so rewrite the From information /* * The From header requires special handling * - we need to preserve the tag, if any, from the original header */ originalFromUrl.getFieldParameter("tag", originalFromTag); Url newFromUrl(callerAlias.data()); newFromUrl.removeFieldParameter("tag"); // specifying a tag is a no-no if ( !originalFromTag.isNull() ) { newFromUrl.setFieldParameter("tag", originalFromTag.data()); } UtlString newFromFieldValue; newFromUrl.toString(newFromFieldValue); // log the change we are making before stripping the tag from the field values Os::Logger::instance().log( FAC_SIP, PRI_INFO, "CallerAlias[%s]::check4andApplyAlias call %s set caller alias\n" " Original-From: %s\n" " Aliased-From: %s", mInstanceName.data(), callId.data(), originalFromField.data(), newFromFieldValue.data() ); // rewrite the caller identity with the aliased value request.setRawFromField(newFromFieldValue.data()); // Factor the tag values out of the field values stored in the RouteState // We do this because otherwise we'll end up encoding and sending two copies // of the tag; since some phones send really long tag values (no one knows why), // this can cause such large Record-Route headers that they cause interop problems. if ( ! originalFromTag.isNull() ) { // find the offset of the tag value in the callers from field ssize_t callerFromTagOffset; callerFromTagOffset = originalFromField.index(originalFromTag); callerFromTagOffsetStr.appendNumber(callerFromTagOffset); // strip the tag value from the original From value to be stored in the RouteState originalFromField.replace(callerFromTagOffset, originalFromTag.length(), ""); // find the offset of the tag value in the aliased from field ssize_t aliasFromTagOffset; aliasFromTagOffset = newFromFieldValue.index(originalFromTag); aliasFromTagOffsetStr.appendNumber(aliasFromTagOffset); // strip the tag value from the aliased From value to be stored in the RouteState newFromFieldValue.replace(aliasFromTagOffset, originalFromTag.length(), ""); } // save the original and new values so that we can fix them later routeState.setParameter(mInstanceName.data(), CALLER_FROM_PARAM,originalFromField); routeState.setParameter(mInstanceName.data(), CALLER_TAG_OFFSET_PARAM,callerFromTagOffsetStr); routeState.setParameter(mInstanceName.data(), ALIAS_FROM_PARAM,newFromFieldValue); routeState.setParameter(mInstanceName.data(), ALIAS_TAG_OFFSET_PARAM,aliasFromTagOffsetStr); } else { Os::Logger::instance().log( FAC_SIP, PRI_DEBUG, "CallerAlias[%s]::check4andApplyAlias call %s found no alias", mInstanceName.data(), callId.data() ); } } else { Os::Logger::instance().log(FAC_SIP, PRI_DEBUG, "CallerAlias[%s]::authorizeAndModify " "not mutable - no rewrite", mInstanceName.data() ); } } else // the callerFrom and aliasFrom parameters were found { /* * This request has had its From rewritten, so fix either the From * or the To depending on which direction this request is going. */ if (!request.isResponse()) // can't modify responses, so don't bother { size_t tagOffset; if (routeState.directionIsCallerToCalled(mInstanceName.data())) { // replace the from tag value in the stored aliased header tagOffset = strtol(aliasFromTagOffsetStr.data(), NULL, 10); aliasFrom.insert(tagOffset, originalFromTag); // put the aliased header into the message request.setRawFromField(aliasFrom); Os::Logger::instance().log(FAC_AUTH, PRI_DEBUG, "CallerAlias[%s]::authorizeAndModify " "call %s reset From", mInstanceName.data(), callId.data() ); } else // direction is Called to Caller { // replace the from tag value in the stored original header tagOffset = strtol(callerFromTagOffsetStr.data(), NULL, 10); callerFrom.insert(tagOffset, originalFromTag); request.setRawToField(callerFrom.data()); Os::Logger::instance().log(FAC_AUTH, PRI_DEBUG, "CallerAlias[%s]::authorizeAndModify " "call %s reset To", mInstanceName.data(), callId.data() ); } } } } return AuthPlugin::CONTINUE; }