OSDN Git Service

Fix no pic
[uclinux-h8/uClinux-dist.git] / user / gnugk / radauth.cxx
1 /*
2  * radauth.cxx
3  *
4  * RADIUS protocol authenticator module for GNU Gatekeeper. 
5  * Please see docs/radauth.txt for more details.
6  *
7  * Copyright (c) 2003, Quarcom FHU, Michal Zygmuntowicz
8  *
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.
13  *
14  * $Log: radauth.cxx,v $
15  * Revision 1.37  2006/12/06 16:34:04  zvision
16  * Handle RADIUS Class attribute correctly
17  *
18  * Revision 1.36  2006/07/06 15:25:13  willamowius
19  * set all deleted pointers to NULL (most probably more than needed)
20  *
21  * Revision 1.35  2006/06/08 08:58:48  willamowius
22  * gcc 4.1 compile fixes
23  *
24  * Revision 1.34  2006/04/14 13:56:19  willamowius
25  * call failover code merged
26  *
27  * Revision 1.2  2005/12/05 13:29:02  zvision
28  * Accept multiple routes from RADIUS/SQL auth modules
29  *
30  * Revision 1.1.1.1  2005/11/21 20:19:58  willamowius
31  *
32  *
33  * Revision 1.4  2005/11/15 19:52:56  jan
34  * Michal v1 (works, but on in routed, not proxy mode)
35  *
36  * Revision 1.33  2005/04/24 16:39:44  zvision
37  * MSVC6.0 compatibility fixed
38  *
39  * Revision 1.32  2005/02/10 23:26:39  zvision
40  * Accounting updates/call disconnect handling does not lock the whole call table
41  *
42  * Revision 1.31  2005/02/01 14:28:10  zvision
43  * Parts of signaling code rewritten
44  *
45  * Revision 1.30  2005/01/28 11:19:42  zvision
46  * All passwords in the config can be stored in an encrypted form
47  *
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
51  *
52  * Revision 1.28  2005/01/25 18:59:08  zvision
53  * Aliases handling fixed, alias type is not appended anymore
54  *
55  * Revision 1.27  2005/01/25 00:37:35  zvision
56  * Handle aliases of type partyNumber properly
57  *
58  * Revision 1.26  2005/01/10 22:49:29  willamowius
59  * typo
60  *
61  * Revision 1.25  2004/12/08 13:02:56  zvision
62  * Better Calling/Called-Station-Id handling
63  *
64  * Revision 1.24  2004/11/15 23:57:42  zvision
65  * Ability to choose between the original and the rewritten dialed number
66  *
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
69  *
70  * Revision 1.22  2004/08/09 21:52:23  zvision
71  * RADIUS based call routing
72  *
73  * Revision 1.21  2004/07/26 12:19:41  zvision
74  * New faster Radius implementation, thanks to Pavel Pavlov for ideas!
75  *
76  * Revision 1.20.2.2  2004/07/07 23:11:07  zvision
77  * Faster and more elegant handling of Cisco VSA
78  *
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!
81  *
82  * Revision 1.20  2004/07/06 23:46:20  zvision
83  * gcc 2.95.x compilation errors fixed
84  *
85  * Revision 1.19  2004/07/05 16:39:45  zvision
86  * Support for CallCreditServiceControl
87  *
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.
91  *
92  * Revision 1.17  2004/06/17 10:47:13  zvision
93  * New h323-ivr-out=h323-call-id accounting attribute
94  *
95  * Revision 1.16  2004/06/17 10:03:17  zvision
96  * Better Framed-IP-Address handling in RadAliasAuth Setup check
97  *
98  * Revision 1.15  2004/06/16 23:46:47  zvision
99  * RadAliasAuth will work even when Setup-UUIE does not contain sourceAddress
100  *
101  * Revision 1.14  2004/05/22 12:25:17  zvision
102  * Check aliases only when authenticating RRQ message
103  *
104  * Revision 1.13  2004/04/17 11:43:43  zvision
105  * Auth/acct API changes.
106  * Header file usage more consistent.
107  *
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
110  *
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.
114  *
115  * Revision 1.10  2003/11/14 00:27:30  zvision
116  * Q.931/H.225 Setup authentication added
117  *
118  * Revision 1.9  2003/10/31 00:01:28  zvision
119  * Improved accounting modules stacking control, optimized radacct/radauth a bit
120  *
121  * Revision 1.8  2003/10/21 15:55:27  zvision
122  * Fixed compiler warnings for gcc < 3
123  *
124  * Revision 1.7  2003/10/15 10:16:57  zvision
125  * Fixed VC6 compiler warnings. Thanks to Hu Yuxin.
126  *
127  * Revision 1.6  2003/10/08 12:40:48  zvision
128  * Realtime accounting updates added
129  *
130  * Revision 1.5  2003/09/28 16:24:31  zvision
131  * Introduced call duration limit feature for registered endpoints (ARQ)
132  *
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.
136  *
137  * Revision 1.3  2003/08/20 14:46:19  zvision
138  * Avoid PString reference copying. Small code improvements.
139  *
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.
144  *
145  * Revision 1.1.2.17  2003/07/31 22:59:24  zvision
146  * Fixed IP address retrieval for unregistered endpoints
147  *
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
150  *
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.
153  *
154  * Revision 1.1.2.14  2003/07/16 22:13:21  zvision
155  * Fixed Radius attributes for answer call ARQs.
156  *
157  * Revision 1.1.2.13  2003/07/07 14:28:30  zvision
158  * Added missing NAS-Identifier attribute in RadAliasAuth. Thanks Julius Stavaris.
159  *
160  * Revision 1.1.2.12  2003/07/07 12:02:55  zvision
161  * Improved H.235 handling.
162  *
163  * Revision 1.1.2.11  2003/06/19 15:33:29  zvision
164  * Removed static modifier from GetConferenceIDString function.
165  *
166  * Revision 1.1.2.10  2003/06/11 13:06:57  zvision
167  * Added gk_const.h include directive (OPENH323_NEWVERSION macro definition)
168  *
169  * Revision 1.1.2.9  2003/06/11 12:14:35  zvision
170  * Cosmetic changes
171  *
172  * Revision 1.1.2.8  2003/06/05 10:03:04  zvision
173  * Small fix to h323-gw-id attribute.
174  *
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)
177  *
178  * Revision 1.1.2.6  2003/05/28 13:25:19  zvision
179  * Added alias based authentication (RadAliasAuth)
180  *
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)
183  *
184  * Revision 1.1.2.4  2003/05/26 23:09:59  zvision
185  * Added new OnSend and OnReceive hooks. LocalInterface config parameter introduced.
186  *
187  * Revision 1.1.2.3  2003/05/13 17:49:49  zvision
188  * Removed acctPort. New includeFramedIP feature. Better tracing. Bug-fixes
189  *
190  * Revision 1.1.2.2  2003/04/29 14:56:27  zvision
191  * Added H.235 capability matching
192  *
193  * Revision 1.1.2.1  2003/04/23 20:15:37  zvision
194  * Initial revision
195  *
196  */
197  
198 #if HAS_RADIUS
199
200 #if defined(_WIN32) && (_MSC_VER <= 1200)
201 #pragma warning(disable:4786) // warning about too long debug symbol off
202 #endif
203
204 #include <vector>
205 #include <ptlib.h>
206 #include <h225ras.h>
207 #include <h323pdu.h>
208 #include <h235.h>
209 #include <h235auth.h>
210 #include "gk_const.h"
211 #include "h323util.h"
212 #include "stl_supp.h"
213 #include "Toolkit.h"
214 #include "RasTbl.h"
215 #include "RasPDU.h"
216 #include "Routing.h"
217 #include "sigmsg.h"
218 #include "radproto.h"
219 #include "gkauth.h"
220 #include "radauth.h"
221
222 using std::vector;
223 using Routing::Route;
224
225 namespace {
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";
230 }
231
232 // OID for CAT (Cisco Access Token) algorithm
233 PString RadAuth::OID_CAT("1.2.840.113548.10.1.2.1");
234
235
236 RadAuthBase::RadAuthBase( 
237         const char* authName,
238         const char* configSectionName,
239         unsigned supportedRasChecks,
240         unsigned supportedMiscChecks
241         )
242         :
243         GkAuthenticator(authName, supportedRasChecks, supportedMiscChecks),
244         m_radiusClient(NULL),
245         m_attrH323CallType(RadiusAttr::CiscoVSA_h323_call_type, false, 
246                 PString("VoIP")),
247         m_attrH323CallOriginOriginate(RadiusAttr::CiscoVSA_h323_call_origin, false,
248                 PString("originate")),
249         m_attrH323CallOriginAnswer(RadiusAttr::CiscoVSA_h323_call_origin, false,
250                 PString("answer"))
251 {
252         // read settings from the config
253         m_appendCiscoAttributes = Toolkit::AsBool(GetConfig()->GetString(
254                 configSectionName,"AppendCiscoAttributes", "1"
255                 ));
256         m_includeTerminalAliases = Toolkit::AsBool(GetConfig()->GetString(
257                 configSectionName, "IncludeTerminalAliases", "1"
258                 ));
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();
268                 else
269                         PTRACE(1, "RADAUTH\t" << GetName() << " cannot determine "
270                                 " NAS IP address"
271                                 );
272         }
273         m_useDialedNumber = Toolkit::AsBool(GetConfig()->GetString(
274                 configSectionName, "UseDialedNumber", "0"
275                 ));
276         m_attrH323GwId = RadiusAttr(RadiusAttr::CiscoVSA_h323_gw_id, false, m_nasIdentifier);
277         m_attrNasIdentifier = RadiusAttr(RadiusAttr::NasIdentifier, m_nasIdentifier);
278 }
279
280 RadAuthBase::~RadAuthBase()
281 {
282         delete m_radiusClient;
283 }
284
285 int RadAuthBase::Check(
286         /// RRQ RAS message to be authenticated
287         RasPDU<H225_RegistrationRequest>& rrqPdu, 
288         /// authorization data (reject reason, ...)
289         RRQAuthData& authData
290         )
291 {
292         H225_RegistrationRequest& rrq = (H225_RegistrationRequest&)rrqPdu;
293         
294         // build RADIUS Access-Request
295         RadiusPDU* const pdu = new RadiusPDU(RadiusPDU::AccessRequest);
296
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) {
301                 delete pdu;
302                 return status;
303         }
304         
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);
314
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)
320                         && addr.IsValid())
321                         ipFound = true;
322         } else if (rrq.m_rasAddress.GetSize() > 0) {
323                 if (GetIPFromTransportAddr(rrq.m_rasAddress[0], addr) 
324                         && addr.IsValid())
325                         ipFound = true;
326         }
327         if (!ipFound) {
328                 PTRACE(2, "RADAUTH\t" << GetName() << " RRQ auth failed: "
329                         "could not determine Framed-IP-Address"
330                         );
331                 authData.m_rejectReason = H225_RegistrationRejectReason::e_invalidCallSignalAddress;
332                 delete pdu;
333                 return e_fail;
334         } else
335                 pdu->AppendAttr(RadiusAttr::FramedIpAddress, addr);
336                                 
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++) {
341                         if(i > 0)
342                                 aliasList += ",";
343                         aliasList += AsString(rrq.m_terminalAlias[i], FALSE);
344                 }
345                 // Cisco-AV-Pair
346                 pdu->AppendCiscoAttr(RadiusAttr::CiscoVSA_AV_Pair,
347                         PString("h323-ivr-out=") + aliasList + ";",
348                         true
349                         );
350         }
351         
352         if (!OnSendPDU(*pdu, rrqPdu, authData)) {
353                 delete pdu;
354                 return e_fail;
355         }
356         // send request and wait for response
357         RadiusPDU* response = NULL;
358         bool result = m_radiusClient->MakeRequest(*pdu, response) && response;
359                 
360         delete pdu;
361                         
362         if (!result) {
363                 PTRACE(2, "RADAUTH\t" << GetName() << " RRQ auth failed: "
364                         " could not receive or decode response from RADIUS"
365                         );
366                 delete response;
367                 response = NULL;
368                 authData.m_rejectReason = H225_RegistrationRejectReason::e_undefinedReason;
369                 return GetDefaultStatus();
370         }
371                                 
372         result = (response->GetCode() == RadiusPDU::AccessAccept);
373
374         PString value;
375         const RadiusAttr* attr;
376         
377         // test for h323-return-code attribute (has to be 0 if accept)
378         if (result) {
379                 attr = response->FindVsaAttr(RadiusAttr::CiscoVendorId, 
380                         RadiusAttr::CiscoVSA_h323_return_code
381                         );
382                 if (attr != NULL) {
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();
387                                 if (retcode != 0) {
388                                         PTRACE(3, "RADAUTH\t" << GetName() << " RRQ check failed: "
389                                                 "return code " << retcode
390                                                 );
391                                         result = false;
392                                 }
393                         } else {
394                                 PTRACE(2, "RADAUTH\t" << GetName() << " RRQ check failed: "
395                                         "invalid h323-return-code attribute '" << value << '\''
396                                         );
397                                 result = false;
398                         }
399                 }
400         }
401         
402         // check for h323-billing-model 
403         if (result) {
404                 attr = response->FindVsaAttr(RadiusAttr::CiscoVendorId, 
405                         RadiusAttr::CiscoVSA_h323_billing_model
406                         );
407                 if (attr != NULL) {
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();
412                                 if (intVal == 0)
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;
416                         } else {
417                                 PTRACE(3, "RADAUTH\t" << GetName() << " invalid h323-billing-model "
418                                         "attribute '" << value << '\''
419                                         );
420                         }
421                 }
422         }
423         
424         // check for h323-credit-amount
425         if (result) {
426                 attr = response->FindVsaAttr(RadiusAttr::CiscoVendorId, 
427                         RadiusAttr::CiscoVSA_h323_credit_amount
428                         );
429                 if (attr != NULL) {
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 << '\''
436                                                 );
437                                         authData.m_amountString = psprintf(PString("%d.%d"), 
438                                                 value.AsInteger() / 100, value.AsInteger() % 100
439                                                 );
440                                 } else
441                                         authData.m_amountString = value;
442                                 
443                                 attr = response->FindVsaAttr(RadiusAttr::CiscoVendorId, 
444                                         RadiusAttr::CiscoVSA_h323_currency
445                                         );
446                                 if (attr != NULL)
447                                         authData.m_amountString += attr->AsCiscoString(); 
448                         } else {
449                                 PTRACE(3, "RADAUTH\t" << GetName() << " invalid h323-credit-amount "
450                                         "attribute '" << value << '\''
451                                         );
452                         }
453                 }
454         }
455
456         // process h323-ivr-in=terminal-alias attribute
457         if (result) {
458                 attr = response->FindVsaAttr(RadiusAttr::CiscoVendorId, 
459                         RadiusAttr::CiscoVSA_AV_Pair
460                         );
461                 while (attr != NULL) {
462                         PINDEX index;
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)
470                                         );
471                                 PStringArray aliases = value.Tokenise(",");
472                                 if (aliases.GetSize() > 0 
473                                         && rrq.HasOptionalField(H225_RegistrationRequest::e_terminalAlias)) {
474                                         PINDEX i = 0;
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);
479                                                 else {
480                                                         i++;
481                                                         aliases.RemoveAt(j);
482                                                 }
483                                         }
484                                 }
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);
488                                         else {
489                                                 rrq.IncludeOptionalField(H225_RegistrationRequest::e_terminalAlias);
490                                                 rrq.m_terminalAlias.SetSize(1);
491                                         }
492                                         H323SetAliasAddress(aliases[i], rrq.m_terminalAlias[rrq.m_terminalAlias.GetSize()-1]);
493                                 }
494                                 break;
495                         }
496                         attr = response->FindVsaAttr(RadiusAttr::CiscoVendorId, 
497                                 RadiusAttr::CiscoVSA_AV_Pair, attr
498                                 );
499                 }
500         }
501
502         if (result)
503                 result = OnReceivedPDU(*response, rrqPdu, authData);
504         else
505                 authData.m_rejectReason = H225_RegistrationRejectReason::e_securityDenial;
506                                 
507         delete response;
508         response = NULL;
509         return result ? e_ok : e_fail;
510 }
511  
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
517         )
518 {
519         H225_AdmissionRequest& arq = (H225_AdmissionRequest&)arqPdu;
520
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;
526         
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;
531                 if (hasCall)
532                         callingEP = authData.m_call->GetCallingParty();
533         } else {
534                 callingEP = authData.m_requestingEP;
535                 if (hasCall)
536                         calledEP = authData.m_call->GetCalledParty();
537                 if (!calledEP && arq.HasOptionalField(H225_AdmissionRequest::e_destCallSignalAddress))
538                         calledEP = RegistrationTable::Instance()->FindBySignalAdr(arq.m_destCallSignalAddress);
539         }
540         
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) {
544                 delete pdu;
545                 PTRACE(3, "RADAUTH\t" << GetName() << " ARQ auth failed: "
546                         "requesting endpoint " << arq.m_endpointIdentifier 
547                         << " not registered"
548                         );
549                 authData.m_rejectReason = arq.m_answerCall
550                         ? H225_AdmissionRejectReason::e_calledPartyNotRegistered
551                         : H225_AdmissionRejectReason::e_callerNotRegistered;
552                 return e_fail;
553         }
554
555         // Append User-Name and a password related attributes
556         // (User-Password or Chap-Password and Chap-Timestamp)
557         PString username;                               
558         const int status = AppendUsernameAndPassword(*pdu, arqPdu, authData, &username);
559         if (status != e_ok) {
560                 delete pdu;
561                 return status;
562         }
563         
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
575                 );
576                                 
577         // append Frame-IP-Address                                      
578         bool ipFound = false;
579         if (arq.m_answerCall) {
580                 if (calledEP 
581                         && GetIPFromTransportAddr(calledEP->GetCallSignalAddress(), addr)
582                         && addr.IsValid())
583                         ipFound = true;
584                 else if (arq.HasOptionalField(arq.e_destCallSignalAddress) 
585                         && GetIPFromTransportAddr(arq.m_destCallSignalAddress, addr)
586                         && addr.IsValid())
587                         ipFound = true;
588         } else {
589                 if (callingEP 
590                         && GetIPFromTransportAddr(callingEP->GetCallSignalAddress(), addr)
591                         && addr.IsValid())
592                         ipFound = true;
593                 else if(arq.HasOptionalField(arq.e_srcCallSignalAddress)
594                         && GetIPFromTransportAddr(arq.m_srcCallSignalAddress, addr) 
595                         && addr.IsValid())
596                         ipFound = true;
597         }
598         if (!ipFound) {
599                 PTRACE(2, "RADAUTH\t" << GetName() << " ARQ auth failed: "
600                         "could not setup Framed-IP-Address"
601                         );
602                 authData.m_rejectReason = H225_AdmissionRejectReason::e_securityDenial;
603                 delete pdu;
604                 return e_fail;
605         } else
606                 pdu->AppendAttr(RadiusAttr::FramedIpAddress, addr);
607                                         
608         // fill Calling-Station-Id and Called-Station-Id fields
609         PString stationId = GetCallingStationId(arqPdu, authData);
610         if (!stationId) {
611                 pdu->AppendAttr(RadiusAttr::CallingStationId, stationId);
612         }
613
614         const PString dialedNumber = GetDialedNumber(arqPdu, authData);
615         const PString calledStationId = GetCalledStationId(arqPdu, authData);
616         
617         stationId = m_useDialedNumber ? dialedNumber : calledStationId;
618         if (stationId.IsEmpty()) {
619                 delete pdu;
620                 PTRACE(2, "RADAUTH\t" << GetName() << " ARQ auth failed: "
621                         "no suitable alias for Calling-Station-Id has been found"
622                         );
623                 authData.m_rejectReason = H225_AdmissionRejectReason::e_securityDenial;
624                 return e_fail;
625         } else
626                 pdu->AppendAttr(RadiusAttr::CalledStationId, stationId);
627         
628         
629         if (m_appendCiscoAttributes) {
630                 pdu->AppendCiscoAttr(RadiusAttr::CiscoVSA_h323_conf_id,
631                         GetGUIDString(arq.m_conferenceID)
632                         );
633                 if (arq.m_answerCall)
634                         pdu->AppendAttr(m_attrH323CallOriginAnswer);
635                 else
636                         pdu->AppendAttr(m_attrH323CallOriginOriginate);
637                 pdu->AppendAttr(m_attrH323CallType);
638                 pdu->AppendAttr(m_attrH323GwId);
639         }
640                                 
641         if (!OnSendPDU(*pdu, arqPdu, authData)) {
642                 delete pdu;
643                 return e_fail;
644         }
645         // send the request and wait for a response
646         RadiusPDU* response = NULL;
647         bool result = m_radiusClient->MakeRequest(*pdu, response) && response;
648                         
649         delete pdu;
650
651         if (!result) {
652                 PTRACE(2, "RADAUTH\t" << GetName() << " ARQ auth failed: "
653                         " could not receive or decode response from RADIUS"
654                         );
655                 delete response;
656                 response = NULL;
657                 authData.m_rejectReason = H225_AdmissionRejectReason::e_undefinedReason;
658                 return GetDefaultStatus();
659         }
660                                 
661         // authenticated?
662         result = (response->GetCode() == RadiusPDU::AccessAccept);
663         
664         PString value;
665         const RadiusAttr* attr;
666         
667         // check for Class attribute
668         if (result) {
669                 attr = response->FindAttr(RadiusAttr::AttrTypeClass);
670                 if (attr != NULL) {
671                         PBYTEArray classData;
672                         if (attr->GetValue(classData))
673                                 authData.m_radiusClass = classData;
674                 }
675         }
676         
677         // test for h323-return-code attribute (has to be 0 if accept)
678         if (result) {
679                 attr = response->FindVsaAttr(RadiusAttr::CiscoVendorId, 
680                         RadiusAttr::CiscoVSA_h323_return_code
681                         );
682                 if (attr != NULL) {
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();
687                                 if (retcode != 0) {
688                                         PTRACE(3, "RADAUTH\t" << GetName() << " ARQ check failed: "
689                                                 "return code " << retcode
690                                                 );
691                                         result = false;
692                                 }
693                         } else {
694                                 PTRACE(2, "RADAUTH\t" << GetName() << " ARQ check failed: "
695                                         "invalid h323-return-code attribute '" << value << '\''
696                                         );
697                                 result = false;
698                         }
699                 }
700         }
701         // check for h323-credit-time attribute (call duration limit)   
702         if (result) {
703                 attr = response->FindVsaAttr(RadiusAttr::CiscoVendorId, 
704                         RadiusAttr::CiscoVSA_h323_credit_time
705                         );
706                 if (attr != NULL) {
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
713                                         );
714                                 if (authData.m_callDurationLimit == 0)                                          
715                                         result = false;
716                         } else {
717                                 PTRACE(2, "RADAUTH\t" << GetName() << " ARQ check failed: "
718                                         "invalid h323-credit-time attribute '" << value << '\''
719                                         );
720                                 result = false;
721                         }
722                 }
723         }
724         // check for Session-Timeout attribute (alternate call duration limit)  
725         if (result) {
726                 const RadiusAttr* const attr = response->FindAttr(RadiusAttr::SessionTimeout);
727                 if (attr != NULL) {
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
734                                         );
735                         }
736                         if (authData.m_callDurationLimit == 0)
737                                 result = false;
738                 }
739         }
740
741         // check for h323-billing-model 
742         if (result) {
743                 attr = response->FindVsaAttr(RadiusAttr::CiscoVendorId, 
744                         RadiusAttr::CiscoVSA_h323_billing_model
745                         );
746                 if (attr != NULL) {
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();
751                                 if (intVal == 0)
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;
755                         } else {
756                                 PTRACE(3, "RADAUTH\t" << GetName() << " invalid h323-billing-model "
757                                         "attribute '" << value << '\''
758                                         );
759                         }
760                 }
761         }
762         // check for h323-credit-amount
763         if (result) {
764                 attr = response->FindVsaAttr(RadiusAttr::CiscoVendorId, 
765                         RadiusAttr::CiscoVSA_h323_credit_amount
766                         );
767                 if (attr != NULL) {
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 << '\''
774                                                 );
775                                         authData.m_amountString = psprintf(PString("%d.%d"), 
776                                                 value.AsInteger() / 100, value.AsInteger() % 100
777                                                 );
778                                 } else
779                                         authData.m_amountString = value;
780                                 
781                                 attr = response->FindVsaAttr(RadiusAttr::CiscoVendorId, 
782                                         RadiusAttr::CiscoVSA_h323_currency
783                                         );
784                                 if (attr != NULL)
785                                         authData.m_amountString += attr->AsCiscoString(); 
786                         } else {
787                                 PTRACE(3, "RADAUTH\t" << GetName() << " invalid h323-credit-amount "
788                                         "attribute '" << value << '\''
789                                         );
790                         }
791                 }
792         }
793
794         // check for h323-redirect-number
795         if (result) {
796                 attr = response->FindVsaAttr(RadiusAttr::CiscoVendorId, 
797                         RadiusAttr::CiscoVSA_h323_redirect_number
798                         );
799                 if (attr != NULL) {
800                         value = attr->AsCiscoString();
801                         if (!value) {
802                                 authData.SetRouteToAlias(value);
803                                 PTRACE(5, "RADAUTH\t" << GetName() << " ARQ check redirect "
804                                         "to the number " << value
805                                         );
806                         }
807                 }
808         }
809
810         // check for h323-redirect-ip-address
811         if (result) {
812                 attr = response->FindVsaAttr(RadiusAttr::CiscoVendorId, 
813                         RadiusAttr::CiscoVSA_h323_redirect_ip_address
814                         );
815                 if (attr != NULL) {
816                         value = attr->AsCiscoString();
817                         if (!value) {
818                                 PStringArray tokens(value.Tokenise("; \t", FALSE));
819                                 for (PINDEX i = 0; i < tokens.GetSize(); ++i) {
820                                         PIPSocket::Address addr;
821                                         WORD port = 0;
822                                         
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)
828                                                         );
829                                                 authData.m_destinationRoutes.push_back(route);
830                                                 PTRACE(5, "RADAUTH\t" << GetName() << " ARQ check redirect "
831                                                         "to the address " << route.AsString()
832                                                         );
833                                         }
834                                 }
835                         }
836                 }
837         }
838         
839         if (result)
840                 result = OnReceivedPDU(*response, arqPdu, authData);
841         else
842                 authData.m_rejectReason = H225_AdmissionRejectReason::e_securityDenial;
843                                         
844         delete response;
845         response = NULL;
846         return result ? e_ok : e_fail;
847 }
848
849 int RadAuthBase::Check(
850         SetupMsg &setup,
851         SetupAuthData &authData
852         )
853 {
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;
860         
861         if (hasCall)
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
866                         );
867                 
868         // Append User-Name and a password related attributes
869         // (User-Password or Chap-Password and Chap-Timestamp)
870         PString username;                               
871         const int status = AppendUsernameAndPassword(*pdu, setup, callingEP, 
872                 authData, &username
873                 );
874         if (status != e_ok) {
875                 delete pdu;
876                 return status;
877         }
878         
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);
889                                 
890         // append Frame-IP-Address
891         bool ipFound = false;
892         WORD dummyPort;
893                 
894         if (hasCall && authData.m_call->GetSrcSignalAddr(addr, dummyPort) 
895                 && addr.IsValid())
896                 ipFound = true; 
897         else if (callingEP 
898                 && GetIPFromTransportAddr(callingEP->GetCallSignalAddress(), addr)
899                 && addr.IsValid())
900                 ipFound = true;
901         else if (setupBody.HasOptionalField(H225_Setup_UUIE::e_sourceCallSignalAddress)
902                 && GetIPFromTransportAddr(setupBody.m_sourceCallSignalAddress, addr)
903                 && addr.IsValid())
904                 ipFound = true;
905         else {
906                 setup.GetPeerAddr(addr);
907                 ipFound = addr.IsValid();
908         }
909         if (!ipFound) {
910                 PTRACE(2, "RADAUTH\t" << GetName() << " Setup auth failed: "
911                         "could not setup Framed-IP-Address"
912                         );
913                 delete pdu;
914                 authData.m_rejectCause = Q931::CallRejected;
915                 return e_fail;
916         } else
917                 pdu->AppendAttr(RadiusAttr::FramedIpAddress, addr);
918                                 
919         // fill Calling-Station-Id and Called-Station-Id fields
920         PString stationId = GetCallingStationId(setup, authData);
921         if (!stationId) {
922                 pdu->AppendAttr(RadiusAttr::CallingStationId, stationId);
923         }
924
925         const PString calledStationId = GetCalledStationId(setup, authData);
926         const PString dialedNumber = GetDialedNumber(setup, authData);
927
928         stationId = m_useDialedNumber ? dialedNumber : calledStationId;
929         if (stationId.IsEmpty()) {
930                 delete pdu;
931                 PTRACE(2, "RADAUTH\t" << GetName() << " Setup check failed: "
932                         "no called station id found"
933                         );
934                 authData.m_rejectReason = H225_ReleaseCompleteReason::e_badFormatAddress;
935                 return e_fail;
936         } else
937                 pdu->AppendAttr(RadiusAttr::CalledStationId, stationId);
938                         
939         if (m_appendCiscoAttributes) {
940                 pdu->AppendCiscoAttr(RadiusAttr::CiscoVSA_h323_conf_id,
941                         GetGUIDString(setupBody.m_conferenceID)
942                         );
943                 pdu->AppendAttr(m_attrH323CallOriginOriginate);
944                 pdu->AppendAttr(m_attrH323CallType);
945                 pdu->AppendAttr(m_attrH323GwId);
946         }
947                                 
948         if (!OnSendPDU(*pdu, setup, authData)) {
949                 delete pdu;
950                 return e_fail;
951         }
952         // send the request and wait for a response
953         RadiusPDU* response = NULL;
954         bool result = m_radiusClient->MakeRequest(*pdu, response) && response;
955                         
956         delete pdu;
957                         
958         if (!result) {
959                 PTRACE(2, "RADAUTH\t" << GetName() << " Setup auth failed: "
960                         " could not receive or decode response from RADIUS"
961                         );
962                 delete response;
963                 response = NULL;
964                 authData.m_rejectCause = Q931::TemporaryFailure;
965                 return GetDefaultStatus();
966         }
967                                 
968         // authenticated?
969         result = (response->GetCode() == RadiusPDU::AccessAccept);
970         
971         PString value;
972         const RadiusAttr* attr;
973         
974         // check for Class attribute
975         if (result) {
976                 attr = response->FindAttr(RadiusAttr::AttrTypeClass);
977                 if (attr != NULL) {
978                         PBYTEArray classData;
979                         if (attr->GetValue(classData))
980                                 authData.m_radiusClass = classData;
981                 }
982         }
983
984         // test for h323-return-code attribute (has to be 0 if accept)
985         if (result) {
986                 attr = response->FindVsaAttr(RadiusAttr::CiscoVendorId, 
987                         RadiusAttr::CiscoVSA_h323_return_code
988                         );
989                 if (attr != NULL) {
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();
994                                 if (retcode != 0) {
995                                         PTRACE(5, "RADAUTH\t" << GetName() << " Setup check failed: "
996                                                 "return code " << retcode
997                                                 );
998                                         result = false;
999                                 }
1000                         } else {
1001                                 PTRACE(2, "RADAUTH\t" << GetName() << " Setup check failed: "
1002                                         "invalid h323-return-code attribute '" << value << '\''
1003                                         );
1004                                 result = false;
1005                         }
1006                 }
1007         }
1008         // check for h323-credit-time attribute (call duration limit)   
1009         if (result) {
1010                 attr = response->FindVsaAttr(RadiusAttr::CiscoVendorId, 
1011                         RadiusAttr::CiscoVSA_h323_credit_time
1012                         );
1013                 if (attr != NULL) {
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
1020                                         );
1021                                 if (authData.m_callDurationLimit == 0)
1022                                         result = false;
1023                         } else {
1024                                 PTRACE(2, "RADAUTH\t" << GetName() << " Setup check failed: "
1025                                         "invalid h323-credit-time attribute '" << value << '\''
1026                                         );
1027                                 result = false;
1028                         }
1029                 }
1030         }
1031         // check for Session-Timeout attribute (alternate call duration limit)  
1032         if (result) {
1033                 const RadiusAttr* const attr = response->FindAttr(RadiusAttr::SessionTimeout);
1034                 if (attr != NULL) {
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
1041                                         );
1042                         }
1043                         if (authData.m_callDurationLimit == 0)
1044                                 result = false;
1045                 }
1046         }
1047
1048         // check for h323-redirect-number
1049         if (result) {
1050                 attr = response->FindVsaAttr(RadiusAttr::CiscoVendorId, 
1051                         RadiusAttr::CiscoVSA_h323_redirect_number
1052                         );
1053                 if (attr != NULL) {
1054                         value = attr->AsCiscoString();
1055                         if (!value) {
1056                                 authData.SetRouteToAlias(value);
1057                                 PTRACE(5, "RADAUTH\t" << GetName() << " ARQ check redirect "
1058                                         "to the number " << value
1059                                         );
1060                         }
1061                 }
1062         }
1063
1064         // check for h323-redirect-ip-address
1065         if (result) {
1066                 attr = response->FindVsaAttr(RadiusAttr::CiscoVendorId, 
1067                         RadiusAttr::CiscoVSA_h323_redirect_ip_address
1068                         );
1069                 if (attr != NULL) {
1070                         value = attr->AsCiscoString();
1071                         if (!value) {
1072                                 PStringArray tokens(value.Tokenise("; \t", FALSE));
1073                                 for (PINDEX i = 0; i < tokens.GetSize(); ++i) {
1074                                         PIPSocket::Address addr;
1075                                         WORD port = 0;
1076                                         
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)
1082                                                         );
1083                                                 authData.m_destinationRoutes.push_back(route);
1084                                                 PTRACE(5, "RADAUTH\t" << GetName() << " Setup check redirect "
1085                                                         "to the address " << route.AsString()
1086                                                         );
1087                                         }
1088                                 }
1089                         }
1090                 }
1091         }
1092                         
1093         if (result)
1094                 result = OnReceivedPDU(*response, setup, authData);
1095         else
1096                 authData.m_rejectCause = Q931::CallRejected;
1097                                         
1098         delete response;
1099         response = NULL;
1100         return result ? e_ok : e_fail;
1101 }               
1102
1103 bool RadAuthBase::OnSendPDU(
1104         RadiusPDU& /*pdu*/,
1105         RasPDU<H225_RegistrationRequest>& /*rrqPdu*/,
1106         RRQAuthData& /*authData*/
1107         )
1108 {
1109         return true;
1110 }
1111
1112 bool RadAuthBase::OnSendPDU(
1113         RadiusPDU& /*pdu*/,
1114         RasPDU<H225_AdmissionRequest>& /*arqPdu*/,
1115         ARQAuthData& /*authData*/
1116         )
1117 {
1118         return true;
1119 }
1120
1121 bool RadAuthBase::OnSendPDU(
1122         RadiusPDU& /*pdu*/,
1123         SetupMsg &/*setup*/,
1124         SetupAuthData& /*authData*/
1125         )
1126 {
1127         return true;
1128 }
1129
1130 bool RadAuthBase::OnReceivedPDU(
1131         RadiusPDU& /*pdu*/,
1132         RasPDU<H225_RegistrationRequest>& /*rrqPdu*/,
1133         RRQAuthData& /*authData*/
1134         )
1135 {
1136         return true;
1137 }
1138
1139 bool RadAuthBase::OnReceivedPDU(
1140         RadiusPDU& /*pdu*/,
1141         RasPDU<H225_AdmissionRequest>& /*arqPdu*/,
1142         ARQAuthData& /*authData*/
1143         )
1144 {
1145         return true;
1146 }
1147
1148 bool RadAuthBase::OnReceivedPDU(
1149         RadiusPDU& /*pdu*/,
1150         SetupMsg &/*setup*/,
1151         SetupAuthData& /*authData*/
1152         )
1153 {
1154         return true;
1155 }
1156
1157 int RadAuthBase::AppendUsernameAndPassword(
1158         RadiusPDU& /*pdu*/,
1159         RasPDU<H225_RegistrationRequest>& /*rrqPdu*/,
1160         RRQAuthData& /*authData*/,
1161         PString* /*username*/
1162         ) const
1163 {
1164         return GetDefaultStatus();
1165 }
1166
1167 int RadAuthBase::AppendUsernameAndPassword(
1168         RadiusPDU& /*pdu*/,
1169         RasPDU<H225_AdmissionRequest>& /*arqPdu*/,
1170         ARQAuthData& /*authData*/,
1171         PString* /*username*/
1172         ) const
1173 {
1174         return GetDefaultStatus();
1175 }
1176
1177 int RadAuthBase::AppendUsernameAndPassword(
1178         RadiusPDU &/*pdu*/,
1179         SetupMsg &/*setup*/,
1180         endptr &/*callingEP*/,
1181         SetupAuthData &/*authData*/,
1182         PString * /*username*/
1183         ) const
1184 {
1185         return GetDefaultStatus();
1186 }
1187
1188 RadAuth::RadAuth(
1189         const char* authName
1190         )
1191         : 
1192         RadAuthBase(authName, RadAuthConfigSectionName)
1193 {
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);
1203 }
1204
1205 RadAuth::~RadAuth() 
1206 {
1207 }
1208
1209 int RadAuth::CheckTokens(
1210         RadiusPDU& pdu,
1211         const H225_ArrayOf_ClearToken& tokens,
1212         const H225_ArrayOf_AliasAddress* aliases,
1213         PString* username
1214         ) const
1215 {
1216         // scan ClearTokens and find CATs
1217         for (PINDEX i = 0; i < tokens.GetSize(); i++) {
1218                 const H235_ClearToken& token = tokens[i];
1219                         
1220                 // is this CAT?
1221                 if (token.m_tokenOID != OID_CAT)
1222                         continue;
1223
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))) 
1229                 {       
1230                         PTRACE(3, "RADAUTH\t" << GetName() << " auth failed: "
1231                                 "CAT without all required fields"
1232                                 );
1233                         return e_fail;
1234                 }
1235                                 
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"
1241                                 );
1242                         return e_fail;
1243                 }
1244                                         
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"
1250                                 );
1251                         return e_fail;
1252                 }
1253                                         
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"
1258                                 );
1259                         return e_fail;
1260                 }
1261                                         
1262                 // append User-Name
1263                 pdu.AppendAttr(RadiusAttr::UserName, id);
1264                 if (username != NULL)
1265                         *username = (const char*)id;
1266                                 
1267                 // build CHAP-Password
1268                 char password[17] = { (BYTE)randomInt };
1269                 memcpy(password + 1, (const BYTE*)(token.m_challenge), 16);
1270                                 
1271                 pdu.AppendAttr(RadiusAttr::ChapPassword, password, sizeof(password));
1272                 pdu.AppendAttr(RadiusAttr::ChapChallenge, (int)(DWORD)token.m_timeStamp);
1273                                 
1274                 return e_ok;
1275         }
1276         PTRACE(3, "RADAUTH\t" << GetName() << " auth failed: no CAT token found");
1277         return GetDefaultStatus();
1278 }
1279
1280 int RadAuth::AppendUsernameAndPassword(
1281         RadiusPDU& pdu,
1282         RasPDU<H225_RegistrationRequest>& rrqPdu,
1283         RRQAuthData& authData,
1284         PString* username
1285         ) const
1286 {
1287         H225_RegistrationRequest& rrq = (H225_RegistrationRequest&)rrqPdu;
1288         
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"
1293                         );
1294                 authData.m_rejectReason = H225_RegistrationRejectReason::e_securityDenial;
1295                 return GetDefaultStatus();
1296         }
1297                 
1298         // check for ClearTokens (CAT uses ClearTokens)
1299         if (!rrq.HasOptionalField(H225_RegistrationRequest::e_tokens)) {
1300                 PTRACE(3, "RADAUTH\t" << GetName() << " RRQ auth failed: "
1301                         "tokens not found"
1302                         );
1303                 authData.m_rejectReason = H225_RegistrationRejectReason::e_securityDenial;
1304                 return GetDefaultStatus();
1305         }
1306
1307         const int result = CheckTokens(pdu, rrq.m_tokens, &rrq.m_terminalAlias, username);
1308         if (result != e_ok)     
1309                 authData.m_rejectReason = H225_RegistrationRejectReason::e_securityDenial;
1310         return result;
1311 }
1312
1313 int RadAuth::AppendUsernameAndPassword(
1314         RadiusPDU& pdu,
1315         RasPDU<H225_AdmissionRequest>& arqPdu,
1316         ARQAuthData& authData,
1317         PString* username
1318         ) const
1319 {
1320         H225_AdmissionRequest& arq = (H225_AdmissionRequest&)arqPdu;
1321         
1322         // check for ClearTokens
1323         if (!arq.HasOptionalField(H225_AdmissionRequest::e_tokens)) {
1324                 PTRACE(3, "RADAUTH\t" << GetName() << " ARQ auth failed: "
1325                         "tokens not found"
1326                         );
1327                 authData.m_rejectReason = H225_AdmissionRejectReason::e_securityDenial;
1328                 return GetDefaultStatus();
1329         }
1330
1331         const int result = CheckTokens(pdu, arq.m_tokens, NULL, username);
1332         if (result != e_ok)     
1333                 authData.m_rejectReason = H225_AdmissionRejectReason::e_securityDenial;
1334         return result;
1335 }
1336
1337 int RadAuth::AppendUsernameAndPassword(
1338         RadiusPDU& pdu,
1339         SetupMsg &setup,
1340         endptr& /*callingEP*/,
1341         SetupAuthData& authData,
1342         PString* username
1343         ) const
1344 {
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();
1351         }
1352
1353         const int result = CheckTokens(pdu, setupBody.m_tokens, NULL, username);
1354         if (result != e_ok)     
1355                 authData.m_rejectCause = Q931::CallRejected;
1356         return result;
1357 }
1358
1359 RadAliasAuth::RadAliasAuth( 
1360         const char* authName 
1361         )
1362         :
1363         RadAuthBase(authName, RadAliasAuthConfigSectionName)
1364 {
1365         m_fixedUsername = GetConfig()->GetString(
1366                 RadAliasAuthConfigSectionName, "FixedUsername", ""
1367                 );
1368         m_fixedPassword = Toolkit::Instance()->ReadPassword(
1369                 RadAliasAuthConfigSectionName, "FixedPassword"
1370                 );
1371 }
1372
1373 RadAliasAuth::~RadAliasAuth()
1374 {
1375 }
1376
1377 int RadAliasAuth::AppendUsernameAndPassword(
1378         RadiusPDU& pdu,
1379         RasPDU<H225_RegistrationRequest>& rrqPdu, 
1380         RRQAuthData& authData,
1381         PString* username
1382         ) const
1383 {
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"
1388                         );
1389                 authData.m_rejectReason = H225_RegistrationRejectReason::e_securityDenial;
1390                 return GetDefaultStatus();
1391         }
1392         
1393         // append User-Name
1394         pdu.AppendAttr(RadiusAttr::UserName, 
1395                 m_fixedUsername.IsEmpty() ? id : m_fixedUsername
1396                 );
1397         
1398         if (username != NULL)
1399                 *username = (const char*)id;
1400                 
1401         // append User-Password
1402         if (!m_fixedPassword)
1403                 pdu.AppendAttr(RadiusAttr::UserPassword, m_fixedPassword);
1404         else 
1405                 pdu.AppendAttr(RadiusAttr::UserPassword, 
1406                         m_fixedUsername.IsEmpty() ? id : m_fixedUsername
1407                         );
1408                 
1409         return e_ok;                    
1410 }
1411
1412 int RadAliasAuth::AppendUsernameAndPassword(
1413         RadiusPDU& pdu,
1414         RasPDU<H225_AdmissionRequest>& arqPdu,
1415         ARQAuthData& authData,
1416         PString* username
1417         ) const
1418 {
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"
1423                         );
1424                 authData.m_rejectReason = H225_AdmissionRejectReason::e_securityDenial;
1425                 return GetDefaultStatus();
1426         }
1427         
1428         // append User-Name
1429         pdu.AppendAttr(RadiusAttr::UserName, 
1430                 m_fixedUsername.IsEmpty() ? id : m_fixedUsername
1431                 );
1432
1433         if (username != NULL)
1434                 *username = (const char*)id;
1435                                 
1436         if (!m_fixedPassword)
1437                 pdu.AppendAttr(RadiusAttr::UserPassword, m_fixedPassword);
1438         else
1439                 pdu.AppendAttr(RadiusAttr::UserPassword, 
1440                         m_fixedUsername.IsEmpty() ? id : m_fixedUsername
1441                         );
1442                         
1443         return e_ok;
1444 }
1445
1446 int RadAliasAuth::AppendUsernameAndPassword(
1447         RadiusPDU& pdu,
1448         SetupMsg &setup,
1449         endptr& /*callingEP*/,
1450         SetupAuthData& authData,
1451         PString* username
1452         ) const
1453 {
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"
1458                         );
1459                 authData.m_rejectReason = H225_ReleaseCompleteReason::e_badFormatAddress;
1460                 return GetDefaultStatus();
1461         }
1462         
1463         // append User-Name
1464         pdu.AppendAttr(RadiusAttr::UserName, 
1465                 m_fixedUsername.IsEmpty() ? id : m_fixedUsername
1466                 );
1467
1468         if (username != NULL)
1469                 *username = (const char*)id;
1470                                 
1471         if (!m_fixedPassword)
1472                 pdu.AppendAttr(RadiusAttr::UserPassword, m_fixedPassword);
1473         else
1474                 pdu.AppendAttr(RadiusAttr::UserPassword, 
1475                         m_fixedUsername.IsEmpty() ? id : m_fixedUsername
1476                         );
1477                         
1478         return e_ok;
1479 }
1480         
1481 namespace {
1482         GkAuthCreator<RadAuth> RadAuthCreator("RadAuth");
1483         GkAuthCreator<RadAliasAuth> RadAliasAuthCreator("RadAliasAuth");
1484 }
1485
1486 #endif /* HAS_RADIUS */