/* Add the server's certificate to our database of trusted servers.  */
static SECStatus
trustNewServer (CERTCertificate *serverCert)
{
  SECStatus secStatus;
  CERTCertTrust *trust = NULL;
  PK11SlotInfo *slot;

  /* Import the certificate.  */
  slot = PK11_GetInternalKeySlot();;
  secStatus = PK11_ImportCert(slot, serverCert, CK_INVALID_HANDLE, "stap-server", PR_FALSE);
  if (secStatus != SECSuccess)
    goto done;
  
  /* Make it a trusted peer.  */
  trust = (CERTCertTrust *)PORT_ZAlloc(sizeof(CERTCertTrust));
  if (! trust)
    {
      secStatus = SECFailure;
      goto done;
    }

  secStatus = CERT_DecodeTrustString(trust, "P,P,P");
  if (secStatus != SECSuccess)
    goto done;

  secStatus = CERT_ChangeCertTrust(CERT_GetDefaultCertDB(), serverCert, trust);
  if (secStatus != SECSuccess)
    goto done;

done:
  if (trust)
    PORT_Free(trust);
  return secStatus;
}
void
SaveIntermediateCerts(const ScopedCERTCertList& certList)
{
  if (!certList) {
    return;
  }

  bool isEndEntity = true;
  for (CERTCertListNode* node = CERT_LIST_HEAD(certList);
        !CERT_LIST_END(node, certList);
        node = CERT_LIST_NEXT(node)) {
    if (isEndEntity) {
      // Skip the end-entity; we only want to store intermediates
      isEndEntity = false;
      continue;
    }

    if (node->cert->slot) {
      // This cert was found on a token, no need to remember it in the temp db.
      continue;
    }

    if (node->cert->isperm) {
      // We don't need to remember certs already stored in perm db.
      continue;
    }

    // We have found a signer cert that we want to remember.
    char* nickname = DefaultServerNicknameForCert(node->cert);
    if (nickname && *nickname) {
      ScopedPtr<PK11SlotInfo, PK11_FreeSlot> slot(PK11_GetInternalKeySlot());
      if (slot) {
        PK11_ImportCert(slot.get(), node->cert, CK_INVALID_HANDLE,
                        nickname, false);
      }
    }
    PR_FREEIF(nickname);
  }
}
// Based on nsNSSCertificateDB::ImportServerCertificate.
bool ImportServerCert(
    const net::CertificateList& certificates,
    net::NSSCertDatabase::TrustBits trustBits,
    net::NSSCertDatabase::ImportCertFailureList* not_imported) {
  if (certificates.empty())
    return false;

  crypto::ScopedPK11Slot slot(crypto::GetPublicNSSKeySlot());
  if (!slot.get()) {
    LOG(ERROR) << "Couldn't get internal key slot!";
    return false;
  }

  for (size_t i = 0; i < certificates.size(); ++i) {
    const scoped_refptr<net::X509Certificate>& cert = certificates[i];

    // Mozilla uses CERT_ImportCerts, which doesn't take a slot arg.  We use
    // PK11_ImportCert instead.
    SECStatus srv = PK11_ImportCert(
        slot.get(),
        cert->os_cert_handle(),
        CK_INVALID_HANDLE,
        cert->GetDefaultNickname(net::SERVER_CERT).c_str(),
        PR_FALSE /* includeTrust (unused) */);
    if (srv != SECSuccess) {
      LOG(ERROR) << "PK11_ImportCert failed with error " << PORT_GetError();
      not_imported->push_back(net::NSSCertDatabase::ImportCertFailure(
          cert, net::ERR_IMPORT_SERVER_CERT_FAILED));
      continue;
    }
  }

  SetCertTrust(certificates[0].get(), net::SERVER_CERT, trustBits);
  // TODO(mattm): Report SetCertTrust result?  Putting in not_imported
  // wouldn't quite match up since it was imported...

  // Any errors importing individual certs will be in listed in |not_imported|.
  return true;
}
Beispiel #4
0
SECStatus
AddCertificateFromFile(const char* basePath, const char* filename)
{
  char buf[16384] = { 0 };
  SECStatus rv = ReadFileToBuffer(basePath, filename, buf);
  if (rv != SECSuccess) {
    return rv;
  }
  SECItem certDER;
  rv = CERT_DecodeCertPackage(buf, strlen(buf), DecodeCertCallback, &certDER);
  if (rv != SECSuccess) {
    PrintPRError("CERT_DecodeCertPackage failed");
    return rv;
  }
  ScopedCERTCertificate cert(CERT_NewTempCertificate(CERT_GetDefaultCertDB(),
                                                     &certDER, nullptr, false,
                                                     true));
  PORT_Free(certDER.data);
  if (!cert) {
    PrintPRError("CERT_NewTempCertificate failed");
    return SECFailure;
  }
  ScopedPK11SlotInfo slot(PK11_GetInternalKeySlot());
  if (!slot) {
    PrintPRError("PK11_GetInternalKeySlot failed");
    return SECFailure;
  }
  // The nickname is the filename without '.pem'.
  std::string nickname(filename, strlen(filename) - 4);
  rv = PK11_ImportCert(slot, cert, CK_INVALID_HANDLE, nickname.c_str(), false);
  if (rv != SECSuccess) {
    PrintPRError("PK11_ImportCert failed");
    return rv;
  }
  return SECSuccess;
}
NS_IMETHODIMP
nsCertOverrideService::RememberValidityOverride(const nsACString & aHostName, PRInt32 aPort,
        nsIX509Cert *aCert,
        PRUint32 aOverrideBits,
        PRBool aTemporary)
{
    NS_ENSURE_ARG_POINTER(aCert);
    if (aHostName.IsEmpty())
        return NS_ERROR_INVALID_ARG;
    if (aPort < -1)
        return NS_ERROR_INVALID_ARG;

    nsCOMPtr<nsIX509Cert2> cert2 = do_QueryInterface(aCert);
    if (!cert2)
        return NS_ERROR_FAILURE;

    CERTCertificate* nsscert = cert2->GetCert();
    if (!nsscert)
        return NS_ERROR_FAILURE;

    CERTCertificateCleaner nsscertCleaner(nsscert);

    nsCAutoString nickname;
    nickname = nsNSSCertificate::defaultServerNickname(nsscert);
    if (!aTemporary && !nickname.IsEmpty())
    {
        PK11SlotInfo *slot = PK11_GetInternalKeySlot();
        if (!slot)
            return NS_ERROR_FAILURE;

        SECStatus srv = PK11_ImportCert(slot, nsscert, CK_INVALID_HANDLE,
                                        const_cast<char*>(nickname.get()), PR_FALSE);
        PK11_FreeSlot(slot);

        if (srv != SECSuccess)
            return NS_ERROR_FAILURE;
    }

    nsCAutoString fpStr;
    nsresult rv = GetCertFingerprintByOidTag(nsscert,
                  mOidTagForStoringNewHashes, fpStr);
    if (NS_FAILED(rv))
        return rv;

    char *dbkey = NULL;
    rv = aCert->GetDbKey(&dbkey);
    if (NS_FAILED(rv) || !dbkey)
        return rv;

    // change \n and \r to spaces in the possibly multi-line-base64-encoded key
    for (char *dbkey_walk = dbkey;
            *dbkey_walk;
            ++dbkey_walk) {
        char c = *dbkey_walk;
        if (c == '\r' || c == '\n') {
            *dbkey_walk = ' ';
        }
    }

    {
        nsAutoMonitor lock(monitor);
        AddEntryToList(aHostName, aPort,
                       aTemporary ? aCert : nsnull,
                       // keep a reference to the cert for temporary overrides
                       aTemporary,
                       mDottedOidForStoringNewHashes, fpStr,
                       (nsCertOverride::OverrideBits)aOverrideBits,
                       nsDependentCString(dbkey));
        Write();
    }

    PR_Free(dbkey);
    return NS_OK;
}
// Based on nsNSSCertificateDB::handleCACertDownload, minus the UI bits.
bool ImportCACerts(const net::CertificateList& certificates,
                   net::X509Certificate* root,
                   net::NSSCertDatabase::TrustBits trustBits,
                   net::NSSCertDatabase::ImportCertFailureList* not_imported) {
  if (certificates.empty() || !root)
    return false;

  crypto::ScopedPK11Slot slot(crypto::GetPublicNSSKeySlot());
  if (!slot.get()) {
    LOG(ERROR) << "Couldn't get internal key slot!";
    return false;
  }

  // Mozilla had some code here to check if a perm version of the cert exists
  // already and use that, but CERT_NewTempCertificate actually does that
  // itself, so we skip it here.

  if (!CERT_IsCACert(root->os_cert_handle(), NULL)) {
    not_imported->push_back(net::NSSCertDatabase::ImportCertFailure(
        root, net::ERR_IMPORT_CA_CERT_NOT_CA));
  } else if (root->os_cert_handle()->isperm) {
    // Mozilla just returns here, but we continue in case there are other certs
    // in the list which aren't already imported.
    // TODO(mattm): should we set/add trust if it differs from the present
    // settings?
    not_imported->push_back(net::NSSCertDatabase::ImportCertFailure(
        root, net::ERR_IMPORT_CERT_ALREADY_EXISTS));
  } else {
    // Mozilla uses CERT_AddTempCertToPerm, however it is privately exported,
    // and it doesn't take the slot as an argument either.  Instead, we use
    // PK11_ImportCert and CERT_ChangeCertTrust.
    SECStatus srv = PK11_ImportCert(
        slot.get(),
        root->os_cert_handle(),
        CK_INVALID_HANDLE,
        root->GetDefaultNickname(net::CA_CERT).c_str(),
        PR_FALSE /* includeTrust (unused) */);
    if (srv != SECSuccess) {
      LOG(ERROR) << "PK11_ImportCert failed with error " << PORT_GetError();
      return false;
    }
    if (!SetCertTrust(root, net::CA_CERT, trustBits))
      return false;
  }

  PRTime now = PR_Now();
  // Import additional delivered certificates that can be verified.
  // This is sort of merged in from Mozilla's ImportValidCACertsInList.  Mozilla
  // uses CERT_FilterCertListByUsage to filter out non-ca certs, but we want to
  // keep using X509Certificates, so that we can use them to build the
  // |not_imported| result.  So, we keep using our net::CertificateList and
  // filter it ourself.
  for (size_t i = 0; i < certificates.size(); i++) {
    const scoped_refptr<net::X509Certificate>& cert = certificates[i];
    if (cert == root) {
      // we already processed that one
      continue;
    }

    // Mozilla uses CERT_FilterCertListByUsage(certList, certUsageAnyCA,
    // PR_TRUE).  Afaict, checking !CERT_IsCACert on each cert is equivalent.
    if (!CERT_IsCACert(cert->os_cert_handle(), NULL)) {
      not_imported->push_back(net::NSSCertDatabase::ImportCertFailure(
          cert, net::ERR_IMPORT_CA_CERT_NOT_CA));
      VLOG(1) << "skipping cert (non-ca)";
      continue;
    }

    if (cert->os_cert_handle()->isperm) {
      not_imported->push_back(net::NSSCertDatabase::ImportCertFailure(
          cert, net::ERR_IMPORT_CERT_ALREADY_EXISTS));
      VLOG(1) << "skipping cert (perm)";
      continue;
    }

    if (CERT_VerifyCert(CERT_GetDefaultCertDB(), cert->os_cert_handle(),
        PR_TRUE, certUsageVerifyCA, now, NULL, NULL) != SECSuccess) {
      // TODO(mattm): use better error code (map PORT_GetError to an appropriate
      // error value).  (maybe make MapSecurityError or MapCertErrorToCertStatus
      // public.)
      not_imported->push_back(net::NSSCertDatabase::ImportCertFailure(
          cert, net::ERR_FAILED));
      VLOG(1) << "skipping cert (verify) " << PORT_GetError();
      continue;
    }

    // Mozilla uses CERT_ImportCerts, which doesn't take a slot arg.  We use
    // PK11_ImportCert instead.
    SECStatus srv = PK11_ImportCert(
        slot.get(),
        cert->os_cert_handle(),
        CK_INVALID_HANDLE,
        cert->GetDefaultNickname(net::CA_CERT).c_str(),
        PR_FALSE /* includeTrust (unused) */);
    if (srv != SECSuccess) {
      LOG(ERROR) << "PK11_ImportCert failed with error " << PORT_GetError();
      // TODO(mattm): Should we bail or continue on error here?  Mozilla doesn't
      // check error code at all.
      not_imported->push_back(net::NSSCertDatabase::ImportCertFailure(
          cert, net::ERR_IMPORT_CA_CERT_FAILED));
    }
  }

  // Any errors importing individual certs will be in listed in |not_imported|.
  return true;
}
  nsresult Generate()
  {
    nsresult rv;

    // Get the key slot for generation later
    UniquePK11SlotInfo slot(PK11_GetInternalKeySlot());
    if (!slot) {
      return mozilla::psm::GetXPCOMFromNSSError(PR_GetError());
    }

    // Remove existing certs with this name (if any)
    rv = RemoveExisting();
    if (NS_FAILED(rv)) {
      return rv;
    }

    // Generate a new cert
    NS_NAMED_LITERAL_CSTRING(commonNamePrefix, "CN=");
    nsAutoCString subjectNameStr(commonNamePrefix + mNickname);
    UniqueCERTName subjectName(CERT_AsciiToName(subjectNameStr.get()));
    if (!subjectName) {
      return mozilla::psm::GetXPCOMFromNSSError(PR_GetError());
    }

    // Use the well-known NIST P-256 curve
    SECOidData* curveOidData = SECOID_FindOIDByTag(SEC_OID_SECG_EC_SECP256R1);
    if (!curveOidData) {
      return mozilla::psm::GetXPCOMFromNSSError(PR_GetError());
    }

    // Get key params from the curve
    ScopedAutoSECItem keyParams(2 + curveOidData->oid.len);
    keyParams.data[0] = SEC_ASN1_OBJECT_ID;
    keyParams.data[1] = curveOidData->oid.len;
    memcpy(keyParams.data + 2, curveOidData->oid.data, curveOidData->oid.len);

    // Generate cert key pair
    SECKEYPublicKey* tempPublicKey;
    UniqueSECKEYPrivateKey privateKey(
      PK11_GenerateKeyPair(slot.get(), CKM_EC_KEY_PAIR_GEN, &keyParams,
                           &tempPublicKey, true /* token */,
                           true /* sensitive */, nullptr));
    UniqueSECKEYPublicKey publicKey(tempPublicKey);
    tempPublicKey = nullptr;
    if (!privateKey || !publicKey) {
      return mozilla::psm::GetXPCOMFromNSSError(PR_GetError());
    }

    // Create subject public key info and cert request
    UniqueCERTSubjectPublicKeyInfo spki(
      SECKEY_CreateSubjectPublicKeyInfo(publicKey.get()));
    if (!spki) {
      return mozilla::psm::GetXPCOMFromNSSError(PR_GetError());
    }
    UniqueCERTCertificateRequest certRequest(
      CERT_CreateCertificateRequest(subjectName.get(), spki.get(), nullptr));
    if (!certRequest) {
      return mozilla::psm::GetXPCOMFromNSSError(PR_GetError());
    }

    // Valid from one day before to 1 year after
    static const PRTime oneDay = PRTime(PR_USEC_PER_SEC)
                               * PRTime(60)  // sec
                               * PRTime(60)  // min
                               * PRTime(24); // hours

    PRTime now = PR_Now();
    PRTime notBefore = now - oneDay;
    PRTime notAfter = now + (PRTime(365) * oneDay);
    UniqueCERTValidity validity(CERT_CreateValidity(notBefore, notAfter));
    if (!validity) {
      return mozilla::psm::GetXPCOMFromNSSError(PR_GetError());
    }

    // Generate random serial
    unsigned long serial;
    // This serial in principle could collide, but it's unlikely
    rv = MapSECStatus(PK11_GenerateRandomOnSlot(
           slot.get(), BitwiseCast<unsigned char*, unsigned long*>(&serial),
           sizeof(serial)));
    if (NS_FAILED(rv)) {
      return rv;
    }

    // Create the cert from these pieces
    UniqueCERTCertificate cert(
      CERT_CreateCertificate(serial, subjectName.get(), validity.get(),
                             certRequest.get()));
    if (!cert) {
      return mozilla::psm::GetXPCOMFromNSSError(PR_GetError());
    }

    // Update the cert version to X509v3
    if (!cert->version.data) {
      return NS_ERROR_INVALID_POINTER;
    }
    *(cert->version.data) = SEC_CERTIFICATE_VERSION_3;
    cert->version.len = 1;

    // Set cert signature algorithm
    PLArenaPool* arena = cert->arena;
    if (!arena) {
      return NS_ERROR_INVALID_POINTER;
    }
    rv = MapSECStatus(
           SECOID_SetAlgorithmID(arena, &cert->signature,
                                 SEC_OID_ANSIX962_ECDSA_SHA256_SIGNATURE, 0));
    if (NS_FAILED(rv)) {
      return rv;
    }

    // Encode and self-sign the cert
    UniqueSECItem certDER(
      SEC_ASN1EncodeItem(nullptr, nullptr, cert.get(),
                         SEC_ASN1_GET(CERT_CertificateTemplate)));
    if (!certDER) {
      return mozilla::psm::GetXPCOMFromNSSError(PR_GetError());
    }
    rv = MapSECStatus(
           SEC_DerSignData(arena, &cert->derCert, certDER->data, certDER->len,
                           privateKey.get(),
                           SEC_OID_ANSIX962_ECDSA_SHA256_SIGNATURE));
    if (NS_FAILED(rv)) {
      return rv;
    }

    // Create a CERTCertificate from the signed data
    UniqueCERTCertificate certFromDER(
      CERT_NewTempCertificate(CERT_GetDefaultCertDB(), &cert->derCert, nullptr,
                              true /* perm */, true /* copyDER */));
    if (!certFromDER) {
      return mozilla::psm::GetXPCOMFromNSSError(PR_GetError());
    }

    // Save the cert in the DB
    rv = MapSECStatus(PK11_ImportCert(slot.get(), certFromDER.get(),
                                      CK_INVALID_HANDLE, mNickname.get(),
                                      false /* unused */));
    if (NS_FAILED(rv)) {
      return rv;
    }

    // We should now have cert in the DB, read it back in nsIX509Cert form
    return GetFromDB();
  }
