~ubuntu-branches/ubuntu/saucy/resiprocate/saucy-proposed

« back to all changes in this revision

Viewing changes to resip/dum/ServerAuthManager.cxx

  • Committer: Package Import Robot
  • Author(s): Daniel Pocock
  • Date: 2012-05-17 19:29:59 UTC
  • Revision ID: package-import@ubuntu.com-20120517192959-vv00m77isztdy64q
Tags: upstream-1.8.2
ImportĀ upstreamĀ versionĀ 1.8.2

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
#include <cassert>
 
2
 
 
3
#include "resip/dum/ChallengeInfo.hxx"
 
4
#include "resip/dum/DumFeature.hxx"
 
5
#include "resip/dum/DumFeatureChain.hxx"
 
6
#include "resip/dum/ServerAuthManager.hxx"
 
7
#include "resip/dum/DialogUsageManager.hxx"
 
8
#include "resip/dum/TargetCommand.hxx"
 
9
#include "rutil/Logger.hxx"
 
10
#include "resip/dum/UserAuthInfo.hxx"
 
11
#include "resip/stack/Helper.hxx"
 
12
#include "rutil/WinLeakCheck.hxx"
 
13
 
 
14
#define RESIPROCATE_SUBSYSTEM Subsystem::DUM
 
15
 
 
16
using namespace resip;
 
17
using namespace std;
 
18
 
 
19
ServerAuthManager::ServerAuthManager(DialogUsageManager& dum, TargetCommand::Target& target) :
 
20
   DumFeature(dum, target)
 
21
{
 
22
}
 
23
 
 
24
 
 
25
ServerAuthManager::~ServerAuthManager()
 
26
{
 
27
   InfoLog(<< "~ServerAuthManager:  " << mMessages.size() << " messages in memory when destroying.");
 
28
}
 
29
 
 
30
// !bwc! We absolutely, positively, MUST NOT throw here. This is because in
 
31
// DialogUsageManager::process(), we do not know if a DumFeature has taken
 
32
// ownership of msg until we get a return. If we throw, the ownership of msg
 
33
// is unknown. This is unacceptable.
 
34
DumFeature::ProcessingResult 
 
35
ServerAuthManager::process(Message* msg)
 
36
{
 
37
   SipMessage* sipMsg = dynamic_cast<SipMessage*>(msg);
 
38
 
 
39
   if (sipMsg)
 
40
   {
 
41
      //!dcm! -- unecessary happens in handle
 
42
      switch ( handle(sipMsg) )
 
43
      {
 
44
         case ServerAuthManager::Challenged:
 
45
            InfoLog(<< "ServerAuth challenged request " << sipMsg->brief());
 
46
            return DumFeature::ChainDoneAndEventDone;            
 
47
         case ServerAuthManager::RequestedInfo:
 
48
            InfoLog(<< "ServerAuth requested info (requiresChallenge) " << sipMsg->brief());
 
49
            return DumFeature::EventTaken;
 
50
         case ServerAuthManager::RequestedCredentials:
 
51
            InfoLog(<< "ServerAuth requested credentials " << sipMsg->brief());
 
52
            return DumFeature::EventTaken;
 
53
         case ServerAuthManager::Rejected:
 
54
            InfoLog(<< "ServerAuth rejected request " << sipMsg->brief());
 
55
            return DumFeature::ChainDoneAndEventDone;            
 
56
         default:   // includes Skipped
 
57
            return DumFeature::FeatureDone;            
 
58
      }
 
59
   }
 
60
 
 
61
   ChallengeInfo* challengeInfo = dynamic_cast<ChallengeInfo*>(msg);
 
62
   if(challengeInfo)
 
63
   {
 
64
      InfoLog(<< "ServerAuth got ChallengeInfo " << challengeInfo->brief());
 
65
      MessageMap::iterator it = mMessages.find(challengeInfo->getTransactionId());
 
66
      assert(it != mMessages.end());
 
67
      std::auto_ptr<SipMessage> sipMsg(it->second);
 
68
      mMessages.erase(it);
 
69
 
 
70
      if(challengeInfo->isFailed()) 
 
71
      {
 
72
        // some kind of failure occurred while checking whether a 
 
73
        // challenge is required
 
74
        InfoLog(<< "ServerAuth requiresChallenge() async failed");
 
75
        SharedPtr<SipMessage> response(new SipMessage);
 
76
        Helper::makeResponse(*response, *sipMsg, 500, "Server Internal Error");
 
77
        mDum.send(response);
 
78
        return DumFeature::ChainDoneAndEventDone;
 
79
      }
 
80
 
 
81
      if(challengeInfo->isChallengeRequired()) 
 
82
      {
 
83
        issueChallenge(sipMsg.get());
 
84
        InfoLog(<< "ServerAuth challenged request (after async) " << sipMsg->brief());
 
85
        return DumFeature::ChainDoneAndEventDone;
 
86
      } 
 
87
      else 
 
88
      {
 
89
        // challenge is not required, re-instate original message
 
90
        postCommand(auto_ptr<Message>(sipMsg));
 
91
        return FeatureDoneAndEventDone;
 
92
      }
 
93
   }
 
94
 
 
95
   UserAuthInfo* userAuth = dynamic_cast<UserAuthInfo*>(msg);
 
96
   if (userAuth)
 
97
   {
 
98
      //InfoLog(<< "Got UserAuthInfo");
 
99
      UserAuthInfo* userAuth = dynamic_cast<UserAuthInfo*>(msg);
 
100
      if (userAuth)
 
101
      {
 
102
         Message* result = handleUserAuthInfo(userAuth);
 
103
         if (result)
 
104
         {
 
105
            postCommand(auto_ptr<Message>(result));
 
106
            return FeatureDoneAndEventDone;
 
107
         }
 
108
         else
 
109
         {
 
110
            InfoLog(<< "ServerAuth rejected request " << *userAuth);
 
111
            return ChainDoneAndEventDone;            
 
112
         }
 
113
      }
 
114
   }
 
115
   return FeatureDone;   
 
116
}
 
