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 / h235auth1.cxx < prev    next >
C/C++ Source or Header  |  2003-04-17  |  15KB  |  544 lines

  1. /*
  2.  * h235auth1.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): Fⁿrbass Franz <franz.fuerbass@infonova.at>
  25.  *
  26.  * $Log: h235auth1.cxx,v $
  27.  * Revision 1.14  2003/04/17 12:19:15  robertj
  28.  * Added windows automatic library inclusion for openssl.
  29.  *
  30.  * Revision 1.13  2003/02/01 13:31:22  robertj
  31.  * Changes to support CAT authentication in RAS.
  32.  *
  33.  * Revision 1.12  2003/01/27 23:15:44  robertj
  34.  * Added more trace logs
  35.  *
  36.  * Revision 1.11  2003/01/08 04:40:34  robertj
  37.  * Added more debug tracing for H.235 authenticators.
  38.  *
  39.  * Revision 1.10  2002/08/13 05:10:29  robertj
  40.  * Fixed bug where incorrect PIN caused infinite loop.
  41.  *
  42.  * Revision 1.9  2002/08/05 05:17:41  robertj
  43.  * Fairly major modifications to support different authentication credentials
  44.  *   in ARQ to the logged in ones on RRQ. For both client and server.
  45.  * Various other H.235 authentication bugs and anomalies fixed on the way.
  46.  *
  47.  * Revision 1.8  2002/07/25 00:56:23  robertj
  48.  * Added logging of timestamps used if authorisation declined for that reason.
  49.  *
  50.  * Revision 1.7  2002/07/24 06:38:57  robertj
  51.  * Fixed GNU compatibility
  52.  *
  53.  * Revision 1.6  2002/07/24 06:35:53  robertj
  54.  * Fixed timestamp check in PDU to assure use of UTC and banded grace time.
  55.  *
  56.  * Revision 1.5  2002/05/17 03:40:25  robertj
  57.  * Fixed problems with H.235 authentication on RAS for server and client.
  58.  *
  59.  * Revision 1.4  2001/12/06 06:44:42  robertj
  60.  * Removed "Win32 SSL xxx" build configurations in favour of system
  61.  *   environment variables to select optional libraries.
  62.  *
  63.  * Revision 1.3  2001/09/13 01:15:20  robertj
  64.  * Added flag to H235Authenticator to determine if gkid and epid is to be
  65.  *   automatically set as the crypto token remote id and local id.
  66.  *
  67.  * Revision 1.2  2001/08/14 05:24:41  robertj
  68.  * Added support for H.235v1 and H.235v2 specifications.
  69.  *
  70.  * Revision 1.1  2001/08/10 11:03:52  robertj
  71.  * Major changes to H.235 support in RAS to support server.
  72.  *
  73.  */
  74.  
  75. #include <ptlib.h>
  76.  
  77. #if P_SSL
  78.  
  79. #include <openssl/sha.h>
  80.  
  81. #include "h235auth.h"
  82. #include "h323pdu.h"
  83.  
  84.  
  85. #ifdef _MSC_VER
  86. #pragma comment(lib, P_SSL_LIB1)
  87. #pragma comment(lib, P_SSL_LIB2)
  88. #endif
  89.  
  90.  
  91. #define REPLY_BUFFER_SIZE 1024
  92.  
  93.  
  94. static const char OID_A[] = "0.0.8.235.0.2.1";
  95. static const char OID_T[] = "0.0.8.235.0.2.5";
  96. static const char OID_U[] = "0.0.8.235.0.2.6";
  97.  
  98. #define OID_VERSION_OFFSET 5
  99.  
  100.  
  101. #define HASH_SIZE 12
  102.  
  103. static const BYTE SearchPattern[HASH_SIZE] = { // Must be 12 bytes
  104.   't', 'W', 'e', 'l', 'V', 'e', '~', 'b', 'y', 't', 'e', 'S'
  105. };
  106.  
  107. #ifndef SHA_DIGESTSIZE
  108. #define SHA_DIGESTSIZE  20
  109. #endif
  110.  
  111. #ifndef SHA_BLOCKSIZE
  112. #define SHA_BLOCKSIZE   64
  113. #endif
  114.  
  115.  
  116. #define new PNEW
  117.  
  118.  
  119. /////////////////////////////////////////////////////////////////////////////
  120.  
  121. /* Function to print the digest */
  122. #if 0
  123. static void pr_sha(FILE* fp, char* s, int t)
  124. {
  125.         int     i ;
  126.  
  127.         fprintf(fp, "0x") ;
  128.         for (i = 0 ; i < t ; i++)
  129.                 fprintf(fp, "%02x", s[i]) ;
  130.         fprintf(fp, "0x") ;
  131. }
  132. #endif
  133.  
  134. static void truncate(unsigned char*    d1,    /* data to be truncated */
  135.                      char*             d2,    /* truncated data */
  136.                      int               len)   /* length in bytes to keep */
  137. {
  138.         int     i ;
  139.         for (i = 0 ; i < len ; i++) d2[i] = d1[i];
  140. }
  141.  
  142. /* Function to compute the digest */
  143. static void hmac_sha (const unsigned char*    k,      /* secret key */
  144.                       int      lk,              /* length of the key in bytes */
  145.                       const unsigned char*    d,      /* data */
  146.                       int      ld,              /* length of data in bytes */
  147.                       char*    out,             /* output buffer, at least "t" bytes */
  148.                       int      t)
  149. {
  150.         SHA_CTX ictx, octx ;
  151.         unsigned char    isha[SHA_DIGESTSIZE], osha[SHA_DIGESTSIZE] ;
  152.         unsigned char    key[SHA_DIGESTSIZE] ;
  153.         char    buf[SHA_BLOCKSIZE] ;
  154.         int     i ;
  155.  
  156.         if (lk > SHA_BLOCKSIZE) {
  157.  
  158.                 SHA_CTX         tctx ;
  159.  
  160.                 SHA1_Init(&tctx) ;
  161.                 SHA1_Update(&tctx, k, lk) ;
  162.                 SHA1_Final(key, &tctx) ;
  163.  
  164.                 k = key ;
  165.                 lk = SHA_DIGESTSIZE ;
  166.         }
  167.  
  168.         /**** Inner Digest ****/
  169.  
  170.         SHA1_Init(&ictx) ;
  171.  
  172.         /* Pad the key for inner digest */
  173.         for (i = 0 ; i < lk ; ++i) buf[i] = (char)(k[i] ^ 0x36);
  174.         for (i = lk ; i < SHA_BLOCKSIZE ; ++i) buf[i] = 0x36;
  175.  
  176.         SHA1_Update(&ictx, buf, SHA_BLOCKSIZE) ;
  177.         SHA1_Update(&ictx, d, ld) ;
  178.  
  179.         SHA1_Final(isha, &ictx) ;
  180.  
  181.         /**** Outter Digest ****/
  182.  
  183.         SHA1_Init(&octx) ;
  184.  
  185.         /* Pad the key for outter digest */
  186.  
  187.         for (i = 0 ; i < lk ; ++i) buf[i] = (char)(k[i] ^ 0x5C);
  188.         for (i = lk ; i < SHA_BLOCKSIZE ; ++i) buf[i] = 0x5C;
  189.  
  190.         SHA1_Update(&octx, buf, SHA_BLOCKSIZE) ;
  191.         SHA1_Update(&octx, isha, SHA_DIGESTSIZE) ;
  192.  
  193.         SHA1_Final(osha, &octx) ;
  194.  
  195.         /* truncate and print the results */
  196.         t = t > SHA_DIGESTSIZE ? SHA_DIGESTSIZE : t ;
  197.         truncate(osha, out, t) ;
  198.  
  199. }
  200.  
  201.  
  202. /////////////////////////////////////////////////////////////////////////////
  203.  
  204. H235AuthProcedure1::H235AuthProcedure1()
  205. {
  206. }
  207.  
  208.  
  209. PObject * H235AuthProcedure1::Clone() const
  210. {
  211.   H235AuthProcedure1 * auth = new H235AuthProcedure1(*this);
  212.  
  213.   // We do NOT copy these fields in Clone()
  214.   auth->lastRandomSequenceNumber = 0;
  215.   auth->lastTimestamp = 0;
  216.  
  217.   return auth;
  218. }
  219.  
  220.  
  221. const char * H235AuthProcedure1::GetName() const
  222. {
  223.   return "H235AnnexD_Procedure1";
  224. }
  225.  
  226.  
  227. H225_CryptoH323Token * H235AuthProcedure1::CreateCryptoToken()
  228. {
  229.   if (!IsActive())
  230.     return NULL;
  231.  
  232.   H225_CryptoH323Token * cryptoToken = new H225_CryptoH323Token;
  233.  
  234.   // Create the H.225 crypto token in the H323 crypto token
  235.   cryptoToken->SetTag(H225_CryptoH323Token::e_nestedcryptoToken);
  236.   H235_CryptoToken & nestedCryptoToken = *cryptoToken;
  237.  
  238.   // We are doing hashed password
  239.   nestedCryptoToken.SetTag(H235_CryptoToken::e_cryptoHashedToken);
  240.   H235_CryptoToken_cryptoHashedToken & cryptoHashedToken = nestedCryptoToken;
  241.  
  242.   // tokenOID = "A"
  243.   cryptoHashedToken.m_tokenOID = OID_A;
  244.   
  245.   //ClearToken
  246.   H235_ClearToken & clearToken = cryptoHashedToken.m_hashedVals;
  247.   
  248.   // tokenOID = "T"
  249.   clearToken.m_tokenOID  = OID_T;
  250.   
  251.   if (!remoteId) {
  252.     clearToken.IncludeOptionalField(H235_ClearToken::e_generalID);
  253.     clearToken.m_generalID = remoteId;
  254.   }
  255.  
  256.   if (!localId) {
  257.     clearToken.IncludeOptionalField(H235_ClearToken::e_sendersID);
  258.     clearToken.m_sendersID = localId;
  259.   }
  260.   
  261.   clearToken.IncludeOptionalField(H235_ClearToken::e_timeStamp);
  262.   clearToken.m_timeStamp = (int)PTime().GetTimeInSeconds();
  263.  
  264.   clearToken.IncludeOptionalField(H235_ClearToken::e_random);
  265.   clearToken.m_random = ++sentRandomSequenceNumber;
  266.  
  267.   //H235_HASHED
  268.   H235_HASHED<H235_EncodedGeneralToken> & encodedToken = cryptoHashedToken.m_token;
  269.   
  270.   //  algorithmOID = "U"
  271.   encodedToken.m_algorithmOID = OID_U;
  272.  
  273.  
  274.   /*******
  275.    * step 1
  276.    *
  277.    * set a pattern for the hash value
  278.    *
  279.    */
  280.  
  281.   encodedToken.m_hash.SetData(HASH_SIZE*8, SearchPattern);
  282.   return cryptoToken;
  283. }
  284.  
  285.  
  286. BOOL H235AuthProcedure1::Finalise(PBYTEArray & rawPDU)
  287. {
  288.   if (!IsActive())
  289.     return FALSE;
  290.  
  291.   // Find the pattern
  292.  
  293.   int foundat = -1;
  294.   for (PINDEX i = 0; i <= rawPDU.GetSize() - HASH_SIZE; i++) {
  295.     if (memcmp(&rawPDU[i], SearchPattern, HASH_SIZE) == 0) { // i'v found it !
  296.       foundat = i;
  297.       break;
  298.     }
  299.   }
  300.   
  301.   if (foundat == -1) {
  302.     //Can't find the search pattern in the ASN1 packet.
  303.     PTRACE(2, "H235RAS\tPDU not prepared for H235AuthProcedure1");
  304.     return FALSE;
  305.   }
  306.   
  307.   // Zero out the search pattern
  308.   memset(&rawPDU[foundat], 0, HASH_SIZE);
  309.  
  310.  /*******
  311.   * 
  312.   * generate a HMAC-SHA1 key over the hole message
  313.   * and save it in at (step 3) located position.
  314.   * in the asn1 packet.
  315.   */
  316.   
  317.   char key[HASH_SIZE];
  318.  
  319.   /** make a SHA1 hash before send to the hmac_sha1 */
  320.   unsigned char secretkey[20];
  321.   
  322.   SHA1((unsigned char *)password.GetPointer(), password.GetSize()-1, secretkey);
  323.  
  324.   hmac_sha(secretkey, 20, rawPDU.GetPointer(), rawPDU.GetSize(), key, HASH_SIZE);
  325.   
  326.   memcpy(&rawPDU[foundat], key, HASH_SIZE);
  327.   
  328.   PTRACE(4, "H235RAS\tH235AuthProcedure1 hashing completed: \"" << password << '"');
  329.   return TRUE;
  330. }
  331.  
  332.  
  333. static BOOL CheckOID(const PASN_ObjectId & oid1, const PASN_ObjectId & oid2)
  334. {
  335.   if (oid1.GetSize() != oid2.GetSize())
  336.     return FALSE;
  337.  
  338.   PINDEX i;
  339.   for (i = 0; i < OID_VERSION_OFFSET; i++) {
  340.     if (oid1[i] != oid2[i])
  341.       return FALSE;
  342.   }
  343.  
  344.   for (i++; i < oid1.GetSize(); i++) {
  345.     if (oid1[i] != oid2[i])
  346.       return FALSE;
  347.   }
  348.  
  349.   return TRUE;
  350. }
  351.  
  352.  
  353. H235Authenticator::ValidationResult H235AuthProcedure1::ValidateCryptoToken(
  354.                                             const H225_CryptoH323Token & cryptoToken,
  355.                                             const PBYTEArray & rawPDU)
  356. {
  357.   //verify the token is of correct type
  358.   if (cryptoToken.GetTag() != H225_CryptoH323Token::e_nestedcryptoToken) {
  359.     PTRACE(4, "H235\tNo nested crypto token!");
  360.     return e_Absent;
  361.   }
  362.   
  363.   const H235_CryptoToken & crNested = cryptoToken;
  364.   if (crNested.GetTag() != H235_CryptoToken::e_cryptoHashedToken) {
  365.     PTRACE(4, "H235\tNo crypto hash token!");
  366.     return e_Absent;
  367.   }
  368.   
  369.   const H235_CryptoToken_cryptoHashedToken & crHashed = crNested;
  370.   
  371.   //verify the crypto OIDs
  372.   
  373.   // "A" indicates that the whole messages is used for authentication.
  374.   if (!CheckOID(crHashed.m_tokenOID, OID_A)) {
  375.     PTRACE(2, "H235RAS\tH235AuthProcedure1 requires all fields are hashed, got OID " << crHashed.m_tokenOID);
  376.     return e_Absent;
  377.   }
  378.   
  379.   // "T" indicates that the hashed token of the CryptoToken is used for authentication.
  380.   if (!CheckOID(crHashed.m_hashedVals.m_tokenOID, OID_T)) {
  381.     PTRACE(2, "H235RAS\tH235AuthProcedure1 requires ClearToken, got OID " << crHashed.m_hashedVals.m_tokenOID);
  382.     return e_Absent;
  383.   }
  384.   
  385.   // "U" indicates that the HMAC-SHA1-96 alorigthm is used.
  386.   if (!CheckOID(crHashed.m_token.m_algorithmOID, OID_U)) {
  387.     PTRACE(2, "H235RAS\tH235AuthProcedure1 requires HMAC-SHA1-96, got OID " << crHashed.m_token.m_algorithmOID);
  388.     return e_Absent;
  389.   }
  390.   
  391.   //first verify the timestamp
  392.   PTime now;
  393.   int deltaTime = now.GetTimeInSeconds() - crHashed.m_hashedVals.m_timeStamp;
  394.   if (PABS(deltaTime) > timestampGracePeriod) {
  395.     PTRACE(1, "H235RAS\tInvalid timestamp ABS(" << now.GetTimeInSeconds() << '-' 
  396.            << (int)crHashed.m_hashedVals.m_timeStamp << ") > " << timestampGracePeriod);
  397.     //the time has elapsed
  398.     return e_InvalidTime;
  399.   }
  400.   
  401.   //verify the randomnumber
  402.   if (lastTimestamp == crHashed.m_hashedVals.m_timeStamp &&
  403.       lastRandomSequenceNumber == crHashed.m_hashedVals.m_random) {
  404.     //a message with this timespamp and the same random number was already verified
  405.     PTRACE(1, "H235RAS\tConsecutive messages with the same random and timestamp");
  406.     return e_ReplyAttack;
  407.   }
  408.   
  409.   // save the values for the next call
  410.   lastRandomSequenceNumber = crHashed.m_hashedVals.m_random;
  411.   lastTimestamp = crHashed.m_hashedVals.m_timeStamp;
  412.   
  413.   //verify the username
  414.   if (!localId && crHashed.m_tokenOID[OID_VERSION_OFFSET] > 1) {
  415.     if (!crHashed.m_hashedVals.HasOptionalField(H235_ClearToken::e_generalID)) {
  416.       PTRACE(1, "H235RAS\tH235AuthProcedure1 requires general ID.");
  417.       return e_Error;
  418.     }
  419.   
  420.     if (crHashed.m_hashedVals.m_generalID.GetValue() != localId) {
  421.       PTRACE(1, "H235RAS\tGeneral ID is \"" << crHashed.m_hashedVals.m_generalID.GetValue()
  422.              << "\", should be \"" << localId << '"');
  423.       return e_Error;
  424.     }
  425.   }
  426.  
  427.   if (!remoteId) {
  428.     if (!crHashed.m_hashedVals.HasOptionalField(H235_ClearToken::e_sendersID)) {
  429.       PTRACE(1, "H235RAS\tH235AuthProcedure1 requires senders ID.");
  430.       return e_Error;
  431.     }
  432.   
  433.     if (crHashed.m_hashedVals.m_sendersID.GetValue() != remoteId) {
  434.       PTRACE(1, "H235RAS\tSenders ID is \"" << crHashed.m_hashedVals.m_sendersID.GetValue()
  435.              << "\", should be \"" << remoteId << '"');
  436.       return e_Error;
  437.     }
  438.   }
  439.  
  440.   
  441.   /****
  442.   * step 1
  443.   * extract the variable hash and save it
  444.   *
  445.   */
  446.   BYTE RV[HASH_SIZE];
  447.   
  448.   if (crHashed.m_token.m_hash.GetSize() != HASH_SIZE*8) {
  449.     PTRACE(2, "H235RAS\tH235AuthProcedure1 requires a hash!");
  450.     return e_Error;
  451.   }
  452.   
  453.   const unsigned char *data = crHashed.m_token.m_hash.GetDataPointer();
  454.   memcpy(RV, data, HASH_SIZE);
  455.   
  456.   unsigned char secretkey[20];
  457.   SHA1((unsigned char *)password.GetPointer(), password.GetSize()-1, secretkey);
  458.     
  459.   
  460.   /****
  461.   * step 4
  462.   * lookup the variable int the orginal ASN1 packet
  463.   * and set it to 0.
  464.   */
  465.   PINDEX foundat = 0;
  466.   bool found = false;
  467.   
  468.   const BYTE * asnPtr = rawPDU;
  469.   PINDEX asnLen = rawPDU.GetSize();
  470.   while (foundat < asnLen - HASH_SIZE) {
  471.     for (PINDEX i = foundat; i <= asnLen - HASH_SIZE; i++) {
  472.       if (memcmp(asnPtr+i, data, HASH_SIZE) == 0) { // i'v found it !
  473.         foundat = i;
  474.         found = true;
  475.         break;
  476.       }
  477.     }
  478.     
  479.     if (!found) {
  480.       if (foundat != 0)
  481.         break;
  482.  
  483.       PTRACE(2, "H235RAS\tH235AuthProcedure1 could not locate embedded hash!");
  484.       return e_Error;
  485.     }
  486.     
  487.     found = false;
  488.     
  489.     memset((BYTE *)asnPtr+foundat, 0, HASH_SIZE);
  490.     
  491.     /****
  492.     * step 5
  493.     * generate a HMAC-SHA1 key over the hole packet
  494.     *
  495.     */
  496.     
  497.     char key[HASH_SIZE];
  498.     hmac_sha(secretkey, 20, asnPtr, asnLen, key, HASH_SIZE);
  499.     
  500.     
  501.     /****
  502.     * step 6
  503.     * compare the two keys
  504.     *
  505.     */
  506.     if(memcmp(key, RV, HASH_SIZE) == 0) // Keys are the same !! Ok
  507.       return e_OK;
  508.  
  509.     // Put it back and look for another
  510.     memcpy((BYTE *)asnPtr+foundat, data, HASH_SIZE);
  511.     foundat++;
  512.   }
  513.  
  514.   PTRACE(1, "H235RAS\tH235AuthProcedure1 hash does not match.");
  515.   return e_BadPassword;
  516. }
  517.  
  518.  
  519. BOOL H235AuthProcedure1::IsCapability(const H235_AuthenticationMechanism & mechansim,
  520.                                       const PASN_ObjectId & algorithmOID)
  521. {
  522.   return mechansim.GetTag() == H235_AuthenticationMechanism::e_pwdHash &&
  523.          algorithmOID.AsString() == OID_U;
  524. }
  525.  
  526.  
  527. BOOL H235AuthProcedure1::SetCapability(H225_ArrayOf_AuthenticationMechanism & mechanisms,
  528.                                       H225_ArrayOf_PASN_ObjectId & algorithmOIDs)
  529. {
  530.   return AddCapability(H235_AuthenticationMechanism::e_pwdHash, OID_U, mechanisms, algorithmOIDs);
  531. }
  532.  
  533.  
  534. BOOL H235AuthProcedure1::UseGkAndEpIdentifiers() const
  535. {
  536.   return TRUE;
  537. }
  538.  
  539.  
  540. #endif // P_SSL
  541.  
  542.  
  543. /////////////////////////////////////////////////////////////////////////////
  544.