|
reSIProcate/DialogUsageManager
9680
|
00001 #include <cassert> 00002 00003 #include "resip/stack/Helper.hxx" 00004 #include "resip/stack/SipMessage.hxx" 00005 #include "resip/dum/ClientAuthManager.hxx" 00006 #include "resip/dum/UserProfile.hxx" 00007 #include "rutil/Logger.hxx" 00008 #include "rutil/Random.hxx" 00009 #include "resip/dum/ClientAuthExtension.hxx" 00010 00011 #define RESIPROCATE_SUBSYSTEM Subsystem::DUM 00012 00013 using namespace resip; 00014 using namespace std; 00015 00016 class ClientAuthDecorator : public MessageDecorator 00017 { 00018 public: 00019 ClientAuthDecorator(bool isProxyCredential, const Auth& auth, const UserProfile::DigestCredential& credential, const Data& authQop, const Data& nonceCountString) : 00020 mIsProxyCredential(isProxyCredential), mAuth(auth), mCredential(credential), mAuthQop(authQop), mNonceCountString(nonceCountString) {} 00021 virtual ~ClientAuthDecorator() {} 00022 virtual void decorateMessage(SipMessage &msg, 00023 const Tuple &source, 00024 const Tuple &destination, 00025 const Data& sigcompId) 00026 { 00027 Data cnonce = Random::getCryptoRandomHex(16); 00028 00029 Auths & target = mIsProxyCredential ? msg.header(h_ProxyAuthorizations) : msg.header(h_Authorizations); 00030 00031 DebugLog( << " Add auth, " << this << " in response to: " << mAuth); 00032 Auth auth; 00033 if (ClientAuthExtension::instance().algorithmAndQopSupported(mAuth)) 00034 { 00035 DebugLog(<<"Using extension to make auth response"); 00036 00037 if(mCredential.isPasswordA1Hash) 00038 { 00039 ClientAuthExtension::instance().makeChallengeResponseAuthWithA1(msg, 00040 mCredential.user, 00041 mCredential.password, 00042 mAuth, 00043 cnonce, 00044 mAuthQop, 00045 mNonceCountString, 00046 auth); 00047 } 00048 else 00049 { 00050 ClientAuthExtension::instance().makeChallengeResponseAuth(msg, 00051 mCredential.user, 00052 mCredential.password, 00053 mAuth, 00054 cnonce, 00055 mAuthQop, 00056 mNonceCountString, 00057 auth); 00058 } 00059 } 00060 else 00061 { 00062 if(mCredential.isPasswordA1Hash) 00063 { 00064 Helper::makeChallengeResponseAuthWithA1(msg, 00065 mCredential.user, 00066 mCredential.password, 00067 mAuth, 00068 cnonce, 00069 mAuthQop, 00070 mNonceCountString, 00071 auth); 00072 } 00073 else 00074 { 00075 Helper::makeChallengeResponseAuth(msg, 00076 mCredential.user, 00077 mCredential.password, 00078 mAuth, 00079 cnonce, 00080 mAuthQop, 00081 mNonceCountString, 00082 auth); 00083 } 00084 } 00085 target.push_back(auth); 00086 00087 DebugLog(<<"ClientAuthDecorator, proxy: " << mIsProxyCredential << " " << target.back()); 00088 } 00089 virtual void rollbackMessage(SipMessage& msg) 00090 { 00091 Auths & target = mIsProxyCredential ? msg.header(h_ProxyAuthorizations) : msg.header(h_Authorizations); 00092 target.pop_back(); 00093 } 00094 virtual MessageDecorator* clone() const { return new ClientAuthDecorator(mIsProxyCredential, mAuth, mCredential, mAuthQop, mNonceCountString); } 00095 private: 00096 bool mIsProxyCredential; 00097 Auth mAuth; 00098 UserProfile::DigestCredential mCredential; 00099 Data mAuthQop; 00100 Data mNonceCountString; 00101 }; 00102 00103 ClientAuthManager::ClientAuthManager() 00104 { 00105 } 00106 00107 00108 bool 00109 ClientAuthManager::handle(UserProfile& userProfile, SipMessage& origRequest, const SipMessage& response) 00110 { 00111 try 00112 { 00113 assert( response.isResponse() ); 00114 assert( origRequest.isRequest() ); 00115 00116 DialogSetId id(origRequest); 00117 00118 const int& code = response.header(h_StatusLine).statusCode(); 00119 if (code < 101 || code >= 500) 00120 { 00121 return false; 00122 } 00123 else if (! ( code == 401 || code == 407 )) // challenge success 00124 { 00125 AttemptedAuthMap::iterator it = mAttemptedAuths.find(id); 00126 if (it != mAttemptedAuths.end()) 00127 { 00128 DebugLog (<< "ClientAuthManager::handle: transitioning " << id << "to cached"); 00129 00130 // cache the result 00131 it->second.authSucceeded(); 00132 } 00133 return false; 00134 } 00135 00136 if (!(response.exists(h_WWWAuthenticates) || response.exists(h_ProxyAuthenticates))) 00137 { 00138 DebugLog (<< "Invalid challenge for " << id << ", nothing to respond to; fail"); 00139 return false; 00140 } 00141 00142 AuthState& authState = mAttemptedAuths[id]; 00143 00144 // based on the UserProfile and the challenge, store credentials in the 00145 // AuthState associated with this DialogSet if the algorithm is supported 00146 if (authState.handleChallenge(userProfile, response)) 00147 { 00148 assert(origRequest.header(h_Vias).size() == 1); 00149 origRequest.header(h_CSeq).sequence()++; 00150 DebugLog (<< "Produced response to digest challenge for " << userProfile ); 00151 return true; 00152 } 00153 else 00154 { 00155 return false; 00156 } 00157 } 00158 catch(BaseException& e) 00159 { 00160 assert(0); 00161 ErrLog(<< "Unexpected exception in ClientAuthManager::handle " << e); 00162 return false; 00163 } 00164 } 00165 00166 void 00167 ClientAuthManager::addAuthentication(SipMessage& request) 00168 { 00169 AttemptedAuthMap::iterator it = mAttemptedAuths.find(DialogSetId(request)); 00170 if (it != mAttemptedAuths.end()) 00171 { 00172 it->second.addAuthentication(request); 00173 } 00174 } 00175 00176 void 00177 ClientAuthManager::clearAuthenticationState(const DialogSetId& dsId) 00178 { 00179 dialogSetDestroyed(dsId); 00180 } 00181 00182 ClientAuthManager::AuthState::AuthState() : 00183 mFailed(false) 00184 { 00185 } 00186 00187 00188 bool 00189 ClientAuthManager::AuthState::handleChallenge(UserProfile& userProfile, const SipMessage& challenge) 00190 { 00191 if (mFailed) 00192 { 00193 return false; 00194 } 00195 bool handled = true; 00196 if (challenge.exists(h_WWWAuthenticates)) 00197 { 00198 for (Auths::const_iterator i = challenge.header(h_WWWAuthenticates).begin(); 00199 i != challenge.header(h_WWWAuthenticates).end(); ++i) 00200 { 00201 if (i->exists(p_realm)) 00202 { 00203 if (!mRealms[i->param(p_realm)].handleAuth(userProfile, *i, false)) 00204 { 00205 handled = false; 00206 break; 00207 } 00208 } 00209 else 00210 { 00211 return false; 00212 } 00213 } 00214 } 00215 if (challenge.exists(h_ProxyAuthenticates)) 00216 { 00217 for (Auths::const_iterator i = challenge.header(h_ProxyAuthenticates).begin(); 00218 i != challenge.header(h_ProxyAuthenticates).end(); ++i) 00219 { 00220 if (i->exists(p_realm)) 00221 { 00222 if (!mRealms[i->param(p_realm)].handleAuth(userProfile, *i, true)) 00223 { 00224 handled = false; 00225 break; 00226 } 00227 } 00228 else 00229 { 00230 return false; 00231 } 00232 } 00233 if(!handled) 00234 { 00235 InfoLog( << "ClientAuthManager::AuthState::handleChallenge failed for: " << challenge); 00236 } 00237 } 00238 return handled; 00239 } 00240 00241 void 00242 ClientAuthManager::AuthState::authSucceeded() 00243 { 00244 for(RealmStates::iterator i = mRealms.begin(); i!=mRealms.end(); i++) 00245 { 00246 i->second.authSucceeded(); 00247 } 00248 } 00249 00250 void 00251 ClientAuthManager::AuthState::addAuthentication(SipMessage& request) 00252 { 00253 request.remove(h_ProxyAuthorizations); 00254 request.remove(h_Authorizations); 00255 00256 if (mFailed) return; 00257 00258 for(RealmStates::iterator i = mRealms.begin(); i!=mRealms.end(); i++) 00259 { 00260 i->second.addAuthentication(request); 00261 } 00262 } 00263 00264 00265 ClientAuthManager::RealmState::RealmState() : 00266 mIsProxyCredential(false), 00267 mState(Invalid), 00268 mNonceCount(0), 00269 mAuthPtr(NULL) 00270 { 00271 } 00272 00273 Data RealmStates[] = 00274 { 00275 "invalid", 00276 "cached", 00277 "current", 00278 "tryonce", 00279 "failed", 00280 }; 00281 00282 const Data& 00283 ClientAuthManager::RealmState::getStateString(State s) 00284 { 00285 return RealmStates[s]; 00286 } 00287 00288 void 00289 ClientAuthManager::RealmState::transition(State s) 00290 { 00291 DebugLog(<< "ClientAuthManager::RealmState::transition from " << getStateString(mState) << " to " << getStateString(s)); 00292 mState = s; 00293 } 00294 00295 void 00296 ClientAuthManager::RealmState::authSucceeded() 00297 { 00298 switch(mState) 00299 { 00300 case Invalid: 00301 assert(0); 00302 break; 00303 case Current: 00304 case Cached: 00305 case TryOnce: 00306 transition(Cached); 00307 break; 00308 case Failed: 00309 assert(0); 00310 break; 00311 }; 00312 } 00313 00314 bool 00315 ClientAuthManager::RealmState::handleAuth(UserProfile& userProfile, const Auth& auth, bool isProxyCredential) 00316 { 00317 DebugLog( << "ClientAuthManager::RealmState::handleAuth: " << this << " " << auth << " is proxy: " << isProxyCredential); 00318 mIsProxyCredential = isProxyCredential; //this changing dynamically would 00319 //be very bizarre..should trap w/ enum 00320 switch(mState) 00321 { 00322 case Invalid: 00323 mAuth = auth; 00324 transition(Current); 00325 break; 00326 case Current: 00327 if (auth.exists(p_stale) && auth.param(p_stale) == "true") 00328 { 00329 DebugLog (<< "Stale nonce:" << auth); 00330 mAuth = auth; 00331 clear(); 00332 } 00333 else if(auth.exists(p_nonce) && auth.param(p_nonce) != mAuth.param(p_nonce)) 00334 { 00335 DebugLog (<< "Different nonce, was: " << mAuth.param(p_nonce) << " now " << auth.param(p_nonce)); 00336 mAuth = auth; 00337 clear(); 00338 transition(TryOnce); 00339 } 00340 else 00341 { 00342 DebugLog( << "Challenge response already failed for: " << auth); 00343 transition(Failed); 00344 return false; 00345 } 00346 break; 00347 case TryOnce: 00348 DebugLog( << "Extra chance still failed: " << auth); 00349 transition(Failed); 00350 return false; 00351 case Cached: //basically 1 free chance, here for interop, may not be 00352 //required w/ nonce check in current 00353 mAuth = auth; 00354 clear(); 00355 transition(Current); 00356 break; 00357 case Failed: 00358 return false; 00359 } 00360 00361 if (findCredential(userProfile, auth)) 00362 { 00363 return true; 00364 } 00365 else 00366 { 00367 transition(Failed); 00368 return false; 00369 } 00370 } 00371 00372 void 00373 ClientAuthManager::RealmState::clear() 00374 { 00375 mNonceCount = 0; 00376 } 00377 00378 bool 00379 ClientAuthManager::RealmState::findCredential(UserProfile& userProfile, const Auth& auth) 00380 { 00381 if (!(Helper::algorithmAndQopSupported(auth) 00382 || (ClientAuthExtension::instance().algorithmAndQopSupported(auth)))) 00383 { 00384 DebugLog(<<"Unsupported algorithm or qop: " << auth); 00385 return false; 00386 } 00387 00388 const Data& realm = auth.param(p_realm); 00390 mCredential = userProfile.getDigestCredential(realm); 00391 if ( mCredential.realm.empty() ) 00392 { 00393 DebugLog( << "Got a 401 or 407 but could not find credentials for realm: " << realm); 00394 // DebugLog (<< auth); 00395 // DebugLog (<< response); 00396 return false; 00397 } 00398 return true; 00399 } 00400 00401 void 00402 ClientAuthManager::RealmState::addAuthentication(SipMessage& request) 00403 { 00404 assert(mState != Failed); 00405 if (mState == Failed) return; 00406 00407 Data nonceCountString; 00408 Data authQop = Helper::qopOption(mAuth); 00409 if(!authQop.empty()) 00410 { 00411 Helper::updateNonceCount(mNonceCount, nonceCountString); 00412 } 00413 00414 // Add client auth decorator so that we ensure any body hashes are calcuated after user defined outbound decorators that 00415 // may be modifying the message body 00416 std::auto_ptr<MessageDecorator> clientAuthDecorator(new ClientAuthDecorator(mIsProxyCredential, mAuth, mCredential, authQop, nonceCountString)); 00417 request.addOutboundDecorator(clientAuthDecorator); 00418 } 00419 00420 void ClientAuthManager::dialogSetDestroyed(const DialogSetId& id) 00421 { 00422 if ( mAttemptedAuths.find(id) != mAttemptedAuths.end()) 00423 { 00424 mAttemptedAuths.erase(id); 00425 } 00426 } 00427 00428 // bool 00429 // ClientAuthManager::CompareAuth::operator()(const Auth& lhs, const Auth& rhs) const 00430 // { 00431 // if (lhs.param(p_realm) < rhs.param(p_realm)) 00432 // { 00433 // return true; 00434 // } 00435 // else if (lhs.param(p_realm) > rhs.param(p_realm)) 00436 // { 00437 // return false; 00438 // } 00439 // else 00440 // { 00441 // return lhs.param(p_username) < rhs.param(p_username); 00442 // } 00443 // } 00444 00445 00446 00447 /* ==================================================================== 00448 * The Vovida Software License, Version 1.0 00449 * 00450 * Copyright (c) 2000 Vovida Networks, Inc. All rights reserved. 00451 * 00452 * Redistribution and use in source and binary forms, with or without 00453 * modification, are permitted provided that the following conditions 00454 * are met: 00455 * 00456 * 1. Redistributions of source code must retain the above copyright 00457 * notice, this list of conditions and the following disclaimer. 00458 * 00459 * 2. Redistributions in binary form must reproduce the above copyright 00460 * notice, this list of conditions and the following disclaimer in 00461 * the documentation and/or other materials provided with the 00462 * distribution. 00463 * 00464 * 3. The names "VOCAL", "Vovida Open Communication Application Library", 00465 * and "Vovida Open Communication Application Library (VOCAL)" must 00466 * not be used to endorse or promote products derived from this 00467 * software without prior written permission. For written 00468 * permission, please contact vocal@vovida.org. 00469 * 00470 * 4. Products derived from this software may not be called "VOCAL", nor 00471 * may "VOCAL" appear in their name, without prior written 00472 * permission of Vovida Networks, Inc. 00473 * 00474 * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESSED OR IMPLIED 00475 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 00476 * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, TITLE AND 00477 * NON-INFRINGEMENT ARE DISCLAIMED. IN NO EVENT SHALL VOVIDA 00478 * NETWORKS, INC. OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT DAMAGES 00479 * IN EXCESS OF $1,000, NOR FOR ANY INDIRECT, INCIDENTAL, SPECIAL, 00480 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 00481 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 00482 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY 00483 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 00484 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE 00485 * USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH 00486 * DAMAGE. 00487 * 00488 * ==================================================================== 00489 * 00490 * This software consists of voluntary contributions made by Vovida 00491 * Networks, Inc. and many individuals on behalf of Vovida Networks, 00492 * Inc. For more information on Vovida Networks, Inc., please see 00493 * <http://www.vovida.org/>. 00494 * 00495 */
1.7.5.1