Beispiel #8
0
SECStatus PR_CALLBACK AuthCertificateCallback(void* client_data, PRFileDesc* fd,
                                              PRBool checksig, PRBool isServer) {
  nsNSSShutDownPreventionLock locker;

  // first the default action
  SECStatus rv = SSL_AuthCertificate(CERT_GetDefaultCertDB(), fd, checksig, isServer);

  // We want to remember the CA certs in the temp db, so that the application can find the
  // complete chain at any time it might need it.
  // But we keep only those CA certs in the temp db, that we didn't already know.
  
  CERTCertificate *serverCert = SSL_PeerCertificate(fd);
  CERTCertificateCleaner serverCertCleaner(serverCert);

  if (serverCert) {
    nsNSSSocketInfo* infoObject = (nsNSSSocketInfo*) fd->higher->secret;
    nsRefPtr<nsSSLStatus> status = infoObject->SSLStatus();
    nsRefPtr<nsNSSCertificate> nsc;

    if (!status || !status->mServerCert) {
      nsc = new nsNSSCertificate(serverCert);
    }

    if (SECSuccess == rv) {
      if (nsc) {
        PRBool dummyIsEV;
        nsc->GetIsExtendedValidation(&dummyIsEV); // the nsc object will cache the status
      }
    
      CERTCertList *certList = CERT_GetCertChainFromCert(serverCert, PR_Now(), certUsageSSLCA);

      nsCOMPtr<nsINSSComponent> nssComponent;
      
      for (CERTCertListNode *node = CERT_LIST_HEAD(certList);
           !CERT_LIST_END(node, certList);
           node = CERT_LIST_NEXT(node)) {

        if (node->cert->slot) {
          // This cert was found on a token, no need to remember it in the temp db.
          continue;
        }

        if (node->cert->isperm) {
          // We don't need to remember certs already stored in perm db.
          continue;
        }
        
        if (node->cert == serverCert) {
          // We don't want to remember the server cert, 
          // the code that cares for displaying page info does this already.
          continue;
        }

        // We have found a signer cert that we want to remember.
        nsCAutoString nickname;
        nickname = nsNSSCertificate::defaultServerNickname(node->cert);
        if (!nickname.IsEmpty()) {
          PK11SlotInfo *slot = PK11_GetInternalKeySlot();
          if (slot) {
            PK11_ImportCert(slot, node->cert, CK_INVALID_HANDLE, 
                            const_cast<char*>(nickname.get()), PR_FALSE);
            PK11_FreeSlot(slot);
          }
        }
      }

      CERT_DestroyCertList(certList);
    }

    // The connection may get terminated, for example, if the server requires
    // a client cert. Let's provide a minimal SSLStatus
    // to the caller that contains at least the cert and its status.
    if (!status) {
      status = new nsSSLStatus();
      infoObject->SetSSLStatus(status);
    }
    if (status && !status->mServerCert) {
      status->mServerCert = nsc;
      PR_LOG(gPIPNSSLog, PR_LOG_DEBUG,
             ("AuthCertificateCallback setting NEW cert %p\n", status->mServerCert.get()));
    }
  }

  return rv;
}