示例#1
0
// Initialize the chain value.
void CallId::initialize()
{
   NetMd5Codec encoder;

   // Get the start time.
   OsTime currentTime;
   OsDateTime::getCurTime(currentTime);
   encoder.hash(&currentTime, sizeof(currentTime));

   // Get the process ID.
   PID processId;
   processId = OsProcess::getCurrentPID();
   encoder.hash(&processId, sizeof(processId));

   // Get the host identity.
   UtlString thisHost;
   OsSocket::getHostIp(&thisHost);
   encoder.hash(thisHost);

   Os::Logger::instance().log(FAC_SIP, PRI_DEBUG,
                 "CallId::initialize sChainValue");

   // Save the initial hash used to seed the sequence
   encoder.appendBase64Sig(sChainValue);

   // Note initialization is done.
   sChainValueInitialized = true;
}
示例#2
0
文件: CallId.cpp 项目: mranga/sipxecs
// Compute the next chain value.
void CallId::nextValue(const char* seed)
{
   // If we haven't initialized yet, do so.
   if (!sChainValueInitialized)
   {
      initialize();
   }

   // Get the time.
   OsTime currentTime;
   OsDateTime::getCurTime(currentTime);

   // Force usecs. to be 6 digits with leading zeros so that we do
   // not have to do 64 bit integer math just to build a big unique
   // string.
   char buffer[256];
   sprintf(buffer, "%s/%d%.6d/%.*s/%s",
           sKey.data(),
           currentTime.seconds(), currentTime.usecs(),
           MAX_SEED_CHARS, seed,
           sChainValue.data());

   // Hash them.
   NetMd5Codec encoder;
   encoder.encode(buffer, sChainValue);
   // Truncate the hash to CHAIN_VALUE_LENGTH characters.
   sChainValue.remove(CHAIN_VALUE_LENGTH);
}
示例#3
0
void SignedUrl::computeSignature( const UtlString& userInfo,
                                  const UtlString& hostPort,
                                  UtlString& signature )
{
    // signature-hash=MD5(<secret><userinfo><hostport>)
    NetMd5Codec signatureHash;

    signatureHash.hash( sSignatureSecret );
    if( !userInfo.isNull() )
    {
        signatureHash.hash( userInfo );
    }
    signatureHash.hash( hostPort );
    signature.remove(0);
    signatureHash.appendHashValue( signature );
}
示例#4
0
// Compute the next chain value.
void CallId::nextValue()
{
   NetMd5Codec encoder;

   // If we haven't initialized yet, do so.
   if (!sChainValueInitialized)
   {
      initialize();
   }

   // Use the previous chain value to seed the next one
   encoder.hash(sChainValue);

   // Get the time and hash it into the next value
   OsTime currentTime;
   OsDateTime::getCurTime(currentTime);
   encoder.hash(&currentTime, sizeof(currentTime));

   // Replace the old chain value with the new one
   sChainValue.remove(0);
   encoder.appendBase64Sig(sChainValue);
}
示例#5
0
文件: CallId.cpp 项目: mranga/sipxecs
// Initialize the chain value.
void CallId::initialize()
{
   // Get the start time.
   OsTime currentTime;
   OsDateTime::getCurTime(currentTime);

   // Get the process ID.
   PID processId;
   processId = OsProcess::getCurrentPID();

   // Get the host identity.
   UtlString thisHost;
   OsSocket::getHostIp(&thisHost);
   // Ensure it does not contain @.
   thisHost.replace('@','*');

   // Force usecs. to be 6 digits with leading zeros so that we do
   // not have to do 64 bit integer math just to build a big unique
   // string.
   char buffer[256];
   sprintf(buffer, "%d/%d.%.6d/%s",
           processId,
           currentTime.seconds(), currentTime.usecs(),
           thisHost.data());
   OsSysLog::add(FAC_SIP, PRI_DEBUG,
                 "CallId::initialize sChainValue generated from '%s'",
                 buffer);
   // Hash them.
   NetMd5Codec encoder;
   encoder.encode(buffer, sChainValue);
   // Truncate the hash to CHAIN_VALUE_LENGTH characters.
   sChainValue.remove(CHAIN_VALUE_LENGTH);

   // Note initialization is done.
   sChainValueInitialized = TRUE;
}
/// Encodes identity info
void SipXauthIdentity::encode(UtlString        & identityValue,
                              const UtlString  & callId,
                              const UtlString  & fromTag,
                              const OsDateTime & timestamp,
                              DialogRule       bindRule
                              )
{

   // calculate timestamp
   OsTime osTime;
   timestamp.cvtToTimeSinceEpoch(osTime);
   long seconds = osTime.seconds();
   char stamp[65];
   sprintf(stamp, "%lX", seconds);
   UtlString strSignature(stamp);

   strSignature.append(SignatureFieldSeparator);

   // signature-hash=MD5(<timestamp><secret><from-tag><call-id><identity>)
   NetMd5Codec signatureHash;
   signatureHash.hash(stamp);
   signatureHash.hash(sSignatureSecret);
   if (requireDialogBinding == bindRule)
   {
      signatureHash.hash(fromTag);
      signatureHash.hash(callId);
   }
   else
   {
      strSignature.append(SignatureFieldSeparator);
   }
   signatureHash.hash(mIdentity);

   UtlString strSignatureHash;
   signatureHash.appendHashValue(strSignature);

   Url encodedUrl(mIdentity);
   encodedUrl.setScheme(Url::SipUrlScheme);
   encodedUrl.setUrlParameter(SignatureUrlParamName, strSignature.data());

   encodedUrl.toString(identityValue);

   Os::Logger::instance().log(FAC_SIP, PRI_DEBUG,
                 "SipXauthIdentity::encode "
                 "identity '%s'",
                 identityValue.data()
                 );
}
示例#7
0
RedirectPlugin::LookUpStatus
SipRedirectorJoin::lookUpDialog(
   const UtlString& requestString,
   const UtlString& incomingCallId,
   ContactList& contactList,
   RedirectPlugin::RequestSeqNo requestSeqNo,
   int redirectorNo,
   SipRedirectorPrivateStorage*& privateStorage,
   const char* subscribeUser,
   State stateFilter)
{
   Os::Logger::instance().log(FAC_SIP, PRI_DEBUG,
                 "%s::lookUpDialog requestString = '%s', "
                 "requestSeqNo = %d, redirectorNo = %d, privateStorage = %p, "
                 "subscribeUser = '******', stateFilter = %d",
                 mLogName.data(),
                 requestString.data(), requestSeqNo, redirectorNo,
                 privateStorage, subscribeUser, stateFilter);

   // If the private storage is already allocated, then this is a
   // reprocessing cycle, and the dialog to pick up (if any) is
   // recorded in private storage.
   if (privateStorage)
   {
      // Cast the private storage pointer to the right type so we can
      // access the needed dialog information.
      SipRedirectorPrivateStorageJoin* dialog_info =
         dynamic_cast<SipRedirectorPrivateStorageJoin*> (privateStorage);

      if (dialog_info->mTargetDialogDuration !=
          SipRedirectorPrivateStorageJoin::TargetDialogDurationAbsent)
      {
         // A dialog has been recorded.  Construct a contact for it.
         // Beware that as recorded in the dialog event notice, the
         // target URI is in addr-spec format; any parameters are URI
         // parameters.  (Field parameters have been broken out in
         // param elements.)
         Url contact_URI(dialog_info->mTargetDialogLocalURI, TRUE);

         // Construct the Join: header value the caller should use.
         UtlString header_value(dialog_info->mTargetDialogCallId);
         // Note that according to RFC 3891, the to-tag parameter is
         // the local tag at the destination of the INVITE/Join.
         // But the INVITE/Join goes to the end of the call that
         // we queried with SUBSCRIBE, so the to-tag in the
         // Join: header is the *local* tag from the NOTIFY.
         header_value.append(";to-tag=");
         header_value.append(dialog_info->mTargetDialogLocalTag);
         header_value.append(";from-tag=");
         header_value.append(dialog_info->mTargetDialogRemoteTag);
         // Add a header parameter to specify the Join: header.
         contact_URI.setHeaderParameter("Join", header_value.data());

         // We add a header parameter to cause the redirection to
         // include a "Require: join" header.  Then if the caller
         // phone does not support INVITE/Join:, the pick-up will
         // fail entirely.  Without it, if the caller phone does not
         // support INVITE/Join, the caller will get a
         // simultaneous incoming call from the executing phone.
         // Previously, we thought the latter behavior was better, but
         // it is not -- Consider if the device is a gateway from the
         // PSTN.  Then the INVITE/Join will generate an outgoing
         // call to the calling phone.
         contact_URI.setHeaderParameter(SIP_REQUIRE_FIELD,
                                        SIP_JOIN_EXTENSION);

         // Record the URI as a contact.
         contactList.add( contact_URI, *this );
      }

      // We do not need to suspend this time.
      return RedirectPlugin::SUCCESS;
   }
   else
   {
      UtlString userId;
      Url requestUri(requestString);
      requestUri.getUserId(userId);
      Os::Logger::instance().log(FAC_SIP, PRI_DEBUG,
                    "%s::lookUpDialog userId '%s'",
                    mLogName.data(), userId.data());

      // Construct the SUBSCRIBE for the call join.
      SipMessage subscribe;
      UtlString subscribeRequestUri("sip:");
      // The user of the request URI is our subscribeUser parameter.
      subscribeRequestUri.append(subscribeUser);
      subscribeRequestUri.append("@");
      subscribeRequestUri.append(mDomain);

      // Construct a Call-Id for the SUBSCRIBE.
      UtlString callId;
      CallId::getNewCallId(callId);

      // Construct the From: value.
      UtlString fromUri;
      {
         // Get the local address and port.
         UtlString address;
         int port;
         mpSipUserAgent->getLocalAddress(&address, &port);
         // Use the first 8 chars of the MD5 of the Call-Id as the from-tag.
         NetMd5Codec encoder;
         UtlString tag;
         encoder.encode(callId.data(), tag);
         tag.remove(8);
         // Assemble the URI.
         SipMessage::buildSipUri(&fromUri,
                                 address.data(),
                                 port,
                                 NULL, // protocol
                                 NULL, // user
                                 NULL, // userLabel,
                                 tag.data());
      }

      // Set the standard request headers.
      // Allow the SipUserAgent to fill in Contact:.
      subscribe.setRequestData(
         SIP_SUBSCRIBE_METHOD,
         subscribeRequestUri.data(), // request URI
         fromUri, // From:
         subscribeRequestUri.data(), // To:
         callId,
         mCSeq);
      // Increment CSeq and roll it over if necessary.
      mCSeq++;
      mCSeq &= 0x0FFFFFFF;
      // Set the Expires header.
      // If "1 second subscriptions" is set (needed for some versions
      // of Snom phones), use a 1-second subscription.  Otherwise, use
      // a 0-second subscription, so we get just one NOTIFY.
      subscribe.setExpiresField(mOneSecondSubscription ? 1 : 0);
      // Set the "Event: dialog" header.
      subscribe.setEventField("dialog");
      // Set the "Accept: application/dialog-info+xml" header.
      // Not strictly necessary (per the I-D), but it makes the SUBSCRIBE
      // more strictly compliant.
      subscribe.setHeaderValue(SIP_ACCEPT_FIELD,
                               DIALOG_EVENT_CONTENT_TYPE);
      // Set the References header for tracing dialog associations.
      {
         UtlString referencesValue(incomingCallId);
         referencesValue.append(";rel=inquiry");
         subscribe.setHeaderValue(SIP_REFERENCES_FIELD,
                                  referencesValue);
      }

      // Send the SUBSCRIBE.
      mpSipUserAgent->send(subscribe);

      // Allocate private storage.
      SipRedirectorPrivateStorageJoin *storage =
         new SipRedirectorPrivateStorageJoin(requestSeqNo,
                                             redirectorNo);
      privateStorage = storage;

      // Record the Call-Id of the SUBSCRIBE, so we can correlated the
      // NOTIFYs with it.
      storage->mSubscribeCallId = callId;
      // Record the state filtering criterion.
      storage->mStateFilter = stateFilter;

      // If we are printing debug messages, record when the SUBSCRIBE
      // was sent, so we can report how long it took to get the NOTIFYs.
      if (Os::Logger::instance().willLog(FAC_SIP, PRI_DEBUG))
      {
         OsDateTime::getCurTime(storage->mSubscribeSendTime);
      }

      // Set the timer to resume.
      storage->mTimer.oneshotAfter(OsTime(mWaitSecs, mWaitUSecs));

      // Suspend processing the request.
      return RedirectPlugin::SEARCH_PENDING;
   }
}
/// Check the signature and parse the identity
bool SipXauthIdentity::decode(const UtlString& identityValue,
                              const UtlString& callId,
                              const UtlString& fromTag,
                              DialogRule       bindRule
                              )
{
  /**
   * See SipXauthIdentity Encoding comment at the top of the file
   */
   Os::Logger::instance().log(FAC_SIP, PRI_DEBUG,
                 "SipXauthIdentity::decode "
                 "parse '%s'",
                 identityValue.data()
                 );
   mIdentity.remove(0);
   mIsValidIdentity = FALSE;

   bool decodeError = false; // false iff the identity was correctly signed and successfully parsed
   UtlString decodedIdentity;
   unsigned long epochTimestamp = 0;
   UtlString timestamp;
   UtlString actualSignatureHash;
   bool isBound = false;

   Url encodedUrl(identityValue, Url::NameAddr);
   if (Url::SipUrlScheme == encodedUrl.getScheme())
   {
      // Only proceed if the URL parsing succeeded
      // Extract the identity
      encodedUrl.getIdentity(decodedIdentity);

      // Extract signature parameter
      UtlString signatureParamValue;
      if (encodedUrl.getUrlParameter(SignatureUrlParamName, signatureParamValue))
      {
         // only proceed if signature parameter was found
         RegEx signatureRegEx(SignatureRegEx);

         if (signatureRegEx.Search(signatureParamValue))
         {
            UtlString secondSeparator;

            isBound = (   signatureRegEx.MatchString(&secondSeparator,2)
                       && secondSeparator.isNull()); // there is only one ':' separator

            if (   (requireDialogBinding == bindRule) // must be bound
                && ! isBound
                )
            {
               decodeError = true;
               Os::Logger::instance().log(FAC_SIP, PRI_WARNING,
                             "SipXauthIdentity::decode "
                             "'%s' is an unbound identity",
                             identityValue.data()
                             );
            }
            else
            {
               // extract timestamp
               if (   !signatureRegEx.MatchString(&timestamp,1)
                   || !from_string(epochTimestamp, timestamp)
                   )
               {
                  decodeError = true;
                  Os::Logger::instance().log(FAC_SIP, PRI_WARNING,
                                "SipXauthIdentity::decode "
                                "'%s' invalid timestamp",
                                identityValue.data()
                                );
               }
               // extract signature
               else if (!signatureRegEx.MatchString(&actualSignatureHash,3))
               {
                  decodeError = true;
                  Os::Logger::instance().log(FAC_SIP, PRI_WARNING,
                                "SipXauthIdentity::decode '%s' missing hash",
                                identityValue.data()
                                );
               }
            }
         }
         else
         {
            decodeError = true;
            Os::Logger::instance().log(FAC_SIP, PRI_WARNING,
                          "SipXauthIdentity::decode "
                          "'%s' invalid signature format",
                          identityValue.data()
                          );
         }
      }
      else
      {
         decodeError = true;
         Os::Logger::instance().log(FAC_SIP, PRI_WARNING,
                       "SipXauthIdentity::decode "
                       "'%s' missing '%s' param",
                       identityValue.data(), SignatureUrlParamName
                       );
      }
   }
   else
   {
      decodeError = true;
      Os::Logger::instance().log(FAC_SIP, PRI_WARNING,
                    "SipXauthIdentity::decode "
                    "'%s' URL parsing failed",
                     identityValue.data()
                    );
   }

   // validate timestamp
   if (!decodeError && !sSignatureValidityInterval.isNoWait())
   {
      // timestamp validity check
      if (epochTimestamp + sSignatureValidityInterval.seconds() < OsDateTime::getSecsSinceEpoch())
      {
         decodeError = true;
         OsDateTime generateDate(OsTime(epochTimestamp,0));
         UtlString generateTimeString;
         generateDate.getIsoTimeStringZ(generateTimeString);
         Os::Logger::instance().log(FAC_SIP, PRI_WARNING,
                       "SipXauthIdentity::decode(%s)"
                       " timestamp '%lX' from '%s' too old with interval of '%d' seconds",
                       identityValue.data(), epochTimestamp, generateTimeString.data(),
                       sSignatureValidityInterval.seconds()
                       );
      }
   }

   // validate signature hash
   if (!decodeError)
   {
      UtlString validSignature;

      // signature-hash=MD5(<timestamp><secret><from-tag><call-id><identity>)
      NetMd5Codec signatureHash;
      signatureHash.hash(timestamp);
      signatureHash.hash(sSignatureSecret);
      if (isBound)
      {
         signatureHash.hash(fromTag);
         signatureHash.hash(callId);
      }
      signatureHash.hash(decodedIdentity);
      signatureHash.appendHashValue(validSignature);

      if (validSignature.compareTo(actualSignatureHash) == 0)
      {
         // the signature checks out
         mIdentity = decodedIdentity;
         mIsValidIdentity = TRUE;
      }
      else
      {
         Os::Logger::instance().log(FAC_SIP, PRI_WARNING,
                       "SipXauthIdentity::decode "
                       "'%s' invalid signature '%s' != '%s'",
                       identityValue.data(), actualSignatureHash.data(), validSignature.data()
                       );
      }
   }

   return mIsValidIdentity;
}