NS_IMETHODIMP
nsDataSignatureVerifier::VerifySignature(const char* aRSABuf,
                                         uint32_t aRSABufLen,
                                         const char* aPlaintext,
                                         uint32_t aPlaintextLen,
                                         int32_t* aErrorCode,
                                         nsICertificatePrincipal** aPrincipal)
{
  if (!aPlaintext || !aPrincipal || !aErrorCode) {
    return NS_ERROR_INVALID_ARG;
  }

  *aErrorCode = VERIFY_ERROR_OTHER;
  *aPrincipal = nullptr;

  nsNSSShutDownPreventionLock locker;

  Digest digest;
  nsresult rv = digest.DigestBuf(SEC_OID_SHA1,
                                 reinterpret_cast<const uint8_t*>(aPlaintext),
                                 aPlaintextLen);
  if (NS_WARN_IF(NS_FAILED(rv))) {
    return rv;
  }

  SECItem buffer = {
    siBuffer,
    reinterpret_cast<uint8_t*>(const_cast<char*>(aRSABuf)),
    aRSABufLen
  };

  VerifyCertificateContext context;
  // XXX: pinArg is missing
  rv = VerifyCMSDetachedSignatureIncludingCertificate(buffer, digest.get(),
                                                      VerifyCertificate,
                                                      &context, nullptr);
  if (NS_SUCCEEDED(rv)) {
    *aErrorCode = VERIFY_OK;
  } else if (NS_ERROR_GET_MODULE(rv) == NS_ERROR_MODULE_SECURITY) {
    if (rv == GetXPCOMFromNSSError(SEC_ERROR_UNKNOWN_ISSUER)) {
      *aErrorCode = VERIFY_ERROR_UNKNOWN_ISSUER;
    } else {
      *aErrorCode = VERIFY_ERROR_OTHER;
    }
    rv = NS_OK;
  }
  if (rv == NS_OK) {
    context.principal.forget(aPrincipal);
  }

  return rv;
}
Beispiel #2
0
NS_IMETHODIMP
nsNSSCertificate::GetSha256SubjectPublicKeyInfoDigest(nsACString& aSha256SPKIDigest)
{
  nsNSSShutDownPreventionLock locker;
  if (isAlreadyShutDown()) {
    return NS_ERROR_NOT_AVAILABLE;
  }

  aSha256SPKIDigest.Truncate();
  Digest digest;
  nsresult rv = digest.DigestBuf(SEC_OID_SHA256, mCert->derPublicKey.data,
                                 mCert->derPublicKey.len);
  if (NS_WARN_IF(NS_FAILED(rv))) {
    return rv;
  }
  rv = Base64Encode(nsDependentCSubstring(
                      reinterpret_cast<const char*> (digest.get().data),
                      digest.get().len),
                    aSha256SPKIDigest);
  if (NS_WARN_IF(NS_FAILED(rv))) {
    return rv;
  }
  return NS_OK;
}
// Called on the worker thread.
bool
BackgroundFileSaver::CheckCompletion()
{
  nsresult rv;

  MOZ_ASSERT(!mAsyncCopyContext,
             "Should not be copying when checking completion conditions.");

  bool failed = true;
  {
    MutexAutoLock lock(mLock);

    if (mComplete) {
      return true;
    }

    // If an error occurred, we don't need to do the checks in this code block,
    // and the operation can be completed immediately with a failure code.
    if (NS_SUCCEEDED(mStatus)) {
      failed = false;

      // We did not incur in an error, so we must determine if we can stop now.
      // If the Finish method has not been called, we can just continue now.
      if (!mFinishRequested) {
        return false;
      }

      // We can only stop when all the operations requested by the control
      // thread have been processed.  First, we check whether we have processed
      // the first SetTarget call, if any.  Then, we check whether we have
      // processed any rename requested by subsequent SetTarget calls.
      if ((mInitialTarget && !mActualTarget) ||
          (mRenamedTarget && mRenamedTarget != mActualTarget)) {
        return false;
      }

      // If we still have data to write to the output file, allow the copy
      // operation to resume.  The Available getter may return an error if one
      // of the pipe's streams has been already closed.
      uint64_t available;
      rv = mPipeInputStream->Available(&available);
      if (NS_SUCCEEDED(rv) && available != 0) {
        return false;
      }
    }

    mComplete = true;
  }

  // Ensure we notify completion now that the operation finished.
  // Do a best-effort attempt to remove the file if required.
  if (failed && mActualTarget && !mActualTargetKeepPartial) {
    (void)mActualTarget->Remove(false);
  }

  // Finish computing the hash
  if (!failed && mDigestContext) {
    nsNSSShutDownPreventionLock lock;
    if (!isAlreadyShutDown()) {
      Digest d;
      rv = d.End(SEC_OID_SHA256, mDigestContext);
      if (NS_SUCCEEDED(rv)) {
        MutexAutoLock lock(mLock);
        mSha256 = nsDependentCSubstring(char_ptr_cast(d.get().data),
                                        d.get().len);
      }
    }
  }

  // Compute the signature of the binary. ExtractSignatureInfo doesn't do
  // anything on non-Windows platforms except return an empty nsIArray.
  if (!failed && mActualTarget) {
    nsString filePath;
    mActualTarget->GetTarget(filePath);
    nsresult rv = ExtractSignatureInfo(filePath);
    if (NS_FAILED(rv)) {
      LOG(("Unable to extract signature information [this = %p].", this));
    } else {
      LOG(("Signature extraction success! [this = %p]", this));
    }
  }

  // Post an event to notify that the operation completed.
  if (NS_FAILED(mControlThread->Dispatch(NewRunnableMethod(this,
                                                           &BackgroundFileSaver::NotifySaveComplete),
                                         NS_DISPATCH_NORMAL))) {
    NS_WARNING("Unable to post completion event to the control thread.");
  }

  return true;
}
/*
 * Verify the signature of a directory structure as if it were a
 * signed JAR file (used for unpacked JARs)
 */
