aboutsummaryrefslogtreecommitdiffstats
path: root/smime/lib
diff options
context:
space:
mode:
authorChris Toshok <toshok@ximian.com>2003-11-26 16:54:48 +0800
committerChris Toshok <toshok@src.gnome.org>2003-11-26 16:54:48 +0800
commit4e4c16760abdbc2ab34b159e52c5c027a1b2ad26 (patch)
tree3011e05a12b84f273ae36b6109293a55fe1aec53 /smime/lib
parent30ff908fcddcf18107d8b0fd39e4504b2a69ef19 (diff)
downloadgsoc2013-evolution-4e4c16760abdbc2ab34b159e52c5c027a1b2ad26.tar
gsoc2013-evolution-4e4c16760abdbc2ab34b159e52c5c027a1b2ad26.tar.gz
gsoc2013-evolution-4e4c16760abdbc2ab34b159e52c5c027a1b2ad26.tar.bz2
gsoc2013-evolution-4e4c16760abdbc2ab34b159e52c5c027a1b2ad26.tar.lz
gsoc2013-evolution-4e4c16760abdbc2ab34b159e52c5c027a1b2ad26.tar.xz
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.am4
-rw-r--r--smime/lib/e-cert-db.c217
-rw-r--r--smime/lib/e-cert.c162
-rw-r--r--smime/lib/e-cert.h16
-rw-r--r--smime/lib/e-pkcs12.c452
-rw-r--r--smime/lib/e-pkcs12.h71
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_ */