reSIProcate
SIP stack, Dialog Usage Manager and related libraries
Loading...
Searching...
No Matches
DigestAuthenticator.cxx
Go to the documentation of this file.
1
2#if defined(HAVE_CONFIG_H)
3 #include "config.h"
4#endif
5
6#include "rutil/DnsUtil.hxx"
11#include "rutil/Logger.hxx"
12
16#include "repro/Proxy.hxx"
18#include "repro/UserStore.hxx"
21#include "rutil/ParseBuffer.hxx"
23
24#define RESIPROCATE_SUBSYSTEM resip::Subsystem::REPRO
25
26using namespace resip;
27using namespace repro;
28using namespace std;
29
31 Dispatcher* authRequestDispatcher,
32 const Data& staticRealm,
33 const std::vector<resip::DigestType>& challengeDigestTypes) :
34 Processor("DigestAuthenticator"),
35 mAuthRequestDispatcher(authRequestDispatcher),
36 mStaticRealm(staticRealm),
37 mNoIdentityHeaders(config.getConfigBool("DisableIdentity", false)),
38 mHttpHostname(config.getConfigData("HttpHostname", "")),
39 mHttpPort(config.getConfigInt("HttpPort", 5080)),
40 mUseAuthInt(!config.getConfigBool("DisableAuthInt", false)),
41 mRejectBadNonces(config.getConfigBool("RejectBadNonces", false)),
42 mAllowInDialogImpersonationWithinRealm(config.getConfigBool("AllowInDialogImpersonationWithinRealm", false)),
43 mChallengeDigestTypes(challengeDigestTypes)
44{
45}
46
50
53{
54 DebugLog(<< "Monkey handling request: " << *this << "; reqcontext = " << rc);
55
56 Message *message = rc.getCurrentEvent();
57
58 SipMessage *sipMessage = dynamic_cast<SipMessage*>(message);
59 UserInfoMessage *userInfo = dynamic_cast<UserInfoMessage*>(message);
60 Proxy &proxy = rc.getProxy();
61
62 if (sipMessage)
63 {
64 if (sipMessage->method() == ACK ||
65 sipMessage->method() == BYE)
66 {
67 // Don't challenge ACK and BYE requests
68 return Continue;
69 }
70
71 if (sipMessage->exists(h_ProxyAuthorizations))
72 {
73 Auths &authHeaders = sipMessage->header(h_ProxyAuthorizations);
74
75 // if we find a Proxy-Authorization header for a realm we handle,
76 // asynchronously fetch the relevant userAuthInfo from the database
77 for (Auths::iterator i = authHeaders.begin() ; i != authHeaders.end() ; ++i)
78 {
79 if (isMyRealm(rc, i->param(p_realm)))
80 {
81 return requestUserAuthInfo(rc, i->param(p_realm));
82 }
83 }
84 }
85
86 // if there was no Proxy-Auth header already, and the request is purportedly From
87 // one of our domains, send a challenge, unless this is from a trusted node in one
88 // of "our" domains (ex: from a gateway).
89 //
90 // Note that other monkeys can still challenge the request later if needed
91 // for other reasons (for example, the StaticRoute monkey)
92 if(!sipMessage->header(h_From).isWellFormed() ||
93 sipMessage->header(h_From).isAllContacts() )
94 {
95 InfoLog(<<"Malformed From header: cannot get realm to challenge with. Rejecting.");
96 rc.sendResponse(*unique_ptr<SipMessage>
97 (Helper::makeResponse(*sipMessage, 400, "Malformed From header")));
98 return SkipAllChains;
99 }
100
101 if (proxy.isMyDomain(sipMessage->header(h_From).uri().host()))
102 {
104 {
105 challengeRequest(rc, false);
106 return SkipAllChains;
107 }
108 }
109 }
110 else if (userInfo)
111 {
112 // Handle response from user authentication database
113 sipMessage = &rc.getOriginalRequest();
114 const Data& realm = userInfo->realm();
115 const Data& user = userInfo->user();
116 InfoLog (<< "Received user auth info for " << user << " at realm " << realm);
118 switch(userInfo->getMode())
119 {
121 authResult = Helper::Failed;
122 break;
123
125 {
126 const Data& a1 = userInfo->A1();
127 StackLog (<< "Received user auth info for " << user << " at realm " << realm
128 << " a1 is " << a1);
129
130 pair<Helper::AuthResult,Data> result =
131 Helper::advancedAuthenticateRequest(*sipMessage, realm, a1, 3000); // was 15
132 authResult = result.first;
133 }
134 break;
135
137 authResult = Helper::Expired;
138 break;
139
141 authResult = Helper::Authenticated;
142 break;
143
145 authResult = Helper::Failed;
146 break;
147
149 authResult = Helper::Failed;
150 WarningLog(<<"UserInfoMessage mode == ERROR");
151 break;
152
153 default:
154 authResult = Helper::Failed;
155 ErrLog(<<"Unrecognised UserInfoMessage mode value: " << userInfo->getMode());
156 }
157
158 switch (authResult)
159 {
160 case Helper::Failed:
161 InfoLog (<< "Authentication failed for " << user << " at realm " << realm << ". Sending 403");
162 rc.sendResponse(*unique_ptr<SipMessage>
163 (Helper::makeResponse(*sipMessage, 403, "Authentication Failed")));
164 return SkipAllChains;
165
166 // !abr! Eventually, this should just append a counter to
167 // the nonce, and increment it on each challenge.
168 // If this count is smaller than some reasonable limit,
169 // then we re-challenge; otherwise, we send a 403 instead.
170
172 InfoLog (<< "Authentication ok for " << user);
173
174 if(!sipMessage->header(h_From).isWellFormed() ||
175 sipMessage->header(h_From).isAllContacts())
176 {
177 InfoLog(<<"From header is malformed in"
178 " digest response.");
179 rc.sendResponse(*unique_ptr<SipMessage>
180 (Helper::makeResponse(*sipMessage, 400, "Malformed From header")));
181 return SkipAllChains;
182 }
183
184 // The mAllowInDialogImpersonationWithinRealm setting allows a request to be authorized
185 // using a digest user that is different from the user specified in the From header. This
186 // applies to mid-dialog requests only. This is necessary to properly support call forwarding
187 // scenarios.Consider this scenario:
188 // -User A calls User B
189 // -User B sends a 302 redierct to redirect A to User C
190 // -User A resends INVITE to User C. This INVITE still has User B in the To header.
191 // -Sometime during the call between A and C, User C decides to put the call on hold.
192 // This results in a reINVITE from C to A, however User B is in From header. If this setting
193 // set to false, then the DigestAuthenticator will return a 403 (forged request) because
194 // the auth info provided is for User C, yet the From header is User B (ie: authorizedForThisIdentity
195 // will return false).
197 sipMessage->header(h_To).exists(p_tag) && // to_tag is present - this is an in-dialog request
198 sipMessage->header(h_From).uri().host() == realm) || // request is From a user in our realm/domain
199 authorizedForThisIdentity(user, realm, sipMessage->header(h_From).uri()))
200 {
201 rc.setDigestIdentity(user);
202
204 {
205 if (sipMessage->exists(h_PPreferredIdentities))
206 {
207 // Ensure any P-AssertedIdentities present are removed (note: this is an illegal condidition)
208 sipMessage->remove(h_PAssertedIdentities);
209
210 // TODO - when we have a concept of multiple identities per user
211 // find the first sip or sips P-Preferred-Identity header and the first tel
212 // bool haveSip = false;
213 // bool haveTel = false;
214 // for (;;)
215 // {
216 // if ((i->uri().scheme() == Symbols::SIP) || (i->uri().scheme() == Symbols::SIPS))
217 // {
218 // if (haveSip)
219 // {
220 // continue; // skip all but the first sip: or sips: URL
221 // }
222 // haveSip = true;
223 //
224 // if (knownSipIdentity( user, realm, i->uri() ) // should be NameAddr?
225 // {
226 // sipMessage->header(h_PAssertedIdentities).push_back( i->uri() );
227 // }
228 // else
229 // {
230 // sipMessage->header(h_PAssertedIdentities).push_back(getDefaultIdentity(user, realm));
231 // }
232 // }
233 // else if ((i->uri().scheme() == Symbols::TEL))
234 // {
235 // if (haveTel)
236 // {
237 // continue; // skip all but the first tel: URL
238 // }
239 // haveTel = true;
240 //
241 // if (knownTelIdentity( user, realm, i->uri() ))
242 // {
243 // sipMessage->header(h_PAssertedIdentities).push_back( i->uri() );
244 // }
245 // }
246 // }
247
248 // We currently don't do anything special with the P-Peferred-Identity hint - just
249 // add default identity
250 sipMessage->header(h_PAssertedIdentities).push_back(getDefaultIdentity(user, realm, sipMessage->header(h_From)));
251
252 // Remove the P-Preferered-Identity header
253 sipMessage->remove(h_PPreferredIdentities);
254 }
255 else
256 {
257 if (!sipMessage->exists(h_PAssertedIdentities))
258 {
259 sipMessage->header(h_PAssertedIdentities).push_back(getDefaultIdentity(user, realm, sipMessage->header(h_From)));
260 }
261 // else TODO
262 // - should implement guidlines in RFC5876 4.5 - whereby the proxy should remove
263 // ignored URI's (ie. a 2nd SIP, SIPS or TEL URI, unknown scheme)
264 }
265 }
266
267#if defined(USE_SSL)
268 // TODO !SLG! RFC4474 has been deprecated by RFC8224 (Authenticated Identity Management). We should remove/adjust this code.
270 {
271 static Data http("http://" + mHttpHostname + ":" + Data(mHttpPort) + "/cert?domain=");
272 // .bwc. Leave pre-existing Identity headers alone.
273 if(!sipMessage->exists(h_Identities))
274 {
275 sipMessage->header(h_Identities).push_back(IdentityCategory(Data::Empty)); // This is a signal to have the TransportSelector fill in the identity header
276 if(sipMessage->exists(h_IdentityInfo))
277 {
278 InfoLog(<<"Somebody sent us a"
279 " request with an Identity-Info, but no Identity"
280 " header. Removing it.");
281 if(!sipMessage->header(h_IdentityInfo).isWellFormed())
282 {
283 InfoLog(<<"...and this "
284 "Identity-Info header was malformed!");
285 }
286
287 sipMessage->remove(h_IdentityInfo);
288 }
289
290 sipMessage->header(h_IdentityInfo).uri() = http + realm;
291 InfoLog (<< "Identity-Info=" << sipMessage->header(h_IdentityInfo).uri());
292 }
293 }
294#endif
295 }
296 else
297 {
298 // !rwm! The user is trying to forge a request. Respond with a 403
299 InfoLog (<< "User: " << user << " at realm: " << realm <<
300 " trying to forge request from: " << sipMessage->header(h_From).uri());
301 rc.sendResponse(*unique_ptr<SipMessage>
302 (Helper::makeResponse(*sipMessage, 403)));
303 return SkipAllChains;
304 }
305
306 return Continue;
307
308 case Helper::Expired:
309 InfoLog (<< "Authentication expired for " << user);
310 challengeRequest(rc, true);
311 return SkipAllChains;
312
314 InfoLog (<< "Authentication nonce badly formed for " << user);
316 {
317 rc.sendResponse(*unique_ptr<SipMessage>
318 (Helper::makeResponse(*sipMessage, 403, "Where on earth did you get that nonce?")));
319 }
320 else
321 {
322 challengeRequest(rc, true);
323 }
324 return SkipAllChains;
325 }
326 }
327
328 return Continue;
329}
330
331bool
333 resip::Uri &fromUri)
334{
335 // !rwm! good enough for now. TODO eventually consult a database to see what
336 // combinations of user/realm combos are authorized for an identity
337 if (fromUri.host() == realm)
338 {
339 if ((fromUri.user() == user) || (fromUri.user() == "anonymous"))
340 {
341 return true;
342 }
343 }
344
345 // Now try the form where the username parameter in the auth
346 // header is the full fromUri, e.g.
347 // Proxy-Authorization: Digest username="user@domain" ...
348 //
349 if (fromUri.getAorNoPort() == user)
350 {
351 return true;
352 }
353
354 // catch-all: access denied
355 return false;
356}
357
360{
361 NameAddr defaultIdentity;
362 defaultIdentity.displayName() = from.displayName();
363 defaultIdentity.uri().scheme() = from.uri().scheme();
364 defaultIdentity.uri().user() = user;
365 defaultIdentity.uri().host() = realm;
366 return defaultIdentity;
367}
368
369void
371{
372 SipMessage &sipMessage = rc.getOriginalRequest();
373 Data realm = getRealm(rc);
374
375 SipMessage *challenge = Helper::makeProxyChallenge(sipMessage, realm,
376 mUseAuthInt /*auth-int*/, stale,
378 rc.sendResponse(*challenge);
379 delete challenge;
380}
381
384{
385 Message *message = rc.getCurrentEvent();
386 SipMessage *sipMessage = dynamic_cast<SipMessage*>(message);
387 resip_assert(sipMessage);
388
389 // Extract the user from the appropriate Proxy-Authorization header
390 Auths &authorizationHeaders = sipMessage->header(h_ProxyAuthorizations);
392 Data user;
393
394 for (i = authorizationHeaders.begin(); i != authorizationHeaders.end(); i++)
395 {
396 if (i->exists(p_realm) &&
397 i->param(p_realm) == realm
398 && i->exists(p_username))
399 {
400 user = i->param(p_username);
401
402 InfoLog (<< "Request user auth info for " << user
403 << " at realm " << realm);
404 break;
405 }
406 }
407
408 if (!user.empty())
409 {
410 UserInfoMessage* async = new UserInfoMessage(*this, rc.getTransactionId(), &(rc.getProxy()));
411 async->user()=user;
412 async->realm()=realm;
413 if(sipMessage->header(h_From).isWellFormed())
414 {
415 async->domain()=sipMessage->header(h_From).uri().host();
416 }
417 else
418 {
419 async->domain()=realm;
420 }
421 Auth& auth = *i;
422 return requestUserAuthInfo(rc, auth, async);
423 }
424 else
425 {
426 challengeRequest(rc, false);
427 return SkipAllChains;
428 }
429}
430
433{
434 std::unique_ptr<ApplicationMessage> app(userInfo);
436 return WaitingForEvent;
437}
438
441{
442 if(!mStaticRealm.empty())
443 {
444 return mStaticRealm;
445 }
446 return rc.getDigestRealm();
447}
448
449bool
451{
452 if(!mStaticRealm.empty())
453 {
454 return mStaticRealm == realm;
455 }
456 Proxy &proxy = rc.getProxy();
457 return proxy.isMyDomain(realm);
458}
459
460
461/* ====================================================================
462 * The Vovida Software License, Version 1.0
463 *
464 * Copyright (c) 2026 SIP Spectrum, Inc. https://www.sipspectrum.com
465 * Copyright (c) 2000 Vovida Networks, Inc. All rights reserved.
466 *
467 * Redistribution and use in source and binary forms, with or without
468 * modification, are permitted provided that the following conditions
469 * are met:
470 *
471 * 1. Redistributions of source code must retain the above copyright
472 * notice, this list of conditions and the following disclaimer.
473 *
474 * 2. Redistributions in binary form must reproduce the above copyright
475 * notice, this list of conditions and the following disclaimer in
476 * the documentation and/or other materials provided with the
477 * distribution.
478 *
479 * 3. The names "VOCAL", "Vovida Open Communication Application Library",
480 * and "Vovida Open Communication Application Library (VOCAL)" must
481 * not be used to endorse or promote products derived from this
482 * software without prior written permission. For written
483 * permission, please contact vocal@vovida.org.
484 *
485 * 4. Products derived from this software may not be called "VOCAL", nor
486 * may "VOCAL" appear in their name, without prior written
487 * permission of Vovida Networks, Inc.
488 *
489 * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESSED OR IMPLIED
490 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
491 * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, TITLE AND
492 * NON-INFRINGEMENT ARE DISCLAIMED. IN NO EVENT SHALL VOVIDA
493 * NETWORKS, INC. OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT DAMAGES
494 * IN EXCESS OF $1,000, NOR FOR ANY INDIRECT, INCIDENTAL, SPECIAL,
495 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
496 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
497 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
498 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
499 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
500 * USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
501 * DAMAGE.
502 *
503 * ====================================================================
504 *
505 * This software consists of voluntary contributions made by Vovida
506 * Networks, Inc. and many individuals on behalf of Vovida Networks,
507 * Inc. For more information on Vovida Networks, Inc., please see
508 * <http://www.vovida.org/>.
509 *
510 */
#define DebugLog(args_)
Definition Logger.hxx:128
#define StackLog(args_)
Definition Logger.hxx:125
#define InfoLog(args_)
Definition Logger.hxx:131
#define WarningLog(args_)
Definition Logger.hxx:134
#define ErrLog(args_)
Definition Logger.hxx:137
#define resip_assert(x)
This file needs to work for both C and C++ code.
virtual resip::Data getRealm(RequestContext &)
virtual resip::NameAddr getDefaultIdentity(const resip::Data &user, const resip::Data &realm, resip::NameAddr &from)
virtual bool isMyRealm(RequestContext &, const resip::Data &realm)
resip::Dispatcher * mAuthRequestDispatcher
std::vector< resip::DigestType > mChallengeDigestTypes
virtual void challengeRequest(RequestContext &, bool stale=false)
virtual bool authorizedForThisIdentity(const resip::Data &user, const resip::Data &realm, resip::Uri &fromUri)
virtual processor_action_t requestUserAuthInfo(RequestContext &, resip::Data &realm)
DigestAuthenticator(ProxyConfig &config, resip::Dispatcher *authRequestDispatcher, const resip::Data &staticRealm=resip::Data::Empty, const std::vector< resip::DigestType > &challengeDigestTypes=std::vector< resip::DigestType >())
virtual processor_action_t process(RequestContext &)
static resip::KeyValueStore::Key mFromTrustedNodeKey
bool isPAssertedIdentityProcessingEnabled()
Definition Proxy.hxx:84
resip::SipMessage & getOriginalRequest()
Returns the SipMessage associated with the server transaction.
void setDigestIdentity(const resip::Data &)
const resip::Data & getDigestRealm()
resip::Message * getCurrentEvent()
Returns the event that we are currently working on.
void sendResponse(resip::SipMessage &response)
resip::KeyValueStore & getKeyValueStore()
resip::Data getTransactionId() const
const resip::Data & A1() const
const resip::Data & user() const
const resip::Data & domain() const
resip::UserAuthInfo::InfoMode getMode() const
const resip::Data & realm() const
Represents the "credentials" and "challenge" elements in the SIP grammar.
Definition Auth.hxx:18
An alternative to std::string, encapsulates an arbitrary buffer of bytes.
Definition Data.hxx:90
static const Data Empty
Constant that represents a zero-length data.
Definition Data.hxx:882
bool empty() const noexcept
Checks whether the Data is empty.
Definition Data.hxx:591
A generic thread-bank class.
virtual bool post(std::unique_ptr< resip::ApplicationMessage > &work)
Posts a message to this thread bank.
static SipMessage * makeProxyChallenge(const SipMessage &request, const Data &realm, bool useAuth=true, bool stale=false, const std::vector< DigestType > &digestTypes=std::vector< DigestType >())
Definition Helper.cxx:1212
static void makeResponse(SipMessage &response, const SipMessage &request, int responseCode, const Data &reason=Data::Empty, const Data &hostname=Data::Empty, const Data &warning=Data::Empty)
Make a response to a provided request.
Definition Helper.cxx:412
static std::pair< AuthResult, Data > advancedAuthenticateRequest(const SipMessage &request, const Data &realm, const Data &resipPasswordHashA1, int expiresDelta=0, bool proxyAuthorization=true)
Definition Helper.cxx:1071
Represents an Identity header from RFC8824.
const bool & getBoolValue(Key key) const
bool isWellFormed() const
Returns true iff this element was parsed successfully, according to the implementation of parse().
The base-class used for message-passing.
Definition Message.hxx:21
Represents the "name-addr" and the "addr-spec" elements in the RFC 3261 grammar.
Definition NameAddr.hxx:19
Data & displayName()
Definition NameAddr.cxx:144
An iterator class (bidirectional)
Container class for ParserCategory, used by SipMessage to represent multi-valued headers (Contact,...
iterator end()
Returns an iterator pointing to the last header field value.
iterator begin()
Returns an iterator pointing to the first header field value.
const Uri & uri() const
Represents a SIP message.
const RequestLine & header(const RequestLineType &l) const
Returns the RequestLine. This is only valid for request messages.
bool exists(const HeaderBase &headerType) const
Returns true if the given header field is present, false otherwise.
void remove(const HeaderBase &headerType)
Prevents a header field from being present when the message is prepared for sending to a transport.
resip::MethodTypes method() const
returns the method type of the message
bool isMyDomain(const Data &domain) const
Returns true iff domain matches one of the domains that this TransactionUser is responsible for.
Represents the "SIP-URI" and "SIPS-URI" elements in the RFC 3261 grammar.
Definition Uri.hxx:22
Data getAorNoPort() const
Definition Uri.cxx:1087
Data & scheme()
Definition Uri.hxx:143
Data & user()
Definition Uri.hxx:42
Data & host()
Definition Uri.hxx:40
This Class is used to implement a primitive form of RPC using loose XML formatting.
dcm! – add UnusedChecking(_enum) below;
H_PPreferredIdentitys h_PPreferredIdentities
Definition Headers.cxx:211
H_PAssertedIdentitys h_PAssertedIdentities
Definition Headers.cxx:215
H_Identitys h_Identities
Definition Headers.cxx:315