nsresult
VerifySignedDirectory(AppTrustedRoot aTrustedRoot,
                      nsIFile* aDirectory,
                      /*out, optional */ nsIX509Cert** aSignerCert)
{
  NS_ENSURE_ARG_POINTER(aDirectory);

  if (aSignerCert) {
    *aSignerCert = nullptr;
  }

  // Make sure there's a META-INF directory

  nsCOMPtr<nsIFile> metaDir;
  nsresult rv = aDirectory->Clone(getter_AddRefs(metaDir));
  if (NS_FAILED(rv)) {
    return rv;
  }
  rv = metaDir->Append(NS_LITERAL_STRING(JAR_META_DIR));
  if (NS_FAILED(rv)) {
    return rv;
  }

  bool exists;
  rv = metaDir->Exists(&exists);
  if (NS_FAILED(rv) || !exists) {
    return NS_ERROR_SIGNED_JAR_NOT_SIGNED;
  }
  bool isDirectory;
  rv = metaDir->IsDirectory(&isDirectory);
  if (NS_FAILED(rv) || !isDirectory) {
    return NS_ERROR_SIGNED_JAR_NOT_SIGNED;
  }

  // Find and load the Signature (RSA) file

  nsAutoString sigFilename;
  rv = FindSignatureFilename(metaDir, sigFilename);
  if (NS_FAILED(rv)) {
    return rv;
  }

  ScopedAutoSECItem sigBuffer;
  rv = LoadOneMetafile(metaDir, sigFilename, sigBuffer, nullptr);
  if (NS_FAILED(rv)) {
    return NS_ERROR_SIGNED_JAR_NOT_SIGNED;
  }

  // Load the signature (SF) file and verify the signature.
  // The .sf and .rsa files must have the same name apart from the extension.

  nsAutoString sfFilename(Substring(sigFilename, 0, sigFilename.Length() - 3)
                          + NS_LITERAL_STRING("sf"));

  ScopedAutoSECItem sfBuffer;
  Digest sfCalculatedDigest;
  rv = LoadOneMetafile(metaDir, sfFilename, sfBuffer, &sfCalculatedDigest);
  if (NS_FAILED(rv)) {
    return NS_ERROR_SIGNED_JAR_MANIFEST_INVALID;
  }

  sigBuffer.type = siBuffer;
  ScopedCERTCertList builtChain;
  rv = VerifySignature(aTrustedRoot, sigBuffer, sfCalculatedDigest.get(),
                       builtChain);
  if (NS_FAILED(rv)) {
    return NS_ERROR_SIGNED_JAR_MANIFEST_INVALID;
  }

  // Get the expected manifest hash from the signed .sf file

  ScopedAutoSECItem mfDigest;
  rv = ParseSF(char_ptr_cast(sfBuffer.data), mfDigest);
  if (NS_FAILED(rv)) {
    return NS_ERROR_SIGNED_JAR_MANIFEST_INVALID;
  }

  // Load manifest (MF) file and verify signature

  nsAutoString mfFilename(NS_LITERAL_STRING("manifest.mf"));
  ScopedAutoSECItem manifestBuffer;
  Digest mfCalculatedDigest;
  rv = LoadOneMetafile(metaDir, mfFilename, manifestBuffer, &mfCalculatedDigest);
  if (NS_FAILED(rv)) {
    return NS_ERROR_SIGNED_JAR_MANIFEST_INVALID;
  }

  if (SECITEM_CompareItem(&mfDigest, &mfCalculatedDigest.get()) != SECEqual) {
    return NS_ERROR_SIGNED_JAR_MANIFEST_INVALID;
  }

  // Parse manifest and verify signed hash of all listed files

  // Allocate the I/O buffer only once per JAR, instead of once per entry, in
  // order to minimize malloc/free calls and in order to avoid fragmenting
  // memory.
  ScopedAutoSECItem buf(128 * 1024);

  nsTHashtable<nsStringHashKey> items;
  rv = ParseMFUnpacked(char_ptr_cast(manifestBuffer.data),
                       aDirectory, items, buf);
  if (NS_FAILED(rv)){
    return rv;
  }

  // We've checked that everything listed in the manifest exists and is signed
  // correctly. Now check on disk for extra (unsigned) files.
  // Deletes found entries from items as it goes.
  rv = CheckDirForUnsignedFiles(aDirectory, EmptyString(), items,
                                sigFilename, sfFilename, mfFilename);
  if (NS_FAILED(rv)) {
    return rv;
  }

  // We verified that every entry that we require to be signed is signed. But,
  // were there any missing entries--that is, entries that are mentioned in the
  // manifest but missing from the directory tree? (There shouldn't be given
  // ParseMFUnpacked() checking them all, but it's a cheap sanity check.)
  if (items.Count() != 0) {
    return NS_ERROR_SIGNED_JAR_ENTRY_MISSING;
  }

  // Return the signer's certificate to the reader if they want it.
  // XXX: We should return an nsIX509CertList with the whole validated chain.
  if (aSignerCert) {
    MOZ_ASSERT(CERT_LIST_HEAD(builtChain));
    nsCOMPtr<nsIX509Cert> signerCert =
      nsNSSCertificate::Create(CERT_LIST_HEAD(builtChain)->cert);
    NS_ENSURE_TRUE(signerCert, NS_ERROR_OUT_OF_MEMORY);
    signerCert.forget(aSignerCert);
  }

  return NS_OK;
}
// Called on the worker thread.
bool
BackgroundFileSaver::CheckCompletion()
{
  nsresult rv;

  MOZ_ASSERT(!mAsyncCopyContext,
             "Should not be copying when checking completion conditions.");

  bool failed = true;
  {
    MutexAutoLock lock(mLock);

    if (mComplete) {
      return true;
    }

    // If an error occurred, we don't need to do the checks in this code block,
    // and the operation can be completed immediately with a failure code.
    if (NS_SUCCEEDED(mStatus)) {
      failed = false;

      // On success, if there is a pending rename operation, we must process it
      // before finishing.  Otherwise, we can finish now if requested.
      if ((mAssignedTarget && mAssignedTarget != mActualTarget) ||
          !mFinishRequested) {
        return false;
      }

      // If completion was requested, but we still have data to write to the
      // output file, allow the copy operation to resume.  The Available getter
      // may return an error if one of the pipe's streams has been already closed.
      uint64_t available;
      rv = mPipeInputStream->Available(&available);
      if (NS_SUCCEEDED(rv) && available != 0) {
        return false;
      }
    }

    mComplete = true;
  }

  // Ensure we notify completion now that the operation finished.
  // Do a best-effort attempt to remove the file if required.
  if (failed && mActualTarget && !mActualTargetKeepPartial) {
    (void)mActualTarget->Remove(false);
  }

  // Finish computing the hash
  if (!failed && mDigestContext) {
    nsNSSShutDownPreventionLock lock;
    if (!isAlreadyShutDown()) {
      Digest d;
      rv = d.End(SEC_OID_SHA256, mDigestContext);
      if (NS_SUCCEEDED(rv)) {
        MutexAutoLock lock(mLock);
        mSha256 = nsDependentCSubstring(char_ptr_cast(d.get().data),
                                        d.get().len);
      }
    }
  }

  // Post an event to notify that the operation completed.
  nsCOMPtr<nsIRunnable> event =
    NS_NewRunnableMethod(this, &BackgroundFileSaver::NotifySaveComplete);
  if (!event ||
      NS_FAILED(mControlThread->Dispatch(event, NS_DISPATCH_NORMAL))) {
    NS_WARNING("Unable to post completion event to the control thread.");
  }

  return true;
}