home *** CD-ROM | disk | FTP | other *** search
/ ftp.muug.mb.ca / 2014.06.ftp.muug.mb.ca.tar / ftp.muug.mb.ca / pub / openh323.tar.gz / openh323.tar / openh323 / src / h235auth.cxx < prev    next >
C/C++ Source or Header  |  2003-04-29  |  22KB  |  738 lines

  1. /*
  2.  * h235auth.cxx
  3.  *
  4.  * H.235 security PDU's
  5.  *
  6.  * Open H323 Library
  7.  *
  8.  * Copyright (c) 1998-2001 Equivalence Pty. Ltd.
  9.  *
  10.  * The contents of this file are subject to the Mozilla Public License
  11.  * Version 1.0 (the "License"); you may not use this file except in
  12.  * compliance with the License. You may obtain a copy of the License at
  13.  * http://www.mozilla.org/MPL/
  14.  *
  15.  * Software distributed under the License is distributed on an "AS IS"
  16.  * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
  17.  * the License for the specific language governing rights and limitations
  18.  * under the License.
  19.  *
  20.  * The Original Code is Open H323 Library.
  21.  *
  22.  * The Initial Developer of the Original Code is Equivalence Pty. Ltd.
  23.  *
  24.  * Contributor(s): __________________________________
  25.  *
  26.  * $Log: h235auth.cxx,v $
  27.  * Revision 1.24  2003/04/30 00:28:54  robertj
  28.  * Redesigned the alternate credentials in ARQ system as old implementation
  29.  *   was fraught with concurrency issues, most importantly it can cause false
  30.  *   detection of replay attacks taking out an endpoint completely.
  31.  *
  32.  * Revision 1.23  2003/04/01 04:47:55  robertj
  33.  * Abstracted H.225 RAS transaction processing (RIP and secondary thread) in
  34.  *   server environment for use by H.501 peer elements.
  35.  *
  36.  * Revision 1.22  2003/02/25 06:48:19  robertj
  37.  * More work on PDU transaction abstraction.
  38.  *
  39.  * Revision 1.21  2003/02/12 07:40:36  robertj
  40.  * Added more logging for authentication errors in validation.
  41.  * Fixed problem with possible negative numbers in CAT random field.
  42.  *
  43.  * Revision 1.20  2003/02/11 04:44:13  robertj
  44.  * Fixed use of asymmetrical authentication schemes such as MD5.
  45.  *
  46.  * Revision 1.19  2003/02/03 02:55:08  robertj
  47.  * Fixed problem with pasing through other tokens transparently.
  48.  *
  49.  * Revision 1.18  2003/02/02 11:46:32  robertj
  50.  * Fixed CAT selection in GRQ authentication mechanisms.
  51.  *
  52.  * Revision 1.17  2003/02/01 13:31:22  robertj
  53.  * Changes to support CAT authentication in RAS.
  54.  *
  55.  * Revision 1.16  2003/01/08 04:40:34  robertj
  56.  * Added more debug tracing for H.235 authenticators.
  57.  *
  58.  * Revision 1.15  2002/11/11 07:04:22  robertj
  59.  * Fixed typo in trace.
  60.  *
  61.  * Revision 1.14  2002/11/05 00:04:21  robertj
  62.  * Returned code back to including trailing NULL in BMPString after
  63.  *   PString::AsUCS2() implementation changes.
  64.  *
  65.  * Revision 1.13  2002/10/31 07:11:16  robertj
  66.  * Added UTF-8/UCS-2 conversion functions to PString.
  67.  *
  68.  * Revision 1.12  2002/08/13 05:11:03  robertj
  69.  * Removed redundent code.
  70.  *
  71.  * Revision 1.11  2002/08/05 10:03:47  robertj
  72.  * Cosmetic changes to normalise the usage of pragma interface/implementation.
  73.  *
  74.  * Revision 1.10  2002/08/05 05:17:41  robertj
  75.  * Fairly major modifications to support different authentication credentials
  76.  *   in ARQ to the logged in ones on RRQ. For both client and server.
  77.  * Various other H.235 authentication bugs and anomalies fixed on the way.
  78.  *
  79.  * Revision 1.9  2002/06/24 00:11:21  robertj
  80.  * Clarified error message during GRQ authentication.
  81.  *
  82.  * Revision 1.8  2002/05/17 03:40:09  robertj
  83.  * Fixed problems with H.235 authentication on RAS for server and client.
  84.  *
  85.  * Revision 1.7  2001/09/21 04:55:27  robertj
  86.  * Removed redundant code, thanks Chih-Wei Huang
  87.  *
  88.  * Revision 1.6  2001/09/14 00:13:39  robertj
  89.  * Fixed problem with some athenticators needing extra conditions to be
  90.  *   "active", so make IsActive() virtual and add localId to H235AuthSimpleMD5
  91.  *
  92.  * Revision 1.5  2001/09/13 01:15:20  robertj
  93.  * Added flag to H235Authenticator to determine if gkid and epid is to be
  94.  *   automatically set as the crypto token remote id and local id.
  95.  *
  96.  * Revision 1.4  2001/08/14 04:26:46  robertj
  97.  * Completed the Cisco compatible MD5 authentications, thanks Wolfgang Platzer.
  98.  *
  99.  * Revision 1.3  2001/08/13 10:03:54  robertj
  100.  * Fixed problem if do not have a localId when doing MD5 authentication.
  101.  *
  102.  * Revision 1.2  2001/08/10 13:49:35  robertj
  103.  * Fixed alpha Linux warning.
  104.  *
  105.  * Revision 1.1  2001/08/10 11:03:52  robertj
  106.  * Major changes to H.235 support in RAS to support server.
  107.  *
  108.  */
  109.  
  110. #include <ptlib.h>
  111.  
  112. #ifdef __GNUC__
  113. #pragma implementation "h235auth.h"
  114. #endif
  115.  
  116. #include "h235auth.h"
  117.  
  118. #include "h323pdu.h"
  119. #include <ptclib/random.h>
  120. #include <ptclib/cypher.h>
  121.  
  122.  
  123. #define new PNEW
  124.  
  125.  
  126. /////////////////////////////////////////////////////////////////////////////
  127.  
  128. H235Authenticator::H235Authenticator()
  129. {
  130.   enabled = TRUE;
  131.   sentRandomSequenceNumber = PRandom::Number()&INT_MAX;
  132.   lastRandomSequenceNumber = 0;
  133.   lastTimestamp = 0;
  134.   timestampGracePeriod = 2*60*60+10; // 2 hours 10 seconds to allow for DST adjustments
  135. }
  136.  
  137.  
  138. void H235Authenticator::PrintOn(ostream & strm) const
  139. {
  140.   PWaitAndSignal m(mutex);
  141.  
  142.   strm << GetName() << '<';
  143.   if (IsActive())
  144.     strm << "active";
  145.   else if (!enabled)
  146.     strm << "disabled";
  147.   else if (password.IsEmpty())
  148.     strm << "no-pwd";
  149.   else
  150.     strm << "inactive";
  151.   strm << '>';
  152. }
  153.  
  154.  
  155. BOOL H235Authenticator::PrepareTokens(PASN_Array & clearTokens,
  156.                                       PASN_Array & cryptoTokens)
  157. {
  158.   PWaitAndSignal m(mutex);
  159.  
  160.   if (!IsActive())
  161.     return FALSE;
  162.  
  163.   H235_ClearToken * clearToken = CreateClearToken();
  164.   if (clearToken != NULL) {
  165.     // Check if already have a token of thsi type and overwrite it
  166.     for (PINDEX i = 0; i < clearTokens.GetSize(); i++) {
  167.       H235_ClearToken & oldToken = (H235_ClearToken &)clearTokens[i];
  168.       if (clearToken->m_tokenOID == oldToken.m_tokenOID) {
  169.         oldToken = *clearToken;
  170.         delete clearToken;
  171.         clearToken = NULL;
  172.         break;
  173.       }
  174.     }
  175.  
  176.     if (clearToken != NULL)
  177.       clearTokens.Append(clearToken);
  178.   }
  179.  
  180.   H225_CryptoH323Token * cryptoToken = CreateCryptoToken();
  181.   if (cryptoToken != NULL)
  182.     cryptoTokens.Append(cryptoToken);
  183.  
  184.   return TRUE;
  185. }
  186.  
  187.  
  188. H235_ClearToken * H235Authenticator::CreateClearToken()
  189. {
  190.   return NULL;
  191. }
  192.  
  193.  
  194. H225_CryptoH323Token * H235Authenticator::CreateCryptoToken()
  195. {
  196.   return NULL;
  197. }
  198.  
  199.  
  200. BOOL H235Authenticator::Finalise(PBYTEArray & /*rawPDU*/)
  201. {
  202.   return TRUE;
  203. }
  204.  
  205.  
  206. H235Authenticator::ValidationResult H235Authenticator::ValidateTokens(
  207.                                         const PASN_Array & clearTokens,
  208.                                         const PASN_Array & cryptoTokens,
  209.                                         const PBYTEArray & rawPDU)
  210. {
  211.   PWaitAndSignal m(mutex);
  212.  
  213.   if (!IsActive())
  214.     return e_Disabled;
  215.  
  216.   PINDEX i;
  217.   for (i = 0; i < clearTokens.GetSize(); i++) {
  218.     ValidationResult s = ValidateClearToken((H235_ClearToken &)clearTokens[i]);
  219.     if (s != e_Absent)
  220.       return s;
  221.   }
  222.  
  223.   for (i = 0; i < cryptoTokens.GetSize(); i++) {
  224.     ValidationResult s = ValidateCryptoToken((H225_CryptoH323Token &)cryptoTokens[i], rawPDU);
  225.     if (s != e_Absent)
  226.       return s;
  227.   }
  228.  
  229.   return e_Absent;
  230. }
  231.  
  232.  
  233. H235Authenticator::ValidationResult H235Authenticator::ValidateClearToken(
  234.                                                  const H235_ClearToken & /*clearToken*/)
  235. {
  236.   return e_Absent;
  237. }
  238.  
  239.  
  240. H235Authenticator::ValidationResult H235Authenticator::ValidateCryptoToken(
  241.                                             const H225_CryptoH323Token & /*cryptoToken*/,
  242.                                             const PBYTEArray & /*rawPDU*/)
  243. {
  244.   return e_Absent;
  245. }
  246.  
  247.  
  248. BOOL H235Authenticator::UseGkAndEpIdentifiers() const
  249. {
  250.   return FALSE;
  251. }
  252.  
  253.  
  254. BOOL H235Authenticator::IsSecuredPDU(unsigned, BOOL) const
  255. {
  256.   return TRUE;
  257. }
  258.  
  259.  
  260. BOOL H235Authenticator::IsActive() const
  261. {
  262.   return enabled && !password;
  263. }
  264.  
  265.  
  266. BOOL H235Authenticator::AddCapability(unsigned mechanism,
  267.                                       const PString & oid,
  268.                                       H225_ArrayOf_AuthenticationMechanism & mechanisms,
  269.                                       H225_ArrayOf_PASN_ObjectId & algorithmOIDs)
  270. {
  271.   PWaitAndSignal m(mutex);
  272.  
  273.   if (!IsActive()) {
  274.     PTRACE(2, "RAS\tAuthenticator " << *this
  275.             << " not active during GRQ SetCapability negotiation");
  276.     return FALSE;
  277.   }
  278.  
  279.   PINDEX i;
  280.   PINDEX size = mechanisms.GetSize();
  281.   for (i = 0; i < size; i++) {
  282.     if (mechanisms[i].GetTag() == mechanism)
  283.       break;
  284.   }
  285.   if (i >= size) {
  286.     mechanisms.SetSize(size+1);
  287.     mechanisms[size].SetTag(mechanism);
  288.   }
  289.  
  290.   size = algorithmOIDs.GetSize();
  291.   for (i = 0; i < size; i++) {
  292.     if (algorithmOIDs[i] == oid)
  293.       break;
  294.   }
  295.   if (i >= size) {
  296.     algorithmOIDs.SetSize(size+1);
  297.     algorithmOIDs[size] = oid;
  298.   }
  299.  
  300.   return TRUE;
  301. }
  302.  
  303.  
  304. ///////////////////////////////////////////////////////////////////////////////
  305.  
  306. void H235Authenticators::PreparePDU(H323TransactionPDU & pdu,
  307.                                     PASN_Array & clearTokens,
  308.                                     unsigned clearOptionalField,
  309.                                     PASN_Array & cryptoTokens,
  310.                                     unsigned cryptoOptionalField) const
  311. {
  312.   // Clean out any crypto tokens in case this is a retry message
  313.   // and we are regenerating the tokens due to possible timestamp
  314.   // issues. We don't do this for clear tokens which may be used by
  315.   // other endpoints and should be passed through unchanged.
  316.   cryptoTokens.RemoveAll();
  317.  
  318.   for (PINDEX i = 0; i < GetSize(); i++) {
  319.     H235Authenticator & authenticator = (*this)[i];
  320.     if (authenticator.IsSecuredPDU(pdu.GetChoice().GetTag(), FALSE) &&
  321.         authenticator.PrepareTokens(clearTokens, cryptoTokens)) {
  322.       PTRACE(4, "H235RAS\tPrepared PDU with authenticator " << authenticator);
  323.     }
  324.   }
  325.  
  326.   PASN_Sequence & subPDU = (PASN_Sequence &)pdu.GetChoice().GetObject();
  327.   if (clearTokens.GetSize() > 0)
  328.     subPDU.IncludeOptionalField(clearOptionalField);
  329.  
  330.   if (cryptoTokens.GetSize() > 0)
  331.     subPDU.IncludeOptionalField(cryptoOptionalField);
  332. }
  333.  
  334.  
  335. H235Authenticator::ValidationResult
  336.        H235Authenticators::ValidatePDU(const H323TransactionPDU & pdu,
  337.                                        const PASN_Array & clearTokens,
  338.                                        unsigned clearOptionalField,
  339.                                        const PASN_Array & cryptoTokens,
  340.                                        unsigned cryptoOptionalField,
  341.                                        const PBYTEArray & rawPDU) const
  342. {
  343.   BOOL noneActive = TRUE;
  344.   PINDEX i;
  345.   for (i = 0; i < GetSize(); i++) {
  346.     H235Authenticator & authenticator = (*this)[i];
  347.     if (authenticator.IsActive() && authenticator.IsSecuredPDU(pdu.GetChoice().GetTag(), TRUE)) {
  348.       noneActive = FALSE;
  349.       break;
  350.     }
  351.   }
  352.  
  353.   if (noneActive)
  354.     return H235Authenticator::e_OK;
  355.  
  356.   //do not accept non secure RAS Messages
  357.   const PASN_Sequence & subPDU = (const PASN_Sequence &)pdu.GetChoice().GetObject();
  358.   if (!subPDU.HasOptionalField(clearOptionalField) &&
  359.       !subPDU.HasOptionalField(cryptoOptionalField)) {
  360.     PTRACE(2, "H235RAS\tReceived unsecured RAS message (no crypto tokens),"
  361.               " need one of:\n" << setfill(',') << *this << setfill(' '));
  362.     return H235Authenticator::e_Absent;
  363.   }
  364.  
  365.   for (i = 0; i < GetSize(); i++) {
  366.     H235Authenticator & authenticator = (*this)[i];
  367.     if (authenticator.IsSecuredPDU(pdu.GetChoice().GetTag(), TRUE)) {
  368.       H235Authenticator::ValidationResult result = authenticator.ValidateTokens(clearTokens, cryptoTokens, rawPDU);
  369.       switch (result) {
  370.         case H235Authenticator::e_OK :
  371.           PTRACE(4, "H235RAS\tAuthenticator " << authenticator << " succeeded");
  372.           return H235Authenticator::e_OK;
  373.  
  374.         case H235Authenticator::e_Absent :
  375.           PTRACE(4, "H235RAS\tAuthenticator " << authenticator << " absent from PDU");
  376.           authenticator.Disable();
  377.           break;
  378.  
  379.         case H235Authenticator::e_Disabled :
  380.           PTRACE(4, "H235RAS\tAuthenticator " << authenticator << " disabled");
  381.           break;
  382.  
  383.         default : // Various other failure modes
  384.           PTRACE(4, "H235RAS\tAuthenticator " << authenticator << " failed: " << (int)result);
  385.           return result;
  386.       }
  387.     }
  388.   }
  389.  
  390.   return H235Authenticator::e_Absent;
  391. }
  392.  
  393.  
  394. ///////////////////////////////////////////////////////////////////////////////
  395.  
  396. static const char OID_MD5[] = "1.2.840.113549.2.5";
  397.  
  398. H235AuthSimpleMD5::H235AuthSimpleMD5()
  399. {
  400. }
  401.  
  402.  
  403. PObject * H235AuthSimpleMD5::Clone() const
  404. {
  405.   return new H235AuthSimpleMD5(*this);
  406. }
  407.  
  408.  
  409. const char * H235AuthSimpleMD5::GetName() const
  410. {
  411.   return "MD5";
  412. }
  413.  
  414.  
  415. static PWORDArray GetUCS2plusNULL(const PString & str)
  416. {
  417.   PWORDArray ucs2 = str.AsUCS2();
  418.   PINDEX len = ucs2.GetSize();
  419.   if (len > 0 && ucs2[len-1] != 0)
  420.     ucs2.SetSize(len+1);
  421.   return ucs2;
  422. }
  423.  
  424.  
  425. H225_CryptoH323Token * H235AuthSimpleMD5::CreateCryptoToken()
  426. {
  427.   if (!IsActive())
  428.     return NULL;
  429.  
  430.   if (localId.IsEmpty()) {
  431.     PTRACE(2, "H235RAS\tH235AuthSimpleMD5 requires local ID for encoding.");
  432.     return NULL;
  433.   }
  434.  
  435.   // Cisco compatible hash calculation
  436.   H235_ClearToken clearToken;
  437.  
  438.   // fill the PwdCertToken to calculate the hash
  439.   clearToken.m_tokenOID = "0.0";
  440.  
  441.   clearToken.IncludeOptionalField(H235_ClearToken::e_generalID);
  442.   clearToken.m_generalID = GetUCS2plusNULL(localId);
  443.  
  444.   clearToken.IncludeOptionalField(H235_ClearToken::e_password);
  445.   clearToken.m_password = GetUCS2plusNULL(password);
  446.  
  447.   clearToken.IncludeOptionalField(H235_ClearToken::e_timeStamp);
  448.   clearToken.m_timeStamp = (int)time(NULL);
  449.  
  450.   // Encode it into PER
  451.   PPER_Stream strm;
  452.   clearToken.Encode(strm);
  453.   strm.CompleteEncoding();
  454.  
  455.   // Generate an MD5 of the clear tokens PER encoding.
  456.   PMessageDigest5 stomach;
  457.   stomach.Process(strm.GetPointer(), strm.GetSize());
  458.   PMessageDigest5::Code digest;
  459.   stomach.Complete(digest);
  460.  
  461.   // Create the H.225 crypto token
  462.   H225_CryptoH323Token * cryptoToken = new H225_CryptoH323Token;
  463.   cryptoToken->SetTag(H225_CryptoH323Token::e_cryptoEPPwdHash);
  464.   H225_CryptoH323Token_cryptoEPPwdHash & cryptoEPPwdHash = *cryptoToken;
  465.  
  466.   // Set the token data that actually goes over the wire
  467.   H323SetAliasAddress(localId, cryptoEPPwdHash.m_alias);
  468.  
  469.   cryptoEPPwdHash.m_timeStamp = clearToken.m_timeStamp;
  470.   cryptoEPPwdHash.m_token.m_algorithmOID = OID_MD5;
  471.   cryptoEPPwdHash.m_token.m_hash.SetData(sizeof(digest)*8, (const BYTE *)&digest);
  472.  
  473.   return cryptoToken;
  474. }
  475.  
  476.  
  477. H235Authenticator::ValidationResult H235AuthSimpleMD5::ValidateCryptoToken(
  478.                                              const H225_CryptoH323Token & cryptoToken,
  479.                                              const PBYTEArray &)
  480. {
  481.   if (!IsActive())
  482.     return e_Disabled;
  483.  
  484.   // verify the token is of correct type
  485.   if (cryptoToken.GetTag() != H225_CryptoH323Token::e_cryptoEPPwdHash)
  486.     return e_Absent;
  487.  
  488.   const H225_CryptoH323Token_cryptoEPPwdHash & cryptoEPPwdHash = cryptoToken;
  489.  
  490.   PString alias = H323GetAliasAddressString(cryptoEPPwdHash.m_alias);
  491.   if (!remoteId && alias != remoteId) {
  492.     PTRACE(1, "H235RAS\tH235AuthSimpleMD5 alias is \"" << alias
  493.            << "\", should be \"" << remoteId << '"');
  494.     return e_Error;
  495.   }
  496.  
  497.   // Build the clear token
  498.   H235_ClearToken clearToken;
  499.   clearToken.m_tokenOID = "0.0";
  500.  
  501.   clearToken.IncludeOptionalField(H235_ClearToken::e_generalID);
  502.   clearToken.m_generalID = GetUCS2plusNULL(alias);
  503.  
  504.   clearToken.IncludeOptionalField(H235_ClearToken::e_password);
  505.   clearToken.m_password = GetUCS2plusNULL(password);
  506.  
  507.   clearToken.IncludeOptionalField(H235_ClearToken::e_timeStamp);
  508.   clearToken.m_timeStamp = cryptoEPPwdHash.m_timeStamp;
  509.  
  510.   // Encode it into PER
  511.   PPER_Stream strm;
  512.   clearToken.Encode(strm);
  513.   strm.CompleteEncoding();
  514.  
  515.   // Generate an MD5 of the clear tokens PER encoding.
  516.   PMessageDigest5 stomach;
  517.   stomach.Process(strm.GetPointer(), strm.GetSize());
  518.   PMessageDigest5::Code digest;
  519.   stomach.Complete(digest);
  520.  
  521.   if (cryptoEPPwdHash.m_token.m_hash.GetSize() == sizeof(digest)*8 &&
  522.       memcmp(cryptoEPPwdHash.m_token.m_hash.GetDataPointer(), &digest, sizeof(digest)) == 0)
  523.     return e_OK;
  524.  
  525.   PTRACE(1, "H235RAS\tH235AuthSimpleMD5 digest does not match.");
  526.   return e_BadPassword;
  527. }
  528.  
  529.  
  530. BOOL H235AuthSimpleMD5::IsCapability(const H235_AuthenticationMechanism & mechanism,
  531.                                      const PASN_ObjectId & algorithmOID)
  532. {
  533.   return mechanism.GetTag() == H235_AuthenticationMechanism::e_pwdHash &&
  534.          algorithmOID.AsString() == OID_MD5;
  535. }
  536.  
  537.  
  538. BOOL H235AuthSimpleMD5::SetCapability(H225_ArrayOf_AuthenticationMechanism & mechanisms,
  539.                                       H225_ArrayOf_PASN_ObjectId & algorithmOIDs)
  540. {
  541.   return AddCapability(H235_AuthenticationMechanism::e_pwdHash, OID_MD5, mechanisms, algorithmOIDs);
  542. }
  543.  
  544.  
  545. BOOL H235AuthSimpleMD5::IsSecuredPDU(unsigned rasPDU, BOOL received) const
  546. {
  547.   switch (rasPDU) {
  548.     case H225_RasMessage::e_registrationRequest :
  549.     case H225_RasMessage::e_unregistrationRequest :
  550.     case H225_RasMessage::e_admissionRequest :
  551.     case H225_RasMessage::e_disengageRequest :
  552.     case H225_RasMessage::e_bandwidthRequest :
  553.     case H225_RasMessage::e_infoRequestResponse :
  554.       return received ? !remoteId.IsEmpty() : !localId.IsEmpty();
  555.  
  556.     default :
  557.       return FALSE;
  558.   }
  559. }
  560.  
  561.  
  562. ///////////////////////////////////////////////////////////////////////////////
  563.  
  564. static const char OID_CAT[] = "1.2.840.113548.10.1.2.1";
  565.  
  566. H235AuthCAT::H235AuthCAT()
  567. {
  568. }
  569.  
  570.  
  571. PObject * H235AuthCAT::Clone() const
  572. {
  573.   return new H235AuthCAT(*this);
  574. }
  575.  
  576.  
  577. const char * H235AuthCAT::GetName() const
  578. {
  579.   return "CAT";
  580. }
  581.  
  582.  
  583. H235_ClearToken * H235AuthCAT::CreateClearToken()
  584. {
  585.   if (!IsActive())
  586.     return NULL;
  587.  
  588.   if (localId.IsEmpty()) {
  589.     PTRACE(2, "H235RAS\tH235AuthCAT requires local ID for encoding.");
  590.     return NULL;
  591.   }
  592.  
  593.   H235_ClearToken * clearToken = new H235_ClearToken;
  594.  
  595.   // Cisco compatible hash calculation
  596.   clearToken->m_tokenOID = OID_CAT;
  597.  
  598.   clearToken->IncludeOptionalField(H235_ClearToken::e_generalID);
  599.   clearToken->m_generalID = GetUCS2plusNULL(localId);
  600.  
  601.   clearToken->IncludeOptionalField(H235_ClearToken::e_timeStamp);
  602.   clearToken->m_timeStamp = (int)time(NULL);
  603.   PUInt32b timeStamp = (DWORD)clearToken->m_timeStamp;
  604.  
  605.   clearToken->IncludeOptionalField(H235_ClearToken::e_random);
  606.   BYTE random = (BYTE)++sentRandomSequenceNumber;
  607.   clearToken->m_random = (unsigned)random;
  608.  
  609.   // Generate an MD5 of the clear tokens PER encoding.
  610.   PMessageDigest5 stomach;
  611.   stomach.Process(&random, 1);
  612.   stomach.Process(password);
  613.   stomach.Process(&timeStamp, 4);
  614.   PMessageDigest5::Code digest;
  615.   stomach.Complete(digest);
  616.  
  617.   clearToken->IncludeOptionalField(H235_ClearToken::e_challenge);
  618.   clearToken->m_challenge.SetValue((const BYTE *)&digest, sizeof(digest));
  619.  
  620.   return clearToken;
  621. }
  622.  
  623.  
  624. H235Authenticator::ValidationResult
  625.         H235AuthCAT::ValidateClearToken(const H235_ClearToken & clearToken)
  626. {
  627.   if (!IsActive())
  628.     return e_Disabled;
  629.  
  630.   if (clearToken.m_tokenOID != OID_CAT)
  631.     return e_Absent;
  632.  
  633.   if (!clearToken.HasOptionalField(H235_ClearToken::e_generalID) ||
  634.       !clearToken.HasOptionalField(H235_ClearToken::e_timeStamp) ||
  635.       !clearToken.HasOptionalField(H235_ClearToken::e_random) ||
  636.       !clearToken.HasOptionalField(H235_ClearToken::e_challenge)) {
  637.     PTRACE(2, "H235RAS\tCAT requires generalID, timeStamp, random and challenge fields");
  638.     return e_Error;
  639.   }
  640.  
  641.   //first verify the timestamp
  642.   PTime now;
  643.   int deltaTime = now.GetTimeInSeconds() - clearToken.m_timeStamp;
  644.   if (PABS(deltaTime) > timestampGracePeriod) {
  645.     PTRACE(1, "H235RAS\tInvalid timestamp ABS(" << now.GetTimeInSeconds() << '-' 
  646.            << (int)clearToken.m_timeStamp << ") > " << timestampGracePeriod);
  647.     //the time has elapsed
  648.     return e_InvalidTime;
  649.   }
  650.  
  651.   //verify the randomnumber
  652.   if (lastTimestamp == clearToken.m_timeStamp &&
  653.       lastRandomSequenceNumber == clearToken.m_random) {
  654.     //a message with this timespamp and the same random number was already verified
  655.     PTRACE(1, "H235RAS\tConsecutive messages with the same random and timestamp");
  656.     return e_ReplyAttack;
  657.   }
  658.  
  659.   // save the values for the next call
  660.   lastRandomSequenceNumber = clearToken.m_random;
  661.   lastTimestamp = clearToken.m_timeStamp;
  662.   
  663.   if (!remoteId && clearToken.m_generalID.GetValue() != remoteId) {
  664.     PTRACE(1, "H235RAS\tGeneral ID is \"" << clearToken.m_generalID.GetValue()
  665.            << "\", should be \"" << remoteId << '"');
  666.     return e_Error;
  667.   }
  668.  
  669.   int randomInt = clearToken.m_random;
  670.   if (randomInt < -127 || randomInt > 255) {
  671.     PTRACE(2, "H235RAS\tCAT requires single byte random field, got " << randomInt);
  672.     return e_Error;
  673.   }
  674.  
  675.   PUInt32b timeStamp = (DWORD)clearToken.m_timeStamp;
  676.   BYTE randomByte = (BYTE)randomInt;
  677.  
  678.   // Generate an MD5 of the clear tokens PER encoding.
  679.   PMessageDigest5 stomach;
  680.   stomach.Process(&randomByte, 1);
  681.   stomach.Process(password);
  682.   stomach.Process(&timeStamp, 4);
  683.   PMessageDigest5::Code digest;
  684.   stomach.Complete(digest);
  685.  
  686.   if (clearToken.m_challenge.GetValue().GetSize() != sizeof(digest)) {
  687.     PTRACE(2, "H235RAS\tCAT requires 16 byte challenge field");
  688.     return e_Error;
  689.   }
  690.  
  691.   if (memcmp((const BYTE *)clearToken.m_challenge.GetValue(), &digest, sizeof(digest)) == 0)
  692.     return e_OK;
  693.  
  694.   PTRACE(2, "H235RAS\tCAT hash does not match");
  695.   return e_BadPassword;
  696. }
  697.  
  698.  
  699. BOOL H235AuthCAT::IsCapability(const H235_AuthenticationMechanism & mechanism,
  700.                                      const PASN_ObjectId & algorithmOID)
  701. {
  702.   if (mechanism.GetTag() != H235_AuthenticationMechanism::e_authenticationBES ||
  703.          algorithmOID.AsString() != OID_CAT)
  704.     return FALSE;
  705.  
  706.   const H235_AuthenticationBES & bes = mechanism;
  707.   return bes.GetTag() == H235_AuthenticationBES::e_radius;
  708. }
  709.  
  710.  
  711. BOOL H235AuthCAT::SetCapability(H225_ArrayOf_AuthenticationMechanism & mechanisms,
  712.                                 H225_ArrayOf_PASN_ObjectId & algorithmOIDs)
  713. {
  714.   if (!AddCapability(H235_AuthenticationMechanism::e_authenticationBES, OID_CAT,
  715.                      mechanisms, algorithmOIDs))
  716.     return FALSE;
  717.  
  718.   H235_AuthenticationBES & bes = mechanisms[mechanisms.GetSize()-1];
  719.   bes.SetTag(H235_AuthenticationBES::e_radius);
  720.   return TRUE;
  721. }
  722.  
  723.  
  724. BOOL H235AuthCAT::IsSecuredPDU(unsigned rasPDU, BOOL received) const
  725. {
  726.   switch (rasPDU) {
  727.     case H225_RasMessage::e_registrationRequest :
  728.     case H225_RasMessage::e_admissionRequest :
  729.       return received ? !remoteId.IsEmpty() : !localId.IsEmpty();
  730.  
  731.     default :
  732.       return FALSE;
  733.   }
  734. }
  735.  
  736.  
  737. /////////////////////////////////////////////////////////////////////////////
  738.