diff options
author | Chris Toshok <toshok@ximian.com> | 2003-11-26 16:54:48 +0800 |
---|---|---|
committer | Chris Toshok <toshok@src.gnome.org> | 2003-11-26 16:54:48 +0800 |
commit | 4e4c16760abdbc2ab34b159e52c5c027a1b2ad26 (patch) | |
tree | 3011e05a12b84f273ae36b6109293a55fe1aec53 /smime/lib | |
parent | 30ff908fcddcf18107d8b0fd39e4504b2a69ef19 (diff) | |
download | gsoc2013-evolution-4e4c16760abdbc2ab34b159e52c5c027a1b2ad26.tar.gz gsoc2013-evolution-4e4c16760abdbc2ab34b159e52c5c027a1b2ad26.tar.zst gsoc2013-evolution-4e4c16760abdbc2ab34b159e52c5c027a1b2ad26.zip |
mostly implement a viewer for certificates.
2003-11-26 Chris Toshok <toshok@ximian.com>
* gui/certificate-viewer.[ch]: mostly implement a viewer for
certificates.
* gui/smime-ui.glade: fingerprints-sh1 -> fingerprints-sha1.
* gui/certificate-manager.c (import_your): new function, use
e-pkcs12 to implement it.
(initialize_yourcerts_ui): hook up the import button.
(view_contact): new function, bring up the certificate viewer.
(initialize_contactcerts_ui): hook up the view button.
(view_ca): new function, bring up the certificate viewer.
(initialize_authoritycerts_ui): hook up the view button.
* gui/Makefile.am (libevolution_smime_la_SOURCES): add
certificate-viewer.[ch]
* lib/e-cert.c (e_cert_dispose): free all the new cached foo.
(e_cert_populate): populate all the new cached foo.
(e_cert_get_issuer_cn): new function.
(e_cert_get_issuer_org): same.
(e_cert_get_issuer_org_unit): same.
(e_cert_get_issued_on_time): same.
(e_cert_get_issued_on): same.
(e_cert_get_expires_on_time): same.
(e_cert_get_expires_on): same.
(e_cert_get_serial_number): same.
(e_cert_get_sha1_fingerprint): same.
(e_cert_get_md5_fingerprint): same.
* lib/e-cert.h: add prototypes for lots more accessors.
* lib/e-cert-db.c (e_cert_db_find_cert_by_key): fix typo.
(e_cert_db_find_cert_by_email_address): call
CERT_DestroyCertificate to free the cert.
(default_nickname): new function.
(e_cert_db_import_user_cert): implement.
(e_cert_db_import_server_cert): add blurb.
* lib/e-pkcs12.[ch]: new files.
* lib/Makefile.am (libessmime_la_SOURCES): add e-pkcs12.[ch]
svn path=/trunk/; revision=23486
Diffstat (limited to 'smime/lib')
-rw-r--r-- | smime/lib/Makefile.am | 4 | ||||
-rw-r--r-- | smime/lib/e-cert-db.c | 217 | ||||
-rw-r--r-- | smime/lib/e-cert.c | 162 | ||||
-rw-r--r-- | smime/lib/e-cert.h | 16 | ||||
-rw-r--r-- | smime/lib/e-pkcs12.c | 452 | ||||
-rw-r--r-- | smime/lib/e-pkcs12.h | 71 |
6 files changed, 913 insertions, 9 deletions
diff --git a/smime/lib/Makefile.am b/smime/lib/Makefile.am index 07093ef5c9..d7d5b11784 100644 --- a/smime/lib/Makefile.am +++ b/smime/lib/Makefile.am @@ -25,4 +25,6 @@ libessmime_la_SOURCES = \ e-cert-trust.c \ e-cert-trust.h \ e-cert-db.c \ - e-cert-db.h + e-cert-db.h \ + e-pkcs12.c \ + e-pkcs12.h diff --git a/smime/lib/e-cert-db.c b/smime/lib/e-cert-db.c index 02959f3505..a597a305af 100644 --- a/smime/lib/e-cert-db.c +++ b/smime/lib/e-cert-db.c @@ -68,7 +68,10 @@ #include "nss.h" #include "pk11func.h" #include "certdb.h" -#include <e-util/e-dialog-utils.h> +#include "plstr.h" +#include "prprf.h" +#include "prmem.h" +#include "e-util/e-dialog-utils.h" #include <gtk/gtkmessagedialog.h> #include <libgnome/gnome-i18n.h> #include <fcntl.h> @@ -249,7 +252,7 @@ e_cert_db_find_cert_by_key (ECertDB *certdb, moduleID = NS_NSS_GET_LONG(keyItem.data); slotID = NS_NSS_GET_LONG(&keyItem.data[NS_NSS_LONG]); - /8 build the issuer/SN structure*/ + /* 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]; @@ -312,21 +315,21 @@ e_cert_db_find_cert_by_email_address (ECertDB *certdb, PR_Now(), PR_TRUE); if (!certlist) { /* XXX gerror */ - /* XXX free any_cert */ + CERT_DestroyCertificate(any_cert); return NULL; } if (SECSuccess != CERT_FilterCertListByUsage(certlist, certUsageEmailRecipient, PR_FALSE)) { /* XXX gerror */ - /* XXX free any_cert */ - /* XXX free certlist */ + CERT_DestroyCertificate(any_cert); + /* XXX free certlist? */ return NULL; } if (CERT_LIST_END(CERT_LIST_HEAD(certlist), certlist)) { /* XXX gerror */ - /* XXX free any_cert */ - /* XXX free certlist */ + CERT_DestroyCertificate(any_cert); + /* XXX free certlist? */ return NULL; } @@ -665,11 +668,208 @@ e_cert_db_import_email_cert (ECertDB *certdb, return rv; } +static char * +default_nickname (CERTCertificate *cert) +{ + /* nsNSSShutDownPreventionLock locker; */ + char *username = NULL; + char *caname = NULL; + char *nickname = NULL; + char *tmp = NULL; + int count; + char *nickFmt=NULL, *nickFmtWithNum = 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"; + nickFmtWithNum = "%1$s's %2$s ID #%3$d"; + + 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, char *data, guint32 length, GError **error) { + /* nsNSSShutDownPreventionLock locker;*/ + PK11SlotInfo *slot; + char * nickname = NULL; + gboolean rv = FALSE; + int numCACerts; + SECItem *CACerts; + CERTDERCerts * collectArgs; + PRArenaPool *arena; + CERTCertificate * cert=NULL; + + arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE); + if ( arena == NULL ) { + /* XXX g_error */ + goto loser; + } + + collectArgs = e_cert_db_get_certs_from_package (arena, data, length); + if (!collectArgs) { + /* XXX g_error */ + goto loser; + } + + cert = CERT_NewTempCertificate(CERT_GetDefaultCertDB(), collectArgs->rawCerts, + (char *)NULL, PR_FALSE, PR_TRUE); + if (!cert) { + /* XXX g_error */ + goto loser; + } + + slot = PK11_KeyForCertExists(cert, NULL, NULL); + if ( slot == NULL ) { + /* XXX g_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) { + /* XXX g_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 @@ -677,6 +877,9 @@ e_cert_db_import_server_cert (ECertDB *certdb, char *data, guint32 length, 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 diff --git a/smime/lib/e-cert.c b/smime/lib/e-cert.c index 9563468de3..a8fa5879c2 100644 --- a/smime/lib/e-cert.c +++ b/smime/lib/e-cert.c @@ -55,10 +55,16 @@ * */ +#include <time.h> + +#include <libgnome/gnome-i18n.h> +#include <gal/util/e-util.h> /* for e_utf8_strftime, what about e_time_format_time? */ + #include "e-cert.h" #include "e-cert-trust.h" #include "pk11func.h" #include "certdb.h" +#include "hasht.h" struct _ECertPrivate { CERTCertificate *cert; @@ -66,8 +72,24 @@ struct _ECertPrivate { /* pointers we cache since the nss implementation allocs the string */ char *org_name; + char *org_unit_name; char *cn; + char *issuer_org_name; + char *issuer_org_unit_name; + char *issuer_cn; + + PRTime issued_on; + PRTime expires_on; + + char *issued_on_string; + char *expires_on_string; + + char *serial_number; + + char *sha1_fingerprint; + char *md5_fingerprint; + gboolean delete; }; @@ -84,9 +106,30 @@ e_cert_dispose (GObject *object) if (ec->priv->org_name) PORT_Free (ec->priv->org_name); + if (ec->priv->org_unit_name) + PORT_Free (ec->priv->org_unit_name); if (ec->priv->cn) PORT_Free (ec->priv->cn); + if (ec->priv->issuer_org_name) + PORT_Free (ec->priv->issuer_org_name); + if (ec->priv->issuer_org_unit_name) + PORT_Free (ec->priv->issuer_org_unit_name); + if (ec->priv->issuer_cn) + PORT_Free (ec->priv->issuer_cn); + + if (ec->priv->issued_on_string) + PORT_Free (ec->priv->issued_on_string); + if (ec->priv->expires_on_string) + PORT_Free (ec->priv->expires_on_string); + if (ec->priv->serial_number) + PORT_Free (ec->priv->serial_number); + + if (ec->priv->sha1_fingerprint) + PORT_Free (ec->priv->sha1_fingerprint); + if (ec->priv->md5_fingerprint) + PORT_Free (ec->priv->md5_fingerprint); + if (ec->priv->delete) { printf ("attempting to delete cert marked for deletion\n"); if (e_cert_get_cert_type (ec) == E_CERT_USER) { @@ -154,8 +197,61 @@ static void e_cert_populate (ECert *cert) { CERTCertificate *c = cert->priv->cert; + unsigned char fingerprint[20]; + SECItem fpItem; + cert->priv->org_name = CERT_GetOrgName (&c->subject); + cert->priv->org_unit_name = CERT_GetOrgUnitName (&c->subject); + + cert->priv->issuer_org_name = CERT_GetOrgName (&c->issuer); + cert->priv->issuer_org_unit_name = CERT_GetOrgUnitName (&c->issuer); + cert->priv->cn = CERT_GetCommonName (&c->subject); + cert->priv->issuer_cn = CERT_GetCommonName (&c->issuer); + + if (SECSuccess == CERT_GetCertTimes (c, &cert->priv->issued_on, &cert->priv->expires_on)) { + PRExplodedTime explodedTime; + struct tm exploded_tm; + char buf[32]; + + PR_ExplodeTime (cert->priv->issued_on, PR_LocalTimeParameters, &explodedTime); + exploded_tm.tm_sec = explodedTime.tm_sec; + exploded_tm.tm_min = explodedTime.tm_min; + exploded_tm.tm_hour = explodedTime.tm_hour; + exploded_tm.tm_mday = explodedTime.tm_mday; + exploded_tm.tm_mon = explodedTime.tm_month; + exploded_tm.tm_year = explodedTime.tm_year - 1900; + e_utf8_strftime (buf, sizeof(buf), _("%d/%m/%Y"), &exploded_tm); + cert->priv->issued_on_string = g_strdup (buf); + + PR_ExplodeTime (cert->priv->expires_on, PR_LocalTimeParameters, &explodedTime); + exploded_tm.tm_sec = explodedTime.tm_sec; + exploded_tm.tm_min = explodedTime.tm_min; + exploded_tm.tm_hour = explodedTime.tm_hour; + exploded_tm.tm_mday = explodedTime.tm_mday; + exploded_tm.tm_mon = explodedTime.tm_month; + exploded_tm.tm_year = explodedTime.tm_year - 1900; + e_utf8_strftime (buf, sizeof(buf), _("%d/%m/%Y"), &exploded_tm); + cert->priv->expires_on_string = g_strdup (buf); + } + + cert->priv->serial_number = CERT_Hexify (&cert->priv->cert->serialNumber, TRUE); + + memset(fingerprint, 0, sizeof fingerprint); + PK11_HashBuf(SEC_OID_SHA1, fingerprint, + cert->priv->cert->derCert.data, + cert->priv->cert->derCert.len); + fpItem.data = fingerprint; + fpItem.len = SHA1_LENGTH; + cert->priv->sha1_fingerprint = CERT_Hexify (&fpItem, TRUE); + + memset(fingerprint, 0, sizeof fingerprint); + PK11_HashBuf(SEC_OID_MD5, fingerprint, + cert->priv->cert->derCert.data, + cert->priv->cert->derCert.len); + fpItem.data = fingerprint; + fpItem.len = MD5_LENGTH; + cert->priv->md5_fingerprint = CERT_Hexify (&fpItem, TRUE); } ECert* @@ -230,6 +326,12 @@ e_cert_get_org (ECert *cert) } const char* +e_cert_get_org_unit (ECert *cert) +{ + return cert->priv->org_unit_name; +} + +const char* e_cert_get_cn (ECert *cert) { return cert->priv->cn; @@ -242,11 +344,71 @@ e_cert_get_issuer_name (ECert *cert) } const char* +e_cert_get_issuer_cn (ECert *cert) +{ + return cert->priv->issuer_cn; +} + +const char* +e_cert_get_issuer_org (ECert *cert) +{ + return cert->priv->issuer_org_name; +} + +const char* +e_cert_get_issuer_org_unit (ECert *cert) +{ + return cert->priv->issuer_org_unit_name; +} + +const char* e_cert_get_subject_name (ECert *cert) { return cert->priv->cert->subjectName; } +PRTime +e_cert_get_issued_on_time (ECert *cert) +{ + return cert->priv->issued_on; +} + +const char* +e_cert_get_issued_on (ECert *cert) +{ + return cert->priv->issued_on_string; +} + +PRTime +e_cert_get_expires_on_time (ECert *cert) +{ + return cert->priv->expires_on; +} + +const char* +e_cert_get_expires_on (ECert *cert) +{ + return cert->priv->expires_on_string; +} + +const char* +e_cert_get_serial_number (ECert *cert) +{ + return cert->priv->serial_number; +} + +const char* +e_cert_get_sha1_fingerprint (ECert *cert) +{ + return cert->priv->sha1_fingerprint; +} + +const char* +e_cert_get_md5_fingerprint (ECert *cert) +{ + return cert->priv->md5_fingerprint; +} + gboolean e_cert_mark_for_deletion (ECert *cert) { diff --git a/smime/lib/e-cert.h b/smime/lib/e-cert.h index 662d26f849..4b5ec05a96 100644 --- a/smime/lib/e-cert.h +++ b/smime/lib/e-cert.h @@ -73,9 +73,23 @@ gboolean e_cert_get_raw_der (ECert *cert, char **data, guint32 const char* e_cert_get_nickname (ECert *cert); const char* e_cert_get_email (ECert *cert); const char* e_cert_get_org (ECert *cert); +const char* e_cert_get_org_unit (ECert *cert); const char* e_cert_get_cn (ECert *cert); const char* e_cert_get_subject_name (ECert *cert); -const char* e_cert_get_issuer_name (ECert *cert); + +const char* e_cert_get_issuer_name (ECert *cert); +const char* e_cert_get_issuer_cn (ECert *cert); +const char* e_cert_get_issuer_org (ECert *cert); +const char* e_cert_get_issuer_org_unit (ECert *cert); + +PRTime e_cert_get_issued_on_time (ECert *cert); +const char* e_cert_get_issued_on (ECert *cert); +PRTime e_cert_get_expires_on_time (ECert *cert); +const char* e_cert_get_expires_on (ECert *cert); + +const char* e_cert_get_serial_number (ECert *cert); +const char* e_cert_get_sha1_fingerprint (ECert *cert); +const char* e_cert_get_md5_fingerprint (ECert *cert); gboolean e_cert_mark_for_deletion (ECert *cert); diff --git a/smime/lib/e-pkcs12.c b/smime/lib/e-pkcs12.c new file mode 100644 index 0000000000..2d1bc79848 --- /dev/null +++ b/smime/lib/e-pkcs12.c @@ -0,0 +1,452 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ +/* e-pkcs12.c + * + * Copyright (C) 2003 Ximian, Inc. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of version 2 of the GNU General Public + * License as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this program; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + * Author: Chris Toshok (toshok@ximian.com) + */ + +/* The following is the mozilla license blurb, as the bodies some of + these functions were derived from the mozilla source. */ + +/* + * 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. + * + * 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 <gtk/gtk.h> +#include <libgnome/gnome-i18n.h> + +#include <time.h> +#include <fcntl.h> +#include <unistd.h> + +#include "e-util/e-passwords.h" +#include "e-pkcs12.h" + +#include "prmem.h" +#include "nss.h" +#include "pkcs12.h" +#include "p12plcy.h" +#include "pk11func.h" +#include "secerr.h" + +struct _EPKCS12Private { + int tmp_fd; + char *tmp_path; +}; + +#define PARENT_TYPE G_TYPE_OBJECT +static GObjectClass *parent_class; + +// static callback functions for the NSS PKCS#12 library +static SECItem * PR_CALLBACK nickname_collision(SECItem *, PRBool *, void *); +static void PR_CALLBACK write_export_file(void *arg, const char *buf, unsigned long len); + +static gboolean handle_error(int myerr); + +#define PKCS12_TMPFILENAME ".p12tmp" +#define PKCS12_BUFFER_SIZE 2048 +#define PKCS12_RESTORE_OK 1 +#define PKCS12_BACKUP_OK 2 +#define PKCS12_USER_CANCELED 3 +#define PKCS12_NOSMARTCARD_EXPORT 4 +#define PKCS12_RESTORE_FAILED 5 +#define PKCS12_BACKUP_FAILED 6 +#define PKCS12_NSS_ERROR 7 + +static void +e_pkcs12_dispose (GObject *object) +{ + EPKCS12 *pk = E_PKCS12 (object); + + if (!pk->priv) + return; + + /* XXX free instance private foo */ + + g_free (pk->priv); + pk->priv = NULL; + + if (G_OBJECT_CLASS (parent_class)->dispose) + G_OBJECT_CLASS (parent_class)->dispose (object); +} + +static void +e_pkcs12_class_init (EPKCS12Class *klass) +{ + GObjectClass *object_class; + + object_class = G_OBJECT_CLASS(klass); + + parent_class = g_type_class_ref (PARENT_TYPE); + + object_class->dispose = e_pkcs12_dispose; +} + +static void +e_pkcs12_init (EPKCS12 *ec) +{ + ec->priv = g_new0 (EPKCS12Private, 1); +} + +GType +e_pkcs12_get_type (void) +{ + static GType pkcs12_type = 0; + + if (!pkcs12_type) { + static const GTypeInfo pkcs12_info = { + sizeof (EPKCS12Class), + NULL, /* base_init */ + NULL, /* base_finalize */ + (GClassInitFunc) e_pkcs12_class_init, + NULL, /* class_finalize */ + NULL, /* class_data */ + sizeof (EPKCS12), + 0, /* n_preallocs */ + (GInstanceInitFunc) e_pkcs12_init, + }; + + pkcs12_type = g_type_register_static (PARENT_TYPE, "EPKCS12", &pkcs12_info, 0); + } + + return pkcs12_type; +} + + + +EPKCS12* +e_pkcs12_new (void) +{ + EPKCS12 *pk = E_PKCS12 (g_object_new (E_TYPE_PKCS12, NULL)); + + return pk; +} + +static gboolean +input_to_decoder (SEC_PKCS12DecoderContext *dcx, const char *path, GError **error) +{ + /* nsNSSShutDownPreventionLock locker; */ + SECStatus srv; + int amount; + char buf[PKCS12_BUFFER_SIZE]; + FILE *fp; + + /* open path */ + fp = fopen (path, "r"); + if (!fp) { + /* XXX gerror */ + printf ("couldn't open `%s'\n", path); + return FALSE; + } + + while (TRUE) { + amount = fread (buf, 1, sizeof (buf), fp); + if (amount < 0) { + printf ("got -1 fread\n"); + fclose (fp); + return FALSE; + } + /* feed the file data into the decoder */ + srv = SEC_PKCS12DecoderUpdate(dcx, + (unsigned char*) buf, + amount); + if (srv) { + /* don't allow the close call to overwrite our precious error code */ + /* XXX g_error */ + int pr_err = PORT_GetError(); + PORT_SetError(pr_err); + printf ("SEC_PKCS12DecoderUpdate returned %d\n", srv); + fclose (fp); + return FALSE; + } + if (amount < PKCS12_BUFFER_SIZE) + break; + } + fclose (fp); + return TRUE; +} + +static gboolean +prompt_for_password (char *title, char *prompt, SECItem *pwd) +{ + char *passwd; + + passwd = e_passwords_ask_password (title, NULL, NULL, prompt, TRUE, + E_PASSWORDS_DO_NOT_REMEMBER, NULL, + NULL); + + if (passwd) { + SECITEM_AllocItem(NULL, pwd, PL_strlen (passwd)); + memcpy (pwd->data, passwd, strlen (passwd)); + g_free (passwd); + } + + return TRUE; +} + +static gboolean +import_from_file_helper (EPKCS12 *pkcs12, const char *path, gboolean *aWantRetry, GError **error) +{ + /*nsNSSShutDownPreventionLock locker; */ + gboolean rv = TRUE; + SECStatus srv = SECSuccess; + SEC_PKCS12DecoderContext *dcx = NULL; + SECItem passwd; + GError *err = NULL; + PK11SlotInfo *slot = PK11_GetInternalKeySlot (); /* XXX toshok - we + hardcode this + here */ + *aWantRetry = FALSE; + + + passwd.data = NULL; + rv = prompt_for_password (_("PKCS12 File Password"), _("Enter password for PKCS12 file:"), &passwd); + if (!rv) goto finish; + if (passwd.data == NULL) { + handle_error (PKCS12_USER_CANCELED); + return TRUE; + } + +#if notyet + /* XXX we don't need this block as long as we hardcode the + slot above */ + nsXPIDLString tokenName; + nsXPIDLCString tokenNameCString; + const char *tokNameRef; + + + mToken->GetTokenName (getter_Copies(tokenName)); + tokenNameCString.Adopt (ToNewUTF8String(tokenName)); + tokNameRef = tokenNameCString; /* I do this here so that the + NS_CONST_CAST below doesn't + break the build on Win32 */ + + slot = PK11_FindSlotByName (NS_CONST_CAST(char*,tokNameRef)); + if (!slot) { + srv = SECFailure; + goto finish; + } +#endif + + /* initialize the decoder */ + dcx = SEC_PKCS12DecoderStart (&passwd, slot, NULL, + NULL, NULL, + NULL, NULL, + pkcs12); + if (!dcx) { + srv = SECFailure; + goto finish; + } + /* read input file and feed it to the decoder */ + rv = input_to_decoder (dcx, path, &err); + if (!rv) { +#if notyet + /* XXX we need this to check the gerror */ + if (NS_ERROR_ABORT == rv) { + // inputToDecoder indicated a NSS error + srv = SECFailure; + } +#endif + goto finish; + } + + /* verify the blob */ + srv = SEC_PKCS12DecoderVerify (dcx); + if (srv) { printf ("decoderverify failed\n"); goto finish; } + /* validate bags */ + srv = SEC_PKCS12DecoderValidateBags (dcx, nickname_collision); + if (srv) { printf ("decodervalidatebags failed\n"); goto finish; } + /* import cert and key */ + srv = SEC_PKCS12DecoderImportBags (dcx); + if (srv) { printf ("decoderimportbags failed\n"); goto finish; } + /* Later - check to see if this should become default email cert */ + handle_error (PKCS12_RESTORE_OK); + finish: + /* If srv != SECSuccess, NSS probably set a specific error code. + We should use that error code instead of inventing a new one + for every error possible. */ + if (srv != SECSuccess) { + printf ("srv != SECSuccess\n"); + if (SEC_ERROR_BAD_PASSWORD == PORT_GetError()) { + printf ("BAD PASSWORD\n"); + *aWantRetry = TRUE; + } + handle_error(PKCS12_NSS_ERROR); + } else if (!rv) { + handle_error(PKCS12_RESTORE_FAILED); + } + if (slot) + PK11_FreeSlot(slot); + // finish the decoder + if (dcx) + SEC_PKCS12DecoderFinish(dcx); + return TRUE; +} + +gboolean +e_pkcs12_import_from_file (EPKCS12 *pkcs12, const char *path, GError **error) +{ + /*nsNSSShutDownPreventionLock locker;*/ + gboolean rv = TRUE; + gboolean wantRetry; + + +#if 0 + /* XXX we don't use tokens yet */ + if (!mToken) { + if (!mTokenSet) { + rv = SetToken(NULL); // Ask the user to pick a slot + if (NS_FAILED(rv)) { + handle_error(PKCS12_USER_CANCELED); + return rv; + } + } + } + + if (!mToken) { + handle_error(PKCS12_RESTORE_FAILED); + return NS_ERROR_NOT_AVAILABLE; + } + + /* init slot */ + rv = mToken->Login(PR_TRUE); + if (NS_FAILED(rv)) return rv; +#endif + + do { + rv = import_from_file_helper (pkcs12, path, &wantRetry, error); + } while (rv && wantRetry); + + return rv; +} + +gboolean +e_pkcs12_export_to_file (EPKCS12 *pkcs12, const char *path, GList *certs, GError **error) +{ +} + +/* what to do when the nickname collides with one already in the db. + TODO: not handled, throw a dialog allowing the nick to be changed? */ +static SECItem * PR_CALLBACK +nickname_collision(SECItem *oldNick, PRBool *cancel, void *wincx) +{ + /* nsNSSShutDownPreventionLock locker; */ + *cancel = PR_FALSE; + int count = 1; + char *nickname = NULL; + char *default_nickname = _("Imported Certificate"); + SECItem *new_nick; + + printf ("nickname_collision\n"); + + /* The user is trying to import a PKCS#12 file that doesn't have the + attribute we use to set the nickname. So in order to reduce the + number of interactions we require with the user, we'll build a nickname + for the user. The nickname isn't prominently displayed in the UI, + so it's OK if we generate one on our own here. + XXX If the NSS API were smarter and actually passed a pointer to + the CERTCertificate* we're importing we could actually just + call default_nickname (which is what the issuance code path + does) and come up with a reasonable nickname. Alas, the NSS + API limits our ability to produce a useful nickname without + bugging the user. :( + */ + while (1) { + CERTCertificate *cert; + + /* If we've gotten this far, that means there isn't a certificate + in the database that has the same subject name as the cert we're + trying to import. So we need to come up with a "nickname" to + satisfy the NSS requirement or fail in trying to import. + Basically we use a default nickname from a properties file and + see if a certificate exists with that nickname. If there isn't, then + create update the count by one and append the string '#1' Or + whatever the count currently is, and look for a cert with + that nickname. Keep updating the count until we find a nickname + without a corresponding cert. + XXX If a user imports *many* certs without the 'friendly name' + attribute, then this may take a long time. :( + */ + if (count > 1) { + g_free (nickname); + nickname = g_strdup_printf ("%s #%d", default_nickname, count); + } else { + g_free (nickname); + nickname = g_strdup (default_nickname); + } + cert = CERT_FindCertByNickname(CERT_GetDefaultCertDB(), + nickname); + if (!cert) { + break; + } + CERT_DestroyCertificate(cert); + count++; + } + + new_nick = PR_Malloc (sizeof (SECItem)); + new_nick->type = siAsciiString; + new_nick->data = nickname; + new_nick->len = strlen((char*)new_nick->data); + return new_nick; +} + +/* write bytes to the exported PKCS#12 file */ +static void PR_CALLBACK +write_export_file(void *arg, const char *buf, unsigned long len) +{ + EPKCS12 *pkcs12 = E_PKCS12 (arg); + EPKCS12Private *priv = pkcs12->priv; + + printf ("write_export_file\n"); + + write (priv->tmp_fd, buf, len); +} + +static gboolean +handle_error(int myerr) +{ + printf ("handle_error (%d)\n", myerr); +} diff --git a/smime/lib/e-pkcs12.h b/smime/lib/e-pkcs12.h new file mode 100644 index 0000000000..e6616aa85c --- /dev/null +++ b/smime/lib/e-pkcs12.h @@ -0,0 +1,71 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ +/* + * Authors: Chris Toshok <toshok@ximian.com> + * + * Copyright (C) 2003 Ximian, Inc. (www.ximian.com) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Street #330, Boston, MA 02111-1307, USA. + * + */ + +#ifndef _E_PKCS12_H_ +#define _E_PKCS12_H_ + +#include <glib-object.h> + +#define E_TYPE_PKCS12 (e_pkcs12_get_type ()) +#define E_PKCS12(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), E_TYPE_PKCS12, EPKCS12)) +#define E_PKCS12_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), E_TYPE_PKCS12, EPKCS12Class)) +#define E_IS_PKCS12(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), E_TYPE_PKCS12)) +#define E_IS_PKCS12_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), E_TYPE_PKCS12)) +#define E_PKCS12_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), E_TYPE_PKCS12, EPKCS12Class)) + +typedef struct _EPKCS12 EPKCS12; +typedef struct _EPKCS12Class EPKCS12Class; +typedef struct _EPKCS12Private EPKCS12Private; + +struct _EPKCS12 { + GObject parent; + + EPKCS12Private *priv; +}; + +struct _EPKCS12Class { + GObjectClass parent_class; + + /* Padding for future expansion */ + void (*_epkcs12_reserved0) (void); + void (*_epkcs12_reserved1) (void); + void (*_epkcs12_reserved2) (void); + void (*_epkcs12_reserved3) (void); + void (*_epkcs12_reserved4) (void); +}; + +GType e_pkcs12_get_type (void); + +EPKCS12* e_pkcs12_new (void); + + +#if 0 +/* XXX we're not going to support additional slots in the initial ssl + stuff, so we just always default to the internal token (and thus + don't need this function yet. */ +gboolean e_pkcs12_set_token (void); +#endif + +gboolean e_pkcs12_import_from_file (EPKCS12 *pkcs12, const char *path, GError **error); +gboolean e_pkcs12_export_to_file (EPKCS12 *pkcs12, const char *path, GList *certs, GError **error); + +#endif /* _E_CERT_H_ */ |