117
 
 
118
SipMessage*
 
119
ServerAuthManager::handleUserAuthInfo(UserAuthInfo* userAuth)
 
120
{
 
121
   assert(userAuth);
 
122
 
 
123
   MessageMap::iterator it = mMessages.find(userAuth->getTransactionId());
 
124
   assert(it != mMessages.end());
 
125
   SipMessage* requestWithAuth = it->second;
 
126
   mMessages.erase(it);
 
127
 
 
128
   InfoLog( << "Checking for auth result in realm=" << userAuth->getRealm() 
 
129
            << " A1=" << userAuth->getA1());
 
130
 
 
131
   if (userAuth->getMode() == UserAuthInfo::UserUnknown || 
 
132
       (userAuth->getMode() == UserAuthInfo::RetrievedA1 && userAuth->getA1().empty()))
 
133
   {
 
134
      InfoLog (<< "User unknown " << userAuth->getUser() << " in " << userAuth->getRealm());
 
135
      SharedPtr<SipMessage> response(new SipMessage);
 
136
      Helper::makeResponse(*response, *requestWithAuth, 404, "User unknown.");
 
137
      mDum.send(response);
 
138
      onAuthFailure(BadCredentials, *requestWithAuth);
 
139
      delete requestWithAuth;
 
140
      return 0;
 
141
   }
 
142
 
 
143
   if (userAuth->getMode() == UserAuthInfo::Error)
 
144
   {
 
145
      InfoLog (<< "Error in auth procedure for " << userAuth->getUser() << " in " << userAuth->getRealm());
 
146
      SharedPtr<SipMessage> response(new SipMessage);
 
147
      Helper::makeResponse(*response, *requestWithAuth, 503, "Server Error.");
 
148
      mDum.send(response);
 
149
      onAuthFailure(Error, *requestWithAuth);
 
150
      delete requestWithAuth;
 
151
      return 0;
 
152
   }
 
153
 
 
154
   bool stale = false;
 
155
   bool digestAccepted = (userAuth->getMode() == UserAuthInfo::DigestAccepted);
 
156
   if(userAuth->getMode() == UserAuthInfo::RetrievedA1)
 
157
   {
 
158
      //!dcm! -- need to handle stale/unit test advancedAuthenticateRequest
 
159
      //!dcm! -- delta? deal with.
 
160
      std::pair<Helper::AuthResult,Data> resPair = 
 
161
         Helper::advancedAuthenticateRequest(*requestWithAuth, 
 
162
                                             userAuth->getRealm(),
 
163
                                             userAuth->getA1(),
 
164
                                             3000,
 
165
                                             proxyAuthenticationMode());
 
166
 
 
167
      switch (resPair.first) 
 
168
      {
 
169
         case Helper::Authenticated:
 
170
            digestAccepted = true;
 
171
            break;
 
172
         case Helper::Failed:
 
173
            // digestAccepted = false;   // already false by default
 
174
            break;
 
175
         case Helper::BadlyFormed:
 
176
            if(rejectBadNonces())
 
177
            {
 
178
               InfoLog (<< "Authentication nonce badly formed for " << userAuth->getUser());
 
179
   
 
180
               SharedPtr<SipMessage> response(new SipMessage);
 
181
               Helper::makeResponse(*response, *requestWithAuth, 403, "Invalid nonce");
 
182
               mDum.send(response);
 
183
               onAuthFailure(InvalidRequest, *requestWithAuth);
 
184
               delete requestWithAuth;
 
185
               return 0;
 
186
            }
 
187
            else
 
188
            {
 
189
               stale=true;
 
190
            }
 
191
            break;
 
192
         case Helper::Expired:
 
193
            stale = true;
 
194
            break;
 
195
         default:
 
196
            break;
 
197
      }
 
198
   }
 
199
 
 
200
   if(stale || userAuth->getMode() == UserAuthInfo::Stale) 
 
201
   {
 
202
      InfoLog (<< "Nonce expired for " << userAuth->getUser());
 
203
 
 
204
      issueChallenge(requestWithAuth);
 
205
      delete requestWithAuth;
 
206
      return 0;
 
207
   }
 
208
 
 
209
   if(digestAccepted)
 
210
   {
 
211
      if (authorizedForThisIdentity(userAuth->getUser(), userAuth->getRealm(),
 
212
                                    requestWithAuth->header(h_From).uri()))
 
213
      {
 
214
         InfoLog (<< "Authorized request for " << userAuth->getRealm());
 
215
         onAuthSuccess(*requestWithAuth);
 
216
         return requestWithAuth;
 
217
      }
 
218
      else
 
219
      {
 
220
         // !rwm! The user is trying to forge a request.  Respond with a 403
 
221
         InfoLog (<< "User: " << userAuth->getUser() << " at realm: " << userAuth->getRealm() <<
 
222
                  " trying to forge request from: " << requestWithAuth->header(h_From).uri());
 
223
 
 
224
         SharedPtr<SipMessage> response(new SipMessage);
 
225
         Helper::makeResponse(*response, *requestWithAuth, 403, "Invalid user name provided");
 
226
         mDum.send(response);
 
227
         onAuthFailure(InvalidRequest, *requestWithAuth);
 
228
         delete requestWithAuth;
 
229
         return 0;
 
230
      }
 
231
   } 
 
232
   else 
 
233
   {
 
234
      // Handles digestAccepted == false, DigestNotAccepted and any other
 
235
      // case that is not recognised by the foregoing logic
 
236
 
 
237
      InfoLog (<< "Invalid password provided for " << userAuth->getUser() << " in " << userAuth->getRealm());
 
238
      InfoLog (<< "  a1 hash of password from db was " << userAuth->getA1() );
 
239
 
 
240
      SharedPtr<SipMessage> response(new SipMessage);
 
241
      Helper::makeResponse(*response, *requestWithAuth, 403, "Invalid password provided");
 
242
      mDum.send(response);
 
243
      onAuthFailure(BadCredentials, *requestWithAuth);
 
244
      delete requestWithAuth;
 
245
      return 0;
 
246
   }
 
247
}
 
