/* * The contents of this file are subject to the Mozilla Public * License Version 1.1 (the "License"); you may not use this file * except in compliance with the License. You may obtain a copy of * the License at http://www.mozilla.org/MPL/ * * Software distributed under the License is distributed on an "AS * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or * implied. See the License for the specific language governing * rights and limitations under the License. * * The Original Code is the Netscape security libraries. * * The Initial Developer of the Original Code is Netscape * Communications Corporation. Portions created by Netscape are * Copyright (C) 2000 Netscape Communications Corporation. All * Rights Reserved. * * Contributor(s): * Ian McGreer * Javier Delgadillo * * Alternatively, the contents of this file may be used under the * terms of the GNU General Public License Version 2 or later (the * "GPL"), in which case the provisions of the GPL are applicable * instead of those above. If you wish to allow use of your * version of this file only under the terms of the GPL and not to * allow others to use your version of this file under the MPL, * indicate your decision by deleting the provisions above and * replace them with the notice and other provisions required by * the GPL. If you do not delete the provisions above, a recipient * may use your version of this file under either the MPL or the * GPL. * */ #include "prmem.h" #include "prerror.h" #include "prprf.h" #include "nsNSSComponent.h" // for PIPNSS string bundle calls. #include "nsCOMPtr.h" #include "nsArray.h" #include "nsNSSCertificate.h" #include "nsNSSCertValidity.h" #include "nsPKCS12Blob.h" #include "nsPK11TokenDB.h" #include "nsIX509Cert.h" #include "nsISMimeCert.h" #include "nsNSSASN1Object.h" #include "nsString.h" #include "nsXPIDLString.h" #include "nsReadableUtils.h" #include "nsIDateTimeFormat.h" #include "nsDateTimeFormatCID.h" #include "nsILocaleService.h" #include "nsIURI.h" #include "nsTime.h" #include "nsIProxyObjectManager.h" #include "nsCRT.h" #include "nsAutoLock.h" #include "nsUsageArrayHelper.h" #include "nsICertificateDialogs.h" #include "nsNSSCertHelper.h" #include "nsISupportsPrimitives.h" #include "nsUnicharUtils.h" #include "nspr.h" extern "C" { #include "pk11func.h" #include "certdb.h" #include "cert.h" #include "secerr.h" #include "nssb64.h" #include "secasn1.h" #include "secder.h" } #include "ssl.h" #include "ocsp.h" #include "plbase64.h" #ifdef PR_LOGGING extern PRLogModuleInfo* gPIPNSSLog; #endif static NS_DEFINE_CID(kNSSComponentCID, NS_NSSCOMPONENT_CID); static NS_DEFINE_CID(kDateTimeFormatCID, NS_DATETIMEFORMAT_CID); /* nsNSSCertificate */ NS_IMPL_THREADSAFE_ISUPPORTS2(nsNSSCertificate, nsIX509Cert, nsISMimeCert) nsNSSCertificate* nsNSSCertificate::ConstructFromDER(char *certDER, int derLen) { nsNSSShutDownPreventionLock locker; if (!certDER || !derLen) return nsnull; CERTCertificate *aCert = CERT_DecodeCertFromPackage(certDER, derLen); if (!aCert) return nsnull; if(aCert->dbhandle == nsnull) { aCert->dbhandle = CERT_GetDefaultCertDB(); } nsNSSCertificate *newObject = new nsNSSCertificate(aCert); CERT_DestroyCertificate(aCert); return newObject; } nsNSSCertificate::nsNSSCertificate(CERTCertificate *cert) : mCert(nsnull), mPermDelete(PR_FALSE), mCertType(nsIX509Cert::UNKNOWN_CERT) { nsNSSShutDownPreventionLock locker; if (isAlreadyShutDown()) return; if (cert) mCert = CERT_DupCertificate(cert); } nsNSSCertificate::~nsNSSCertificate() { nsNSSShutDownPreventionLock locker; if (isAlreadyShutDown()) return; destructorSafeDestroyNSSReference(); shutdown(calledFromObject); } void nsNSSCertificate::virtualDestroyNSSReference() { destructorSafeDestroyNSSReference(); } void nsNSSCertificate::destructorSafeDestroyNSSReference() { if (isAlreadyShutDown()) return; if (mPermDelete) { if (mCertType == nsNSSCertificate::USER_CERT) { nsCOMPtr cxt = new PipUIContext(); PK11_DeleteTokenCertAndKey(mCert, cxt); } else if (!PK11_IsReadOnly(mCert->slot)) { // If the list of built-ins does contain a non-removable // copy of this certificate, our call will not remove // the certificate permanently, but rather remove all trust. SEC_DeletePermCertificate(mCert); } } if (mCert) { CERT_DestroyCertificate(mCert); mCert = nsnull; } } nsresult nsNSSCertificate::SetCertType(PRUint32 aCertType) { mCertType = aCertType; return NS_OK; } nsresult nsNSSCertificate::GetCertType(PRUint32 *aCertType) { *aCertType = mCertType; return NS_OK; } nsresult nsNSSCertificate::MarkForPermDeletion() { nsNSSShutDownPreventionLock locker; if (isAlreadyShutDown()) return NS_ERROR_NOT_AVAILABLE; // make sure user is logged in to the token nsCOMPtr ctx = new PipUIContext(); if (PK11_NeedLogin(mCert->slot) && !PK11_NeedUserInit(mCert->slot) && !PK11_IsInternal(mCert->slot)) { if (SECSuccess != PK11_Authenticate(mCert->slot, PR_TRUE, ctx)) { return NS_ERROR_FAILURE; } } mPermDelete = PR_TRUE; return NS_OK; } nsresult nsNSSCertificate::FormatUIStrings(const nsAutoString &nickname, nsAutoString &nickWithSerial, nsAutoString &details) { nsresult rv = NS_OK; nsCOMPtr proxyman(do_GetService(NS_XPCOMPROXY_CONTRACTID, &rv)); if (NS_FAILED(rv) || !proxyman) { return NS_ERROR_FAILURE; } nsCOMPtr nssComponent(do_GetService(kNSSComponentCID, &rv)); if (NS_FAILED(rv) || !nssComponent) { return NS_ERROR_FAILURE; } nsCOMPtr x509Proxy; proxyman->GetProxyForObject( NS_UI_THREAD_EVENTQ, nsIX509Cert::GetIID(), NS_STATIC_CAST(nsIX509Cert*, this), PROXY_SYNC | PROXY_ALWAYS, getter_AddRefs(x509Proxy)); if (!x509Proxy) { rv = NS_ERROR_OUT_OF_MEMORY; } else { rv = NS_OK; nsAutoString info; nsAutoString temp1; nickWithSerial.Append(nickname); if (NS_SUCCEEDED(nssComponent->GetPIPNSSBundleString("CertInfoIssuedFor", info))) { details.Append(info); details.Append(PRUnichar(' ')); if (NS_SUCCEEDED(x509Proxy->GetSubjectName(temp1)) && !temp1.IsEmpty()) { details.Append(temp1); } details.Append(PRUnichar('\n')); } if (NS_SUCCEEDED(x509Proxy->GetSerialNumber(temp1)) && !temp1.IsEmpty()) { details.Append(NS_LITERAL_STRING(" ")); if (NS_SUCCEEDED(nssComponent->GetPIPNSSBundleString("CertDumpSerialNo", info))) { details.Append(info); details.Append(NS_LITERAL_STRING(": ")); } details.Append(temp1); nickWithSerial.Append(NS_LITERAL_STRING(" [")); nickWithSerial.Append(temp1); nickWithSerial.Append(PRUnichar(']')); details.Append(PRUnichar('\n')); } { nsCOMPtr validity; nsCOMPtr originalValidity; rv = x509Proxy->GetValidity(getter_AddRefs(originalValidity)); if (NS_SUCCEEDED(rv) && originalValidity) { proxyman->GetProxyForObject( NS_UI_THREAD_EVENTQ, nsIX509CertValidity::GetIID(), originalValidity, PROXY_SYNC | PROXY_ALWAYS, getter_AddRefs(validity)); } if (validity) { details.Append(NS_LITERAL_STRING(" ")); if (NS_SUCCEEDED(nssComponent->GetPIPNSSBundleString("CertInfoValid", info))) { details.Append(info); } if (NS_SUCCEEDED(validity->GetNotBeforeLocalTime(temp1)) && !temp1.IsEmpty()) { details.Append(PRUnichar(' ')); if (NS_SUCCEEDED(nssComponent->GetPIPNSSBundleString("CertInfoFrom", info))) { details.Append(info); details.Append(PRUnichar(' ')); } details.Append(temp1); } if (NS_SUCCEEDED(validity->GetNotAfterLocalTime(temp1)) && !temp1.IsEmpty()) { details.Append(PRUnichar(' ')); if (NS_SUCCEEDED(nssComponent->GetPIPNSSBundleString("CertInfoTo", info))) { details.Append(info); details.Append(PRUnichar(' ')); } details.Append(temp1); } details.Append(PRUnichar('\n')); } } PRUint32 tempInt = 0; if (NS_SUCCEEDED(x509Proxy->GetUsagesString(PR_FALSE, &tempInt, temp1)) && !temp1.IsEmpty()) { details.Append(NS_LITERAL_STRING(" ")); if (NS_SUCCEEDED(nssComponent->GetPIPNSSBundleString("CertInfoPurposes", info))) { details.Append(info); details.Append(NS_LITERAL_STRING(": ")); } details.Append(temp1); details.Append(PRUnichar('\n')); } if (NS_SUCCEEDED(nssComponent->GetPIPNSSBundleString("CertInfoIssuedBy", info))) { details.Append(info); details.Append(PRUnichar(' ')); if (NS_SUCCEEDED(x509Proxy->GetIssuerName(temp1)) && !temp1.IsEmpty()) { details.Append(temp1); } details.Append(PRUnichar('\n')); } if (NS_SUCCEEDED(nssComponent->GetPIPNSSBundleString("CertInfoStoredIn", info))) { details.Append(info); details.Append(PRUnichar(' ')); if (NS_SUCCEEDED(x509Proxy->GetTokenName(temp1)) && !temp1.IsEmpty()) { details.Append(temp1); } } /* the above produces output the following output: Issued to: $subjectName Serial number: $serialNumber Valid from: $starting_date to $expiration_date Purposes: $purposes Issued by: $issuerName Stored in: $token */ } return rv; } /* readonly attribute string dbKey; */ NS_IMETHODIMP nsNSSCertificate::GetDbKey(char * *aDbKey) { nsNSSShutDownPreventionLock locker; if (isAlreadyShutDown()) return NS_ERROR_NOT_AVAILABLE; SECItem key; NS_ENSURE_ARG(aDbKey); *aDbKey = nsnull; key.len = NS_NSS_LONG*4+mCert->serialNumber.len+mCert->derIssuer.len; key.data = (unsigned char *)nsMemory::Alloc(key.len); NS_NSS_PUT_LONG(0,key.data); // later put moduleID NS_NSS_PUT_LONG(0,&key.data[NS_NSS_LONG]); // later put slotID NS_NSS_PUT_LONG(mCert->serialNumber.len,&key.data[NS_NSS_LONG*2]); NS_NSS_PUT_LONG(mCert->derIssuer.len,&key.data[NS_NSS_LONG*3]); memcpy(&key.data[NS_NSS_LONG*4], mCert->serialNumber.data, mCert->serialNumber.len); memcpy(&key.data[NS_NSS_LONG*4+mCert->serialNumber.len], mCert->derIssuer.data, mCert->derIssuer.len); *aDbKey = NSSBase64_EncodeItem(nsnull, nsnull, 0, &key); nsMemory::Free(key.data); // SECItem is a 'c' type without a destrutor return (*aDbKey) ? NS_OK : NS_ERROR_FAILURE; } /* readonly attribute string windowTitle; */ NS_IMETHODIMP nsNSSCertificate::GetWindowTitle(char * *aWindowTitle) { nsNSSShutDownPreventionLock locker; if (isAlreadyShutDown()) return NS_ERROR_NOT_AVAILABLE; NS_ENSURE_ARG(aWindowTitle); if (mCert) { if (mCert->nickname) { *aWindowTitle = PL_strdup(mCert->nickname); } else { *aWindowTitle = CERT_GetCommonName(&mCert->subject); if (!*aWindowTitle) { *aWindowTitle = PL_strdup(mCert->subjectName); } } } else { NS_ASSERTION(0,"Somehow got nsnull for mCertificate in nsNSSCertificate."); *aWindowTitle = nsnull; } return NS_OK; } NS_IMETHODIMP nsNSSCertificate::GetNickname(nsAString &aNickname) { nsNSSShutDownPreventionLock locker; if (isAlreadyShutDown()) return NS_ERROR_NOT_AVAILABLE; if (mCert->nickname) { CopyUTF8toUTF16(mCert->nickname, aNickname); } else { nsresult rv; nsCOMPtr nssComponent(do_GetService(kNSSComponentCID, &rv)); if (NS_FAILED(rv) || !nssComponent) { return NS_ERROR_FAILURE; } nssComponent->GetPIPNSSBundleString("CertNoNickname", aNickname); } return NS_OK; } NS_IMETHODIMP nsNSSCertificate::GetEmailAddress(nsAString &aEmailAddress) { nsNSSShutDownPreventionLock locker; if (isAlreadyShutDown()) return NS_ERROR_NOT_AVAILABLE; if (mCert->emailAddr) { CopyUTF8toUTF16(mCert->emailAddr, aEmailAddress); } else { nsresult rv; nsCOMPtr nssComponent(do_GetService(kNSSComponentCID, &rv)); if (NS_FAILED(rv) || !nssComponent) { return NS_ERROR_FAILURE; } nssComponent->GetPIPNSSBundleString("CertNoEmailAddress", aEmailAddress); } return NS_OK; } NS_IMETHODIMP nsNSSCertificate::GetEmailAddresses(PRUint32 *aLength, PRUnichar*** aAddresses) { nsNSSShutDownPreventionLock locker; if (isAlreadyShutDown()) return NS_ERROR_NOT_AVAILABLE; NS_ENSURE_ARG(aLength); NS_ENSURE_ARG(aAddresses); *aLength = 0; const char *aAddr; for (aAddr = CERT_GetFirstEmailAddress(mCert) ; aAddr ; aAddr = CERT_GetNextEmailAddress(mCert, aAddr)) { ++(*aLength); } *aAddresses = (PRUnichar **)nsMemory::Alloc(sizeof(PRUnichar *) * (*aLength)); if (!aAddresses) return NS_ERROR_OUT_OF_MEMORY; PRUint32 iAddr; for (aAddr = CERT_GetFirstEmailAddress(mCert), iAddr = 0 ; aAddr ; aAddr = CERT_GetNextEmailAddress(mCert, aAddr), ++iAddr) { (*aAddresses)[iAddr] = ToNewUnicode(NS_ConvertUTF8toUCS2(aAddr)); } return NS_OK; } NS_IMETHODIMP nsNSSCertificate::ContainsEmailAddress(const nsAString &aEmailAddress, PRBool *result) { nsNSSShutDownPreventionLock locker; if (isAlreadyShutDown()) return NS_ERROR_NOT_AVAILABLE; NS_ENSURE_ARG(result); *result = PR_FALSE; const char *aAddr = nsnull; for (aAddr = CERT_GetFirstEmailAddress(mCert) ; aAddr ; aAddr = CERT_GetNextEmailAddress(mCert, aAddr)) { NS_ConvertUTF8toUCS2 certAddr(aAddr); ToLowerCase(certAddr); nsAutoString testAddr(aEmailAddress); ToLowerCase(testAddr); if (certAddr == testAddr) { *result = PR_TRUE; break; } } return NS_OK; } NS_IMETHODIMP nsNSSCertificate::GetCommonName(nsAString &aCommonName) { nsNSSShutDownPreventionLock locker; if (isAlreadyShutDown()) return NS_ERROR_NOT_AVAILABLE; aCommonName.Truncate(); if (mCert) { char *commonName = CERT_GetCommonName(&mCert->subject); if (commonName) { aCommonName = NS_ConvertUTF8toUCS2(commonName); PORT_Free(commonName); } /*else { *aCommonName = ToNewUnicode(NS_LITERAL_STRING("")), }*/ } return NS_OK; } NS_IMETHODIMP nsNSSCertificate::GetOrganization(nsAString &aOrganization) { nsNSSShutDownPreventionLock locker; if (isAlreadyShutDown()) return NS_ERROR_NOT_AVAILABLE; aOrganization.Truncate(); if (mCert) { char *organization = CERT_GetOrgName(&mCert->subject); if (organization) { aOrganization = NS_ConvertUTF8toUCS2(organization); PORT_Free(organization); } /*else { *aOrganization = ToNewUnicode(NS_LITERAL_STRING("")), }*/ } return NS_OK; } NS_IMETHODIMP nsNSSCertificate::GetIssuerCommonName(nsAString &aCommonName) { nsNSSShutDownPreventionLock locker; if (isAlreadyShutDown()) return NS_ERROR_NOT_AVAILABLE; aCommonName.Truncate(); if (mCert) { char *commonName = CERT_GetCommonName(&mCert->issuer); if (commonName) { aCommonName = NS_ConvertUTF8toUCS2(commonName); PORT_Free(commonName); } } return NS_OK; } NS_IMETHODIMP nsNSSCertificate::GetIssuerOrganization(nsAString &aOrganization) { nsNSSShutDownPreventionLock locker; if (isAlreadyShutDown()) return NS_ERROR_NOT_AVAILABLE; aOrganization.Truncate(); if (mCert) { char *organization = CERT_GetOrgName(&mCert->issuer); if (organization) { aOrganization = NS_ConvertUTF8toUCS2(organization); PORT_Free(organization); } } return NS_OK; } NS_IMETHODIMP nsNSSCertificate::GetIssuerOrganizationUnit(nsAString &aOrganizationUnit) { nsNSSShutDownPreventionLock locker; if (isAlreadyShutDown()) return NS_ERROR_NOT_AVAILABLE; aOrganizationUnit.Truncate(); if (mCert) { char *organizationUnit = CERT_GetOrgUnitName(&mCert->issuer); if (organizationUnit) { aOrganizationUnit = NS_ConvertUTF8toUCS2(organizationUnit); PORT_Free(organizationUnit); } } return NS_OK; } /* readonly attribute nsIX509Cert issuer; */ NS_IMETHODIMP nsNSSCertificate::GetIssuer(nsIX509Cert * *aIssuer) { nsNSSShutDownPreventionLock locker; if (isAlreadyShutDown()) return NS_ERROR_NOT_AVAILABLE; NS_ENSURE_ARG(aIssuer); *aIssuer = nsnull; CERTCertificate *issuer; issuer = CERT_FindCertIssuer(mCert, PR_Now(), certUsageSSLClient); if (issuer) { nsCOMPtr cert = new nsNSSCertificate(issuer); *aIssuer = cert; NS_ADDREF(*aIssuer); CERT_DestroyCertificate(issuer); } return NS_OK; } NS_IMETHODIMP nsNSSCertificate::GetOrganizationalUnit(nsAString &aOrganizationalUnit) { nsNSSShutDownPreventionLock locker; if (isAlreadyShutDown()) return NS_ERROR_NOT_AVAILABLE; aOrganizationalUnit.Truncate(); if (mCert) { char *orgunit = CERT_GetOrgUnitName(&mCert->subject); if (orgunit) { aOrganizationalUnit = NS_ConvertUTF8toUCS2(orgunit); PORT_Free(orgunit); } /*else { *aOrganizationalUnit = ToNewUnicode(NS_LITERAL_STRING("")), }*/ } return NS_OK; } /* * nsIEnumerator getChain(); */ NS_IMETHODIMP nsNSSCertificate::GetChain(nsIArray **_rvChain) { nsNSSShutDownPreventionLock locker; if (isAlreadyShutDown()) return NS_ERROR_NOT_AVAILABLE; NS_ENSURE_ARG(_rvChain); nsresult rv; /* Get the cert chain from NSS */ CERTCertList *nssChain = NULL; PR_LOG(gPIPNSSLog, PR_LOG_DEBUG, ("Getting chain for \"%s\"\n", mCert->nickname)); // XXX This function is buggy - if it can't find the issuer, it crashes // on a null pointer. Will have to wait until it is fixed in NSS. #ifdef NSS_CHAIN_BUG_FIXED nssChain = CERT_GetCertChainFromCert(mCert, PR_Now(), certUsageSSLClient); if (!nssChain) return NS_ERROR_FAILURE; /* enumerate the chain for scripting purposes */ nsCOMPtr array; rv = NS_NewArray(getter_AddRefs(array)); if (NS_FAILED(rv)) { goto done; } for (node = CERT_LIST_HEAD(nssChain); !CERT_LIST_END(node, nssChain); node = CERT_LIST_NEXT(node)) { PR_LOG(gPIPNSSLog, PR_LOG_DEBUG, ("adding %s to chain\n", node->cert->nickname)); nsCOMPtr cert = new nsNSSCertificate(node->cert); array->AppendElement(cert, PR_FALSE); } #else // workaround here CERTCertificate *cert = nsnull; /* enumerate the chain for scripting purposes */ nsCOMPtr array; rv = NS_NewArray(getter_AddRefs(array)); if (NS_FAILED(rv)) { goto done; } PR_LOG(gPIPNSSLog, PR_LOG_DEBUG, ("Getting chain for \"%s\"\n", mCert->nickname)); cert = CERT_DupCertificate(mCert); while (cert) { nsCOMPtr pipCert = new nsNSSCertificate(cert); PR_LOG(gPIPNSSLog, PR_LOG_DEBUG, ("adding %s to chain\n", cert->nickname)); array->AppendElement(pipCert, PR_FALSE); PRBool wantToBreak = PR_FALSE; CERTCertificate *next_cert = nsnull; if (SECITEM_CompareItem(&cert->derIssuer, &cert->derSubject) == SECEqual) { wantToBreak = PR_TRUE; } else { next_cert = CERT_FindCertIssuer(cert, PR_Now(), certUsageSSLClient); } CERT_DestroyCertificate(cert); if (wantToBreak) { break; } cert = next_cert; } #endif // NSS_CHAIN_BUG_FIXED *_rvChain = array; NS_IF_ADDREF(*_rvChain); rv = NS_OK; done: if (nssChain) CERT_DestroyCertList(nssChain); return rv; } NS_IMETHODIMP nsNSSCertificate::GetSubjectName(nsAString &_subjectName) { nsNSSShutDownPreventionLock locker; if (isAlreadyShutDown()) return NS_ERROR_NOT_AVAILABLE; _subjectName.Truncate(); if (mCert->subjectName) { _subjectName = NS_ConvertUTF8toUCS2(mCert->subjectName); return NS_OK; } return NS_ERROR_FAILURE; } NS_IMETHODIMP nsNSSCertificate::GetIssuerName(nsAString &_issuerName) { nsNSSShutDownPreventionLock locker; if (isAlreadyShutDown()) return NS_ERROR_NOT_AVAILABLE; _issuerName.Truncate(); if (mCert->issuerName) { _issuerName = NS_ConvertUTF8toUCS2(mCert->issuerName); return NS_OK; } return NS_ERROR_FAILURE; } NS_IMETHODIMP nsNSSCertificate::GetSerialNumber(nsAString &_serialNumber) { nsNSSShutDownPreventionLock locker; if (isAlreadyShutDown()) return NS_ERROR_NOT_AVAILABLE; _serialNumber.Truncate(); nsXPIDLCString tmpstr; tmpstr.Adopt(CERT_Hexify(&mCert->serialNumber, 1)); if (tmpstr.get()) { _serialNumber = NS_ConvertASCIItoUCS2(tmpstr); return NS_OK; } return NS_ERROR_FAILURE; } NS_IMETHODIMP nsNSSCertificate::GetSha1Fingerprint(nsAString &_sha1Fingerprint) { nsNSSShutDownPreventionLock locker; if (isAlreadyShutDown()) return NS_ERROR_NOT_AVAILABLE; _sha1Fingerprint.Truncate(); unsigned char fingerprint[20]; SECItem fpItem; memset(fingerprint, 0, sizeof fingerprint); PK11_HashBuf(SEC_OID_SHA1, fingerprint, mCert->derCert.data, mCert->derCert.len); fpItem.data = fingerprint; fpItem.len = SHA1_LENGTH; nsXPIDLCString fpStr; fpStr.Adopt(CERT_Hexify(&fpItem, 1)); if (fpStr.get()) { _sha1Fingerprint = NS_ConvertASCIItoUCS2(fpStr); return NS_OK; } return NS_ERROR_FAILURE; } NS_IMETHODIMP nsNSSCertificate::GetMd5Fingerprint(nsAString &_md5Fingerprint) { nsNSSShutDownPreventionLock locker; if (isAlreadyShutDown()) return NS_ERROR_NOT_AVAILABLE; _md5Fingerprint.Truncate(); unsigned char fingerprint[20]; SECItem fpItem; memset(fingerprint, 0, sizeof fingerprint); PK11_HashBuf(SEC_OID_MD5, fingerprint, mCert->derCert.data, mCert->derCert.len); fpItem.data = fingerprint; fpItem.len = MD5_LENGTH; nsXPIDLCString fpStr; fpStr.Adopt(CERT_Hexify(&fpItem, 1)); if (fpStr.get()) { _md5Fingerprint = NS_ConvertASCIItoUCS2(fpStr); return NS_OK; } return NS_ERROR_FAILURE; } NS_IMETHODIMP nsNSSCertificate::GetTokenName(nsAString &aTokenName) { nsNSSShutDownPreventionLock locker; if (isAlreadyShutDown()) return NS_ERROR_NOT_AVAILABLE; aTokenName.Truncate(); if (mCert) { // HACK alert // When the trust of a builtin cert is modified, NSS copies it into the // cert db. At this point, it is now "managed" by the user, and should // not be listed with the builtins. However, in the collection code // used by PK11_ListCerts, the cert is found in the temp db, where it // has been loaded from the token. Though the trust is correct (grabbed // from the cert db), the source is wrong. I believe this is a safe // way to work around this. if (mCert->slot) { char *token = PK11_GetTokenName(mCert->slot); if (token) { aTokenName = NS_ConvertUTF8toUCS2(token); } } else { nsresult rv; nsAutoString tok; nsCOMPtr nssComponent(do_GetService(kNSSComponentCID, &rv)); if (NS_FAILED(rv)) return rv; rv = nssComponent->GetPIPNSSBundleString("InternalToken", tok); if (NS_SUCCEEDED(rv)) aTokenName = tok; } } return NS_OK; } NS_IMETHODIMP nsNSSCertificate::GetRawDER(PRUint32 *aLength, PRUint8 **aArray) { nsNSSShutDownPreventionLock locker; if (isAlreadyShutDown()) return NS_ERROR_NOT_AVAILABLE; if (mCert) { *aArray = (PRUint8 *)mCert->derCert.data; *aLength = mCert->derCert.len; return NS_OK; } *aLength = 0; return NS_ERROR_FAILURE; } CERTCertificate * nsNSSCertificate::GetCert() { nsNSSShutDownPreventionLock locker; if (isAlreadyShutDown()) return nsnull; return (mCert) ? CERT_DupCertificate(mCert) : nsnull; } NS_IMETHODIMP nsNSSCertificate::GetValidity(nsIX509CertValidity **aValidity) { nsNSSShutDownPreventionLock locker; if (isAlreadyShutDown()) return NS_ERROR_NOT_AVAILABLE; NS_ENSURE_ARG(aValidity); nsX509CertValidity *validity = new nsX509CertValidity(mCert); if (nsnull == validity) return NS_ERROR_OUT_OF_MEMORY; NS_ADDREF(validity); *aValidity = NS_STATIC_CAST(nsIX509CertValidity*, validity); return NS_OK; } NS_IMETHODIMP nsNSSCertificate::VerifyForUsage(PRUint32 usage, PRUint32 *verificationResult) { nsNSSShutDownPreventionLock locker; if (isAlreadyShutDown()) return NS_ERROR_NOT_AVAILABLE; NS_ENSURE_ARG(verificationResult); SECCertUsage nss_usage; switch (usage) { case CERT_USAGE_SSLClient: nss_usage = certUsageSSLClient; break; case CERT_USAGE_SSLServer: nss_usage = certUsageSSLServer; break; case CERT_USAGE_SSLServerWithStepUp: nss_usage = certUsageSSLServerWithStepUp; break; case CERT_USAGE_SSLCA: nss_usage = certUsageSSLCA; break; case CERT_USAGE_EmailSigner: nss_usage = certUsageEmailSigner; break; case CERT_USAGE_EmailRecipient: nss_usage = certUsageEmailRecipient; break; case CERT_USAGE_ObjectSigner: nss_usage = certUsageObjectSigner; break; case CERT_USAGE_UserCertImport: nss_usage = certUsageUserCertImport; break; case CERT_USAGE_VerifyCA: nss_usage = certUsageVerifyCA; break; case CERT_USAGE_ProtectedObjectSigner: nss_usage = certUsageProtectedObjectSigner; break; case CERT_USAGE_StatusResponder: nss_usage = certUsageStatusResponder; break; case CERT_USAGE_AnyCA: nss_usage = certUsageAnyCA; break; default: return NS_ERROR_FAILURE; } CERTCertDBHandle *defaultcertdb = CERT_GetDefaultCertDB(); if (CERT_VerifyCertNow(defaultcertdb, mCert, PR_TRUE, nss_usage, NULL) == SECSuccess) { *verificationResult = VERIFIED_OK; } else { int err = PR_GetError(); // this list was cloned from verifyFailed switch (err) { case SEC_ERROR_INADEQUATE_KEY_USAGE: case SEC_ERROR_INADEQUATE_CERT_TYPE: *verificationResult = USAGE_NOT_ALLOWED; break; case SEC_ERROR_REVOKED_CERTIFICATE: *verificationResult = CERT_REVOKED; break; case SEC_ERROR_EXPIRED_CERTIFICATE: *verificationResult = CERT_EXPIRED; break; case SEC_ERROR_UNTRUSTED_CERT: *verificationResult = CERT_NOT_TRUSTED; break; case SEC_ERROR_UNTRUSTED_ISSUER: *verificationResult = ISSUER_NOT_TRUSTED; break; case SEC_ERROR_UNKNOWN_ISSUER: *verificationResult = ISSUER_UNKNOWN; break; case SEC_ERROR_EXPIRED_ISSUER_CERTIFICATE: *verificationResult = INVALID_CA; break; case SEC_ERROR_CERT_USAGES_INVALID: default: *verificationResult = NOT_VERIFIED_UNKNOWN; break; } } return NS_OK; } NS_IMETHODIMP nsNSSCertificate::GetUsagesArray(PRBool ignoreOcsp, PRUint32 *_verified, PRUint32 *_count, PRUnichar ***_usages) { nsNSSShutDownPreventionLock locker; if (isAlreadyShutDown()) return NS_ERROR_NOT_AVAILABLE; nsresult rv; const int max_usages = 13; PRUnichar *tmpUsages[max_usages]; const char *suffix = ""; PRUint32 tmpCount; nsUsageArrayHelper uah(mCert); rv = uah.GetUsagesArray(suffix, ignoreOcsp, max_usages, _verified, &tmpCount, tmpUsages); if (tmpCount > 0) { *_usages = (PRUnichar **)nsMemory::Alloc(sizeof(PRUnichar *) * tmpCount); for (PRUint32 i=0; i0) _usages.Append(NS_LITERAL_STRING(",")); _usages.Append(tmpUsages[i]); nsMemory::Free(tmpUsages[i]); } return NS_OK; } static nsresult ProcessSECAlgorithmID(SECAlgorithmID *algID, nsINSSComponent *nssComponent, nsIASN1Sequence **retSequence) { nsCOMPtr sequence = new nsNSSASN1Sequence(); if (sequence == nsnull) return NS_ERROR_OUT_OF_MEMORY; *retSequence = nsnull; nsString text; GetOIDText(&algID->algorithm, nssComponent, text); if (!algID->parameters.len || algID->parameters.data[0] == nsIASN1Object::ASN1_NULL) { sequence->SetDisplayValue(text); sequence->SetIsValidContainer(PR_FALSE); } else { nsCOMPtr printableItem = new nsNSSASN1PrintableItem(); printableItem->SetDisplayValue(text); nsCOMPtr asn1Objects; sequence->GetASN1Objects(getter_AddRefs(asn1Objects)); asn1Objects->AppendElement(printableItem, PR_FALSE); nssComponent->GetPIPNSSBundleString("CertDumpAlgID", text); printableItem->SetDisplayName(text); printableItem = new nsNSSASN1PrintableItem(); asn1Objects->AppendElement(printableItem, PR_FALSE); nssComponent->GetPIPNSSBundleString("CertDumpParams", text); printableItem->SetDisplayName(text); ProcessRawBytes(&algID->parameters,text); printableItem->SetDisplayValue(text); } *retSequence = sequence; NS_ADDREF(*retSequence); return NS_OK; } static nsresult ProcessTime(PRTime dispTime, const PRUnichar *displayName, nsIASN1Sequence *parentSequence) { nsresult rv; nsCOMPtr dateFormatter = do_CreateInstance(kDateTimeFormatCID, &rv); if (NS_FAILED(rv)) return rv; nsString text; nsString tempString; PRExplodedTime explodedTime; PR_ExplodeTime(dispTime, PR_LocalTimeParameters, &explodedTime); dateFormatter->FormatPRExplodedTime(nsnull, kDateFormatShort, kTimeFormatSecondsForce24Hour, &explodedTime, tempString); text.Append(tempString); text.Append(NS_LITERAL_STRING("\n(")); PRExplodedTime explodedTimeGMT; PR_ExplodeTime(dispTime, PR_GMTParameters, &explodedTimeGMT); dateFormatter->FormatPRExplodedTime(nsnull, kDateFormatShort, kTimeFormatSecondsForce24Hour, &explodedTimeGMT, tempString); text.Append(tempString); text.Append(NS_LITERAL_STRING(" GMT)")); nsCOMPtr printableItem = new nsNSSASN1PrintableItem(); if (printableItem == nsnull) return NS_ERROR_OUT_OF_MEMORY; printableItem->SetDisplayValue(text); printableItem->SetDisplayName(nsDependentString(displayName)); nsCOMPtr asn1Objects; parentSequence->GetASN1Objects(getter_AddRefs(asn1Objects)); asn1Objects->AppendElement(printableItem, PR_FALSE); return NS_OK; } static nsresult ProcessSubjectPublicKeyInfo(CERTSubjectPublicKeyInfo *spki, nsIASN1Sequence *parentSequence, nsINSSComponent *nssComponent) { nsCOMPtr spkiSequence = new nsNSSASN1Sequence(); if (spkiSequence == nsnull) return NS_ERROR_OUT_OF_MEMORY; nsString text; nssComponent->GetPIPNSSBundleString("CertDumpSPKI", text); spkiSequence->SetDisplayName(text); nssComponent->GetPIPNSSBundleString("CertDumpSPKIAlg", text); nsCOMPtr sequenceItem; nsresult rv = ProcessSECAlgorithmID(&spki->algorithm, nssComponent, getter_AddRefs(sequenceItem)); if (NS_FAILED(rv)) return rv; sequenceItem->SetDisplayName(text); nsCOMPtr asn1Objects; spkiSequence->GetASN1Objects(getter_AddRefs(asn1Objects)); asn1Objects->AppendElement(sequenceItem, PR_FALSE); // The subjectPublicKey field is encoded as a bit string. // ProcessRawBytes expects the lenght to be in bytes, so // let's convert the lenght into a temporary SECItem. SECItem data; data.data = spki->subjectPublicKey.data; data.len = spki->subjectPublicKey.len / 8; text.Truncate(); ProcessRawBytes(&data, text); nsCOMPtr printableItem = new nsNSSASN1PrintableItem(); if (printableItem == nsnull) return NS_ERROR_OUT_OF_MEMORY; printableItem->SetDisplayValue(text); nssComponent->GetPIPNSSBundleString("CertDumpSubjPubKey", text); printableItem->SetDisplayName(text); asn1Objects->AppendElement(printableItem, PR_FALSE); parentSequence->GetASN1Objects(getter_AddRefs(asn1Objects)); asn1Objects->AppendElement(spkiSequence, PR_FALSE); return NS_OK; } static nsresult ProcessExtensions(CERTCertExtension **extensions, nsIASN1Sequence *parentSequence, nsINSSComponent *nssComponent) { nsCOMPtr extensionSequence = new nsNSSASN1Sequence; if (extensionSequence == nsnull) return NS_ERROR_OUT_OF_MEMORY; nsString text; nssComponent->GetPIPNSSBundleString("CertDumpExtensions", text); extensionSequence->SetDisplayName(text); PRInt32 i; nsresult rv; nsCOMPtr newExtension; nsCOMPtr asn1Objects; extensionSequence->GetASN1Objects(getter_AddRefs(asn1Objects)); for (i=0; extensions[i] != nsnull; i++) { rv = ProcessSingleExtension(extensions[i], nssComponent, getter_AddRefs(newExtension)); if (NS_FAILED(rv)) return rv; asn1Objects->AppendElement(newExtension, PR_FALSE); } parentSequence->GetASN1Objects(getter_AddRefs(asn1Objects)); asn1Objects->AppendElement(extensionSequence, PR_FALSE); return NS_OK; } static nsresult ProcessName(CERTName *name, nsINSSComponent *nssComponent, PRUnichar **value) { CERTRDN** rdns; CERTRDN** rdn; CERTAVA** avas; CERTAVA* ava; SECItem *decodeItem = nsnull; nsString finalString; rdns = name->rdns; nsString type; nsresult rv; const PRUnichar *params[2]; nsString avavalue; nsAutoString temp; CERTRDN **lastRdn; lastRdn = rdns; /* find last RDN */ lastRdn = rdns; while (*lastRdn) lastRdn++; // The above whille loop will put us at the last member // of the array which is a NULL pointer. So let's back // up one spot so that we have the last non-NULL entry in // the array in preparation for traversing the // RDN's (Relative Distinguished Name) in reverse oder. lastRdn--; /* * Loop over name contents in _reverse_ RDN order appending to string * When building the Ascii string, NSS loops over these entries in * reverse order, so I will as well. The difference is that NSS * will always place them in a one line string separated by commas, * where I want each entry on a single line. I can't just use a comma * as my delimitter because it is a valid character to have in the * value portion of the AVA and could cause trouble when parsing. */ for (rdn = lastRdn; rdn >= rdns; rdn--) { avas = (*rdn)->avas; while ((ava = *avas++) != 0) { rv = GetOIDText(&ava->type, nssComponent, type); if (NS_FAILED(rv)) return rv; //This function returns a string in UTF8 format. decodeItem = CERT_DecodeAVAValue(&ava->value); if(!decodeItem) { return NS_ERROR_FAILURE; } avavalue = NS_ConvertUTF8toUTF16((char*)decodeItem->data, decodeItem->len); SECITEM_FreeItem(decodeItem, PR_TRUE); params[0] = type.get(); params[1] = avavalue.get(); nssComponent->PIPBundleFormatStringFromName("AVATemplate", params, 2, temp); finalString += temp + NS_LITERAL_STRING("\n"); } } *value = ToNewUnicode(finalString); return NS_OK; } nsresult nsNSSCertificate::CreateTBSCertificateASN1Struct(nsIASN1Sequence **retSequence, nsINSSComponent *nssComponent) { nsNSSShutDownPreventionLock locker; if (isAlreadyShutDown()) return NS_ERROR_NOT_AVAILABLE; // // TBSCertificate ::= SEQUENCE { // version [0] EXPLICIT Version DEFAULT v1, // serialNumber CertificateSerialNumber, // signature AlgorithmIdentifier, // issuer Name, // validity Validity, // subject Name, // subjectPublicKeyInfo SubjectPublicKeyInfo, // issuerUniqueID [1] IMPLICIT UniqueIdentifier OPTIONAL, // -- If present, version shall be v2 or v3 // subjectUniqueID [2] IMPLICIT UniqueIdentifier OPTIONAL, // -- If present, version shall be v2 or v3 // extensions [3] EXPLICIT Extensions OPTIONAL // -- If present, version shall be v3 // } // // This is the ASN1 structure we should be dealing with at this point. // The code in this method will assert this is the structure we're dealing // and then add more user friendly text for that field. nsCOMPtr sequence = new nsNSSASN1Sequence(); if (sequence == nsnull) return NS_ERROR_OUT_OF_MEMORY; nsString text; nssComponent->GetPIPNSSBundleString("CertDumpCertificate", text); sequence->SetDisplayName(text); nsCOMPtr printableItem; nsCOMPtr asn1Objects; sequence->GetASN1Objects(getter_AddRefs(asn1Objects)); nsresult rv = ProcessVersion(&mCert->version, nssComponent, getter_AddRefs(printableItem)); if (NS_FAILED(rv)) return rv; asn1Objects->AppendElement(printableItem, PR_FALSE); rv = ProcessSerialNumberDER(&mCert->serialNumber, nssComponent, getter_AddRefs(printableItem)); if (NS_FAILED(rv)) return rv; asn1Objects->AppendElement(printableItem, PR_FALSE); nsCOMPtr algID; rv = ProcessSECAlgorithmID(&mCert->signature, nssComponent, getter_AddRefs(algID)); if (NS_FAILED(rv)) return rv; nssComponent->GetPIPNSSBundleString("CertDumpSigAlg", text); algID->SetDisplayName(text); asn1Objects->AppendElement(algID, PR_FALSE); nsXPIDLString value; ProcessName(&mCert->issuer, nssComponent, getter_Copies(value)); printableItem = new nsNSSASN1PrintableItem(); if (printableItem == nsnull) return NS_ERROR_OUT_OF_MEMORY; printableItem->SetDisplayValue(value); nssComponent->GetPIPNSSBundleString("CertDumpIssuer", text); printableItem->SetDisplayName(text); asn1Objects->AppendElement(printableItem, PR_FALSE); nsCOMPtr validitySequence = new nsNSSASN1Sequence(); nssComponent->GetPIPNSSBundleString("CertDumpValidity", text); validitySequence->SetDisplayName(text); asn1Objects->AppendElement(validitySequence, PR_FALSE); nssComponent->GetPIPNSSBundleString("CertDumpNotBefore", text); nsCOMPtr validityData; GetValidity(getter_AddRefs(validityData)); PRTime notBefore, notAfter; validityData->GetNotBefore(¬Before); validityData->GetNotAfter(¬After); validityData = 0; rv = ProcessTime(notBefore, text.get(), validitySequence); if (NS_FAILED(rv)) return rv; nssComponent->GetPIPNSSBundleString("CertDumpNotAfter", text); rv = ProcessTime(notAfter, text.get(), validitySequence); if (NS_FAILED(rv)) return rv; nssComponent->GetPIPNSSBundleString("CertDumpSubject", text); printableItem = new nsNSSASN1PrintableItem(); if (printableItem == nsnull) return NS_ERROR_OUT_OF_MEMORY; printableItem->SetDisplayName(text); ProcessName(&mCert->subject, nssComponent,getter_Copies(value)); printableItem->SetDisplayValue(value); asn1Objects->AppendElement(printableItem, PR_FALSE); rv = ProcessSubjectPublicKeyInfo(&mCert->subjectPublicKeyInfo, sequence, nssComponent); if (NS_FAILED(rv)) return rv; SECItem data; // Is there an issuerUniqueID? if (mCert->issuerID.data != nsnull) { // The issuerID is encoded as a bit string. // The function ProcessRawBytes expects the // length to be in bytes, so let's convert the // length in a temporary SECItem data.data = mCert->issuerID.data; data.len = mCert->issuerID.len / 8; ProcessRawBytes(&data, text); printableItem = new nsNSSASN1PrintableItem(); if (printableItem == nsnull) return NS_ERROR_OUT_OF_MEMORY; printableItem->SetDisplayValue(text); nssComponent->GetPIPNSSBundleString("CertDumpIssuerUniqueID", text); printableItem->SetDisplayName(text); asn1Objects->AppendElement(printableItem, PR_FALSE); } if (mCert->subjectID.data) { // The subjectID is encoded as a bit string. // The function ProcessRawBytes expects the // length to be in bytes, so let's convert the // length in a temporary SECItem data.data = mCert->issuerID.data; data.len = mCert->issuerID.len / 8; ProcessRawBytes(&data, text); printableItem = new nsNSSASN1PrintableItem(); if (printableItem == nsnull) return NS_ERROR_OUT_OF_MEMORY; printableItem->SetDisplayValue(text); nssComponent->GetPIPNSSBundleString("CertDumpSubjectUniqueID", text); printableItem->SetDisplayName(text); asn1Objects->AppendElement(printableItem, PR_FALSE); } if (mCert->extensions) { rv = ProcessExtensions(mCert->extensions, sequence, nssComponent); if (NS_FAILED(rv)) return rv; } *retSequence = sequence; NS_ADDREF(*retSequence); return NS_OK; } #if defined(DEBUG_javi) || defined(DEBUG_jgmyers) void DumpASN1Object(nsIASN1Object *object, unsigned int level) { nsAutoString dispNameU, dispValU; unsigned int i; nsCOMPtr asn1Objects; nsCOMPtr isupports; nsCOMPtr currObject; PRBool processObjects; PRUint32 numObjects; for (i=0; iGetDisplayName(dispNameU); nsCOMPtr sequence(do_QueryInterface(object)); if (sequence) { printf ("%s ", NS_ConvertUCS2toUTF8(dispNameU).get()); sequence->GetIsValidContainer(&processObjects); if (processObjects) { printf("\n"); sequence->GetASN1Objects(getter_AddRefs(asn1Objects)); asn1Objects->GetLength(&numObjects); for (i=0; iQueryElementAt(i, NS_GET_IID(nsISupports), getter_AddRefs(currObject)); DumpASN1Object(currObject, level+1); } } else { object->GetDisplayValue(dispValU); printf("= %s\n", NS_ConvertUCS2toUTF8(dispValU).get()); } } else { object->GetDisplayValue(dispValU); printf("%s = %s\n",NS_ConvertUCS2toUTF8(dispNameU).get(), NS_ConvertUCS2toUTF8(dispValU).get()); } } #endif nsresult nsNSSCertificate::CreateASN1Struct() { nsNSSShutDownPreventionLock locker; if (isAlreadyShutDown()) return NS_ERROR_NOT_AVAILABLE; nsCOMPtr sequence = new nsNSSASN1Sequence(); mASN1Structure = sequence; if (mASN1Structure == nsnull) { return NS_ERROR_OUT_OF_MEMORY; } nsCOMPtr asn1Objects; sequence->GetASN1Objects(getter_AddRefs(asn1Objects)); nsXPIDLCString title; GetWindowTitle(getter_Copies(title)); mASN1Structure->SetDisplayName(NS_ConvertUTF8toUCS2(title)); // This sequence will be contain the tbsCertificate, signatureAlgorithm, // and signatureValue. nsresult rv; nsCOMPtr nssComponent(do_GetService(kNSSComponentCID, &rv)); if (NS_FAILED(rv)) return rv; rv = CreateTBSCertificateASN1Struct(getter_AddRefs(sequence), nssComponent); if (NS_FAILED(rv)) return rv; asn1Objects->AppendElement(sequence, PR_FALSE); nsCOMPtr algID; rv = ProcessSECAlgorithmID(&mCert->signatureWrap.signatureAlgorithm, nssComponent, getter_AddRefs(algID)); if (NS_FAILED(rv)) return rv; nsString text; nssComponent->GetPIPNSSBundleString("CertDumpSigAlg", text); algID->SetDisplayName(text); asn1Objects->AppendElement(algID, PR_FALSE); nsCOMPtrprintableItem = new nsNSSASN1PrintableItem(); nssComponent->GetPIPNSSBundleString("CertDumpCertSig", text); printableItem->SetDisplayName(text); // The signatureWrap is encoded as a bit string. // The function ProcessRawBytes expects the // length to be in bytes, so let's convert the // length in a temporary SECItem SECItem temp; temp.data = mCert->signatureWrap.signature.data; temp.len = mCert->signatureWrap.signature.len / 8; text.Truncate(); ProcessRawBytes(&temp,text); printableItem->SetDisplayValue(text); asn1Objects->AppendElement(printableItem, PR_FALSE); return NS_OK; } /* readonly attribute nsIASN1Object ASN1Structure; */ NS_IMETHODIMP nsNSSCertificate::GetASN1Structure(nsIASN1Object * *aASN1Structure) { nsNSSShutDownPreventionLock locker; nsresult rv = NS_OK; NS_ENSURE_ARG_POINTER(aASN1Structure); if (mASN1Structure == nsnull) { // First create the recursive structure os ASN1Objects // which tells us the layout of the cert. rv = CreateASN1Struct(); if (NS_FAILED(rv)) { return rv; } #ifdef DEBUG_javi DumpASN1Object(mASN1Structure, 0); #endif } *aASN1Structure = mASN1Structure; NS_IF_ADDREF(*aASN1Structure); return rv; } NS_IMETHODIMP nsNSSCertificate::Equals(nsIX509Cert *other, PRBool *result) { nsNSSShutDownPreventionLock locker; if (isAlreadyShutDown()) return NS_ERROR_NOT_AVAILABLE; NS_ENSURE_ARG(other); NS_ENSURE_ARG(result); nsNSSCertificate *other2 = NS_STATIC_CAST(nsNSSCertificate*, other); if (!other2) return NS_ERROR_FAILURE; *result = (mCert == other2->mCert); return NS_OK; } NS_IMETHODIMP nsNSSCertificate::SaveSMimeProfile() { nsNSSShutDownPreventionLock locker; if (isAlreadyShutDown()) return NS_ERROR_NOT_AVAILABLE; if (SECSuccess != CERT_SaveSMimeProfile(mCert, nsnull, nsnull)) return NS_ERROR_FAILURE; else return NS_OK; } char* nsNSSCertificate::defaultServerNickname(CERTCertificate* cert) { nsNSSShutDownPreventionLock locker; char* nickname = nsnull; int count; PRBool conflict; char* servername = nsnull; servername = CERT_GetCommonName(&cert->subject); if (servername == NULL) { return nsnull; } count = 1; while (1) { if (count == 1) { nickname = PR_smprintf("%s", servername); } else { nickname = PR_smprintf("%s #%d", servername, count); } if (nickname == NULL) { break; } conflict = SEC_CertNicknameConflict(nickname, &cert->derSubject, cert->dbhandle); if (conflict == PR_SUCCESS) { break; } PR_Free(nickname); count++; } PR_FREEIF(servername); return nickname; }