4 * RADIUS protocol authenticator module for GNU Gatekeeper.
5 * Please see docs/radauth.txt for more details.
7 * Copyright (c) 2003, Quarcom FHU, Michal Zygmuntowicz
9 * This work is published under the GNU Public License (GPL)
10 * see file COPYING for details.
11 * We also explicitely grant the right to link this code
12 * with the OpenH323 library.
14 * $Log: radauth.cxx,v $
15 * Revision 1.37 2006/12/06 16:34:04 zvision
16 * Handle RADIUS Class attribute correctly
18 * Revision 1.36 2006/07/06 15:25:13 willamowius
19 * set all deleted pointers to NULL (most probably more than needed)
21 * Revision 1.35 2006/06/08 08:58:48 willamowius
22 * gcc 4.1 compile fixes
24 * Revision 1.34 2006/04/14 13:56:19 willamowius
25 * call failover code merged
27 * Revision 1.2 2005/12/05 13:29:02 zvision
28 * Accept multiple routes from RADIUS/SQL auth modules
30 * Revision 1.1.1.1 2005/11/21 20:19:58 willamowius
33 * Revision 1.4 2005/11/15 19:52:56 jan
34 * Michal v1 (works, but on in routed, not proxy mode)
36 * Revision 1.33 2005/04/24 16:39:44 zvision
37 * MSVC6.0 compatibility fixed
39 * Revision 1.32 2005/02/10 23:26:39 zvision
40 * Accounting updates/call disconnect handling does not lock the whole call table
42 * Revision 1.31 2005/02/01 14:28:10 zvision
43 * Parts of signaling code rewritten
45 * Revision 1.30 2005/01/28 11:19:42 zvision
46 * All passwords in the config can be stored in an encrypted form
48 * Revision 1.29 2005/01/26 23:50:26 zvision
49 * Framed-IP-Address could not be determined to unregistered calls without
50 * Setup-UUIE.sourceCallSignalAddress field
52 * Revision 1.28 2005/01/25 18:59:08 zvision
53 * Aliases handling fixed, alias type is not appended anymore
55 * Revision 1.27 2005/01/25 00:37:35 zvision
56 * Handle aliases of type partyNumber properly
58 * Revision 1.26 2005/01/10 22:49:29 willamowius
61 * Revision 1.25 2004/12/08 13:02:56 zvision
62 * Better Calling/Called-Station-Id handling
64 * Revision 1.24 2004/11/15 23:57:42 zvision
65 * Ability to choose between the original and the rewritten dialed number
67 * Revision 1.23 2004/11/03 10:40:17 zvision
68 * Add/remove RRQ aliases using h323-ivr-in=terminal-alias Cisco AV-Pair attr
70 * Revision 1.22 2004/08/09 21:52:23 zvision
71 * RADIUS based call routing
73 * Revision 1.21 2004/07/26 12:19:41 zvision
74 * New faster Radius implementation, thanks to Pavel Pavlov for ideas!
76 * Revision 1.20.2.2 2004/07/07 23:11:07 zvision
77 * Faster and more elegant handling of Cisco VSA
79 * Revision 1.20.2.1 2004/07/07 20:50:14 zvision
80 * New, faster, Radius client implementation. Thanks to Pavel Pavlov for ideas!
82 * Revision 1.20 2004/07/06 23:46:20 zvision
83 * gcc 2.95.x compilation errors fixed
85 * Revision 1.19 2004/07/05 16:39:45 zvision
86 * Support for CallCreditServiceControl
88 * Revision 1.18 2004/06/25 13:33:19 zvision
89 * Better Username, Calling-Station-Id and Called-Station-Id handling.
90 * New SetupUnreg option in Gatekeeper::Auth section.
92 * Revision 1.17 2004/06/17 10:47:13 zvision
93 * New h323-ivr-out=h323-call-id accounting attribute
95 * Revision 1.16 2004/06/17 10:03:17 zvision
96 * Better Framed-IP-Address handling in RadAliasAuth Setup check
98 * Revision 1.15 2004/06/16 23:46:47 zvision
99 * RadAliasAuth will work even when Setup-UUIE does not contain sourceAddress
101 * Revision 1.14 2004/05/22 12:25:17 zvision
102 * Check aliases only when authenticating RRQ message
104 * Revision 1.13 2004/04/17 11:43:43 zvision
105 * Auth/acct API changes.
106 * Header file usage more consistent.
108 * Revision 1.12 2004/03/17 00:00:38 zvision
109 * Conditional compilation to allow to control RADIUS on Windows just by setting HA_RADIUS macro
111 * Revision 1.11 2004/02/20 14:44:11 zvision
112 * Changed API for GkAuthenticator class. Modified RadAuth/RadAliasAuth classes.
113 * Added Q.931 Setup authentication for RadAuth module.
115 * Revision 1.10 2003/11/14 00:27:30 zvision
116 * Q.931/H.225 Setup authentication added
118 * Revision 1.9 2003/10/31 00:01:28 zvision
119 * Improved accounting modules stacking control, optimized radacct/radauth a bit
121 * Revision 1.8 2003/10/21 15:55:27 zvision
122 * Fixed compiler warnings for gcc < 3
124 * Revision 1.7 2003/10/15 10:16:57 zvision
125 * Fixed VC6 compiler warnings. Thanks to Hu Yuxin.
127 * Revision 1.6 2003/10/08 12:40:48 zvision
128 * Realtime accounting updates added
130 * Revision 1.5 2003/09/28 16:24:31 zvision
131 * Introduced call duration limit feature for registered endpoints (ARQ)
133 * Revision 1.4 2003/08/25 12:53:38 zvision
134 * Introduced includeTerminalAliases config option. Changed visibility
135 * of some member variables to private.
137 * Revision 1.3 2003/08/20 14:46:19 zvision
138 * Avoid PString reference copying. Small code improvements.
140 * Revision 1.2 2003/08/19 10:47:37 zvision
141 * Initially added to 2.2 brach. Completely redesigned.
142 * Redundant code removed. Added h323-return-code, h323-credit-time
143 * and Session-Timeout respone attributes processing.
145 * Revision 1.1.2.17 2003/07/31 22:59:24 zvision
146 * Fixed IP address retrieval for unregistered endpoints
148 * Revision 1.1.2.16 2003/07/31 13:09:15 zvision
149 * Added Q.931 Setup message authentication and call duration limit feature
151 * Revision 1.1.2.15 2003/07/17 14:40:39 zvision
152 * Conditional compilation of features available only when HAS_ACCT is defined.
154 * Revision 1.1.2.14 2003/07/16 22:13:21 zvision
155 * Fixed Radius attributes for answer call ARQs.
157 * Revision 1.1.2.13 2003/07/07 14:28:30 zvision
158 * Added missing NAS-Identifier attribute in RadAliasAuth. Thanks Julius Stavaris.
160 * Revision 1.1.2.12 2003/07/07 12:02:55 zvision
161 * Improved H.235 handling.
163 * Revision 1.1.2.11 2003/06/19 15:33:29 zvision
164 * Removed static modifier from GetConferenceIDString function.
166 * Revision 1.1.2.10 2003/06/11 13:06:57 zvision
167 * Added gk_const.h include directive (OPENH323_NEWVERSION macro definition)
169 * Revision 1.1.2.9 2003/06/11 12:14:35 zvision
172 * Revision 1.1.2.8 2003/06/05 10:03:04 zvision
173 * Small fix to h323-gw-id attribute.
175 * Revision 1.1.2.7 2003/05/29 17:21:22 zvision
176 * Fixed compilation errors with OpenH323 versions prior to 1.11.5 (no H235AuthCAT)
178 * Revision 1.1.2.6 2003/05/28 13:25:19 zvision
179 * Added alias based authentication (RadAliasAuth)
181 * Revision 1.1.2.5 2003/05/27 00:13:05 zvision
182 * Smart Calling and Called -Station-Id selection (dialedDigits and partyNumber alias types preferred)
184 * Revision 1.1.2.4 2003/05/26 23:09:59 zvision
185 * Added new OnSend and OnReceive hooks. LocalInterface config parameter introduced.
187 * Revision 1.1.2.3 2003/05/13 17:49:49 zvision
188 * Removed acctPort. New includeFramedIP feature. Better tracing. Bug-fixes
190 * Revision 1.1.2.2 2003/04/29 14:56:27 zvision
191 * Added H.235 capability matching
193 * Revision 1.1.2.1 2003/04/23 20:15:37 zvision
200 #if defined(_WIN32) && (_MSC_VER <= 1200)
201 #pragma warning(disable:4786) // warning about too long debug symbol off
209 #include <h235auth.h>
210 #include "gk_const.h"
211 #include "h323util.h"
212 #include "stl_supp.h"
218 #include "radproto.h"
223 using Routing::Route;
226 // Settings for H.235 based module will be stored inside [RadAuth] config section
227 const char* const RadAuthConfigSectionName = "RadAuth";
228 // Settings for alias based module will be stored inside [RadAliasAuth] config section
229 const char* const RadAliasAuthConfigSectionName = "RadAliasAuth";
232 // OID for CAT (Cisco Access Token) algorithm
233 PString RadAuth::OID_CAT("1.2.840.113548.10.1.2.1");
236 RadAuthBase::RadAuthBase(
237 const char* authName,
238 const char* configSectionName,
239 unsigned supportedRasChecks,
240 unsigned supportedMiscChecks
243 GkAuthenticator(authName, supportedRasChecks, supportedMiscChecks),
244 m_radiusClient(NULL),
245 m_attrH323CallType(RadiusAttr::CiscoVSA_h323_call_type, false,
247 m_attrH323CallOriginOriginate(RadiusAttr::CiscoVSA_h323_call_origin, false,
248 PString("originate")),
249 m_attrH323CallOriginAnswer(RadiusAttr::CiscoVSA_h323_call_origin, false,
252 // read settings from the config
253 m_appendCiscoAttributes = Toolkit::AsBool(GetConfig()->GetString(
254 configSectionName,"AppendCiscoAttributes", "1"
256 m_includeTerminalAliases = Toolkit::AsBool(GetConfig()->GetString(
257 configSectionName, "IncludeTerminalAliases", "1"
259 m_nasIdentifier = Toolkit::Instance()->GKName();
260 /// build RADIUS client
261 m_radiusClient = new RadiusClient(*GetConfig(), configSectionName);
262 m_nasIpAddress = m_radiusClient->GetLocalAddress();
263 if (m_nasIpAddress == INADDR_ANY) {
264 vector<PIPSocket::Address> interfaces;
265 Toolkit::Instance()->GetGKHome(interfaces);
266 if (!interfaces.empty())
267 m_nasIpAddress = interfaces.front();
269 PTRACE(1, "RADAUTH\t" << GetName() << " cannot determine "
273 m_useDialedNumber = Toolkit::AsBool(GetConfig()->GetString(
274 configSectionName, "UseDialedNumber", "0"
276 m_attrH323GwId = RadiusAttr(RadiusAttr::CiscoVSA_h323_gw_id, false, m_nasIdentifier);
277 m_attrNasIdentifier = RadiusAttr(RadiusAttr::NasIdentifier, m_nasIdentifier);
280 RadAuthBase::~RadAuthBase()
282 delete m_radiusClient;
285 int RadAuthBase::Check(
286 /// RRQ RAS message to be authenticated
287 RasPDU<H225_RegistrationRequest>& rrqPdu,
288 /// authorization data (reject reason, ...)
289 RRQAuthData& authData
292 H225_RegistrationRequest& rrq = (H225_RegistrationRequest&)rrqPdu;
294 // build RADIUS Access-Request
295 RadiusPDU* const pdu = new RadiusPDU(RadiusPDU::AccessRequest);
297 // Append User-Name and a password related attributes
298 // (User-Password or Chap-Password and Chap-Timestamp)
299 const int status = AppendUsernameAndPassword(*pdu, rrqPdu, authData);
300 if (status != e_ok) {
305 // Gk works as NAS point, so append NAS IP
306 pdu->AppendAttr(RadiusAttr::NasIpAddress, m_nasIpAddress);
307 // NAS-Identifier as Gk name
308 pdu->AppendAttr(m_attrNasIdentifier);
309 // Gk does not have a concept of physical ports,
310 // so define port type as NAS-Port-Virtual
311 pdu->AppendAttr(RadiusAttr::NasPortType, RadiusAttr::NasPort_Virtual);
312 // RRQ service type is Login-User
313 pdu->AppendAttr(RadiusAttr::ServiceType, RadiusAttr::ST_Login);
315 // append Framed-IP-Address
316 PIPSocket::Address addr;
317 bool ipFound = false;
318 if (rrq.m_callSignalAddress.GetSize() > 0) {
319 if (GetIPFromTransportAddr(rrq.m_callSignalAddress[0], addr)
322 } else if (rrq.m_rasAddress.GetSize() > 0) {
323 if (GetIPFromTransportAddr(rrq.m_rasAddress[0], addr)
328 PTRACE(2, "RADAUTH\t" << GetName() << " RRQ auth failed: "
329 "could not determine Framed-IP-Address"
331 authData.m_rejectReason = H225_RegistrationRejectReason::e_invalidCallSignalAddress;
335 pdu->AppendAttr(RadiusAttr::FramedIpAddress, addr);
337 if (m_appendCiscoAttributes && m_includeTerminalAliases
338 && rrq.HasOptionalField(H225_RegistrationRequest::e_terminalAlias)) {
339 PString aliasList("terminal-alias:");
340 for (PINDEX i = 0; i < rrq.m_terminalAlias.GetSize(); i++) {
343 aliasList += AsString(rrq.m_terminalAlias[i], FALSE);
346 pdu->AppendCiscoAttr(RadiusAttr::CiscoVSA_AV_Pair,
347 PString("h323-ivr-out=") + aliasList + ";",
352 if (!OnSendPDU(*pdu, rrqPdu, authData)) {
356 // send request and wait for response
357 RadiusPDU* response = NULL;
358 bool result = m_radiusClient->MakeRequest(*pdu, response) && response;
363 PTRACE(2, "RADAUTH\t" << GetName() << " RRQ auth failed: "
364 " could not receive or decode response from RADIUS"
368 authData.m_rejectReason = H225_RegistrationRejectReason::e_undefinedReason;
369 return GetDefaultStatus();
372 result = (response->GetCode() == RadiusPDU::AccessAccept);
375 const RadiusAttr* attr;
377 // test for h323-return-code attribute (has to be 0 if accept)
379 attr = response->FindVsaAttr(RadiusAttr::CiscoVendorId,
380 RadiusAttr::CiscoVSA_h323_return_code
383 value = attr->AsCiscoString();
384 if (value.GetLength() > 0
385 && strspn((const char*)value, "0123456789") == (size_t)value.GetLength()) {
386 const unsigned retcode = value.AsUnsigned();
388 PTRACE(3, "RADAUTH\t" << GetName() << " RRQ check failed: "
389 "return code " << retcode
394 PTRACE(2, "RADAUTH\t" << GetName() << " RRQ check failed: "
395 "invalid h323-return-code attribute '" << value << '\''
402 // check for h323-billing-model
404 attr = response->FindVsaAttr(RadiusAttr::CiscoVendorId,
405 RadiusAttr::CiscoVSA_h323_billing_model
408 value = attr->AsCiscoString();
409 if (value.GetLength() > 0
410 && strspn((const char*)value,"0123456789") == (size_t)value.GetLength()) {
411 const int intVal = value.AsInteger();
413 authData.m_billingMode = H225_CallCreditServiceControl_billingMode::e_credit;
414 else if (intVal == 1 || intVal == 2)
415 authData.m_billingMode = H225_CallCreditServiceControl_billingMode::e_debit;
417 PTRACE(3, "RADAUTH\t" << GetName() << " invalid h323-billing-model "
418 "attribute '" << value << '\''
424 // check for h323-credit-amount
426 attr = response->FindVsaAttr(RadiusAttr::CiscoVendorId,
427 RadiusAttr::CiscoVSA_h323_credit_amount
430 value = attr->AsCiscoString();
431 if (value.GetLength() > 0
432 && strspn((const char*)value,"0123456789.") == (size_t)value.GetLength()) {
433 if (value.Find('.') == P_MAX_INDEX) {
434 PTRACE(3, "RADAUTH\t" << GetName() << " h323-credit-amount "
435 "without a decimal dot is ambiguous '" << value << '\''
437 authData.m_amountString = psprintf(PString("%d.%d"),
438 value.AsInteger() / 100, value.AsInteger() % 100
441 authData.m_amountString = value;
443 attr = response->FindVsaAttr(RadiusAttr::CiscoVendorId,
444 RadiusAttr::CiscoVSA_h323_currency
447 authData.m_amountString += attr->AsCiscoString();
449 PTRACE(3, "RADAUTH\t" << GetName() << " invalid h323-credit-amount "
450 "attribute '" << value << '\''
456 // process h323-ivr-in=terminal-alias attribute
458 attr = response->FindVsaAttr(RadiusAttr::CiscoVendorId,
459 RadiusAttr::CiscoVSA_AV_Pair
461 while (attr != NULL) {
463 value = attr->AsCiscoString();
464 if (value.Find("h323-ivr-in=") == 0
465 && ((index = value.Find("terminal-alias:")) != P_MAX_INDEX)) {
466 index += strlen("terminal-alias:");
467 const PINDEX semicolonpos = value.Find(';', index);
468 value = value.Mid(index, semicolonpos == P_MAX_INDEX
469 ? P_MAX_INDEX : (semicolonpos-index)
471 PStringArray aliases = value.Tokenise(",");
472 if (aliases.GetSize() > 0
473 && rrq.HasOptionalField(H225_RegistrationRequest::e_terminalAlias)) {
475 while (i < rrq.m_terminalAlias.GetSize()) {
476 PINDEX j = aliases.GetStringsIndex(AsString(rrq.m_terminalAlias[i], FALSE));
477 if( j == P_MAX_INDEX )
478 rrq.m_terminalAlias.RemoveAt(i);
485 for (PINDEX i = 0; i < aliases.GetSize(); i++) {
486 if (rrq.HasOptionalField(H225_RegistrationRequest::e_terminalAlias))
487 rrq.m_terminalAlias.SetSize(rrq.m_terminalAlias.GetSize()+1);
489 rrq.IncludeOptionalField(H225_RegistrationRequest::e_terminalAlias);
490 rrq.m_terminalAlias.SetSize(1);
492 H323SetAliasAddress(aliases[i], rrq.m_terminalAlias[rrq.m_terminalAlias.GetSize()-1]);
496 attr = response->FindVsaAttr(RadiusAttr::CiscoVendorId,
497 RadiusAttr::CiscoVSA_AV_Pair, attr
503 result = OnReceivedPDU(*response, rrqPdu, authData);
505 authData.m_rejectReason = H225_RegistrationRejectReason::e_securityDenial;
509 return result ? e_ok : e_fail;
512 int RadAuthBase::Check(
513 /// ARQ nessage to be authenticated
514 RasPDU<H225_AdmissionRequest> & arqPdu,
515 /// authorization data (call duration limit, reject reason, ...)
516 ARQAuthData& authData
519 H225_AdmissionRequest& arq = (H225_AdmissionRequest&)arqPdu;
521 // build RADIUS Access-Request packet
522 RadiusPDU* const pdu = new RadiusPDU(RadiusPDU::AccessRequest);
523 const bool hasCall = authData.m_call.operator->() != NULL;
524 PIPSocket::Address addr;
525 endptr callingEP, calledEP;
527 // try to extract calling/called endpoints from RegistrationTable
528 // (unregistered endpoints will not be present there)
529 if (arq.m_answerCall) {
530 calledEP = authData.m_requestingEP;
532 callingEP = authData.m_call->GetCallingParty();
534 callingEP = authData.m_requestingEP;
536 calledEP = authData.m_call->GetCalledParty();
537 if (!calledEP && arq.HasOptionalField(H225_AdmissionRequest::e_destCallSignalAddress))
538 calledEP = RegistrationTable::Instance()->FindBySignalAdr(arq.m_destCallSignalAddress);
541 // at least requesting endpoint (the one that is sending ARQ)
542 // has to be present in the RegistrationTable
543 if (arq.m_answerCall ? !calledEP : !callingEP) {
545 PTRACE(3, "RADAUTH\t" << GetName() << " ARQ auth failed: "
546 "requesting endpoint " << arq.m_endpointIdentifier
549 authData.m_rejectReason = arq.m_answerCall
550 ? H225_AdmissionRejectReason::e_calledPartyNotRegistered
551 : H225_AdmissionRejectReason::e_callerNotRegistered;
555 // Append User-Name and a password related attributes
556 // (User-Password or Chap-Password and Chap-Timestamp)
558 const int status = AppendUsernameAndPassword(*pdu, arqPdu, authData, &username);
559 if (status != e_ok) {
564 // Gk acts as NAS, so include NAS IP
565 pdu->AppendAttr(RadiusAttr::NasIpAddress, m_nasIpAddress);
566 // NAS-Identifier as Gk name
567 pdu->AppendAttr(m_attrNasIdentifier);
568 // NAS-Port-Type as Virtual, since Gk does
569 // not care about physical ports concept
570 pdu->AppendAttr(RadiusAttr::NasPortType, RadiusAttr::NasPort_Virtual);
571 // Service-Type is Login-User if originating the call
572 // and Call Check if answering the call
573 pdu->AppendAttr(RadiusAttr::ServiceType,
574 arq.m_answerCall ? RadiusAttr::ST_CallCheck : RadiusAttr::ST_Login
577 // append Frame-IP-Address
578 bool ipFound = false;
579 if (arq.m_answerCall) {
581 && GetIPFromTransportAddr(calledEP->GetCallSignalAddress(), addr)
584 else if (arq.HasOptionalField(arq.e_destCallSignalAddress)
585 && GetIPFromTransportAddr(arq.m_destCallSignalAddress, addr)
590 && GetIPFromTransportAddr(callingEP->GetCallSignalAddress(), addr)
593 else if(arq.HasOptionalField(arq.e_srcCallSignalAddress)
594 && GetIPFromTransportAddr(arq.m_srcCallSignalAddress, addr)
599 PTRACE(2, "RADAUTH\t" << GetName() << " ARQ auth failed: "
600 "could not setup Framed-IP-Address"
602 authData.m_rejectReason = H225_AdmissionRejectReason::e_securityDenial;
606 pdu->AppendAttr(RadiusAttr::FramedIpAddress, addr);
608 // fill Calling-Station-Id and Called-Station-Id fields
609 PString stationId = GetCallingStationId(arqPdu, authData);
611 pdu->AppendAttr(RadiusAttr::CallingStationId, stationId);
614 const PString dialedNumber = GetDialedNumber(arqPdu, authData);
615 const PString calledStationId = GetCalledStationId(arqPdu, authData);
617 stationId = m_useDialedNumber ? dialedNumber : calledStationId;
618 if (stationId.IsEmpty()) {
620 PTRACE(2, "RADAUTH\t" << GetName() << " ARQ auth failed: "
621 "no suitable alias for Calling-Station-Id has been found"
623 authData.m_rejectReason = H225_AdmissionRejectReason::e_securityDenial;
626 pdu->AppendAttr(RadiusAttr::CalledStationId, stationId);
629 if (m_appendCiscoAttributes) {
630 pdu->AppendCiscoAttr(RadiusAttr::CiscoVSA_h323_conf_id,
631 GetGUIDString(arq.m_conferenceID)
633 if (arq.m_answerCall)
634 pdu->AppendAttr(m_attrH323CallOriginAnswer);
636 pdu->AppendAttr(m_attrH323CallOriginOriginate);
637 pdu->AppendAttr(m_attrH323CallType);
638 pdu->AppendAttr(m_attrH323GwId);
641 if (!OnSendPDU(*pdu, arqPdu, authData)) {
645 // send the request and wait for a response
646 RadiusPDU* response = NULL;
647 bool result = m_radiusClient->MakeRequest(*pdu, response) && response;
652 PTRACE(2, "RADAUTH\t" << GetName() << " ARQ auth failed: "
653 " could not receive or decode response from RADIUS"
657 authData.m_rejectReason = H225_AdmissionRejectReason::e_undefinedReason;
658 return GetDefaultStatus();
662 result = (response->GetCode() == RadiusPDU::AccessAccept);
665 const RadiusAttr* attr;
667 // check for Class attribute
669 attr = response->FindAttr(RadiusAttr::AttrTypeClass);
671 PBYTEArray classData;
672 if (attr->GetValue(classData))
673 authData.m_radiusClass = classData;
677 // test for h323-return-code attribute (has to be 0 if accept)
679 attr = response->FindVsaAttr(RadiusAttr::CiscoVendorId,
680 RadiusAttr::CiscoVSA_h323_return_code
683 value = attr->AsCiscoString();
684 if (value.GetLength() > 0
685 && strspn((const char*)value, "0123456789") == (size_t)value.GetLength()) {
686 const unsigned retcode = value.AsUnsigned();
688 PTRACE(3, "RADAUTH\t" << GetName() << " ARQ check failed: "
689 "return code " << retcode
694 PTRACE(2, "RADAUTH\t" << GetName() << " ARQ check failed: "
695 "invalid h323-return-code attribute '" << value << '\''
701 // check for h323-credit-time attribute (call duration limit)
703 attr = response->FindVsaAttr(RadiusAttr::CiscoVendorId,
704 RadiusAttr::CiscoVSA_h323_credit_time
707 value = attr->AsCiscoString();
708 if (value.GetLength() > 0
709 && strspn((const char*)value,"0123456789") == (size_t)value.GetLength()) {
710 authData.m_callDurationLimit = value.AsInteger();
711 PTRACE(5, "RADAUTH\t" << GetName() << " ARQ check set duration "
712 "limit: " << authData.m_callDurationLimit
714 if (authData.m_callDurationLimit == 0)
717 PTRACE(2, "RADAUTH\t" << GetName() << " ARQ check failed: "
718 "invalid h323-credit-time attribute '" << value << '\''
724 // check for Session-Timeout attribute (alternate call duration limit)
726 const RadiusAttr* const attr = response->FindAttr(RadiusAttr::SessionTimeout);
728 const long sessionTimeout = attr->AsInteger();
729 if (authData.m_callDurationLimit < 0
730 || authData.m_callDurationLimit > sessionTimeout) {
731 authData.m_callDurationLimit = sessionTimeout;
732 PTRACE(5, "RADAUTH\t" << GetName() << " ARQ check set "
733 "duration limit set " << authData.m_callDurationLimit
736 if (authData.m_callDurationLimit == 0)
741 // check for h323-billing-model
743 attr = response->FindVsaAttr(RadiusAttr::CiscoVendorId,
744 RadiusAttr::CiscoVSA_h323_billing_model
747 value = attr->AsCiscoString();
748 if (value.GetLength() > 0
749 && strspn((const char*)value,"0123456789") == (size_t)value.GetLength()) {
750 const int intVal = value.AsInteger();
752 authData.m_billingMode = H225_CallCreditServiceControl_billingMode::e_credit;
753 else if (intVal == 1 || intVal == 2)
754 authData.m_billingMode = H225_CallCreditServiceControl_billingMode::e_debit;
756 PTRACE(3, "RADAUTH\t" << GetName() << " invalid h323-billing-model "
757 "attribute '" << value << '\''
762 // check for h323-credit-amount
764 attr = response->FindVsaAttr(RadiusAttr::CiscoVendorId,
765 RadiusAttr::CiscoVSA_h323_credit_amount
768 value = attr->AsCiscoString();
769 if (value.GetLength() > 0
770 && strspn((const char*)value,"0123456789.") == (size_t)value.GetLength()) {
771 if (value.Find('.') == P_MAX_INDEX) {
772 PTRACE(3, "RADAUTH\t" << GetName() << " h323-credit-amount "
773 "without a decimal dot is ambiguous '" << value << '\''
775 authData.m_amountString = psprintf(PString("%d.%d"),
776 value.AsInteger() / 100, value.AsInteger() % 100
779 authData.m_amountString = value;
781 attr = response->FindVsaAttr(RadiusAttr::CiscoVendorId,
782 RadiusAttr::CiscoVSA_h323_currency
785 authData.m_amountString += attr->AsCiscoString();
787 PTRACE(3, "RADAUTH\t" << GetName() << " invalid h323-credit-amount "
788 "attribute '" << value << '\''
794 // check for h323-redirect-number
796 attr = response->FindVsaAttr(RadiusAttr::CiscoVendorId,
797 RadiusAttr::CiscoVSA_h323_redirect_number
800 value = attr->AsCiscoString();
802 authData.SetRouteToAlias(value);
803 PTRACE(5, "RADAUTH\t" << GetName() << " ARQ check redirect "
804 "to the number " << value
810 // check for h323-redirect-ip-address
812 attr = response->FindVsaAttr(RadiusAttr::CiscoVendorId,
813 RadiusAttr::CiscoVSA_h323_redirect_ip_address
816 value = attr->AsCiscoString();
818 PStringArray tokens(value.Tokenise("; \t", FALSE));
819 for (PINDEX i = 0; i < tokens.GetSize(); ++i) {
820 PIPSocket::Address addr;
823 if (GetTransportAddress(tokens[i], GK_DEF_ENDPOINT_SIGNAL_PORT, addr, port)
824 && addr.IsValid() && port != 0) {
825 Route route("RADIUS", addr, port);
826 route.m_destEndpoint = RegistrationTable::Instance()->FindBySignalAdr(
827 SocketToH225TransportAddr(addr, port)
829 authData.m_destinationRoutes.push_back(route);
830 PTRACE(5, "RADAUTH\t" << GetName() << " ARQ check redirect "
831 "to the address " << route.AsString()
840 result = OnReceivedPDU(*response, arqPdu, authData);
842 authData.m_rejectReason = H225_AdmissionRejectReason::e_securityDenial;
846 return result ? e_ok : e_fail;
849 int RadAuthBase::Check(
851 SetupAuthData &authData
854 // build RADIUS Access-Request packet
855 RadiusPDU* const pdu = new RadiusPDU(RadiusPDU::AccessRequest);
856 H225_Setup_UUIE& setupBody = setup.GetUUIEBody();
857 const bool hasCall = authData.m_call.operator->() != NULL;
858 PIPSocket::Address addr;
859 endptr callingEP, calledEP;
862 callingEP = authData.m_call->GetCallingParty();
863 if (!callingEP && setupBody.HasOptionalField(H225_Setup_UUIE::e_endpointIdentifier))
864 callingEP = RegistrationTable::Instance()->FindByEndpointId(
865 setupBody.m_endpointIdentifier
868 // Append User-Name and a password related attributes
869 // (User-Password or Chap-Password and Chap-Timestamp)
871 const int status = AppendUsernameAndPassword(*pdu, setup, callingEP,
874 if (status != e_ok) {
879 // Gk acts as NAS, so include NAS IP
880 pdu->AppendAttr(RadiusAttr::NasIpAddress, m_nasIpAddress);
881 // NAS-Identifier as Gk name
882 pdu->AppendAttr(m_attrNasIdentifier);
883 // NAS-Port-Type as Virtual, since Gk does
884 // not care about physical ports concept
885 pdu->AppendAttr(RadiusAttr::NasPortType, RadiusAttr::NasPort_Virtual);
886 // Service-Type is Login-User if originating the call
887 // and Call Check if answering the call
888 pdu->AppendAttr(RadiusAttr::ServiceType, RadiusAttr::ST_Login);
890 // append Frame-IP-Address
891 bool ipFound = false;
894 if (hasCall && authData.m_call->GetSrcSignalAddr(addr, dummyPort)
898 && GetIPFromTransportAddr(callingEP->GetCallSignalAddress(), addr)
901 else if (setupBody.HasOptionalField(H225_Setup_UUIE::e_sourceCallSignalAddress)
902 && GetIPFromTransportAddr(setupBody.m_sourceCallSignalAddress, addr)
906 setup.GetPeerAddr(addr);
907 ipFound = addr.IsValid();
910 PTRACE(2, "RADAUTH\t" << GetName() << " Setup auth failed: "
911 "could not setup Framed-IP-Address"
914 authData.m_rejectCause = Q931::CallRejected;
917 pdu->AppendAttr(RadiusAttr::FramedIpAddress, addr);
919 // fill Calling-Station-Id and Called-Station-Id fields
920 PString stationId = GetCallingStationId(setup, authData);
922 pdu->AppendAttr(RadiusAttr::CallingStationId, stationId);
925 const PString calledStationId = GetCalledStationId(setup, authData);
926 const PString dialedNumber = GetDialedNumber(setup, authData);
928 stationId = m_useDialedNumber ? dialedNumber : calledStationId;
929 if (stationId.IsEmpty()) {
931 PTRACE(2, "RADAUTH\t" << GetName() << " Setup check failed: "
932 "no called station id found"
934 authData.m_rejectReason = H225_ReleaseCompleteReason::e_badFormatAddress;
937 pdu->AppendAttr(RadiusAttr::CalledStationId, stationId);
939 if (m_appendCiscoAttributes) {
940 pdu->AppendCiscoAttr(RadiusAttr::CiscoVSA_h323_conf_id,
941 GetGUIDString(setupBody.m_conferenceID)
943 pdu->AppendAttr(m_attrH323CallOriginOriginate);
944 pdu->AppendAttr(m_attrH323CallType);
945 pdu->AppendAttr(m_attrH323GwId);
948 if (!OnSendPDU(*pdu, setup, authData)) {
952 // send the request and wait for a response
953 RadiusPDU* response = NULL;
954 bool result = m_radiusClient->MakeRequest(*pdu, response) && response;
959 PTRACE(2, "RADAUTH\t" << GetName() << " Setup auth failed: "
960 " could not receive or decode response from RADIUS"
964 authData.m_rejectCause = Q931::TemporaryFailure;
965 return GetDefaultStatus();
969 result = (response->GetCode() == RadiusPDU::AccessAccept);
972 const RadiusAttr* attr;
974 // check for Class attribute
976 attr = response->FindAttr(RadiusAttr::AttrTypeClass);
978 PBYTEArray classData;
979 if (attr->GetValue(classData))
980 authData.m_radiusClass = classData;
984 // test for h323-return-code attribute (has to be 0 if accept)
986 attr = response->FindVsaAttr(RadiusAttr::CiscoVendorId,
987 RadiusAttr::CiscoVSA_h323_return_code
990 value = attr->AsCiscoString();
991 if (value.GetLength() > 0
992 && strspn((const char*)value, "0123456789") == (size_t)value.GetLength()) {
993 const unsigned retcode = value.AsUnsigned();
995 PTRACE(5, "RADAUTH\t" << GetName() << " Setup check failed: "
996 "return code " << retcode
1001 PTRACE(2, "RADAUTH\t" << GetName() << " Setup check failed: "
1002 "invalid h323-return-code attribute '" << value << '\''
1008 // check for h323-credit-time attribute (call duration limit)
1010 attr = response->FindVsaAttr(RadiusAttr::CiscoVendorId,
1011 RadiusAttr::CiscoVSA_h323_credit_time
1014 value = attr->AsCiscoString();
1015 if (value.GetLength() > 0
1016 && strspn((const char*)value,"0123456789") == (size_t)value.GetLength() ) {
1017 authData.m_callDurationLimit = value.AsInteger();
1018 PTRACE(5, "RADAUTH\t" << GetName() << " Setup check set duration "
1019 "limit: " << authData.m_callDurationLimit
1021 if (authData.m_callDurationLimit == 0)
1024 PTRACE(2, "RADAUTH\t" << GetName() << " Setup check failed: "
1025 "invalid h323-credit-time attribute '" << value << '\''
1031 // check for Session-Timeout attribute (alternate call duration limit)
1033 const RadiusAttr* const attr = response->FindAttr(RadiusAttr::SessionTimeout);
1035 const long sessionTimeout = attr->AsInteger();
1036 if (authData.m_callDurationLimit < 0
1037 || authData.m_callDurationLimit > sessionTimeout) {
1038 authData.m_callDurationLimit = sessionTimeout;
1039 PTRACE(5, "RADAUTH\t" << GetName() << " Setup check "
1040 "set duration limit: " << authData.m_callDurationLimit
1043 if (authData.m_callDurationLimit == 0)
1048 // check for h323-redirect-number
1050 attr = response->FindVsaAttr(RadiusAttr::CiscoVendorId,
1051 RadiusAttr::CiscoVSA_h323_redirect_number
1054 value = attr->AsCiscoString();
1056 authData.SetRouteToAlias(value);
1057 PTRACE(5, "RADAUTH\t" << GetName() << " ARQ check redirect "
1058 "to the number " << value
1064 // check for h323-redirect-ip-address
1066 attr = response->FindVsaAttr(RadiusAttr::CiscoVendorId,
1067 RadiusAttr::CiscoVSA_h323_redirect_ip_address
1070 value = attr->AsCiscoString();
1072 PStringArray tokens(value.Tokenise("; \t", FALSE));
1073 for (PINDEX i = 0; i < tokens.GetSize(); ++i) {
1074 PIPSocket::Address addr;
1077 if (GetTransportAddress(tokens[i], GK_DEF_ENDPOINT_SIGNAL_PORT, addr, port)
1078 && addr.IsValid() && port != 0) {
1079 Route route("RADIUS", addr, port);
1080 route.m_destEndpoint = RegistrationTable::Instance()->FindBySignalAdr(
1081 SocketToH225TransportAddr(addr, port)
1083 authData.m_destinationRoutes.push_back(route);
1084 PTRACE(5, "RADAUTH\t" << GetName() << " Setup check redirect "
1085 "to the address " << route.AsString()
1094 result = OnReceivedPDU(*response, setup, authData);
1096 authData.m_rejectCause = Q931::CallRejected;
1100 return result ? e_ok : e_fail;
1103 bool RadAuthBase::OnSendPDU(
1105 RasPDU<H225_RegistrationRequest>& /*rrqPdu*/,
1106 RRQAuthData& /*authData*/
1112 bool RadAuthBase::OnSendPDU(
1114 RasPDU<H225_AdmissionRequest>& /*arqPdu*/,
1115 ARQAuthData& /*authData*/
1121 bool RadAuthBase::OnSendPDU(
1123 SetupMsg &/*setup*/,
1124 SetupAuthData& /*authData*/
1130 bool RadAuthBase::OnReceivedPDU(
1132 RasPDU<H225_RegistrationRequest>& /*rrqPdu*/,
1133 RRQAuthData& /*authData*/
1139 bool RadAuthBase::OnReceivedPDU(
1141 RasPDU<H225_AdmissionRequest>& /*arqPdu*/,
1142 ARQAuthData& /*authData*/
1148 bool RadAuthBase::OnReceivedPDU(
1150 SetupMsg &/*setup*/,
1151 SetupAuthData& /*authData*/
1157 int RadAuthBase::AppendUsernameAndPassword(
1159 RasPDU<H225_RegistrationRequest>& /*rrqPdu*/,
1160 RRQAuthData& /*authData*/,
1161 PString* /*username*/
1164 return GetDefaultStatus();
1167 int RadAuthBase::AppendUsernameAndPassword(
1169 RasPDU<H225_AdmissionRequest>& /*arqPdu*/,
1170 ARQAuthData& /*authData*/,
1171 PString* /*username*/
1174 return GetDefaultStatus();
1177 int RadAuthBase::AppendUsernameAndPassword(
1179 SetupMsg &/*setup*/,
1180 endptr &/*callingEP*/,
1181 SetupAuthData &/*authData*/,
1182 PString * /*username*/
1185 return GetDefaultStatus();
1189 const char* authName
1192 RadAuthBase(authName, RadAuthConfigSectionName)
1194 // setup H.235 algorithm and method types used
1195 // by this authenticator - this will make sure
1196 // GCF H.235 alogirthm selection will not skip
1197 // information required by this authenticator
1198 H235AuthCAT* authenticator = new H235AuthCAT;
1199 authenticator->SetLocalId("dummy");
1200 authenticator->SetRemoteId("dummy");
1201 authenticator->SetPassword("dummy");
1202 AppendH235Authenticator(authenticator);
1209 int RadAuth::CheckTokens(
1211 const H225_ArrayOf_ClearToken& tokens,
1212 const H225_ArrayOf_AliasAddress* aliases,
1216 // scan ClearTokens and find CATs
1217 for (PINDEX i = 0; i < tokens.GetSize(); i++) {
1218 const H235_ClearToken& token = tokens[i];
1221 if (token.m_tokenOID != OID_CAT)
1224 // these field are required for CAT
1225 if (!(token.HasOptionalField(H235_ClearToken::e_generalID)
1226 && token.HasOptionalField(H235_ClearToken::e_random)
1227 && token.HasOptionalField(H235_ClearToken::e_timeStamp)
1228 && token.HasOptionalField(H235_ClearToken::e_challenge)))
1230 PTRACE(3, "RADAUTH\t" << GetName() << " auth failed: "
1231 "CAT without all required fields"
1236 // generalID should be present in the list of terminal aliases
1237 const PString id = token.m_generalID;
1238 if (aliases && FindAlias(*aliases, id) == P_MAX_INDEX) {
1239 PTRACE(3, "RADAUTH\t" << GetName() << " auth failed: "
1240 "CAT m_generalID is not a valid alias"
1245 // CAT pseudo-random has to be one byte only
1246 const int randomInt = token.m_random;
1247 if (randomInt < -127 || randomInt > 255) {
1248 PTRACE(3, "RADAUTH\t" << GetName() << " auth failed: "
1249 "CAT m_random out of range"
1254 // CAT challenge has to be 16 bytes
1255 if (token.m_challenge.GetValue().GetSize() < 16) {
1256 PTRACE(3, "RADAUTH\t" << GetName() << " auth failed: "
1257 "m_challenge less than 16 bytes"
1263 pdu.AppendAttr(RadiusAttr::UserName, id);
1264 if (username != NULL)
1265 *username = (const char*)id;
1267 // build CHAP-Password
1268 char password[17] = { (BYTE)randomInt };
1269 memcpy(password + 1, (const BYTE*)(token.m_challenge), 16);
1271 pdu.AppendAttr(RadiusAttr::ChapPassword, password, sizeof(password));
1272 pdu.AppendAttr(RadiusAttr::ChapChallenge, (int)(DWORD)token.m_timeStamp);
1276 PTRACE(3, "RADAUTH\t" << GetName() << " auth failed: no CAT token found");
1277 return GetDefaultStatus();
1280 int RadAuth::AppendUsernameAndPassword(
1282 RasPDU<H225_RegistrationRequest>& rrqPdu,
1283 RRQAuthData& authData,
1287 H225_RegistrationRequest& rrq = (H225_RegistrationRequest&)rrqPdu;
1289 // RRQ has to carry at least one terminalAlias
1290 if (!rrq.HasOptionalField(H225_RegistrationRequest::e_terminalAlias)) {
1291 PTRACE(3, "RADAUTH\t" << GetName() << " RRQ auth failed: "
1292 "no m_terminalAlias field"
1294 authData.m_rejectReason = H225_RegistrationRejectReason::e_securityDenial;
1295 return GetDefaultStatus();
1298 // check for ClearTokens (CAT uses ClearTokens)
1299 if (!rrq.HasOptionalField(H225_RegistrationRequest::e_tokens)) {
1300 PTRACE(3, "RADAUTH\t" << GetName() << " RRQ auth failed: "
1303 authData.m_rejectReason = H225_RegistrationRejectReason::e_securityDenial;
1304 return GetDefaultStatus();
1307 const int result = CheckTokens(pdu, rrq.m_tokens, &rrq.m_terminalAlias, username);
1309 authData.m_rejectReason = H225_RegistrationRejectReason::e_securityDenial;
1313 int RadAuth::AppendUsernameAndPassword(
1315 RasPDU<H225_AdmissionRequest>& arqPdu,
1316 ARQAuthData& authData,
1320 H225_AdmissionRequest& arq = (H225_AdmissionRequest&)arqPdu;
1322 // check for ClearTokens
1323 if (!arq.HasOptionalField(H225_AdmissionRequest::e_tokens)) {
1324 PTRACE(3, "RADAUTH\t" << GetName() << " ARQ auth failed: "
1327 authData.m_rejectReason = H225_AdmissionRejectReason::e_securityDenial;
1328 return GetDefaultStatus();
1331 const int result = CheckTokens(pdu, arq.m_tokens, NULL, username);
1333 authData.m_rejectReason = H225_AdmissionRejectReason::e_securityDenial;
1337 int RadAuth::AppendUsernameAndPassword(
1340 endptr& /*callingEP*/,
1341 SetupAuthData& authData,
1345 H225_Setup_UUIE &setupBody = setup.GetUUIEBody();
1346 // check for ClearTokens (CAT uses ClearTokens)
1347 if (!setupBody.HasOptionalField(H225_Setup_UUIE::e_tokens)) {
1348 PTRACE(3, "RADAUTH\t" << GetName() << " Setup auth failed: no tokens");
1349 authData.m_rejectReason = H225_ReleaseCompleteReason::e_securityDenied;
1350 return GetDefaultStatus();
1353 const int result = CheckTokens(pdu, setupBody.m_tokens, NULL, username);
1355 authData.m_rejectCause = Q931::CallRejected;
1359 RadAliasAuth::RadAliasAuth(
1360 const char* authName
1363 RadAuthBase(authName, RadAliasAuthConfigSectionName)
1365 m_fixedUsername = GetConfig()->GetString(
1366 RadAliasAuthConfigSectionName, "FixedUsername", ""
1368 m_fixedPassword = Toolkit::Instance()->ReadPassword(
1369 RadAliasAuthConfigSectionName, "FixedPassword"
1373 RadAliasAuth::~RadAliasAuth()
1377 int RadAliasAuth::AppendUsernameAndPassword(
1379 RasPDU<H225_RegistrationRequest>& rrqPdu,
1380 RRQAuthData& authData,
1384 const PString id = GetUsername(rrqPdu);
1385 if (id.IsEmpty() && m_fixedUsername.IsEmpty()) {
1386 PTRACE(3, "RADAUTH\t" << GetName() << " RRQ check failed: "
1387 "neither FixedUsername nor alias inside RRQ were found"
1389 authData.m_rejectReason = H225_RegistrationRejectReason::e_securityDenial;
1390 return GetDefaultStatus();
1394 pdu.AppendAttr(RadiusAttr::UserName,
1395 m_fixedUsername.IsEmpty() ? id : m_fixedUsername
1398 if (username != NULL)
1399 *username = (const char*)id;
1401 // append User-Password
1402 if (!m_fixedPassword)
1403 pdu.AppendAttr(RadiusAttr::UserPassword, m_fixedPassword);
1405 pdu.AppendAttr(RadiusAttr::UserPassword,
1406 m_fixedUsername.IsEmpty() ? id : m_fixedUsername
1412 int RadAliasAuth::AppendUsernameAndPassword(
1414 RasPDU<H225_AdmissionRequest>& arqPdu,
1415 ARQAuthData& authData,
1419 const PString id = GetUsername(arqPdu, authData);
1420 if (id.IsEmpty() && m_fixedUsername.IsEmpty()) {
1421 PTRACE(3, "RADAUTH\t" << GetName() << " ARQ check failed: "
1422 "neither FixedUsername nor alias inside ARQ were found"
1424 authData.m_rejectReason = H225_AdmissionRejectReason::e_securityDenial;
1425 return GetDefaultStatus();
1429 pdu.AppendAttr(RadiusAttr::UserName,
1430 m_fixedUsername.IsEmpty() ? id : m_fixedUsername
1433 if (username != NULL)
1434 *username = (const char*)id;
1436 if (!m_fixedPassword)
1437 pdu.AppendAttr(RadiusAttr::UserPassword, m_fixedPassword);
1439 pdu.AppendAttr(RadiusAttr::UserPassword,
1440 m_fixedUsername.IsEmpty() ? id : m_fixedUsername
1446 int RadAliasAuth::AppendUsernameAndPassword(
1449 endptr& /*callingEP*/,
1450 SetupAuthData& authData,
1454 const PString id = GetUsername(setup, authData);
1455 if (id.IsEmpty() && m_fixedUsername.IsEmpty()) {
1456 PTRACE(3, "RADAUTH\t" << GetName() << " Setup check failed: "
1457 "neither FixedUsername nor alias inside Setup were found"
1459 authData.m_rejectReason = H225_ReleaseCompleteReason::e_badFormatAddress;
1460 return GetDefaultStatus();
1464 pdu.AppendAttr(RadiusAttr::UserName,
1465 m_fixedUsername.IsEmpty() ? id : m_fixedUsername
1468 if (username != NULL)
1469 *username = (const char*)id;
1471 if (!m_fixedPassword)
1472 pdu.AppendAttr(RadiusAttr::UserPassword, m_fixedPassword);
1474 pdu.AppendAttr(RadiusAttr::UserPassword,
1475 m_fixedUsername.IsEmpty() ? id : m_fixedUsername
1482 GkAuthCreator<RadAuth> RadAuthCreator("RadAuth");
1483 GkAuthCreator<RadAliasAuth> RadAliasAuthCreator("RadAliasAuth");
1486 #endif /* HAS_RADIUS */