248
 
 
249
            
 
250
bool
 
251
ServerAuthManager::useAuthInt() const
 
252
{
 
253
   return false;
 
254
}
 
255
 
 
256
 
 
257
bool
 
258
ServerAuthManager::proxyAuthenticationMode() const
 
259
{
 
260
   return true;
 
261
}
 
262
 
 
263
 
 
264
bool
 
265
ServerAuthManager::rejectBadNonces() const
 
266
{
 
267
   return true;
 
268
}
 
269
 
 
270
 
 
271
ServerAuthManager::AsyncBool
 
272
ServerAuthManager::requiresChallenge(const SipMessage& msg)
 
273
{
 
274
   return True;  
 
275
}
 
276
 
 
277
 
 
278
bool
 
279
ServerAuthManager::authorizedForThisIdentity(const resip::Data &user, 
 
280
                                               const resip::Data &realm, 
 
281
                                                resip::Uri &fromUri)
 
282
{
 
283
   // !rwm! good enough for now.  TODO eventually consult a database to see what
 
284
   // combinations of user/realm combos are authorized for an identity
 
285
 
 
286
   // First try the form where the username parameter in the auth
 
287
   // header is just the username component of the fromUri
 
288
   //
 
289
   if ((fromUri.user() == user) && (fromUri.host() == realm))
 
290
      return true;
 
291
 
 
292
   // Now try the form where the username parameter in the auth
 
293
   // header is the full fromUri, e.g.
 
294
   //    Proxy-Authorization: Digest username="user@domain" ...
 
295
   //
 
296
   if ((fromUri.getAorNoPort() == user) && (fromUri.host() == realm))
 
297
      return true;
 
298
 
 
299
   // catch-all: access denied
 
300
   return false;
 
301
}
 
