diff options
Diffstat (limited to 'smime/lib/e-cert-db.c')
-rw-r--r-- | smime/lib/e-cert-db.c | 1521 |
1 files changed, 1521 insertions, 0 deletions
diff --git a/smime/lib/e-cert-db.c b/smime/lib/e-cert-db.c new file mode 100644 index 0000000000..bb4a68e61d --- /dev/null +++ b/smime/lib/e-cert-db.c @@ -0,0 +1,1521 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ +/* The following is the mozilla license blurb, as the bodies some of + * these functions were derived from the mozilla source. */ +/* e-cert-db.c + * + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * 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 the Initial Developer are Copyright (C) 1994-2000 + * the Initial Developer. All Rights Reserved. + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + */ + +/* + * Author: Chris Toshok (toshok@ximian.com) + * + * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com) + */ +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include <gtk/gtk.h> +#include <glib/gi18n.h> +#include <glib/gstdio.h> + +#include <camel/camel.h> + +/* private NSS defines used by PSM */ +/* (must be declated before cert.h) */ +#define CERT_NewTempCertificate __CERT_NewTempCertificate +#define CERT_AddTempCertToPerm __CERT_AddTempCertToPerm + +#include "e-cert-db.h" +#include "e-cert-trust.h" +#include "e-pkcs12.h" + +#include "gmodule.h" + +#include "nss.h" +#include "ssl.h" +#include "p12plcy.h" +#include "pk11func.h" +#include "nssckbi.h" +#include <secerr.h> +#include "secmod.h" +#include "certdb.h" +#include "plstr.h" +#include "prprf.h" +#include "prmem.h" +#include "e-util/e-util.h" +#include "e-util/e-util-private.h" +#include <sys/types.h> +#include <sys/stat.h> +#include <unistd.h> + +enum { + PK11_PASSWD, + PK11_CHANGE_PASSWD, + CONFIRM_CA_CERT_IMPORT, + LAST_SIGNAL +}; + +static guint e_cert_db_signals[LAST_SIGNAL]; + +G_DEFINE_TYPE (ECertDB, e_cert_db, G_TYPE_OBJECT) + +GQuark +e_certdb_error_quark (void) +{ + static GQuark q = 0; + if (q == 0) + q = g_quark_from_static_string ("e-certdb-error-quark"); + + return q; +} + +static const gchar * +nss_error_to_string (glong errorcode) +{ +#define cs(a,b) case a: return b; + + switch (errorcode) { + cs (SEC_ERROR_IO, "An I/O error occurred during security authorization.") + cs (SEC_ERROR_LIBRARY_FAILURE, "security library failure.") + cs (SEC_ERROR_BAD_DATA, "security library: received bad data.") + cs (SEC_ERROR_OUTPUT_LEN, "security library: output length error.") + cs (SEC_ERROR_INPUT_LEN, "security library has experienced an input length error.") + cs (SEC_ERROR_INVALID_ARGS, "security library: invalid arguments.") + cs (SEC_ERROR_INVALID_ALGORITHM, "security library: invalid algorithm.") + cs (SEC_ERROR_INVALID_AVA, "security library: invalid AVA.") + cs (SEC_ERROR_INVALID_TIME, "Improperly formatted time string.") + cs (SEC_ERROR_BAD_DER, "security library: improperly formatted DER-encoded message.") + cs (SEC_ERROR_BAD_SIGNATURE, "Peer's certificate has an invalid signature.") + cs (SEC_ERROR_EXPIRED_CERTIFICATE, "Peer's Certificate has expired.") + cs (SEC_ERROR_REVOKED_CERTIFICATE, "Peer's Certificate has been revoked.") + cs (SEC_ERROR_UNKNOWN_ISSUER, "Peer's Certificate issuer is not recognized.") + cs (SEC_ERROR_BAD_KEY, "Peer's public key is invalid.") + cs (SEC_ERROR_BAD_PASSWORD, "The security password entered is incorrect.") + cs (SEC_ERROR_RETRY_PASSWORD, "New password entered incorrectly. Please try again.") + cs (SEC_ERROR_NO_NODELOCK, "security library: no nodelock.") + cs (SEC_ERROR_BAD_DATABASE, "security library: bad database.") + cs (SEC_ERROR_NO_MEMORY, "security library: memory allocation failure.") + cs (SEC_ERROR_UNTRUSTED_ISSUER, "Peer's certificate issuer has been marked as not trusted by the user.") + cs (SEC_ERROR_UNTRUSTED_CERT, "Peer's certificate has been marked as not trusted by the user.") + cs (SEC_ERROR_DUPLICATE_CERT, "Certificate already exists in your database.") + cs (SEC_ERROR_DUPLICATE_CERT_NAME, "Downloaded certificate's name duplicates one already in your database.") + cs (SEC_ERROR_ADDING_CERT, "Error adding certificate to database.") + cs (SEC_ERROR_FILING_KEY, "Error refiling the key for this certificate.") + cs (SEC_ERROR_NO_KEY, "The private key for this certificate cannot be found in key database") + cs (SEC_ERROR_CERT_VALID, "This certificate is valid.") + cs (SEC_ERROR_CERT_NOT_VALID, "This certificate is not valid.") + cs (SEC_ERROR_CERT_NO_RESPONSE, "Cert Library: No Response") + cs (SEC_ERROR_EXPIRED_ISSUER_CERTIFICATE, "The certificate issuer's certificate has expired. Check your system date and time.") + cs (SEC_ERROR_CRL_EXPIRED, "The CRL for the certificate's issuer has expired. Update it or check your system date and time.") + cs (SEC_ERROR_CRL_BAD_SIGNATURE, "The CRL for the certificate's issuer has an invalid signature.") + cs (SEC_ERROR_CRL_INVALID, "New CRL has an invalid format.") + cs (SEC_ERROR_EXTENSION_VALUE_INVALID, "Certificate extension value is invalid.") + cs (SEC_ERROR_EXTENSION_NOT_FOUND, "Certificate extension not found.") + cs (SEC_ERROR_CA_CERT_INVALID, "Issuer certificate is invalid.") + cs (SEC_ERROR_PATH_LEN_CONSTRAINT_INVALID, "Certificate path length constraint is invalid.") + cs (SEC_ERROR_CERT_USAGES_INVALID, "Certificate usages field is invalid.") + cs (SEC_INTERNAL_ONLY, "**Internal ONLY module**") + cs (SEC_ERROR_INVALID_KEY, "The key does not support the requested operation.") + cs (SEC_ERROR_UNKNOWN_CRITICAL_EXTENSION, "Certificate contains unknown critical extension.") + cs (SEC_ERROR_OLD_CRL, "New CRL is not later than the current one.") + cs (SEC_ERROR_NO_EMAIL_CERT, "Not encrypted or signed: you do not yet have an email certificate.") + cs (SEC_ERROR_NO_RECIPIENT_CERTS_QUERY, "Not encrypted: you do not have certificates for each of the recipients.") + cs (SEC_ERROR_NOT_A_RECIPIENT, "Cannot decrypt: you are not a recipient, or matching certificate and private key not found.") + cs (SEC_ERROR_PKCS7_KEYALG_MISMATCH, "Cannot decrypt: key encryption algorithm does not match your certificate.") + cs (SEC_ERROR_PKCS7_BAD_SIGNATURE, "Signature verification failed: no signer found, too many signers found, or improper or corrupted data.") + cs (SEC_ERROR_UNSUPPORTED_KEYALG, "Unsupported or unknown key algorithm.") + cs (SEC_ERROR_DECRYPTION_DISALLOWED, "Cannot decrypt: encrypted using a disallowed algorithm or key size.") + cs (XP_SEC_FORTEZZA_BAD_CARD, "Fortezza card has not been properly initialized. Please remove it and return it to your issuer.") + cs (XP_SEC_FORTEZZA_NO_CARD, "No Fortezza cards Found") + cs (XP_SEC_FORTEZZA_NONE_SELECTED, "No Fortezza card selected") + cs (XP_SEC_FORTEZZA_MORE_INFO, "Please select a personality to get more info on") + cs (XP_SEC_FORTEZZA_PERSON_NOT_FOUND, "Personality not found") + cs (XP_SEC_FORTEZZA_NO_MORE_INFO, "No more information on that Personality") + cs (XP_SEC_FORTEZZA_BAD_PIN, "Invalid Pin") + cs (XP_SEC_FORTEZZA_PERSON_ERROR, "Couldn't initialize Fortezza personalities.") + cs (SEC_ERROR_NO_KRL, "No KRL for this site's certificate has been found.") + cs (SEC_ERROR_KRL_EXPIRED, "The KRL for this site's certificate has expired.") + cs (SEC_ERROR_KRL_BAD_SIGNATURE, "The KRL for this site's certificate has an invalid signature.") + cs (SEC_ERROR_REVOKED_KEY, "The key for this site's certificate has been revoked.") + cs (SEC_ERROR_KRL_INVALID, "New KRL has an invalid format.") + cs (SEC_ERROR_NEED_RANDOM, "security library: need random data.") + cs (SEC_ERROR_NO_MODULE, "security library: no security module can perform the requested operation.") + cs (SEC_ERROR_NO_TOKEN, "The security card or token does not exist, needs to be initialized, or has been removed.") + cs (SEC_ERROR_READ_ONLY, "security library: read-only database.") + cs (SEC_ERROR_NO_SLOT_SELECTED, "No slot or token was selected.") + cs (SEC_ERROR_CERT_NICKNAME_COLLISION, "A certificate with the same nickname already exists.") + cs (SEC_ERROR_KEY_NICKNAME_COLLISION, "A key with the same nickname already exists.") + cs (SEC_ERROR_SAFE_NOT_CREATED, "error while creating safe object") + cs (SEC_ERROR_BAGGAGE_NOT_CREATED, "error while creating baggage object") + cs (XP_JAVA_REMOVE_PRINCIPAL_ERROR, "Couldn't remove the principal") + cs (XP_JAVA_DELETE_PRIVILEGE_ERROR, "Couldn't delete the privilege") + cs (XP_JAVA_CERT_NOT_EXISTS_ERROR, "This principal doesn't have a certificate") + cs (SEC_ERROR_BAD_EXPORT_ALGORITHM, "Required algorithm is not allowed.") + cs (SEC_ERROR_EXPORTING_CERTIFICATES, "Error attempting to export certificates.") + cs (SEC_ERROR_IMPORTING_CERTIFICATES, "Error attempting to import certificates.") + cs (SEC_ERROR_PKCS12_DECODING_PFX, "Unable to import. Decoding error. File not valid.") + cs (SEC_ERROR_PKCS12_INVALID_MAC, "Unable to import. Invalid MAC. Incorrect password or corrupt file.") + cs (SEC_ERROR_PKCS12_UNSUPPORTED_MAC_ALGORITHM, "Unable to import. MAC algorithm not supported.") + cs (SEC_ERROR_PKCS12_UNSUPPORTED_TRANSPORT_MODE, "Unable to import. Only password integrity and privacy modes supported.") + cs (SEC_ERROR_PKCS12_CORRUPT_PFX_STRUCTURE, "Unable to import. File structure is corrupt.") + cs (SEC_ERROR_PKCS12_UNSUPPORTED_PBE_ALGORITHM, "Unable to import. Encryption algorithm not supported.") + cs (SEC_ERROR_PKCS12_UNSUPPORTED_VERSION, "Unable to import. File version not supported.") + cs (SEC_ERROR_PKCS12_PRIVACY_PASSWORD_INCORRECT, "Unable to import. Incorrect privacy password.") + cs (SEC_ERROR_PKCS12_CERT_COLLISION, "Unable to import. Same nickname already exists in database.") + cs (SEC_ERROR_USER_CANCELLED, "The user pressed cancel.") + cs (SEC_ERROR_PKCS12_DUPLICATE_DATA, "Not imported, already in database.") + cs (SEC_ERROR_MESSAGE_SEND_ABORTED, "Message not sent.") + cs (SEC_ERROR_INADEQUATE_KEY_USAGE, "Certificate key usage inadequate for attempted operation.") + cs (SEC_ERROR_INADEQUATE_CERT_TYPE, "Certificate type not approved for application.") + cs (SEC_ERROR_CERT_ADDR_MISMATCH, "Address in signing certificate does not match address in message headers.") + cs (SEC_ERROR_PKCS12_UNABLE_TO_IMPORT_KEY, "Unable to import. Error attempting to import private key.") + cs (SEC_ERROR_PKCS12_IMPORTING_CERT_CHAIN, "Unable to import. Error attempting to import certificate chain.") + cs (SEC_ERROR_PKCS12_UNABLE_TO_LOCATE_OBJECT_BY_NAME, "Unable to export. Unable to locate certificate or key by nickname.") + cs (SEC_ERROR_PKCS12_UNABLE_TO_EXPORT_KEY, "Unable to export. Private Key could not be located and exported.") + cs (SEC_ERROR_PKCS12_UNABLE_TO_WRITE, "Unable to export. Unable to write the export file.") + cs (SEC_ERROR_PKCS12_UNABLE_TO_READ, "Unable to import. Unable to read the import file.") + cs (SEC_ERROR_PKCS12_KEY_DATABASE_NOT_INITIALIZED, "Unable to export. Key database corrupt or deleted.") + cs (SEC_ERROR_KEYGEN_FAIL, "Unable to generate public/private key pair.") + cs (SEC_ERROR_INVALID_PASSWORD, "Password entered is invalid. Please pick a different one.") + cs (SEC_ERROR_RETRY_OLD_PASSWORD, "Old password entered incorrectly. Please try again.") + cs (SEC_ERROR_BAD_NICKNAME, "Certificate nickname already in use.") + cs (SEC_ERROR_NOT_FORTEZZA_ISSUER, "Peer FORTEZZA chain has a non-FORTEZZA Certificate.") + cs (SEC_ERROR_CANNOT_MOVE_SENSITIVE_KEY, "A sensitive key cannot be moved to the slot where it is needed.") + cs (SEC_ERROR_JS_INVALID_MODULE_NAME, "Invalid module name.") + cs (SEC_ERROR_JS_INVALID_DLL, "Invalid module path/filename") + cs (SEC_ERROR_JS_ADD_MOD_FAILURE, "Unable to add module") + cs (SEC_ERROR_JS_DEL_MOD_FAILURE, "Unable to delete module") + cs (SEC_ERROR_OLD_KRL, "New KRL is not later than the current one.") + cs (SEC_ERROR_CKL_CONFLICT, "New CKL has different issuer than current CKL. Delete current CKL.") + cs (SEC_ERROR_CERT_NOT_IN_NAME_SPACE, "The Certifying Authority for this certificate is not permitted to issue a certificate with this name.") + cs (SEC_ERROR_KRL_NOT_YET_VALID, "The key revocation list for this certificate is not yet valid.") + cs (SEC_ERROR_CRL_NOT_YET_VALID, "The certificate revocation list for this certificate is not yet valid.") + cs (SEC_ERROR_UNKNOWN_CERT, "The requested certificate could not be found.") + cs (SEC_ERROR_UNKNOWN_SIGNER, "The signer's certificate could not be found.") + cs (SEC_ERROR_CERT_BAD_ACCESS_LOCATION, "The location for the certificate status server has invalid format.") + cs (SEC_ERROR_OCSP_UNKNOWN_RESPONSE_TYPE, "The OCSP response cannot be fully decoded; it is of an unknown type.") + cs (SEC_ERROR_OCSP_BAD_HTTP_RESPONSE, "The OCSP server returned unexpected/invalid HTTP data.") + cs (SEC_ERROR_OCSP_MALFORMED_REQUEST, "The OCSP server found the request to be corrupted or improperly formed.") + cs (SEC_ERROR_OCSP_SERVER_ERROR, "The OCSP server experienced an internal error.") + cs (SEC_ERROR_OCSP_TRY_SERVER_LATER, "The OCSP server suggests trying again later.") + cs (SEC_ERROR_OCSP_REQUEST_NEEDS_SIG, "The OCSP server requires a signature on this request.") + cs (SEC_ERROR_OCSP_UNAUTHORIZED_REQUEST, "The OCSP server has refused this request as unauthorized.") + cs (SEC_ERROR_OCSP_UNKNOWN_RESPONSE_STATUS, "The OCSP server returned an unrecognizable status.") + cs (SEC_ERROR_OCSP_UNKNOWN_CERT, "The OCSP server has no status for the certificate.") + cs (SEC_ERROR_OCSP_NOT_ENABLED, "You must enable OCSP before performing this operation.") + cs (SEC_ERROR_OCSP_NO_DEFAULT_RESPONDER, "You must set the OCSP default responder before performing this operation.") + cs (SEC_ERROR_OCSP_MALFORMED_RESPONSE, "The response from the OCSP server was corrupted or improperly formed.") + cs (SEC_ERROR_OCSP_UNAUTHORIZED_RESPONSE, "The signer of the OCSP response is not authorized to give status for this certificate.") + cs (SEC_ERROR_OCSP_FUTURE_RESPONSE, "The OCSP response is not yet valid (contains a date in the future).") + cs (SEC_ERROR_OCSP_OLD_RESPONSE, "The OCSP response contains out-of-date information.") + cs (SEC_ERROR_DIGEST_NOT_FOUND, "The CMS or PKCS #7 Digest was not found in signed message.") + cs (SEC_ERROR_UNSUPPORTED_MESSAGE_TYPE, "The CMS or PKCS #7 Message type is unsupported.") + cs (SEC_ERROR_MODULE_STUCK, "PKCS #11 module could not be removed because it is still in use.") + cs (SEC_ERROR_BAD_TEMPLATE, "Could not decode ASN.1 data. Specified template was invalid.") + cs (SEC_ERROR_CRL_NOT_FOUND, "No matching CRL was found.") + cs (SEC_ERROR_REUSED_ISSUER_AND_SERIAL, "You are attempting to import a cert with the same issuer/serial as an existing cert, but that is not the same cert.") + cs (SEC_ERROR_BUSY, "NSS could not shutdown. Objects are still in use.") + cs (SEC_ERROR_EXTRA_INPUT, "DER-encoded message contained extra unused data.") + cs (SEC_ERROR_UNSUPPORTED_ELLIPTIC_CURVE, "Unsupported elliptic curve.") + cs (SEC_ERROR_UNSUPPORTED_EC_POINT_FORM, "Unsupported elliptic curve point form.") + cs (SEC_ERROR_UNRECOGNIZED_OID, "Unrecognized Object Identifier.") + cs (SEC_ERROR_OCSP_INVALID_SIGNING_CERT, "Invalid OCSP signing certificate in OCSP response.") + cs (SEC_ERROR_REVOKED_CERTIFICATE_CRL, "Certificate is revoked in issuer's certificate revocation list.") + cs (SEC_ERROR_REVOKED_CERTIFICATE_OCSP, "Issuer's OCSP responder reports certificate is revoked.") + cs (SEC_ERROR_CRL_INVALID_VERSION, "Issuer's Certificate Revocation List has an unknown version number.") + cs (SEC_ERROR_CRL_V1_CRITICAL_EXTENSION, "Issuer's V1 Certificate Revocation List has a critical extension.") + cs (SEC_ERROR_CRL_UNKNOWN_CRITICAL_EXTENSION, "Issuer's V2 Certificate Revocation List has an unknown critical extension.") + cs (SEC_ERROR_UNKNOWN_OBJECT_TYPE, "Unknown object type specified.") + cs (SEC_ERROR_INCOMPATIBLE_PKCS11, "PKCS #11 driver violates the spec in an incompatible way.") + cs (SEC_ERROR_NO_EVENT, "No new slot event is available at this time.") + cs (SEC_ERROR_CRL_ALREADY_EXISTS, "CRL already exists.") + cs (SEC_ERROR_NOT_INITIALIZED, "NSS is not initialized.") + cs (SEC_ERROR_TOKEN_NOT_LOGGED_IN, "The operation failed because the PKCS#11 token is not logged in.") + cs (SEC_ERROR_OCSP_RESPONDER_CERT_INVALID, "Configured OCSP responder's certificate is invalid.") + cs (SEC_ERROR_OCSP_BAD_SIGNATURE, "OCSP response has an invalid signature.") + + #if defined (NSS_VMAJOR) && defined (NSS_VMINOR) && defined (NSS_VPATCH) && (NSS_VMAJOR > 3 || (NSS_VMAJOR == 3 && NSS_VMINOR > 12) || (NSS_VMAJOR == 3 && NSS_VMINOR == 12 && NSS_VPATCH >= 2)) + cs (SEC_ERROR_OUT_OF_SEARCH_LIMITS, "Cert validation search is out of search limits") + cs (SEC_ERROR_INVALID_POLICY_MAPPING, "Policy mapping contains anypolicy") + cs (SEC_ERROR_POLICY_VALIDATION_FAILED, "Cert chain fails policy validation") + cs (SEC_ERROR_UNKNOWN_AIA_LOCATION_TYPE, "Unknown location type in cert AIA extension") + cs (SEC_ERROR_BAD_HTTP_RESPONSE, "Server returned bad HTTP response") + cs (SEC_ERROR_BAD_LDAP_RESPONSE, "Server returned bad LDAP response") + cs (SEC_ERROR_FAILED_TO_ENCODE_DATA, "Failed to encode data with ASN1 encoder") + cs (SEC_ERROR_BAD_INFO_ACCESS_LOCATION, "Bad information access location in cert extension") + cs (SEC_ERROR_LIBPKIX_INTERNAL, "Libpkix internal error occured during cert validation.") + cs (SEC_ERROR_PKCS11_GENERAL_ERROR, "A PKCS #11 module returned CKR_GENERAL_ERROR, indicating that an unrecoverable error has occurred.") + cs (SEC_ERROR_PKCS11_FUNCTION_FAILED, "A PKCS #11 module returned CKR_FUNCTION_FAILED, indicating that the requested function could not be performed. Trying the same operation again might succeed.") + cs (SEC_ERROR_PKCS11_DEVICE_ERROR, "A PKCS #11 module returned CKR_DEVICE_ERROR, indicating that a problem has occurred with the token or slot.") + #endif + } + + #undef cs + + return NULL; +} + +static void +set_nss_error (GError **error) +{ + glong err_code; + const gchar *err_str; + + if (!error) + return; + + g_return_if_fail (*error == NULL); + + err_code = PORT_GetError (); + + if (!err_code) + return; + + err_str = nss_error_to_string (err_code); + if (!err_str) + return; + + *error = g_error_new_literal (E_CERTDB_ERROR, err_code, err_str); +} + +static SECStatus PR_CALLBACK +collect_certs (gpointer arg, + SECItem **certs, + gint numcerts) +{ + CERTDERCerts *collectArgs; + SECItem *cert; + SECStatus rv; + + collectArgs = (CERTDERCerts *) arg; + + collectArgs->numcerts = numcerts; + collectArgs->rawCerts = (SECItem *) PORT_ArenaZAlloc ( + collectArgs->arena, sizeof (SECItem) * numcerts); + if (collectArgs->rawCerts == NULL) + return (SECFailure); + + cert = collectArgs->rawCerts; + + while (numcerts--) { + rv = SECITEM_CopyItem (collectArgs->arena, cert, *certs); + if (rv == SECFailure) + return (SECFailure); + cert++; + certs++; + } + + return (SECSuccess); +} + +static CERTDERCerts * +e_cert_db_get_certs_from_package (PRArenaPool *arena, + gchar *data, + guint32 length) +{ + /*nsNSSShutDownPreventionLock locker;*/ + CERTDERCerts *collectArgs = + (CERTDERCerts *) PORT_ArenaZAlloc (arena, sizeof (CERTDERCerts)); + SECStatus sec_rv; + + if (!collectArgs) + return NULL; + + collectArgs->arena = arena; + sec_rv = CERT_DecodeCertPackage ( + data, + length, collect_certs, + (gpointer) collectArgs); + + if (sec_rv != SECSuccess) + return NULL; + + return collectArgs; +} + +#ifdef notyet +PRBool +ucs2_ascii_conversion_fn (PRBool toUnicode, + guchar *inBuf, + guint inBufLen, + guchar *outBuf, + guint maxOutBufLen, + guint *outBufLen, + PRBool swapBytes) +{ + printf ("in ucs2_ascii_conversion_fn\n"); +} +#endif + +static gchar * PR_CALLBACK +pk11_password (PK11SlotInfo *slot, + PRBool retry, + gpointer arg) +{ + gchar *pwd; + gchar *nsspwd; + + gboolean rv = FALSE; + + g_signal_emit ( + e_cert_db_peek (), + e_cert_db_signals[PK11_PASSWD], 0, + slot, + retry, + &pwd, + &rv); + + if (pwd == NULL) + return NULL; + + nsspwd = PORT_Strdup (pwd); + memset (pwd, 0, strlen (pwd)); + g_free (pwd); + return nsspwd; +} + +static void +initialize_nss (void) +{ + /* Use camel_init() to initialise NSS consistently... */ + camel_init (e_get_user_data_dir (), TRUE); + + /* ... except for the bits we only seem to do here. FIXME */ + PK11_SetPasswordFunc (pk11_password); + + /* Enable ciphers for PKCS#12 */ + SEC_PKCS12EnableCipher (PKCS12_RC4_40, 1); + SEC_PKCS12EnableCipher (PKCS12_RC4_128, 1); + SEC_PKCS12EnableCipher (PKCS12_RC2_CBC_40, 1); + SEC_PKCS12EnableCipher (PKCS12_RC2_CBC_128, 1); + SEC_PKCS12EnableCipher (PKCS12_DES_56, 1); + SEC_PKCS12EnableCipher (PKCS12_DES_EDE3_168, 1); + SEC_PKCS12SetPreferredCipher (PKCS12_DES_EDE3_168, 1); +#ifdef notyet + PORT_SetUCS2_ASCIIConversionFunction (ucs2_ascii_conversion_fn); +#endif +} + +static void +install_loadable_roots (void) +{ + SECMODModuleList *list = SECMOD_GetDefaultModuleList (); + SECMODListLock *lock = SECMOD_GetDefaultModuleListLock (); + SECMODModule *RootsModule = NULL; + gint i; + + SECMOD_GetReadLock (lock); + while (!RootsModule && list) { + SECMODModule *module = list->module; + + for (i = 0; i < module->slotCount; i++) { + PK11SlotInfo *slot = module->slots[i]; + if (PK11_IsPresent (slot)) { + if (PK11_HasRootCerts (slot)) { + RootsModule = module; + break; + } + } + } + + list = list->next; + } + SECMOD_ReleaseReadLock (lock); + + if (RootsModule) { + /* Check version, and unload module if it is too old */ + CK_INFO info; + + if (PK11_GetModInfo (RootsModule, &info) != SECSuccess) { + /* Do not use this module */ + RootsModule = NULL; + } else { + /* NSS_BUILTINS_LIBRARY_VERSION_MAJOR and NSS_BUILTINS_LIBRARY_VERSION_MINOR + * define the version we expect to have. + * Later version are fine. + * Older versions are not ok, and we will replace with our own version. + */ + if ((info.libraryVersion.major < NSS_BUILTINS_LIBRARY_VERSION_MAJOR) + || (info.libraryVersion.major == NSS_BUILTINS_LIBRARY_VERSION_MAJOR + && info.libraryVersion.minor < NSS_BUILTINS_LIBRARY_VERSION_MINOR)) { + PRInt32 modType; + + SECMOD_DeleteModule (RootsModule->commonName, &modType); + + RootsModule = NULL; + } + } + } + + if (!RootsModule) { +#ifndef G_OS_WIN32 + /* grovel in various places for mozilla's built-in + * cert module. + * + * XXX yes this is gross. *sigh * + */ + const gchar *paths_to_check[] = { +#ifdef MOZILLA_NSS_LIB_DIR + MOZILLA_NSS_LIB_DIR, +#endif + "/usr/lib", + "/usr/lib/mozilla", + "/opt/mozilla/lib", + "/opt/mozilla/lib/mozilla" + }; + + for (i = 0; i < G_N_ELEMENTS (paths_to_check); i++) { + gchar *dll_path = g_module_build_path (paths_to_check[i], "nssckbi"); + + if (g_file_test (dll_path, G_FILE_TEST_EXISTS)) { + PRInt32 modType; + + /* Delete the existing module */ + SECMOD_DeleteModule ("Mozilla Root Certs", &modType); + + SECMOD_AddNewModule ("Mozilla Root Certs",dll_path, 0, 0); + g_free (dll_path); + break; + } + + g_free (dll_path); + } +#else + /* FIXME: Might be useful to look up if there is a + * Mozilla installation on the machine and use the + * nssckbi.dll from there. + */ +#endif + } +} + +static void +e_cert_db_class_init (ECertDBClass *class) +{ + GObjectClass *object_class; + + object_class = G_OBJECT_CLASS (class); + + initialize_nss (); + /* check to see if you have a rootcert module installed */ + install_loadable_roots (); + + e_cert_db_signals[PK11_PASSWD] = g_signal_new ( + "pk11_passwd", + G_OBJECT_CLASS_TYPE (object_class), + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET (ECertDBClass, pk11_passwd), + NULL, NULL, + e_marshal_BOOLEAN__POINTER_BOOLEAN_POINTER, + G_TYPE_BOOLEAN, 3, + G_TYPE_POINTER, G_TYPE_BOOLEAN, G_TYPE_POINTER); + + e_cert_db_signals[PK11_CHANGE_PASSWD] = g_signal_new ( + "pk11_change_passwd", + G_OBJECT_CLASS_TYPE (object_class), + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET (ECertDBClass, pk11_change_passwd), + NULL, NULL, + e_marshal_BOOLEAN__POINTER_POINTER, + G_TYPE_BOOLEAN, 2, + G_TYPE_POINTER, G_TYPE_POINTER); + + e_cert_db_signals[CONFIRM_CA_CERT_IMPORT] = g_signal_new ( + "confirm_ca_cert_import", + G_OBJECT_CLASS_TYPE (object_class), + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET (ECertDBClass, confirm_ca_cert_import), + NULL, NULL, + e_marshal_BOOLEAN__POINTER_POINTER_POINTER_POINTER, + G_TYPE_BOOLEAN, 4, + G_TYPE_POINTER, G_TYPE_POINTER, G_TYPE_POINTER, G_TYPE_POINTER); +} + +static void +e_cert_db_init (ECertDB *ec) +{ +} + +GMutex init_mutex; +static ECertDB *cert_db = NULL; + +ECertDB * +e_cert_db_peek (void) +{ + g_mutex_lock (&init_mutex); + if (!cert_db) + cert_db = g_object_new (E_TYPE_CERT_DB, NULL); + g_mutex_unlock (&init_mutex); + + return cert_db; +} + +void +e_cert_db_shutdown (void) +{ + /* XXX */ +} + +/* searching for certificates */ +ECert * +e_cert_db_find_cert_by_nickname (ECertDB *certdb, + const gchar *nickname, + GError **error) +{ + /* nsNSSShutDownPreventionLock locker;*/ + CERTCertificate *cert = NULL; + + /*PR_LOG(gPIPNSSLog, PR_LOG_DEBUG, ("Getting \"%s\"\n", asciiname));*/ + cert = PK11_FindCertFromNickname ((gchar *) nickname, NULL); + if (!cert) { + cert = CERT_FindCertByNickname (CERT_GetDefaultCertDB (), (gchar *) nickname); + } + + if (cert) { + /* PR_LOG(gPIPNSSLog, PR_LOG_DEBUG, ("got it\n"));*/ + ECert *ecert = e_cert_new (cert); + return ecert; + } + else { + set_nss_error (error); + return NULL; + } +} + +#ifdef notyet +ECert * +e_cert_db_find_cert_by_key (ECertDB *certdb, + const gchar *db_key, + GError **error) +{ + /* nsNSSShutDownPreventionLock locker;*/ + SECItem keyItem = {siBuffer, NULL, 0}; + SECItem *dummy; + CERTIssuerAndSN issuerSN; + gulong moduleID,slotID; + CERTCertificate *cert; + + if (!db_key) { + set_nss_error (error); + return NULL; + } + + dummy = NSSBase64_DecodeBuffer ( + NULL, &keyItem, db_key, + (PRUint32) PL_strlen (db_key)); + + /* someday maybe we can speed up the search using the moduleID and slotID*/ + moduleID = NS_NSS_GET_LONG (keyItem.data); + slotID = NS_NSS_GET_LONG (&keyItem.data[NS_NSS_LONG]); + + /* build the issuer/SN structure*/ + issuerSN.serialNumber.len = NS_NSS_GET_LONG (&keyItem.data[NS_NSS_LONG *2]); + issuerSN.derIssuer.len = NS_NSS_GET_LONG (&keyItem.data[NS_NSS_LONG *3]); + issuerSN.serialNumber.data= &keyItem.data[NS_NSS_LONG *4]; + issuerSN.derIssuer.data= &keyItem.data[NS_NSS_LONG *4+ + issuerSN.serialNumber.len]; + + cert = CERT_FindCertByIssuerAndSN (CERT_GetDefaultCertDB (), &issuerSN); + PR_FREEIF (keyItem.data); + if (cert) { + ECert *ecert = e_cert_new (cert); + return e_cert; + } + + set_nss_error (error); + return NULL; +} + +GList * +e_cert_db_get_cert_nicknames (ECertDB *certdb, + ECertType cert_type, + GError **error) +{ +} + +ECert * +e_cert_db_find_email_encryption_cert (ECertDB *certdb, + const gchar *nickname, + GError **error) +{ +} + +ECert * +e_cert_db_find_email_signing_cert (ECertDB *certdb, + const gchar *nickname, + GError **error) +{ +} +#endif + +ECert * +e_cert_db_find_cert_by_email_address (ECertDB *certdb, + const gchar *email, + GError **error) +{ + /* nsNSSShutDownPreventionLock locker; */ + ECert *cert; + CERTCertificate *any_cert; + CERTCertList *certlist; + + any_cert = CERT_FindCertByNicknameOrEmailAddr ( + CERT_GetDefaultCertDB (), (gchar *) email); + + if (!any_cert) { + set_nss_error (error); + return NULL; + } + + /* any_cert now contains a cert with the right subject, + * but it might not have the correct usage. */ + certlist = CERT_CreateSubjectCertList ( + NULL, + CERT_GetDefaultCertDB (), + &any_cert->derSubject, + PR_Now (), PR_TRUE); + if (!certlist) { + set_nss_error (error); + CERT_DestroyCertificate (any_cert); + return NULL; + } + + if (SECSuccess != CERT_FilterCertListByUsage ( + certlist, certUsageEmailRecipient, PR_FALSE)) { + set_nss_error (error); + CERT_DestroyCertificate (any_cert); + CERT_DestroyCertList (certlist); + return NULL; + } + + if (CERT_LIST_END (CERT_LIST_HEAD (certlist), certlist)) { + set_nss_error (error); + CERT_DestroyCertificate (any_cert); + CERT_DestroyCertList (certlist); + return NULL; + } + + cert = e_cert_new (CERT_DupCertificate (CERT_LIST_HEAD (certlist)->cert)); + + CERT_DestroyCertList (certlist); + CERT_DestroyCertificate (any_cert); + + return cert; +} + +static gboolean +confirm_download_ca_cert (ECertDB *cert_db, + ECert *cert, + gboolean *trust_ssl, + gboolean *trust_email, + gboolean *trust_objsign) +{ + gboolean rv = FALSE; + + *trust_ssl = + *trust_email = + *trust_objsign = FALSE; + + g_signal_emit ( + e_cert_db_peek (), + e_cert_db_signals[CONFIRM_CA_CERT_IMPORT], 0, + cert, + trust_ssl, + trust_email, + trust_objsign, + &rv); + + return rv; +} + +static gboolean +handle_ca_cert_download (ECertDB *cert_db, + GList *certs, + GError **error) +{ + ECert *certToShow; + SECItem der; + gchar *raw_der = NULL; + CERTCertificate *tmpCert; + + /* 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 chain up to its root + * in either ascending or descending order. What we're gonna + * do is compare the first 2 entries, if the first was signed + * by the second, we assume the leaf cert is the first cert + * and display it. If the second cert was signed by the first + * cert, then we assume the first cert is the root and the + * last cert in the array is the leaf. In this case we + * display the last cert. + */ + + /* nsNSSShutDownPreventionLock locker;*/ + + if (certs == NULL) { + g_warning ("Didn't get any certs to import."); + return TRUE; + } + else if (certs->next == NULL) { + /* there's 1 cert */ + certToShow = E_CERT (certs->data); + } + else { + /* there are multiple certs */ + ECert *cert0; + ECert *cert1; + const gchar * cert0SubjectName; + const gchar * cert0IssuerName; + const gchar * cert1SubjectName; + const gchar * cert1IssuerName; + + cert0 = E_CERT (certs->data); + cert1 = E_CERT (certs->next->data); + + cert0IssuerName = e_cert_get_issuer_name (cert0); + cert0SubjectName = e_cert_get_subject_name (cert0); + + cert1IssuerName = e_cert_get_issuer_name (cert1); + cert1SubjectName = e_cert_get_subject_name (cert1); + + if (!strcmp (cert1IssuerName, cert0SubjectName)) { + /* In this case, the first cert in the list signed the second, + * so the first cert is the root. Let's display the last cert + * in the list. */ + certToShow = E_CERT (g_list_last (certs)->data); + } + else if (!strcmp (cert0IssuerName, cert1SubjectName)) { + /* In this case the second cert has signed the first cert. The + * first cert is the leaf, so let's display it. */ + certToShow = cert0; + } else { + /* It's not a chain, so let's just show the first one in the + * downloaded list. */ + certToShow = cert0; + } + } + + if (!certToShow) { + set_nss_error (error); + return FALSE; + } + + if (!e_cert_get_raw_der (certToShow, &raw_der, &der.len)) { + set_nss_error (error); + return FALSE; + } + + der.data = (guchar *) raw_der; + + { + /*PR_LOG(gPIPNSSLog, PR_LOG_DEBUG, ("Creating temp cert\n"));*/ + CERTCertDBHandle *certdb = CERT_GetDefaultCertDB (); + tmpCert = CERT_FindCertByDERCert (certdb, &der); + if (!tmpCert) { + tmpCert = CERT_NewTempCertificate ( + certdb, &der, + NULL, PR_FALSE, PR_TRUE); + } + if (!tmpCert) { + g_warning ("Couldn't create cert from DER blob"); + set_nss_error (error); + return FALSE; + } + } + +#if 0 + CERTCertificateCleaner tmpCertCleaner (tmpCert); +#endif + + if (tmpCert->isperm) { + if (error && !*error) + *error = g_error_new_literal (E_CERTDB_ERROR, 0, _("Certificate already exists")); + return FALSE; + } + else { + gboolean trust_ssl, trust_email, trust_objsign; + gchar *nickname; + SECStatus srv; + CERTCertTrust trust; + + if (!confirm_download_ca_cert ( + cert_db, certToShow, &trust_ssl, + &trust_email, &trust_objsign)) { + set_nss_error (error); + return FALSE; + } + + /*PR_LOG(gPIPNSSLog, PR_LOG_DEBUG, ("trust is %d\n", trustBits));*/ + + nickname = CERT_MakeCANickname (tmpCert); + + /*PR_LOG(gPIPNSSLog, PR_LOG_DEBUG, ("Created nick \"%s\"\n", nickname.get()));*/ + + e_cert_trust_init (&trust); + e_cert_trust_set_valid_ca (&trust); + e_cert_trust_add_ca_trust ( + &trust, + trust_ssl, + trust_email, + trust_objsign); + + srv = CERT_AddTempCertToPerm ( + tmpCert, + nickname, + &trust); + + /* If we aren't logged into the token, then what *should* + * happen is the above call should fail, and we should + * authenticate and then try again. But see NSS bug #595861. + * With NSS 3.12.6 at least, the above call will fail, but + * it *will* have added the cert to the database, with + * random trust bits. We have to authenticate and then set + * the trust bits correctly. And calling + * CERT_AddTempCertToPerm() again doesn't work either -- it'll + * fail even though it arguably ought to succeed (which is + * probably another NSS bug). + * So if we get SEC_ERROR_TOKEN_NOT_LOGGED_IN, we first try + * CERT_ChangeCertTrust(), and if that doesn't work we hope + * we're on a fixed version of NSS and we try calling + * CERT_AddTempCertToPerm() again instead. */ + if (srv != SECSuccess && + PORT_GetError () == SEC_ERROR_TOKEN_NOT_LOGGED_IN && + e_cert_db_login_to_slot (NULL, PK11_GetInternalKeySlot ())) { + srv = CERT_ChangeCertTrust ( + CERT_GetDefaultCertDB (), + tmpCert, &trust); + if (srv != SECSuccess) + srv = CERT_AddTempCertToPerm ( + tmpCert, + nickname, + &trust); + } + if (srv != SECSuccess) { + set_nss_error (error); + return FALSE; + } + +#if 0 + /* Now it's time to add the rest of the certs we just downloaded. + * Since we didn't prompt the user about any of these certs, we + * won't set any trust bits for them. */ + e_cert_trust_init (&trust); + e_cert_trust_set_valid_ca (&trust); + e_cert_trusts_add_ca_trust (&trust, 0, 0, 0); + for (PRUint32 i = 0; i < numCerts; i++) { + if (i == selCertIndex) + continue; + + certToShow = do_QueryElementAt (x509Certs, i); + certToShow->GetRawDER (&der.len, (PRUint8 **) &der.data); + + CERTCertificate *tmpCert2 = + CERT_NewTempCertificate (certdb, &der, nsnull, PR_FALSE, PR_TRUE); + + if (!tmpCert2) { + NS_ASSERTION (0, "Couldn't create temp cert from DER blob\n"); + continue; /* Let's try to import the rest of 'em */ + } + nickname.Adopt (CERT_MakeCANickname (tmpCert2)); + CERT_AddTempCertToPerm ( + tmpCert2, NS_CONST_CAST (gchar *,nickname.get ()), + defaultTrust.GetTrust ()); + CERT_DestroyCertificate (tmpCert2); + } +#endif + return TRUE; + } +} +gboolean e_cert_db_change_cert_trust (CERTCertificate *cert, CERTCertTrust *trust) +{ + SECStatus srv; + + srv = CERT_ChangeCertTrust ( + CERT_GetDefaultCertDB (), + cert, trust); + if (srv != SECSuccess && + PORT_GetError () == SEC_ERROR_TOKEN_NOT_LOGGED_IN && + e_cert_db_login_to_slot (NULL, PK11_GetInternalKeySlot ())) + srv = CERT_ChangeCertTrust ( + CERT_GetDefaultCertDB (), + cert, trust); + + if (srv != SECSuccess) { + glong err = PORT_GetError (); + g_warning ( + "CERT_ChangeCertTrust() failed: %s\n", + nss_error_to_string (err)); + return FALSE; + } + return TRUE; +} + +/* deleting certificates */ +gboolean +e_cert_db_delete_cert (ECertDB *certdb, + ECert *ecert) +{ + /* nsNSSShutDownPreventionLock locker; + * nsNSSCertificate *nssCert = NS_STATIC_CAST (nsNSSCertificate *, aCert); */ + + CERTCertificate *cert; + + if (!e_cert_mark_for_deletion (ecert)) { + return FALSE; + } + + cert = e_cert_get_internal_cert (ecert); + if (cert->slot && e_cert_get_cert_type (ecert) != E_CERT_USER) { + /* To delete a cert of a slot (builtin, most likely), mark it as + * completely untrusted. This way we keep a copy cached in the + * local database, and next time we try to load it off of the + * external token/slot, we'll know not to trust it. We don't + * want to do that with user certs, because a user may re-store + * the cert onto the card again at which point we *will* want to + * trust that cert if it chains up properly. */ + CERTCertTrust trust; + + e_cert_trust_init_with_values (&trust, 0, 0, 0); + return e_cert_db_change_cert_trust (cert, &trust); + } + + return TRUE; +} + +/* importing certificates */ +gboolean +e_cert_db_import_certs (ECertDB *certdb, + gchar *data, + guint32 length, + ECertType cert_type, + GSList **imported_certs, + GError **error) +{ + /*nsNSSShutDownPreventionLock locker;*/ + PRArenaPool *arena = PORT_NewArena (DER_DEFAULT_CHUNKSIZE); + GList *certs = NULL; + CERTDERCerts *certCollection = e_cert_db_get_certs_from_package (arena, data, length); + gint i; + gboolean rv; + + if (!certCollection) { + set_nss_error (error); + PORT_FreeArena (arena, PR_FALSE); + return FALSE; + } + + /* Now let's create some certs to work with */ + for (i = 0; i < certCollection->numcerts; i++) { + SECItem *currItem = &certCollection->rawCerts[i]; + ECert *cert; + + cert = e_cert_new_from_der ((gchar *) currItem->data, currItem->len); + if (!cert) { + set_nss_error (error); + g_list_foreach (certs, (GFunc) g_object_unref, NULL); + g_list_free (certs); + PORT_FreeArena (arena, PR_FALSE); + return FALSE; + } + certs = g_list_append (certs, cert); + } + switch (cert_type) { + case E_CERT_CA: + rv = handle_ca_cert_download (certdb, certs, error); + if (rv && imported_certs) { + GList *l; + + /* copy certificates to the caller */ + *imported_certs = NULL; + for (l = certs; l; l = l->next) { + ECert *cert = l->data; + + if (cert) + *imported_certs = g_slist_prepend (*imported_certs, g_object_ref (cert)); + } + + *imported_certs = g_slist_reverse (*imported_certs); + } + break; + default: + /* We only deal with import CA certs in this method currently.*/ + set_nss_error (error); + PORT_FreeArena (arena, PR_FALSE); + rv = FALSE; + } + + g_list_foreach (certs, (GFunc) g_object_unref, NULL); + g_list_free (certs); + PORT_FreeArena (arena, PR_FALSE); + return rv; +} + +gboolean +e_cert_db_import_email_cert (ECertDB *certdb, + gchar *data, + guint32 length, + GSList **imported_certs, + GError **error) +{ + /*nsNSSShutDownPreventionLock locker;*/ + SECStatus srv = SECFailure; + gboolean rv = TRUE; + CERTCertificate * cert; + SECItem **rawCerts; + gint numcerts; + gint i; + PRArenaPool *arena = PORT_NewArena (DER_DEFAULT_CHUNKSIZE); + CERTDERCerts *certCollection = e_cert_db_get_certs_from_package (arena, data, length); + + if (!certCollection) { + set_nss_error (error); + PORT_FreeArena (arena, PR_FALSE); + return FALSE; + } + + cert = CERT_NewTempCertificate ( + CERT_GetDefaultCertDB (), certCollection->rawCerts, + (gchar *) NULL, PR_FALSE, PR_TRUE); + if (!cert) { + set_nss_error (error); + rv = FALSE; + goto loser; + } + numcerts = certCollection->numcerts; + rawCerts = (SECItem **) PORT_Alloc (sizeof (SECItem *) * numcerts); + if (!rawCerts) { + set_nss_error (error); + rv = FALSE; + goto loser; + } + + for (i = 0; i < numcerts; i++) { + rawCerts[i] = &certCollection->rawCerts[i]; + } + + srv = CERT_ImportCerts ( + CERT_GetDefaultCertDB (), certUsageEmailSigner, + numcerts, rawCerts, NULL, PR_TRUE, PR_FALSE, + NULL); + if (srv != SECSuccess) { + set_nss_error (error); + rv = FALSE; + goto loser; + } + CERT_SaveSMimeProfile (cert, NULL, NULL); + + if (imported_certs) { + *imported_certs = NULL; + for (i = 0; i < certCollection->numcerts; i++) { + SECItem *currItem = &certCollection->rawCerts[i]; + ECert *cert; + + cert = e_cert_new_from_der ((gchar *) currItem->data, currItem->len); + if (cert) + *imported_certs = g_slist_prepend (*imported_certs, cert); + } + + *imported_certs = g_slist_reverse (*imported_certs); + } + + PORT_Free (rawCerts); + loser: + if (cert) + CERT_DestroyCertificate (cert); + if (arena) + PORT_FreeArena (arena, PR_TRUE); + return rv; +} + +static gchar * +default_nickname (CERTCertificate *cert) +{ + /* nsNSSShutDownPreventionLock locker; */ + gchar *username = NULL; + gchar *caname = NULL; + gchar *nickname = NULL; + gchar *tmp = NULL; + gint count; + const gchar *nickFmt = NULL; + CERTCertificate *dummycert; + PK11SlotInfo *slot = NULL; + CK_OBJECT_HANDLE keyHandle; + + CERTCertDBHandle *defaultcertdb = CERT_GetDefaultCertDB (); + + username = CERT_GetCommonName (&cert->subject); + if (username == NULL) + username = PL_strdup (""); + + if (username == NULL) + goto loser; + + caname = CERT_GetOrgName (&cert->issuer); + if (caname == NULL) + caname = PL_strdup (""); + + if (caname == NULL) + goto loser; + + count = 1; + + nickFmt = "%1$s's %2$s ID"; + + nickname = PR_smprintf (nickFmt, username, caname); + /* + * 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. + */ + slot = PK11_KeyForCertExists (cert, &keyHandle, NULL); + if (slot == NULL) { + goto loser; + } + if (!PK11_IsInternal (slot)) { + tmp = PR_smprintf ("%s:%s", PK11_GetTokenName (slot), nickname); + PR_Free (nickname); + nickname = tmp; + tmp = NULL; + } + tmp = nickname; + while (1) { + if (count > 1) { + nickname = PR_smprintf ("%s #%d", tmp, count); + } + + if (nickname == NULL) + goto loser; + + if (PK11_IsInternal (slot)) { + /* look up the nickname to make sure it isn't in use already */ + dummycert = CERT_FindCertByNickname (defaultcertdb, nickname); + + } else { + /* + * Check the cert against others that already live on the smart + * card. + */ + dummycert = PK11_FindCertFromNickname (nickname, NULL); + if (dummycert != NULL) { + /* + * 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. + */ + CERT_DestroyCertificate (dummycert); + dummycert = NULL; + } + } + } + if (dummycert == NULL) + goto done; + + /* found a cert, destroy it and loop */ + CERT_DestroyCertificate (dummycert); + if (tmp != nickname) PR_Free (nickname); + count++; + } /* end of while (1) */ + + loser: + if (nickname) { + PR_Free (nickname); + } + nickname = NULL; + done: + if (caname) { + PR_Free (caname); + } + if (username) { + PR_Free (username); + } + if (slot != NULL) { + PK11_FreeSlot (slot); + if (nickname != NULL) { + tmp = nickname; + nickname = strchr (tmp, ':'); + if (nickname != NULL) { + nickname++; + nickname = PL_strdup (nickname); + PR_Free (tmp); + tmp = NULL; + } else { + nickname = tmp; + tmp = NULL; + } + } + } + PR_FREEIF (tmp); + return (nickname); +} + +gboolean +e_cert_db_import_user_cert (ECertDB *certdb, + gchar *data, + guint32 length, + GError **error) +{ + /* nsNSSShutDownPreventionLock locker;*/ + PK11SlotInfo *slot; + gchar * nickname = NULL; + gboolean rv = FALSE; + gint numCACerts; + SECItem *CACerts; + CERTDERCerts * collectArgs; + PRArenaPool *arena; + CERTCertificate * cert = NULL; + + arena = PORT_NewArena (DER_DEFAULT_CHUNKSIZE); + if (arena == NULL) { + set_nss_error (error); + goto loser; + } + + collectArgs = e_cert_db_get_certs_from_package (arena, data, length); + if (!collectArgs) { + set_nss_error (error); + goto loser; + } + + cert = CERT_NewTempCertificate ( + CERT_GetDefaultCertDB (), collectArgs->rawCerts, + (gchar *) NULL, PR_FALSE, PR_TRUE); + if (!cert) { + set_nss_error (error); + goto loser; + } + + slot = PK11_KeyForCertExists (cert, NULL, NULL); + if (slot == NULL) { + set_nss_error (error); + goto loser; + } + PK11_FreeSlot (slot); + + /* pick a nickname for the cert */ + if (cert->nickname) { + /* sigh, we need a call to look up other certs with this subject and + * identify nicknames from them. We can no longer walk down internal + * database structures rjr */ + nickname = cert->nickname; + } + else { + nickname = default_nickname (cert); + } + + /* user wants to import the cert */ + slot = PK11_ImportCertForKey (cert, nickname, NULL); + if (!slot) { + set_nss_error (error); + goto loser; + } + PK11_FreeSlot (slot); + numCACerts = collectArgs->numcerts - 1; + + if (numCACerts) { + CACerts = collectArgs->rawCerts + 1; + if (!CERT_ImportCAChain (CACerts, numCACerts, certUsageUserCertImport)) { + rv = TRUE; + } + } + + loser: + if (arena) { + PORT_FreeArena (arena, PR_FALSE); + } + if (cert) { + CERT_DestroyCertificate (cert); + } + return rv; +} + +gboolean +e_cert_db_import_server_cert (ECertDB *certdb, + gchar *data, + guint32 length, + GSList **imported_certs, + GError **error) +{ + /* not c&p'ing this over at the moment, as we don't have a UI + * for server certs anyway */ + return FALSE; +} + +gboolean +e_cert_db_import_certs_from_file (ECertDB *cert_db, + const gchar *file_path, + ECertType cert_type, + GSList **imported_certs, + GError **error) +{ + gboolean rv; + gint fd; + struct stat sb; + gchar *buf; + gint bytes_read; + + switch (cert_type) { + case E_CERT_CA: + case E_CERT_CONTACT: + case E_CERT_SITE: + /* good */ + break; + + default: + /* not supported (yet) */ + set_nss_error (error); + return FALSE; + } + + fd = g_open (file_path, O_RDONLY | O_BINARY, 0); + if (fd == -1) { + set_nss_error (error); + return FALSE; + } + + if (-1 == fstat (fd, &sb)) { + set_nss_error (error); + close (fd); + return FALSE; + } + + buf = g_malloc (sb.st_size); + if (!buf) { + set_nss_error (error); + close (fd); + return FALSE; + } + + bytes_read = read (fd, buf, sb.st_size); + + close (fd); + + if (bytes_read != sb.st_size) { + set_nss_error (error); + rv = FALSE; + } + else { + printf ("importing %d bytes from '%s'\n", bytes_read, file_path); + + switch (cert_type) { + case E_CERT_CA: + rv = e_cert_db_import_certs (cert_db, buf, bytes_read, cert_type, imported_certs, error); + break; + + case E_CERT_SITE: + rv = e_cert_db_import_server_cert (cert_db, buf, bytes_read, imported_certs, error); + break; + + case E_CERT_CONTACT: + rv = e_cert_db_import_email_cert (cert_db, buf, bytes_read, imported_certs, error); + break; + + default: + rv = FALSE; + break; + } + } + + g_free (buf); + return rv; +} + +gboolean +e_cert_db_import_pkcs12_file (ECertDB *cert_db, + const gchar *file_path, + GError **error) +{ + EPKCS12 *pkcs12 = e_pkcs12_new (); + GError *e = NULL; + + if (!e_pkcs12_import_from_file (pkcs12, file_path, &e)) { + g_propagate_error (error, e); + return FALSE; + } + + return TRUE; +} + +#ifdef notyet +gboolean +e_cert_db_export_pkcs12_file (ECertDB *cert_db, + const gchar *file_path, + GList *certs, + GError **error) +{ +} +#endif + +gboolean +e_cert_db_login_to_slot (ECertDB *cert_db, + PK11SlotInfo *slot) +{ + if (PK11_NeedLogin (slot)) { + PK11_Logout (slot); + + if (PK11_NeedUserInit (slot)) { + gchar *pwd; + gboolean rv = FALSE; + + printf ("initializing slot password\n"); + + g_signal_emit ( + e_cert_db_peek (), + e_cert_db_signals[PK11_CHANGE_PASSWD], 0, + NULL, + &pwd, + &rv); + + if (!rv) + return FALSE; + + /* the user needs to specify the initial password */ + PK11_InitPin (slot, "", pwd); + } + + PK11_SetPasswordFunc (pk11_password); + if (PK11_Authenticate (slot, PR_TRUE, NULL) != SECSuccess) { + printf ( + "PK11_Authenticate failed (err = %d/%d)\n", + PORT_GetError (), PORT_GetError () + 0x2000); + return FALSE; + } + } + + return TRUE; +} |