D:/Storage/CVS_Head/h323plus/src/h235auth1.cxx

00001 /*
00002  * h235auth1.cxx
00003  *
00004  * H.235 security PDU's
00005  *
00006  * Open H323 Library
00007  *
00008  * Copyright (c) 1998-2001 Equivalence Pty. Ltd.
00009  *
00010  * The contents of this file are subject to the Mozilla Public License
00011  * Version 1.0 (the "License"); you may not use this file except in
00012  * compliance with the License. You may obtain a copy of the License at
00013  * http://www.mozilla.org/MPL/
00014  *
00015  * Software distributed under the License is distributed on an "AS IS"
00016  * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
00017  * the License for the specific language governing rights and limitations
00018  * under the License.
00019  *
00020  * The Original Code is Open H323 Library.
00021  *
00022  * The Initial Developer of the Original Code is Equivalence Pty. Ltd.
00023  *
00024  * Contributor(s): Fürbass Franz <franz.fuerbass@infonova.at>
00025  *
00026  * $Log: h235auth1.cxx,v $
00027  * Revision 1.2  2007/08/20 19:13:28  shorne
00028  * Added Generic Capability support. Fixed Linux compile errors
00029  *
00030  * Revision 1.1  2007/08/06 20:51:05  shorne
00031  * First commit of h323plus
00032  *
00033  * Revision 1.20.2.3  2007/07/19 19:57:36  shorne
00034  * added missiing secure signal PDU check
00035  *
00036  * Revision 1.20.2.2  2007/05/23 07:03:37  shorne
00037  * Renamed Authenticator just to H.235.1
00038  *
00039  * Revision 1.20.2.1  2007/04/19 15:01:36  shorne
00040  * Added missing IsSecurePDU to H235.1 authenticator
00041  *
00042  * Revision 1.20  2006/06/23 03:15:58  shorne
00043  * Updated H.235 class name
00044  *
00045  * Revision 1.19  2006/02/13 11:24:23  csoutheren
00046  * Fixed problem with H235 authenticator factory not being populated
00047  *
00048  * Revision 1.18  2006/01/26 03:25:55  shorne
00049  * Caller Authentication added
00050  *
00051  * Revision 1.17  2005/02/03 02:46:07  csoutheren
00052  * Altered authentication functions to only update state information if authentication
00053  *  is correct. Thanks to Michal Zygmuntowicz
00054  *
00055  * Revision 1.16  2005/01/04 08:08:45  csoutheren
00056  * More changes to implement the new configuration methodology, and also to
00057  * attack the global static problem
00058  *
00059  * Revision 1.15  2004/11/12 06:04:44  csoutheren
00060  * Changed H235Authentiators to use PFactory
00061  *
00062  * Revision 1.14  2003/04/17 12:19:15  robertj
00063  * Added windows automatic library inclusion for openssl.
00064  *
00065  * Revision 1.13  2003/02/01 13:31:22  robertj
00066  * Changes to support CAT authentication in RAS.
00067  *
00068  * Revision 1.12  2003/01/27 23:15:44  robertj
00069  * Added more trace logs
00070  *
00071  * Revision 1.11  2003/01/08 04:40:34  robertj
00072  * Added more debug tracing for H.235 authenticators.
00073  *
00074  * Revision 1.10  2002/08/13 05:10:29  robertj
00075  * Fixed bug where incorrect PIN caused infinite loop.
00076  *
00077  * Revision 1.9  2002/08/05 05:17:41  robertj
00078  * Fairly major modifications to support different authentication credentials
00079  *   in ARQ to the logged in ones on RRQ. For both client and server.
00080  * Various other H.235 authentication bugs and anomalies fixed on the way.
00081  *
00082  * Revision 1.8  2002/07/25 00:56:23  robertj
00083  * Added logging of timestamps used if authorisation declined for that reason.
00084  *
00085  * Revision 1.7  2002/07/24 06:38:57  robertj
00086  * Fixed GNU compatibility
00087  *
00088  * Revision 1.6  2002/07/24 06:35:53  robertj
00089  * Fixed timestamp check in PDU to assure use of UTC and banded grace time.
00090  *
00091  * Revision 1.5  2002/05/17 03:40:25  robertj
00092  * Fixed problems with H.235 authentication on RAS for server and client.
00093  *
00094  * Revision 1.4  2001/12/06 06:44:42  robertj
00095  * Removed "Win32 SSL xxx" build configurations in favour of system
00096  *   environment variables to select optional libraries.
00097  *
00098  * Revision 1.3  2001/09/13 01:15:20  robertj
00099  * Added flag to H235Authenticator to determine if gkid and epid is to be
00100  *   automatically set as the crypto token remote id and local id.
00101  *
00102  * Revision 1.2  2001/08/14 05:24:41  robertj
00103  * Added support for H.235v1 and H.235v2 specifications.
00104  *
00105  * Revision 1.1  2001/08/10 11:03:52  robertj
00106  * Major changes to H.235 support in RAS to support server.
00107  *
00108  */
00109 
00110 #include <ptlib.h>
00111 
00112 #if P_SSL
00113 
00114 #include <openssl/sha.h>
00115 
00116 #include "h235auth.h"
00117 #include "h323pdu.h"
00118 
00119 namespace PWLibStupidLinkerHacks {
00120   int h235AuthProcedure1Loader;
00121 };
00122 
00123 #ifdef _MSC_VER
00124 #pragma comment(lib, P_SSL_LIB1)
00125 #pragma comment(lib, P_SSL_LIB2)
00126 #endif
00127 
00128 
00129 #define REPLY_BUFFER_SIZE 1024
00130 
00131 
00132 static const char OID_A[] = "0.0.8.235.0.2.1";
00133 static const char OID_T[] = "0.0.8.235.0.2.5";
00134 static const char OID_U[] = "0.0.8.235.0.2.6";
00135 
00136 #define OID_VERSION_OFFSET 5
00137 
00138 
00139 #define HASH_SIZE 12
00140 
00141 static const BYTE SearchPattern[HASH_SIZE] = { // Must be 12 bytes
00142   't', 'W', 'e', 'l', 'V', 'e', '~', 'b', 'y', 't', 'e', 'S'
00143 };
00144 
00145 #ifndef SHA_DIGESTSIZE
00146 #define SHA_DIGESTSIZE  20
00147 #endif
00148 
00149 #ifndef SHA_BLOCKSIZE
00150 #define SHA_BLOCKSIZE   64
00151 #endif
00152 
00153 
00154 #define new PNEW
00155 
00156 
00158 
00159 /* Function to print the digest */
00160 #if 0
00161 static void pr_sha(FILE* fp, char* s, int t)
00162 {
00163         int     i ;
00164 
00165         fprintf(fp, "0x") ;
00166         for (i = 0 ; i < t ; i++)
00167                 fprintf(fp, "%02x", s[i]) ;
00168         fprintf(fp, "0x") ;
00169 }
00170 #endif
00171 
00172 static void truncate(unsigned char*    d1,    /* data to be truncated */
00173                      char*             d2,    /* truncated data */
00174                      int               len)   /* length in bytes to keep */
00175 {
00176         int     i ;
00177         for (i = 0 ; i < len ; i++) d2[i] = d1[i];
00178 }
00179 
00180 /* Function to compute the digest */
00181 static void hmac_sha (const unsigned char*    k,      /* secret key */
00182                       int      lk,              /* length of the key in bytes */
00183                       const unsigned char*    d,      /* data */
00184                       int      ld,              /* length of data in bytes */
00185                       char*    out,             /* output buffer, at least "t" bytes */
00186                       int      t)
00187 {
00188         SHA_CTX ictx, octx ;
00189         unsigned char    isha[SHA_DIGESTSIZE], osha[SHA_DIGESTSIZE] ;
00190         unsigned char    key[SHA_DIGESTSIZE] ;
00191         char    buf[SHA_BLOCKSIZE] ;
00192         int     i ;
00193 
00194         if (lk > SHA_BLOCKSIZE) {
00195 
00196                 SHA_CTX         tctx ;
00197 
00198                 SHA1_Init(&tctx) ;
00199                 SHA1_Update(&tctx, k, lk) ;
00200                 SHA1_Final(key, &tctx) ;
00201 
00202                 k = key ;
00203                 lk = SHA_DIGESTSIZE ;
00204         }
00205 
00206         /**** Inner Digest ****/
00207 
00208         SHA1_Init(&ictx) ;
00209 
00210         /* Pad the key for inner digest */
00211         for (i = 0 ; i < lk ; ++i) buf[i] = (char)(k[i] ^ 0x36);
00212         for (i = lk ; i < SHA_BLOCKSIZE ; ++i) buf[i] = 0x36;
00213 
00214         SHA1_Update(&ictx, buf, SHA_BLOCKSIZE) ;
00215         SHA1_Update(&ictx, d, ld) ;
00216 
00217         SHA1_Final(isha, &ictx) ;
00218 
00219         /**** Outter Digest ****/
00220 
00221         SHA1_Init(&octx) ;
00222 
00223         /* Pad the key for outter digest */
00224 
00225         for (i = 0 ; i < lk ; ++i) buf[i] = (char)(k[i] ^ 0x5C);
00226         for (i = lk ; i < SHA_BLOCKSIZE ; ++i) buf[i] = 0x5C;
00227 
00228         SHA1_Update(&octx, buf, SHA_BLOCKSIZE) ;
00229         SHA1_Update(&octx, isha, SHA_DIGESTSIZE) ;
00230 
00231         SHA1_Final(osha, &octx) ;
00232 
00233         /* truncate and print the results */
00234         t = t > SHA_DIGESTSIZE ? SHA_DIGESTSIZE : t ;
00235         truncate(osha, out, t) ;
00236 
00237 }
00238 
00239 
00241 
00242 static PFactory<H235Authenticator>::Worker<H2351_Authenticator> factoryH2351_Authenticator("H2351_Authenticator");
00243 
00244 H2351_Authenticator::H2351_Authenticator()
00245 {
00246         usage = AnyApplication;  // Can be used either for GKAdmission or EPAuthenticstion
00247 }
00248 
00249 
00250 PObject * H2351_Authenticator::Clone() const
00251 {
00252   H2351_Authenticator * auth = new H2351_Authenticator(*this);
00253 
00254   // We do NOT copy these fields in Clone()
00255   auth->lastRandomSequenceNumber = 0;
00256   auth->lastTimestamp = 0;
00257 
00258   return auth;
00259 }
00260 
00261 
00262 const char * H2351_Authenticator::GetName() const
00263 {
00264   return "H.235.1";
00265 }
00266 
00267 
00268 H225_CryptoH323Token * H2351_Authenticator::CreateCryptoToken()
00269 {
00270   if (!IsActive())
00271     return NULL;
00272 
00273   H225_CryptoH323Token * cryptoToken = new H225_CryptoH323Token;
00274 
00275   // Create the H.225 crypto token in the H323 crypto token
00276   cryptoToken->SetTag(H225_CryptoH323Token::e_nestedcryptoToken);
00277   H235_CryptoToken & nestedCryptoToken = *cryptoToken;
00278 
00279   // We are doing hashed password
00280   nestedCryptoToken.SetTag(H235_CryptoToken::e_cryptoHashedToken);
00281   H235_CryptoToken_cryptoHashedToken & cryptoHashedToken = nestedCryptoToken;
00282 
00283   // tokenOID = "A"
00284   cryptoHashedToken.m_tokenOID = OID_A;
00285   
00286   //ClearToken
00287   H235_ClearToken & clearToken = cryptoHashedToken.m_hashedVals;
00288   
00289   // tokenOID = "T"
00290   clearToken.m_tokenOID  = OID_T;
00291   
00292   if (!remoteId) {
00293     clearToken.IncludeOptionalField(H235_ClearToken::e_generalID);
00294     clearToken.m_generalID = remoteId;
00295   }
00296 
00297   if (!localId) {
00298     clearToken.IncludeOptionalField(H235_ClearToken::e_sendersID);
00299     clearToken.m_sendersID = localId;
00300   }
00301   
00302   clearToken.IncludeOptionalField(H235_ClearToken::e_timeStamp);
00303   clearToken.m_timeStamp = (int)PTime().GetTimeInSeconds();
00304 
00305   clearToken.IncludeOptionalField(H235_ClearToken::e_random);
00306   clearToken.m_random = ++sentRandomSequenceNumber;
00307 
00308   //H235_HASHED
00309   H235_HASHED<H235_EncodedGeneralToken> & encodedToken = cryptoHashedToken.m_token;
00310   
00311   //  algorithmOID = "U"
00312   encodedToken.m_algorithmOID = OID_U;
00313 
00314 
00315   /*******
00316    * step 1
00317    *
00318    * set a pattern for the hash value
00319    *
00320    */
00321 
00322   encodedToken.m_hash.SetData(HASH_SIZE*8, SearchPattern);
00323   return cryptoToken;
00324 }
00325 
00326 
00327 BOOL H2351_Authenticator::Finalise(PBYTEArray & rawPDU)
00328 {
00329   if (!IsActive())
00330     return FALSE;
00331 
00332   // Find the pattern
00333 
00334   int foundat = -1;
00335   for (PINDEX i = 0; i <= rawPDU.GetSize() - HASH_SIZE; i++) {
00336     if (memcmp(&rawPDU[i], SearchPattern, HASH_SIZE) == 0) { // i'v found it !
00337       foundat = i;
00338       break;
00339     }
00340   }
00341   
00342   if (foundat == -1) {
00343     //Can't find the search pattern in the ASN1 packet.
00344     PTRACE(2, "H235RAS\tPDU not prepared for H2351_Authenticator");
00345     return FALSE;
00346   }
00347   
00348   // Zero out the search pattern
00349   memset(&rawPDU[foundat], 0, HASH_SIZE);
00350 
00351  /*******
00352   * 
00353   * generate a HMAC-SHA1 key over the hole message
00354   * and save it in at (step 3) located position.
00355   * in the asn1 packet.
00356   */
00357   
00358   char key[HASH_SIZE];
00359  
00361   unsigned char secretkey[20];
00362   
00363   SHA1((unsigned char *)password.GetPointer(), password.GetSize()-1, secretkey);
00364 
00365   hmac_sha(secretkey, 20, rawPDU.GetPointer(), rawPDU.GetSize(), key, HASH_SIZE);
00366   
00367   memcpy(&rawPDU[foundat], key, HASH_SIZE);
00368   
00369   PTRACE(4, "H235RAS\tH2351_Authenticator hashing completed: \"" << password << '"');
00370   return TRUE;
00371 }
00372 
00373 
00374 static BOOL CheckOID(const PASN_ObjectId & oid1, const PASN_ObjectId & oid2)
00375 {
00376   if (oid1.GetSize() != oid2.GetSize())
00377     return FALSE;
00378 
00379   PINDEX i;
00380   for (i = 0; i < OID_VERSION_OFFSET; i++) {
00381     if (oid1[i] != oid2[i])
00382       return FALSE;
00383   }
00384 
00385   for (i++; i < oid1.GetSize(); i++) {
00386     if (oid1[i] != oid2[i])
00387       return FALSE;
00388   }
00389 
00390   return TRUE;
00391 }
00392 
00393 
00394 H235Authenticator::ValidationResult H2351_Authenticator::ValidateCryptoToken(
00395                                             const H225_CryptoH323Token & cryptoToken,
00396                                             const PBYTEArray & rawPDU)
00397 {
00398   //verify the token is of correct type
00399   if (cryptoToken.GetTag() != H225_CryptoH323Token::e_nestedcryptoToken) {
00400     PTRACE(4, "H235\tNo nested crypto token!");
00401     return e_Absent;
00402   }
00403   
00404   const H235_CryptoToken & crNested = cryptoToken;
00405   if (crNested.GetTag() != H235_CryptoToken::e_cryptoHashedToken) {
00406     PTRACE(4, "H235\tNo crypto hash token!");
00407     return e_Absent;
00408   }
00409   
00410   const H235_CryptoToken_cryptoHashedToken & crHashed = crNested;
00411   
00412   //verify the crypto OIDs
00413   
00414   // "A" indicates that the whole messages is used for authentication.
00415   if (!CheckOID(crHashed.m_tokenOID, OID_A)) {
00416     PTRACE(2, "H235RAS\tH2351_Authenticator requires all fields are hashed, got OID " << crHashed.m_tokenOID);
00417     return e_Absent;
00418   }
00419   
00420   // "T" indicates that the hashed token of the CryptoToken is used for authentication.
00421   if (!CheckOID(crHashed.m_hashedVals.m_tokenOID, OID_T)) {
00422     PTRACE(2, "H235RAS\tH2351_Authenticator requires ClearToken, got OID " << crHashed.m_hashedVals.m_tokenOID);
00423     return e_Absent;
00424   }
00425   
00426   // "U" indicates that the HMAC-SHA1-96 alorigthm is used.
00427   if (!CheckOID(crHashed.m_token.m_algorithmOID, OID_U)) {
00428     PTRACE(2, "H235RAS\tH2351_Authenticator requires HMAC-SHA1-96, got OID " << crHashed.m_token.m_algorithmOID);
00429     return e_Absent;
00430   }
00431   
00432   //first verify the timestamp
00433   PTime now;
00434   int deltaTime = now.GetTimeInSeconds() - crHashed.m_hashedVals.m_timeStamp;
00435   if (PABS(deltaTime) > timestampGracePeriod) {
00436     PTRACE(1, "H235RAS\tInvalid timestamp ABS(" << now.GetTimeInSeconds() << '-' 
00437            << (int)crHashed.m_hashedVals.m_timeStamp << ") > " << timestampGracePeriod);
00438     //the time has elapsed
00439     return e_InvalidTime;
00440   }
00441   
00442   //verify the randomnumber
00443   if (lastTimestamp == crHashed.m_hashedVals.m_timeStamp &&
00444       lastRandomSequenceNumber == crHashed.m_hashedVals.m_random) {
00445     //a message with this timespamp and the same random number was already verified
00446     PTRACE(1, "H235RAS\tConsecutive messages with the same random and timestamp");
00447     return e_ReplyAttack;
00448   }
00449   
00450 #ifndef DISABLE_CALLAUTH
00451   // If has connection then EP Authenticator so CallBack to Check SenderID and Set Password
00452   if (connection != NULL) { 
00453         // Senders ID is required for signal authentication
00454     if (!crHashed.m_hashedVals.HasOptionalField(H235_ClearToken::e_sendersID)) {
00455       PTRACE(1, "H235RAS\tH2351_Authenticator requires senders ID.");
00456       return e_Error;
00457     }
00458 
00459         localId = crHashed.m_hashedVals.m_sendersID.GetValue();
00460         remoteId = PString::Empty();
00461         if (!connection->OnCallAuthentication(localId,password)) {
00462         PTRACE(1, "H235EP\tH2351_Authenticator Authentication Fail UserName \""  
00463                         << localId << "\", not Authorised. \"");
00464         return e_BadPassword;
00465         }
00466   } else {
00467 #endif
00468   //verify the username
00469       if (!localId && crHashed.m_tokenOID[OID_VERSION_OFFSET] > 1) {
00470         if (!crHashed.m_hashedVals.HasOptionalField(H235_ClearToken::e_generalID)) {
00471           PTRACE(1, "H235RAS\tH2351_Authenticator requires general ID.");
00472           return e_Error;
00473         }
00474   
00475         if (crHashed.m_hashedVals.m_generalID.GetValue() != localId) {
00476            PTRACE(1, "H235RAS\tGeneral ID is \"" << crHashed.m_hashedVals.m_generalID.GetValue()
00477                  << "\", should be \"" << localId << '"');
00478           return e_Error;
00479         }
00480      }
00481 #ifndef DISABLE_CALLAUTH
00482   }
00483 #endif
00484 
00485   if (!remoteId) {
00486     if (!crHashed.m_hashedVals.HasOptionalField(H235_ClearToken::e_sendersID)) {
00487       PTRACE(1, "H235RAS\tH2351_Authenticator requires senders ID.");
00488       return e_Error;
00489     }
00490   
00491     if (crHashed.m_hashedVals.m_sendersID.GetValue() != remoteId) {
00492       PTRACE(1, "H235RAS\tSenders ID is \"" << crHashed.m_hashedVals.m_sendersID.GetValue()
00493              << "\", should be \"" << remoteId << '"');
00494       return e_Error;
00495     }
00496   }
00497 
00498   
00499   /****
00500   * step 1
00501   * extract the variable hash and save it
00502   *
00503   */
00504   BYTE RV[HASH_SIZE];
00505   
00506   if (crHashed.m_token.m_hash.GetSize() != HASH_SIZE*8) {
00507     PTRACE(2, "H235RAS\tH2351_Authenticator requires a hash!");
00508     return e_Error;
00509   }
00510   
00511   const unsigned char *data = crHashed.m_token.m_hash.GetDataPointer();
00512   memcpy(RV, data, HASH_SIZE);
00513   
00514   unsigned char secretkey[20];
00515   SHA1((unsigned char *)password.GetPointer(), password.GetSize()-1, secretkey);
00516    
00517   
00518   /****
00519   * step 4
00520   * lookup the variable int the orginal ASN1 packet
00521   * and set it to 0.
00522   */
00523   PINDEX foundat = 0;
00524   bool found = false;
00525   
00526   const BYTE * asnPtr = rawPDU;
00527   PINDEX asnLen = rawPDU.GetSize();
00528   while (foundat < asnLen - HASH_SIZE) {
00529     for (PINDEX i = foundat; i <= asnLen - HASH_SIZE; i++) {
00530       if (memcmp(asnPtr+i, data, HASH_SIZE) == 0) { // i'v found it !
00531         foundat = i;
00532         found = true;
00533         break;
00534       }
00535     }
00536     
00537     if (!found) {
00538       if (foundat != 0)
00539         break;
00540 
00541       PTRACE(2, "H235RAS\tH2351_Authenticator could not locate embedded hash!");
00542       return e_Error;
00543     }
00544     
00545     found = false;
00546     
00547     memset((BYTE *)asnPtr+foundat, 0, HASH_SIZE);
00548     
00549     /****
00550     * step 5
00551     * generate a HMAC-SHA1 key over the hole packet
00552     *
00553     */
00554     
00555     char key[HASH_SIZE];
00556     hmac_sha(secretkey, 20, asnPtr, asnLen, key, HASH_SIZE);
00557     
00558     /****
00559     * step 6
00560     * compare the two keys
00561     *
00562     */
00563     if (memcmp(key, RV, HASH_SIZE) == 0) { // Keys are the same !! Ok 
00564       // save the values for the next call
00565       lastRandomSequenceNumber = crHashed.m_hashedVals.m_random;
00566       lastTimestamp = crHashed.m_hashedVals.m_timeStamp;
00567   
00568       return e_OK;
00569     }
00570 
00571     // Put it back and look for another
00572     memcpy((BYTE *)asnPtr+foundat, data, HASH_SIZE);
00573     foundat++;
00574   }
00575 
00576   PTRACE(1, "H235RAS\tH2351_Authenticator hash does not match.");
00577   return e_BadPassword;
00578 }
00579 
00580 
00581 BOOL H2351_Authenticator::IsCapability(const H235_AuthenticationMechanism & mechansim,
00582                                       const PASN_ObjectId & algorithmOID)
00583 {
00584   return mechansim.GetTag() == H235_AuthenticationMechanism::e_pwdHash &&
00585          algorithmOID.AsString() == OID_U;
00586 }
00587 
00588 
00589 BOOL H2351_Authenticator::SetCapability(H225_ArrayOf_AuthenticationMechanism & mechanisms,
00590                                       H225_ArrayOf_PASN_ObjectId & algorithmOIDs)
00591 {
00592   return AddCapability(H235_AuthenticationMechanism::e_pwdHash, OID_U, mechanisms, algorithmOIDs);
00593 }
00594 
00595 BOOL H2351_Authenticator::IsSecuredPDU(unsigned rasPDU, BOOL received) const
00596 {
00597   switch (rasPDU) {
00598     case H225_RasMessage::e_registrationRequest :
00599     case H225_RasMessage::e_admissionRequest :
00600       return received ? !remoteId.IsEmpty() : !localId.IsEmpty();
00601 
00602     default :
00603       return FALSE;
00604   }  
00605 }
00606 
00607 BOOL H2351_Authenticator::IsSecuredSignalPDU(unsigned signalPDU, BOOL received) const
00608 {
00609   switch (signalPDU) {
00610     case H225_H323_UU_PDU_h323_message_body::e_setup:       
00611       return received ? !remoteId.IsEmpty() : !localId.IsEmpty();
00612 
00613     default :
00614       return FALSE;
00615   }
00616 }
00617 
00618 BOOL H2351_Authenticator::UseGkAndEpIdentifiers() const
00619 {
00620   return TRUE;
00621 }
00622 
00623 
00624 #endif // P_SSL
00625 
00626 

Generated on Thu Oct 25 13:42:53 2007 for h323plus by  doxygen 1.5.2