302
 
 
303
 
 
304
const Data& 
 
305
ServerAuthManager::getChallengeRealm(const SipMessage& msg)
 
306
{
 
307
   return msg.header(h_RequestLine).uri().host();
 
308
}
 
309
 
 
310
 
 
311
bool
 
312
ServerAuthManager::isMyRealm(const Data& realm)
 
313
{
 
314
   return mDum.isMyDomain(realm);
 
315
}
 
316
 
 
317
 
 
318
// return true if request has been consumed 
 
319
ServerAuthManager::Result
 
320
ServerAuthManager::handle(SipMessage* sipMsg)
 
321
{
 
322
   //InfoLog( << "trying to do auth" );
 
323
   if (sipMsg->isRequest() && 
 
324
       sipMsg->header(h_RequestLine).method() != ACK && 
 
325
       sipMsg->header(h_RequestLine).method() != CANCEL)  // Do not challenge ACKs or CANCELs
 
326
   {
 
327
      ParserContainer<Auth>* auths;
 
328
      if (proxyAuthenticationMode())
 
329
      {
 
330
         if(!sipMsg->exists(h_ProxyAuthorizations))
 
331
         {
 
332
            return issueChallengeIfRequired(sipMsg);
 
333
         }
 
334
         auths = &sipMsg->header(h_ProxyAuthorizations);
 
335
      }
 
336
      else
 
337
      {
 
338
         if(!sipMsg->exists(h_Authorizations))
 
339
         {
 
340
            return issueChallengeIfRequired(sipMsg);
 
341
         }
 
342
         auths = &sipMsg->header(h_Authorizations);
 
343
      }
 
344
 
 
345
      try
 
346
      {
 
347
         for(Auths::iterator it = auths->begin(); it != auths->end(); it++)
 
348
         {
 
349
            if (isMyRealm(it->param(p_realm)))
 
350
            {
 
351
               InfoLog (<< "Requesting credential for " 
 
352
                        << it->param(p_username) << " @ " << it->param(p_realm));
 
353
               
 
354
               requestCredential(it->param(p_username),
 
355
                                 it->param(p_realm), 
 
356
                                 *sipMsg,
 
357
                                  *it,
 
358
                                 sipMsg->getTransactionId());
 
359
               mMessages[sipMsg->getTransactionId()] = sipMsg;
 
360
               return RequestedCredentials;
 
361
            }
 
362
         }
 
363
 
 
364
         InfoLog (<< "Didn't find matching realm ");
 
365
         return issueChallengeIfRequired(sipMsg);
 
366
      }
 
367
      catch(BaseException& e)
 
368
      {
 
369
         InfoLog (<< "Invalid auth header provided " << e);
 
370
         SharedPtr<SipMessage> response(new SipMessage);
 
371
         Helper::makeResponse(*response, *sipMsg, 400, "Invalid auth header");
 
372
         mDum.send(response);
 
373
         onAuthFailure(InvalidRequest, *sipMsg);
 
374
         return Rejected;
 
375
      }
 
376
   }
 
377
   return Skipped;
 
378
}
 
