AuthPlugin::AuthResult TransferControl::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 wrt modifying bool bSpiralingRequest, ///< request spiraling indication UtlString& reason ///< rejection reason ) { AuthResult result = CONTINUE; // get the call-id to use in logging UtlString callId; request.getCallIdField(&callId); UtlString hostAddress; int hostPort; UtlString hostProtocols; //requestUri.getHostAddress(hostAddress); //request.getContactUri(0, &hostAddress);; request.getContactAddress(0, &hostAddress,&hostPort,&hostProtocols); if (DENY != priorResult) { if (method.compareTo(SIP_REFER_METHOD) == 0) { UtlString targetStr; if (request.getReferToField(targetStr)) { Url target(targetStr, Url::NameAddr); // parse the target URL UtlString targetMethod; if ( Url::SipUrlScheme == target.getScheme() /* REFER can create requests other than INVITE: we don't care about those * * so check that the method is INVITE or is unspecified (INVITE is the default) */ && ( ! target.getUrlParameter(SIP_METHOD_URI_PARAMETER, targetMethod) || (0==targetMethod.compareTo(SIP_INVITE_METHOD, UtlString::ignoreCase)) )) { if (id.isNull()) { // UnAuthenticated REFER. Do challenge the REFER to confirm the // identity of the transferor. Note: prior to XECS-2487, we used to challenge // only the unauthenticated REFERs that didn't carry a Replaces header. // The fix for XECS-2487 now requires that all unauthenticated REFERs // be challenged so that consultative transfers get routed properly // when user-based gateway section is used. See tracker for the details if (mpSipRouter->isLocalDomain(target)) { //White list of two servets to let Exchange REFER to sipXecs endpoints if (hostAddress.compareTo(server1, UtlString::ignoreCase) == 0 || hostAddress.compareTo(server2, UtlString::ignoreCase) == 0) { Os::Logger::instance().log(FAC_AUTH, PRI_INFO, "TransferControl[%s]::authorizeAndModify " "Whitelist host '%s' in call '%s'", mInstanceName.data(),hostAddress.data(),callId.data() ); result = ALLOW; //Whitelist matched so allow the transfer }else{ Os::Logger::instance().log(FAC_AUTH, PRI_INFO, "TransferControl[%s]::authorizeAndModify " "challenging transfer in call '%s' from host '%s'", mInstanceName.data(), callId.data(),hostAddress.data() ); result = DENY; // we need an identity to attach to the Refer-To URI } } else { /* * This is a transfer to a target outside our domain, so let it go * unchallenged. See XECS-806 */ Os::Logger::instance().log(FAC_AUTH, PRI_DEBUG, "TransferControl[%s]::authorizeAndModify " "allowing foriegn transfer in call '%s'", mInstanceName.data(), callId.data() ); // Add the References to the refer-to. Adding the callId field as a reference // header (will be used in resulting INVITE) in the Refer-To provides us // with enough information to be able to logically tie the calls together. // Useful for CDR records. UtlString refcallId(callId); refcallId.append(";rel=refer"); target.setHeaderParameter(SIP_REFERENCES_FIELD, refcallId.data()); Os::Logger::instance().log(FAC_AUTH, PRI_DEBUG, "TransferControl[%s]::authorizeAndModify " "adding Reference field [%s] to refer-to", mInstanceName.data(), callId.data() ); request.setReferToField(target.toString().data()); result = ALLOW; } } else { UtlString contactString; request.getContactEntry(0, &contactString); Url contactUri( contactString ); UtlString userId; contactUri.getUserId(contactString); Os::Logger::instance().log(FAC_AUTH, PRI_DEBUG, "TransferControl::authorizeAndModify - Contact field is: %s ", contactString.data()); if (contactString != "callcontroller") { // Authenticated REFER // annotate the refer-to with the authenticated controller identity SipXauthIdentity controllerIdentity; controllerIdentity.setIdentity(id); controllerIdentity.encodeUri(target); // add the References to the refer-to. UtlString refcallId(callId); refcallId.append(";rel=refer"); target.setHeaderParameter(SIP_REFERENCES_FIELD, refcallId.data()); Os::Logger::instance().log(FAC_AUTH, PRI_DEBUG, "TransferControl[%s]::authorizeAndModify " "adding Reference field [%s] to refer-to", mInstanceName.data(), callId.data() ); request.setReferToField(target.toString().data()); } } } else { Os::Logger::instance().log(FAC_AUTH, PRI_WARNING, "TransferControl[%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, "TransferControl[%s]::authorizeAndModify " "REFER method without Refer-To in call '%s'", mInstanceName.data(), callId.data() ); } } else if (method.compareTo(SIP_INVITE_METHOD) == 0) { UtlString targetCallId; UtlString targetFromTag; UtlString targetToTag; if (request.getReplacesData(targetCallId, targetToTag, targetFromTag)) { /* * This is an INVITE with Replaces: probably either the completion * of a call pickup or a consultative transfer. * In any case, it will not create a new call - just connect something * to an existing call - so we don't need to make any new authorization * decisions. */ result = ALLOW; } else { // INVITE without Replaces: is not a transfer - ignore it. } } else { // neither REFER nor INVITE, so is not a transfer - ignore it. } } else { // Some earlier plugin already denied this - don't waste time figuring it out. Os::Logger::instance().log(FAC_AUTH, PRI_DEBUG, "TransferControl[%s]::authorizeAndModify " "prior authorization result %s for call %s", mInstanceName.data(), AuthResultStr(priorResult), callId.data() ); } return result; }
RedirectPlugin::LookUpStatus SipRedirectorAliasDB::lookUp( const SipMessage& message, UtlString& requestString, Url& requestUri, const UtlString& method, ContactList& contactList, RequestSeqNo requestSeqNo, int redirectorNo, SipRedirectorPrivateStorage*& privateStorage, ErrorDescriptor& errorDescriptor) { // If url param sipx-userforward = false, do not redirect to user-forward // aliases. UtlString userforwardParam; requestUri.getUrlParameter("sipx-userforward", userforwardParam); bool disableForwarding = userforwardParam.compareTo("false", UtlString::ignoreCase) == 0; if (disableForwarding) { Os::Logger::instance().log(FAC_SIP, PRI_DEBUG, "%s::lookUp user forwarding disabled by parameter", mLogName.data()); } if (_enableEarlyAliasResolution) { resolveAlias(message, requestString, requestUri); } UtlString requestIdentity; requestUri.getIdentity(requestIdentity); EntityDB::Aliases aliases; bool isUserIdentity = false; EntityDB* entityDb = SipRegistrar::getInstance(NULL)->getEntityDB(); entityDb->getAliasContacts(requestUri, aliases, isUserIdentity); int numAliasContacts = aliases.size(); if (numAliasContacts > 0) { Os::Logger::instance().log(FAC_SIP, PRI_DEBUG, "%s::lookUp " "got %d AliasDB contacts", mLogName.data(), numAliasContacts); // Check if the request identity is a real user/extension UtlString realm; UtlString authType; SipXauthIdentity authIdentity; authIdentity.setIdentity(requestIdentity); for (EntityDB::Aliases::iterator iter = aliases.begin(); iter != aliases.end(); iter++) { // If disableForwarding and the relation value is "userforward", // do not record this contact. if (!(disableForwarding && iter->relation == ALIASDB_RELATION_USERFORWARD)) { UtlString contact = iter->contact.c_str(); Url contactUri(contact); // if the request identity is a real user if (isUserIdentity) { // Encode AuthIdentity into the URI authIdentity.encodeUri(contactUri, message); } contactUri.setUrlParameter(SIP_SIPX_CALL_DEST_FIELD, "AL"); contactList.add( contactUri, *this ); if (_enableDiversionHeader && contactList.getDiversionHeader().empty()) { // // Add a Diversion header for all deflections // UtlString stringUri; message.getRequestUri(&stringUri); // The requestUri is an addr-spec, not a name-addr. Url diversionUri(stringUri, TRUE); UtlString userId; diversionUri.getUserId(userId); UtlString host; diversionUri.getHostWithPort(host); std::ostringstream strm; strm << "<sip:"; if (!userId.isNull()) strm << userId.data() << "@"; strm << host.data(); strm << ">;reason=unconditional;sipxfwd=" << iter->relation; UtlString diversion = strm.str().c_str(); OS_LOG_INFO(FAC_SIP, "SipRedirectorAliasDB::lookUp inserting diversion from " << diversion.data()); contactList.setDiversionHeader(diversion.data()); } } } } return RedirectPlugin::SUCCESS; }
RedirectPlugin::LookUpStatus SipRedirectorAliasDB::lookUp( const SipMessage& message, UtlString& requestString, Url& requestUri, const UtlString& method, ContactList& contactList, RequestSeqNo requestSeqNo, int redirectorNo, SipRedirectorPrivateStorage*& privateStorage, ErrorDescriptor& errorDescriptor) { // If url param sipx-userforward = false, do not redirect to user-forward // aliases. UtlString userforwardParam; requestUri.getUrlParameter("sipx-userforward", userforwardParam); bool disableForwarding = userforwardParam.compareTo("false", UtlString::ignoreCase) == 0; if (disableForwarding) { Os::Logger::instance().log(FAC_SIP, PRI_DEBUG, "%s::lookUp user forwarding disabled by parameter", mLogName.data()); } bool isDomainAlias = false; UtlString domain; UtlString hostAlias; requestUri.getHostAddress(domain); UtlBoolean isMyHostAlias = mpSipUserAgent->isMyHostAlias(requestUri); if (mpSipUserAgent && domain != _localDomain && isMyHostAlias) { isDomainAlias = true; hostAlias = domain; requestUri.setHostAddress(_localDomain); } UtlString requestIdentity; requestUri.getIdentity(requestIdentity); OS_LOG_DEBUG(FAC_SIP, mLogName.data() << "::lookUp identity: " << requestIdentity.data() << " domain: " << domain.data() << " local-domain: " << _localDomain.data() << " isHostAlias: " << isMyHostAlias); //ResultSet aliases; //AliasDB::getInstance()->getContacts(requestUri, aliases); //int numAliasContacts = aliases.getSize(); EntityDB::Aliases aliases; bool isUserIdentity = false; EntityDB* entityDb = SipRegistrar::getInstance(NULL)->getEntityDB(); entityDb->getAliasContacts(requestUri, aliases, isUserIdentity); int numAliasContacts = aliases.size(); if (numAliasContacts > 0) { Os::Logger::instance().log(FAC_SIP, PRI_DEBUG, "%s::lookUp " "got %d AliasDB contacts", mLogName.data(), numAliasContacts); // Check if the request identity is a real user/extension UtlString realm; UtlString authType; SipXauthIdentity authIdentity; authIdentity.setIdentity(requestIdentity); for (EntityDB::Aliases::iterator iter = aliases.begin(); iter != aliases.end(); iter++) { // If disableForwarding and the relation value is "userforward", // do not record this contact. if (!(disableForwarding && iter->relation == ALIASDB_RELATION_USERFORWARD)) { UtlString contact = iter->contact.c_str(); Url contactUri(contact); // if the request identity is a real user if (isUserIdentity) { // Encode AuthIdentity into the URI authIdentity.encodeUri(contactUri, message); } contactUri.setUrlParameter(SIP_SIPX_CALL_DEST_FIELD, "AL"); if (numAliasContacts == 1 && isDomainAlias && isUserIdentity) { UtlString userId; contactUri.getUserId(userId); requestUri.setUserId(userId.data()); requestUri.getUri(requestString); OS_LOG_NOTICE(FAC_SIP, "SipRedirectorAliasDB::lookUp normalized request-uri to " << requestString.data()); } else { // Add the contact. contactList.add( contactUri, *this ); } } } } else if (isDomainAlias) { // // No alias found. If this is was towards a domain alias, make sure to reset it back to // the old value prior to feeding it to the rest of the redirectors. // requestUri.setHostAddress(hostAlias); requestUri.getUri(requestString); } return RedirectPlugin::SUCCESS; }
// Test that permissions of authIdentity are taken into consideration void testAuthIdentity() { UtlString identity("*****@*****.**"); // has only 'fishing' permission Url okRequestUri("sip:user@boat"); UtlSList noRemovedRoutes; UtlString rejectReason; const char* okMessage = "INVITE sip:user@boat SIP/2.0\r\n" // 'lodge' requires 'hunting' permission "Via: SIP/2.0/TCP 10.1.1.3:33855\r\n" "To: sip:user@boat\r\n" "From: Caller <sip:[email protected]>; tag=30543f3483e1cb11ecb40866edd3295b\r\n" "Call-Id: exception-1\r\n" "Cseq: 2 INVITE\r\n" "Max-Forwards: 20\r\n" "Contact: [email protected]\r\n" "Content-Length: 0\r\n" "\r\n"; SipMessage okMsg(okMessage, strlen(okMessage)); UtlString routeName("example.com"); RouteState okRouteState( okMsg, noRemovedRoutes, routeName ); UtlString method("INVITE"); const bool bSpiralingRequest = false; AuthPlugin::AuthResult priorResult = AuthPlugin::CONTINUE; // confirm that supercaster can call boat CPPUNIT_ASSERT(AuthPlugin::ALLOW == enforcer->authorizeAndModify(identity, okRequestUri, okRouteState, method, priorResult, okMsg, bSpiralingRequest, rejectReason )); CPPUNIT_ASSERT(rejectReason.isNull()); okRouteState.update(&okMsg); UtlString recordRoute; CPPUNIT_ASSERT(okMsg.getRecordRouteField(0, &recordRoute)); ASSERT_STR_EQUAL( "<sip:example.com;lr;sipXecs-rs=enforce%2Aauth%7E%210083f7f42bdf4998911a18d41fb3aa01>", recordRoute ); // now try the same request, but mightyhunter as authIdentity // so this time it should NOT work // Modify the identity to mightyhunter and verify that he can't call boat const char* notOkMessage = "INVITE sip:user@boat SIP/2.0\r\n" // 'lodge' requires 'hunting' permission "Via: SIP/2.0/TCP 10.1.1.3:33855\r\n" "To: sip:user@boat\r\n" "From: Caller <sip:[email protected]>; tag=30543f3483e1cb11ecb40866edd3295b\r\n" "Call-Id: exception-1\r\n" "Cseq: 2 INVITE\r\n" "Max-Forwards: 20\r\n" "Contact: [email protected]\r\n" "Content-Length: 0\r\n" "\r\n"; SipMessage notOkMsg( notOkMessage, strlen(notOkMessage)); SipXauthIdentity authIdentity; authIdentity.setIdentity("*****@*****.**"); authIdentity.insert(notOkMsg, SipXauthIdentity::AuthIdentityHeaderName); rejectReason.remove(0); // confirm that mightyhunter can't call boat CPPUNIT_ASSERT(AuthPlugin::DENY == enforcer->authorizeAndModify(identity, okRequestUri, okRouteState, method, priorResult, notOkMsg, bSpiralingRequest, rejectReason )); CPPUNIT_ASSERT(!rejectReason.compareTo("Requires fishing")); // check that the authidentity is still present SipXauthIdentity testIdentity(notOkMsg, SipXauthIdentity::AuthIdentityHeaderName); UtlString testIdentityString; CPPUNIT_ASSERT(testIdentity.getIdentity(testIdentityString)); ASSERT_STR_EQUAL(testIdentityString, "*****@*****.**"); const char* noprivMessage = "INVITE sip:user@lodge SIP/2.0\r\n" // 'lodge' requires 'hunting' permission "Via: SIP/2.0/TCP 10.1.1.3:33855\r\n" "To: sip:user@lodge\r\n" "From: Caller <sip:[email protected]>; tag=30543f3483e1cb11ecb40866edd3295b\r\n" "Call-Id: exception-2\r\n" "Cseq: 2 INVITE\r\n" "Max-Forwards: 20\r\n" "Contact: [email protected]\r\n" "Content-Length: 0\r\n" "\r\n"; SipMessage noprivMsg(noprivMessage, strlen(noprivMessage)); RouteState noprivRouteState( noprivMsg, noRemovedRoutes, routeName ); Url noprivRequestUri("sip:user@lodge"); rejectReason.remove(0); // confirm that supercaster cannot call lodge CPPUNIT_ASSERT(AuthPlugin::DENY == enforcer->authorizeAndModify(identity, noprivRequestUri, noprivRouteState, method, priorResult, noprivMsg, bSpiralingRequest, rejectReason )); ASSERT_STR_EQUAL("Requires hunting", rejectReason.data()); // now try the same request, but mightyhunter as authIdentity // so this time it should work const char* allowedMessage = "INVITE sip:allowed@lodge SIP/2.0\r\n" // 'lodge' requires 'hunting' permission "Via: SIP/2.0/TCP 10.1.1.3:33855\r\n" "To: sip:allowed@lodge\r\n" "From: Caller <sip:[email protected]>; tag=30543f3483e1cb11ecb40866edd3295b\r\n" "Call-Id: exception-3\r\n" "Cseq: 2 INVITE\r\n" "Max-Forwards: 20\r\n" "Contact: [email protected]\r\n" "Content-Length: 0\r\n" "\r\n"; SipMessage allowedMsg(allowedMessage, strlen(allowedMessage)); RouteState allowedRouteState( allowedMsg, noRemovedRoutes, routeName ); rejectReason.remove(0); authIdentity.insert(allowedMsg, SipXauthIdentity::AuthIdentityHeaderName); CPPUNIT_ASSERT(AuthPlugin::ALLOW == enforcer->authorizeAndModify(identity, noprivRequestUri, allowedRouteState, method, priorResult, allowedMsg, bSpiralingRequest, rejectReason )); CPPUNIT_ASSERT(rejectReason.isNull()); // check that the authidentity is still present SipXauthIdentity testIdentity1(notOkMsg, SipXauthIdentity::AuthIdentityHeaderName); CPPUNIT_ASSERT(testIdentity1.getIdentity(testIdentityString)); ASSERT_STR_EQUAL(testIdentityString, "*****@*****.**"); allowedRouteState.update(&allowedMsg); CPPUNIT_ASSERT(allowedMsg.getRecordRouteField(0, &recordRoute)); ASSERT_STR_EQUAL( "<sip:example.com;lr;sipXecs-rs=enforce%2Aauth%7E%2175da650843a06eee569f3c93b0f94ee5>", recordRoute ); // check that invalid authidentity can not help bypass permissions rejectReason.remove(0); // invalid authidentity - Call-ID does not match allowedMsg.addHeaderField("X-Sipx-Authidentity", "<sip:[email protected];signature=46A66059%3Ab1b86dffc2e38191cdfad0500bf9a209>"); CPPUNIT_ASSERT(AuthPlugin::DENY == enforcer->authorizeAndModify(identity, noprivRequestUri, allowedRouteState, method, priorResult, allowedMsg, bSpiralingRequest, rejectReason )); ASSERT_STR_EQUAL("Requires hunting", rejectReason.data()); }
RedirectPlugin::LookUpStatus SipRedirectorAliasDB::lookUp( const SipMessage& message, const UtlString& requestString, const Url& requestUri, const UtlString& method, ContactList& contactList, RequestSeqNo requestSeqNo, int redirectorNo, SipRedirectorPrivateStorage*& privateStorage, ErrorDescriptor& errorDescriptor) { // If url param sipx-userforward = false, do not redirect to its aliases UtlString disableForwarding; requestUri.getUrlParameter("sipx-userforward", disableForwarding); if (disableForwarding.compareTo("false", UtlString::ignoreCase) == 0) { OsSysLog::add(FAC_SIP, PRI_DEBUG, "%s::lookUp user forwarding disabled by parameter", mLogName.data()); } else { UtlString requestIdentity; requestUri.getIdentity(requestIdentity); OsSysLog::add(FAC_SIP, PRI_DEBUG, "%s::lookUp identity '%s'", mLogName.data(), requestIdentity.data()); ResultSet aliases; AliasDB::getInstance()->getContacts(requestUri, aliases); int numAliasContacts = aliases.getSize(); if (numAliasContacts > 0) { OsSysLog::add(FAC_SIP, PRI_DEBUG, "%s::lookUp " "got %d AliasDB contacts", mLogName.data(), numAliasContacts); // Check if the request identity is a real user/extension UtlString realm; UtlString authType; bool isUserIdentity = CredentialDB::getInstance()->isUriDefined(requestUri, realm, authType); SipXauthIdentity authIdentity; authIdentity.setIdentity(requestIdentity); for (int i = 0; i < numAliasContacts; i++) { static UtlString contactKey("contact"); UtlHashMap record; if (aliases.getIndex(i, record)) { UtlString contact = *((UtlString*)record.findValue(&contactKey)); Url contactUri(contact); // if the request identity is a real user if (isUserIdentity) { // Encode AuthIdentity into the URI authIdentity.encodeUri(contactUri, message); } contactUri.setUrlParameter(SIP_SIPX_CALL_DEST_FIELD, "AL"); // Add the contact. contactList.add( contactUri, *this ); } } } } return RedirectPlugin::SUCCESS; }