/* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
if (aDBKey.IsEmpty()) { return NS_ERROR_INVALID_ARG;
}
nsresult rv = BlockUntilLoadableCertsLoaded(); if (NS_FAILED(rv)) { return rv;
}
UniqueCERTCertificate cert;
rv = FindCertByDBKey(aDBKey, cert); if (NS_FAILED(rv)) { return rv;
} // If we can't find the certificate, that's not an error. Just return null. if (!cert) { return NS_OK;
}
nsCOMPtr<nsIX509Cert> nssCert = new nsNSSCertificate(cert.get());
nssCert.forget(_cert); return NS_OK;
}
nsresult nsNSSCertificateDB::FindCertByDBKey(const nsACString& aDBKey,
UniqueCERTCertificate& cert) {
static_assert(sizeof(uint64_t) == 8, "type size sanity check");
static_assert(sizeof(uint32_t) == 4, "type size sanity check"); // (From nsNSSCertificate::GetDbKey) // The format of the key is the base64 encoding of the following: // 4 bytes: {0, 0, 0, 0} (this was intended to be the module ID, but it was // never implemented) // 4 bytes: {0, 0, 0, 0} (this was intended to be the slot ID, but it was // never implemented) // 4 bytes: <serial number length in big-endian order> // 4 bytes: <DER-encoded issuer distinguished name length in big-endian order> // n bytes: <bytes of serial number> // m bytes: <DER-encoded issuer distinguished name>
nsAutoCString decoded;
nsAutoCString tmpDBKey(aDBKey); // Filter out any whitespace for backwards compatibility.
tmpDBKey.StripWhitespace();
nsresult rv = Base64Decode(tmpDBKey, decoded); if (NS_FAILED(rv)) { return rv;
} if (decoded.Length() < 16) { return NS_ERROR_ILLEGAL_INPUT;
} constchar* reader = decoded.BeginReading();
uint64_t zeroes = *BitwiseCast<const uint64_t*, constchar*>(reader); if (zeroes != 0) { return NS_ERROR_ILLEGAL_INPUT;
}
reader += sizeof(uint64_t); // Note: We surround the ntohl() argument with parentheses to stop the macro // from thinking two arguments were passed.
uint32_t serialNumberLen =
ntohl((*BitwiseCast<const uint32_t*, constchar*>(reader)));
reader += sizeof(uint32_t);
uint32_t issuerLen =
ntohl((*BitwiseCast<const uint32_t*, constchar*>(reader)));
reader += sizeof(uint32_t); if (decoded.Length() != 16ULL + serialNumberLen + issuerLen) { return NS_ERROR_ILLEGAL_INPUT;
}
CERTIssuerAndSN issuerSN;
issuerSN.serialNumber.len = serialNumberLen;
issuerSN.serialNumber.data = BitwiseCast<unsignedchar*, constchar*>(reader);
reader += serialNumberLen;
issuerSN.derIssuer.len = issuerLen;
issuerSN.derIssuer.data = BitwiseCast<unsignedchar*, constchar*>(reader);
reader += issuerLen;
MOZ_ASSERT(reader == decoded.EndReading());
// When using the sql-backed softoken, trust settings are authenticated using a // key in the secret database. Thus, if the user has a password, we need to // authenticate to the token in order to be able to change trust settings.
SECStatus ChangeCertTrustWithPossibleAuthentication( const UniqueCERTCertificate& cert, CERTCertTrust& trust, void* ctx) {
MOZ_ASSERT(cert, "cert must be non-null"); if (!cert) {
PR_SetError(SEC_ERROR_LIBRARY_FAILURE, 0); return SECFailure;
}
RefPtr<SharedCertVerifier> certVerifier(GetDefaultCertVerifier()); if (!certVerifier) {
PR_SetError(SEC_ERROR_LIBRARY_FAILURE, 0); return SECFailure;
}
// NSS ignores the first argument to CERT_ChangeCertTrust
SECStatus srv = CERT_ChangeCertTrust(nullptr, cert.get(), &trust); if (srv != SECSuccess && PR_GetError() != SEC_ERROR_TOKEN_NOT_LOGGED_IN) { return SECFailure;
} if (srv == SECSuccess) {
certVerifier->ClearTrustCache(); return SECSuccess;
}
// CERT_ChangeCertTrust failed with SEC_ERROR_TOKEN_NOT_LOGGED_IN, so // authenticate and try again. if (cert->slot) { // If this certificate is on an external PKCS#11 token, we have to // authenticate to that token.
srv = PK11_Authenticate(cert->slot, PR_TRUE, ctx);
} else { // Otherwise, the certificate is on the internal module.
UniquePK11SlotInfo internalSlot(PK11_GetInternalKeySlot());
srv = PK11_Authenticate(internalSlot.get(), PR_TRUE, ctx);
} if (srv != SECSuccess) { return srv;
}
srv = CERT_ChangeCertTrust(nullptr, cert.get(), &trust); if (srv != SECSuccess) { return srv;
}
if (encounteredFailure) { return GetXPCOMFromNSSError(savedErrorCode);
}
return NS_OK;
}
nsresult nsNSSCertificateDB::handleCACertDownload(NotNull<nsIArray*> x509Certs,
nsIInterfaceRequestor* ctx) { // First thing we have to do is figure out which certificate we're // gonna present to the user. The CA may have sent down a list of // certs which may or may not be a chained list of certs. Until // the day we can design some solid UI for the general case, we'll // code to the > 90% case. That case is where a CA sends down a // list that is a hierarchy whose root is either the first or // the last cert. What we're gonna do is compare the first // 2 entries, if the second was signed by the first, we assume // the root cert is the first cert and display it. Otherwise, // we compare the last 2 entries, if the second to last cert was // signed by the last cert, then we assume the last cert is the // root and display it.
uint32_t numCerts;
x509Certs->GetLength(&numCerts);
if (numCerts == 0) return NS_OK; // Nothing to import, so nothing to do.
nsCOMPtr<nsIX509Cert> certToShow;
uint32_t selCertIndex; if (numCerts == 1) { // There's only one cert, so let's show it.
selCertIndex = 0;
certToShow = do_QueryElementAt(x509Certs, selCertIndex);
} else {
nsCOMPtr<nsIX509Cert> cert0; // first cert
nsCOMPtr<nsIX509Cert> cert1; // second cert
nsCOMPtr<nsIX509Cert> certn_2; // second to last cert
nsCOMPtr<nsIX509Cert> certn_1; // last cert
if (cert1IssuerName.Equals(cert0SubjectName)) { // In this case, the first cert in the list signed the second, // so the first cert is the root. Let's display it.
selCertIndex = 0;
certToShow = cert0;
} elseif (certn_2IssuerName.Equals(certn_1SubjectName)) { // In this case the last cert has signed the second to last cert. // The last cert is the root, so let's display it.
selCertIndex = numCerts - 1;
certToShow = certn_1;
} else { // It's not a chain, so let's just show the first one in the // downloaded list.
selCertIndex = 0;
certToShow = cert0;
}
}
NS_IMETHODIMP
nsNSSCertificateDB::ImportCertificates(uint8_t* data, uint32_t length,
uint32_t type,
nsIInterfaceRequestor* ctx) { // We currently only handle CA certificates. if (type != nsIX509Cert::CA_CERT) { return NS_ERROR_FAILURE;
}
nsCOMPtr<nsIMutableArray> array = nsArrayBase::Create(); if (!array) { return NS_ERROR_FAILURE;
}
// Now let's create some certs to work with for (nsTArray<uint8_t>& certDER : certsArray) {
nsCOMPtr<nsIX509Cert> cert = new nsNSSCertificate(std::move(certDER));
nsresult rv = array->AppendElement(cert); if (NS_FAILED(rv)) { return rv;
}
}
/** * Decodes a given array of DER-encoded certificates into temporary storage. * * @param certs * Array in which the decoded certificates are stored as arrays of * unsigned chars. * @param temporaryCerts * List of decoded certificates.
*/ static nsresult ImportCertsIntoTempStorage(
nsTArray<nsTArray<uint8_t>>& certs, /*out*/ const UniqueCERTCertList& temporaryCerts) {
NS_ENSURE_ARG_POINTER(temporaryCerts);
void nsNSSCertificateDB::DisplayCertificateAlert(nsIInterfaceRequestor* ctx, constchar* stringID,
nsIX509Cert* certToShow) { if (!NS_IsMainThread()) {
NS_ERROR( "nsNSSCertificateDB::DisplayCertificateAlert called off the main " "thread"); return;
}
nsCOMPtr<nsIInterfaceRequestor> my_ctx = ctx; if (!my_ctx) {
my_ctx = new PipUIContext();
}
// This shall be replaced by embedding ovverridable prompts // as discussed in bug 310446, and should make use of certToShow.
NS_IMETHODIMP
nsNSSCertificateDB::ImportUserCertificate(uint8_t* data, uint32_t length,
nsIInterfaceRequestor* ctx) { if (!NS_IsMainThread()) {
NS_ERROR( "nsNSSCertificateDB::ImportUserCertificate called off the main thread"); return NS_ERROR_NOT_SAME_THREAD;
}
/* pick a nickname for the cert */
nsAutoCString nickname; if (cert->nickname) {
nickname = cert->nickname;
} else {
get_default_nickname(cert.get(), ctx, nickname);
}
/* user wants to import the cert */
slot.reset(PK11_ImportCertForKey(cert.get(), nickname.get(), ctx)); if (!slot) { return NS_ERROR_FAILURE;
}
slot = nullptr;
{
nsCOMPtr<nsIX509Cert> certToShow = new nsNSSCertificate(cert.get());
DisplayCertificateAlert(ctx, "UserCertImported", certToShow);
}
nsresult rv = BlockUntilLoadableCertsLoaded(); if (NS_FAILED(rv)) { return rv;
}
SECStatus srv;
UniqueCERTCertificate nsscert(cert->GetCert());
CERTCertTrust nsstrust;
srv = CERT_GetCertTrust(nsscert.get(), &nsstrust); if (srv != SECSuccess) { // CERT_GetCertTrust returns SECFailure if given a temporary cert that // doesn't have any trust information yet. This isn't an error. return NS_OK;
}
if (bytesObtained != fileInfo.size) { return NS_ERROR_FAILURE;
}
nsCOMPtr<nsIInterfaceRequestor> cxt = new PipUIContext();
switch (aType) { case nsIX509Cert::CA_CERT: return ImportCertificates(buf.get(), bytesObtained, aType, cxt); case nsIX509Cert::EMAIL_CERT: return ImportEmailCertificate(buf.get(), bytesObtained, cxt); default:
MOZ_ASSERT(false, "Unsupported type should have been filtered out"); break;
}
// Base64Decode() doesn't consider a zero length input as an error, and just // returns the empty string. We don't want this behavior, so the below check // catches this case. if (base64.Length() < 1) { return NS_ERROR_ILLEGAL_VALUE;
}
nsAutoCString baseName;
baseName.AppendPrintf(nickFmt.get(), username.get(), caname.get()); if (baseName.IsEmpty()) { return;
}
nickname = baseName;
/* * We need to see if the private key exists on a token, if it does * then we need to check for nicknames that already exist on the smart * card.
*/
UniquePK11SlotInfo slot(PK11_KeyForCertExists(cert, &keyHandle, ctx)); if (!slot) return;
int count = 1; while (true) { if (count > 1) {
nsAutoCString tmp;
tmp.AppendPrintf("%s #%d", baseName.get(), count); if (tmp.IsEmpty()) {
nickname.Truncate(); return;
}
nickname = tmp;
}
UniqueCERTCertificate dummycert;
if (PK11_IsInternal(slot.get())) { /* look up the nickname to make sure it isn't in use already */
dummycert.reset(CERT_FindCertByNickname(defaultcertdb, nickname.get()));
} else { // Check the cert against others that already live on the smart card.
dummycert.reset(PK11_FindCertFromNickname(nickname.get(), ctx)); if (dummycert) { // Make sure the subject names are different. if (CERT_CompareName(&cert->subject, &dummycert->subject) == SECEqual) { /* * There is another certificate with the same nickname and * the same subject name on the smart card, so let's use this * nickname.
*/
dummycert = nullptr;
}
}
} if (!dummycert) { break;
}
count++;
}
}
NS_IMETHODIMP
nsNSSCertificateDB::AddCertFromBase64(const nsACString& aBase64, const nsACString& aTrust,
nsIX509Cert** addedCertificate) { // Base64Decode() doesn't consider a zero length input as an error, and just // returns the empty string. We don't want this behavior, so the below check // catches this case. if (aBase64.Length() < 1) { return NS_ERROR_ILLEGAL_VALUE;
}
UniqueCERTCertificate tmpCert(newCert->GetCert()); if (!tmpCert) { return NS_ERROR_FAILURE;
}
// If there's already a certificate that matches this one in the database, we // still want to set its trust to the given value. if (tmpCert->isperm) {
rv = SetCertTrustFromString(newCert, aTrust); if (NS_FAILED(rv)) { return rv;
}
newCert.forget(addedCertificate); return NS_OK;
}
UniquePLArenaPool arena(PORT_NewArena(1024)); if (!arena) {
MOZ_LOG(gPIPNSSLog, LogLevel::Debug,
("nsNSSCertificateDB::AsPKCS7Blob - out of memory")); return NS_ERROR_OUT_OF_MEMORY;
}
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung ist noch experimentell.