379
 
 
380
ServerAuthManager::Result
 
381
ServerAuthManager::issueChallengeIfRequired(SipMessage *sipMsg) 
 
382
{
 
383
   // Is challenge required for this message
 
384
   AsyncBool required = requiresChallenge(*sipMsg);
 
385
   switch(required) 
 
386
   {
 
387
     case False:
 
388
        return Skipped;
 
389
     case Async:
 
390
        mMessages[sipMsg->getTransactionId()] = sipMsg;
 
391
        return RequestedInfo;
 
392
     case True:
 
393
     default:
 
394
        issueChallenge(sipMsg);
 
395
        return Challenged;
 
396
   }
 
397
}
 
398
 
 
399
void
 
400
ServerAuthManager::issueChallenge(SipMessage *sipMsg) 
 
401
{
 
402
  //assume TransactionUser has matched/repaired a realm
 
403
  SharedPtr<SipMessage> challenge(Helper::makeChallenge(*sipMsg,
 
404
                                                        getChallengeRealm(*sipMsg), 
 
405
                                                        useAuthInt(), 
 
406
                                                        false /*stale*/,
 
407
                                                        proxyAuthenticationMode()));
 
408
 
 
409
  InfoLog (<< "Sending challenge to " << sipMsg->brief());
 
410
  mDum.send(challenge);
 
411
}
 
412
 
 
413
void 
 
414
ServerAuthManager::onAuthSuccess(const SipMessage& msg) 
 
415
{
 
416
   // sub class may want to create a log entry
 
417
}
 
418
 
 
419
void 
 
420
ServerAuthManager::onAuthFailure(AuthFailureReason reason, const SipMessage& msg) 
 
421
{
 
422
   // sub class may want to create a log entry
 
423
}
 
424
 
 
425
 
 
426
/* ====================================================================
 
427
 * The Vovida Software License, Version 1.0 
 
428
 * 
 
429
 * Copyright (c) 2000 Vovida Networks, Inc.  All rights reserved.
 
430
 * 
 
431
 * Redistribution and use in source and binary forms, with or without
 
432
 * modification, are permitted provided that the following conditions
 
433
 * are met:
 
434
 * 
 
435
 * 1. Redistributions of source code must retain the above copyright
 
436
 *    notice, this list of conditions and the following disclaimer.
 
437
 * 
 
438
 * 2. Redistributions in binary form must reproduce the above copyright
 
439
 *    notice, this list of conditions and the following disclaimer in
 
440
 *    the documentation and/or other materials provided with the
 
441
 *    distribution.
 
442
 * 
 
443
 * 3. The names "VOCAL", "Vovida Open Communication Application Library",
 
444
 *    and "Vovida Open Communication Application Library (VOCAL)" must
 
445
 *    not be used to endorse or promote products derived from this
 
446
 *    software without prior written permission. For written
 
447
 *    permission, please contact vocal@vovida.org.
 
448
 *
 
449
 * 4. Products derived from this software may not be called "VOCAL", nor
 
450
 *    may "VOCAL" appear in their name, without prior written
 
451
 *    permission of Vovida Networks, Inc.
 
452
 * 
 
453
 * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESSED OR IMPLIED
 
454
 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
 
455
 * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, TITLE AND
 
456
 * NON-INFRINGEMENT ARE DISCLAIMED.  IN NO EVENT SHALL VOVIDA
 
457
 * NETWORKS, INC. OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT DAMAGES
 
458
 * IN EXCESS OF $1,000, NOR FOR ANY INDIRECT, INCIDENTAL, SPECIAL,
 
459
 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
 
460
 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
 
461
 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
 
462
 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 
463
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
 
464
 * USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
 
465
 * DAMAGE.
 
466
 * 
 
467
 * ====================================================================
 
468
 * 
 
469
 * This software consists of voluntary contributions made by Vovida
 
470
 * Networks, Inc. and many individuals on behalf of Vovida Networks,
 
471
 * Inc.  For more information on Vovida Networks, Inc., please see
 
472
 * <http://www.vovida.org/>.
 
473
 *
 
474
 */