diff options
-rw-r--r-- | smime/ChangeLog | 57 | ||||
-rw-r--r-- | smime/gui/certificate-manager.c | 323 | ||||
-rw-r--r-- | smime/gui/smime-ui.glade | 219 | ||||
-rw-r--r-- | smime/lib/Makefile.am | 6 | ||||
-rw-r--r-- | smime/lib/e-cert-db.c | 822 | ||||
-rw-r--r-- | smime/lib/e-cert-db.h | 128 | ||||
-rw-r--r-- | smime/lib/e-cert.c | 138 | ||||
-rw-r--r-- | smime/lib/e-cert.h | 26 | ||||
-rw-r--r-- | smime/tests/Makefile.am | 1 | ||||
-rw-r--r-- | smime/tests/import-cert.c | 12 |
10 files changed, 1656 insertions, 76 deletions
diff --git a/smime/ChangeLog b/smime/ChangeLog index 5123fbff1c..181825ad3f 100644 --- a/smime/ChangeLog +++ b/smime/ChangeLog @@ -1,3 +1,60 @@ +2003-11-11 Chris Toshok <toshok@ximian.com> + + * tests/import-cert.c (main): don't init NSS here. it's done in + e_cert_db_peek. + + * lib/Makefile.am (libessmime_la_SOURCES): add e-cert-db.[ch] + + * gui/smime-ui.glade: set the initial sensitivity of the buttons + here, and add the beginnings of the CA import dialog (where you + assign trust levels to it.) + + * gui/certificate-manager.c (handle_selection_changed): + sensitize/desensitize all the various buttons correctly when the + GtkTreeView's selection changes. + (yourcerts_selection_changed): new, selection change handler for + the Your Certs tab. + (initialize_yourcerts_ui): hook up the tree selection, and add a + model column for the ECert. + (contactcerts_selection_changed): new, selection change handler + for the Contact Certs tab. + (initialize_contactcerts_ui): hook up the tree selection, and add + a model column for the ECert. + (import_ca): new function. + (delete_ca): new function. + (authoritycerts_selection_changed): new, selection change handler + for the Authority Certs tab. + (create_authoritycerts_treemodel): new function for creating the + authority cert tree model. the other tabs will eventually use a + separate function for this too, as unload_certs gets fleshed out. + (initialize_authoritycerts_ui): hook up the tree selection, and + add import/delete buttons. + (destroy_key): dtor for the keys in our hashes. + (destroy_value): dtor for the values in our hashes. + (unload_certs): new function. basically destroy/recreate the + model and hash for the particular cert type/tab. + (load_certs): use e_cert_get_cert_type. + (populate_ui): use unload_certs as well as load_certs. + (certificate_manager_config_control_new): call e_cert_db_peek + ,which will initialize all of NSS. hook up all the widgets from + libglade. + + * lib/e-cert.h: add prototypes for all the new methods, and add + the ECertType enum. + + * lib/e-cert.c (e_cert_dispose): handle deletion from the DB here. + (e_cert_new_from_der): new function. + (e_cert_get_internal_cert): new function. + (e_cert_get_raw_der): new function. + (e_cert_get_issuer_name): new + (e_cert_get_subject_name): new + (e_cert_mark_for_deletion): new + (e_cert_get_cert_type): new. + (e_cert_is_ca_cert): nuke. + + * lib/e-cert-db.[ch]: new, partly implemented, derived from + mozilla's nsNSSCertificateDB code. + 2003-10-30 Chris Toshok <toshok@ximian.com> * gui/certificate-manager.h: add boilerplate. diff --git a/smime/gui/certificate-manager.c b/smime/gui/certificate-manager.c index 7e4e1b1e1d..e49617a2db 100644 --- a/smime/gui/certificate-manager.c +++ b/smime/gui/certificate-manager.c @@ -22,11 +22,7 @@ #define GLADE_FILE_NAME "smime-ui.glade" -#include <gtk/gtkcontainer.h> -#include <gtk/gtktreeview.h> -#include <gtk/gtktreestore.h> -#include <gtk/gtktreemodelsort.h> -#include <gtk/gtkcellrenderertext.h> +#include <gtk/gtk.h> #include <libgnome/gnome-i18n.h> @@ -35,6 +31,7 @@ #include "certificate-manager.h" #include "e-cert.h" +#include "e-cert-db.h" #include "nss.h" #include <cms.h> @@ -49,6 +46,11 @@ typedef struct { GtkWidget *yourcerts_treeview; GtkTreeStore *yourcerts_treemodel; GHashTable *yourcerts_root_hash; + GtkWidget *view_your_button; + GtkWidget *backup_your_button; + GtkWidget *backup_all_your_button; + GtkWidget *import_your_button; + GtkWidget *delete_your_button; GtkWidget *contactcerts_treeview; GtkTreeStore *contactcerts_treemodel; @@ -57,18 +59,72 @@ typedef struct { GtkWidget *authoritycerts_treeview; GtkTreeStore *authoritycerts_treemodel; GHashTable *authoritycerts_root_hash; + GtkWidget *view_ca_button; + GtkWidget *edit_ca_button; + GtkWidget *import_ca_button; + GtkWidget *delete_ca_button; + } CertificateManagerData; -typedef enum { - USER_CERT, - CONTACT_CERT, - CA_CERT -} CertType; +typedef void (*AddCertCb)(CertificateManagerData *cfm, ECert *cert); + +static void unload_certs (CertificateManagerData *cfm, ECertType type); +static void load_certs (CertificateManagerData *cfm, ECertType type, AddCertCb add_cert); + +static void add_user_cert (CertificateManagerData *cfm, ECert *cert); +static void add_contact_cert (CertificateManagerData *cfm, ECert *cert); +static void add_ca_cert (CertificateManagerData *cfm, ECert *cert); + +static void +handle_selection_changed (GtkTreeSelection *selection, + int cert_column, + GtkWidget *view_button, + GtkWidget *edit_button, + GtkWidget *delete_button) +{ + GtkTreeIter iter; + gboolean cert_selected = FALSE; + GtkTreeModel *model; + + if (gtk_tree_selection_get_selected (selection, + &model, + &iter)) { + ECert *cert; + + gtk_tree_model_get (model, + &iter, + cert_column, &cert, + -1); + + if (cert) { + cert_selected = TRUE; + g_object_unref (cert); + } + } + + if (delete_button) + gtk_widget_set_sensitive (delete_button, cert_selected); + if (edit_button) + gtk_widget_set_sensitive (edit_button, cert_selected); + if (view_button) + gtk_widget_set_sensitive (view_button, cert_selected); +} + +static void +yourcerts_selection_changed (GtkTreeSelection *selection, CertificateManagerData *cfm) +{ + handle_selection_changed (gtk_tree_view_get_selection (GTK_TREE_VIEW(cfm->yourcerts_treeview)), + 4, + cfm->view_your_button, + cfm->backup_your_button, /* yes yes, not really "edit", it's a hack :) */ + cfm->delete_your_button); +} static void initialize_yourcerts_ui (CertificateManagerData *cfm) { GtkCellRenderer *cell = gtk_cell_renderer_text_new (); + GtkTreeSelection *selection; gtk_tree_view_append_column (GTK_TREE_VIEW (cfm->yourcerts_treeview), gtk_tree_view_column_new_with_attributes (_("Certificate Name"), @@ -94,22 +150,59 @@ initialize_yourcerts_ui (CertificateManagerData *cfm) "text", 3, NULL)); - cfm->yourcerts_treemodel = gtk_tree_store_new (4, + cfm->yourcerts_treemodel = gtk_tree_store_new (5, + G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING, - G_TYPE_STRING); + G_TYPE_OBJECT); gtk_tree_view_set_model (GTK_TREE_VIEW (cfm->yourcerts_treeview), GTK_TREE_MODEL (cfm->yourcerts_treemodel)); cfm->yourcerts_root_hash = g_hash_table_new (g_str_hash, g_str_equal); + + selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (cfm->yourcerts_treeview)); + g_signal_connect (selection, "changed", G_CALLBACK (yourcerts_selection_changed), cfm); + + if (cfm->import_your_button) { + /* g_signal_connect */ + } + + if (cfm->delete_your_button) { + /* g_signal_connect */ + } + + if (cfm->view_your_button) { + /* g_signal_connect */ + } + + if (cfm->backup_your_button) { + /* g_signal_connect */ + } + + if (cfm->backup_all_your_button) { + /* g_signal_connect */ + } +} + +static void +contactcerts_selection_changed (GtkTreeSelection *selection, CertificateManagerData *cfm) +{ +#if 0 + handle_selection_changed (gtk_tree_view_get_selection (GTK_TREE_VIEW(cfm->contactcerts_treeview)), + 1 /* XXX */, + NULL, + NULL, + NULL); +#endif } static void initialize_contactcerts_ui (CertificateManagerData *cfm) { GtkCellRenderer *cell = gtk_cell_renderer_text_new (); + GtkTreeSelection *selection; gtk_tree_view_append_column (GTK_TREE_VIEW (cfm->contactcerts_treeview), gtk_tree_view_column_new_with_attributes (_("Certificate Name"), @@ -132,12 +225,16 @@ initialize_contactcerts_ui (CertificateManagerData *cfm) cfm->contactcerts_treemodel = gtk_tree_store_new (3, G_TYPE_STRING, G_TYPE_STRING, - G_TYPE_STRING); + G_TYPE_STRING, + G_TYPE_OBJECT); gtk_tree_view_set_model (GTK_TREE_VIEW (cfm->contactcerts_treeview), GTK_TREE_MODEL (cfm->contactcerts_treemodel)); cfm->contactcerts_root_hash = g_hash_table_new (g_str_hash, g_str_equal); + + selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (cfm->contactcerts_treeview)); + g_signal_connect (selection, "changed", G_CALLBACK (contactcerts_selection_changed), cfm); } static gint @@ -160,9 +257,84 @@ iter_string_compare (GtkTreeModel *model, } static void +import_ca (GtkWidget *widget, CertificateManagerData *cfm) +{ + GtkWidget *filesel = gtk_file_selection_new (_("Select a cert to import...")); + + if (GTK_RESPONSE_OK == gtk_dialog_run (GTK_DIALOG (filesel))) { + const char *filename = gtk_file_selection_get_filename (GTK_FILE_SELECTION (filesel)); + + if (e_cert_db_import_certs_from_file (e_cert_db_peek (), + filename, + E_CERT_CA, + NULL)) { + + /* there's no telling how many certificates were added during the import, + so we blow away the CA cert display and regenerate it. */ + unload_certs (cfm, E_CERT_CA); + load_certs (cfm, E_CERT_CA, add_ca_cert); + } + } + + gtk_widget_destroy (filesel); +} + +static void +delete_ca (GtkWidget *widget, CertificateManagerData *cfm) +{ + GtkTreeIter iter; + + if (gtk_tree_selection_get_selected (gtk_tree_view_get_selection (GTK_TREE_VIEW(cfm->authoritycerts_treeview)), + NULL, + &iter)) { + ECert *cert; + + gtk_tree_model_get (GTK_TREE_MODEL (cfm->authoritycerts_treemodel), + &iter, + 1, &cert, + -1); + + if (cert) { + printf ("DELETE\n"); + e_cert_db_delete_cert (e_cert_db_peek (), cert); + gtk_tree_store_remove (cfm->authoritycerts_treemodel, + &iter); + + /* we need two unrefs here, one to unref the + gtk_tree_model_get above, and one to unref + the initial ref when we created the cert + and added it to the tree */ + g_object_unref (cert); + g_object_unref (cert); + } + } + +} + +static void +authoritycerts_selection_changed (GtkTreeSelection *selection, CertificateManagerData *cfm) +{ + handle_selection_changed (gtk_tree_view_get_selection (GTK_TREE_VIEW(cfm->authoritycerts_treeview)), + 1, + cfm->view_ca_button, + cfm->edit_ca_button, + cfm->delete_ca_button); +} + +static GtkTreeStore* +create_authoritycerts_treemodel (void) +{ + return gtk_tree_store_new (2, + G_TYPE_STRING, + G_TYPE_OBJECT); + +} + +static void initialize_authoritycerts_ui (CertificateManagerData *cfm) { GtkCellRenderer *cell = gtk_cell_renderer_text_new (); + GtkTreeSelection *selection; gtk_tree_view_append_column (GTK_TREE_VIEW (cfm->authoritycerts_treeview), gtk_tree_view_column_new_with_attributes (_("Certificate Name"), @@ -170,9 +342,6 @@ initialize_authoritycerts_ui (CertificateManagerData *cfm) "text", 0, NULL)); - cfm->authoritycerts_treemodel = gtk_tree_store_new (1, - G_TYPE_STRING); - gtk_tree_sortable_set_sort_func (GTK_TREE_SORTABLE (cfm->authoritycerts_treemodel), 0, iter_string_compare, NULL, NULL); @@ -181,35 +350,22 @@ initialize_authoritycerts_ui (CertificateManagerData *cfm) 0, GTK_SORT_ASCENDING); - gtk_tree_view_set_model (GTK_TREE_VIEW (cfm->authoritycerts_treeview), - GTK_TREE_MODEL (cfm->authoritycerts_treemodel)); + selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (cfm->authoritycerts_treeview)); + g_signal_connect (selection, "changed", G_CALLBACK (authoritycerts_selection_changed), cfm); - cfm->authoritycerts_root_hash = g_hash_table_new (g_str_hash, g_str_equal); -} - -static CertType -get_cert_type (ECert *cert) -{ - const char *nick = e_cert_get_nickname (cert); - const char *email = e_cert_get_email (cert); + if (cfm->import_ca_button) + g_signal_connect (cfm->import_ca_button, "clicked", G_CALLBACK (import_ca), cfm); - if (e_cert_is_ca_cert (cert)) - return CA_CERT; - - /* XXX more stuff in here */ - else - return USER_CERT; + if (cfm->delete_ca_button) + g_signal_connect (cfm->delete_ca_button, "clicked", G_CALLBACK (delete_ca), cfm); } -typedef void (*AddCertCb)(CertificateManagerData *cfm, ECert *cert); - static void add_user_cert (CertificateManagerData *cfm, ECert *cert) { GtkTreeIter iter; GtkTreeIter *parent_iter = NULL; const char *organization = e_cert_get_org (cert); - const char *common_name; if (organization) { parent_iter = g_hash_table_lookup (cfm->yourcerts_root_hash, organization); @@ -229,14 +385,16 @@ add_user_cert (CertificateManagerData *cfm, ECert *cert) gtk_tree_store_append (GTK_TREE_STORE (cfm->yourcerts_treemodel), &iter, parent_iter); - common_name = e_cert_get_cn (cert); - if (common_name) { + if (e_cert_get_cn (cert)) gtk_tree_store_set (GTK_TREE_STORE (cfm->yourcerts_treemodel), &iter, - 0, common_name, -1); - } + 0, e_cert_get_cn (cert), + 4, cert, + -1); else gtk_tree_store_set (GTK_TREE_STORE (cfm->yourcerts_treemodel), &iter, - 0, e_cert_get_nickname (cert), -1); + 0, e_cert_get_nickname (cert), + 4, cert, + -1); } static void @@ -251,7 +409,6 @@ add_ca_cert (CertificateManagerData *cfm, ECert *cert) GtkTreeIter iter; GtkTreeIter *parent_iter = NULL; const char *organization = e_cert_get_org (cert); - const char *common_name; if (organization) { parent_iter = g_hash_table_lookup (cfm->authoritycerts_root_hash, organization); @@ -272,19 +429,60 @@ add_ca_cert (CertificateManagerData *cfm, ECert *cert) gtk_tree_store_append (GTK_TREE_STORE (cfm->authoritycerts_treemodel), &iter, parent_iter); - common_name = e_cert_get_cn (cert); - if (common_name) { + if (e_cert_get_cn (cert)) gtk_tree_store_set (GTK_TREE_STORE (cfm->authoritycerts_treemodel), &iter, - 0, common_name, -1); - } + 0, e_cert_get_cn (cert), + 1, cert, + -1); else gtk_tree_store_set (GTK_TREE_STORE (cfm->authoritycerts_treemodel), &iter, - 0, e_cert_get_nickname (cert), -1); + 0, e_cert_get_nickname (cert), + 1, cert, + -1); +} + +static void +destroy_key (gpointer data) +{ + g_free (data); +} + +static void +destroy_value (gpointer data) +{ + gtk_tree_iter_free (data); +} + +static void +unload_certs (CertificateManagerData *cfm, + ECertType type) +{ + switch (type) { + case E_CERT_USER: + break; + case E_CERT_CONTACT: + break; + case E_CERT_SITE: + break; + case E_CERT_CA: + cfm->authoritycerts_treemodel = create_authoritycerts_treemodel (); + gtk_tree_view_set_model (GTK_TREE_VIEW (cfm->authoritycerts_treeview), + GTK_TREE_MODEL (cfm->authoritycerts_treemodel)); + + if (cfm->authoritycerts_root_hash) + g_hash_table_destroy (cfm->authoritycerts_root_hash); + + cfm->authoritycerts_root_hash = g_hash_table_new_full (g_str_hash, g_str_equal, + destroy_key, destroy_value); + + + break; + } } static void load_certs (CertificateManagerData *cfm, - CertType type, + ECertType type, AddCertCb add_cert) { CERTCertList *certList; @@ -298,11 +496,10 @@ load_certs (CertificateManagerData *cfm, !CERT_LIST_END(node, certList); node = CERT_LIST_NEXT(node)) { ECert *cert = e_cert_new ((CERTCertificate*)node->cert); - if (get_cert_type(cert) == type) { + if (e_cert_get_cert_type(cert) == type) { printf ("cert (nickname = '%s') matches\n", e_cert_get_nickname (cert)); add_cert (cfm, cert); } - /* XXX we leak cert */ } } @@ -310,9 +507,14 @@ load_certs (CertificateManagerData *cfm, static void populate_ui (CertificateManagerData *cfm) { - load_certs (cfm, USER_CERT, add_user_cert); - load_certs (cfm, CONTACT_CERT, add_contact_cert); - load_certs (cfm, CA_CERT, add_ca_cert); + unload_certs (cfm, E_CERT_USER); + load_certs (cfm, E_CERT_USER, add_user_cert); + + unload_certs (cfm, E_CERT_CONTACT); + load_certs (cfm, E_CERT_CONTACT, add_contact_cert); + + unload_certs (cfm, E_CERT_CA); + load_certs (cfm, E_CERT_CA, add_ca_cert); } EvolutionConfigControl* @@ -321,10 +523,8 @@ certificate_manager_config_control_new (void) CertificateManagerData *cfm_data; GtkWidget *control_widget; - /* XXX this should happen someplace else, and shouldn't - reference my default mozilla profile :) */ - if (SECSuccess != NSS_InitReadWrite ("/home/toshok/.mozilla/default/xuvq7jx3.slt")) - return NULL; + /* We need to peek the db here to make sure it (and NSS) are fully initialized. */ + e_cert_db_peek (); cfm_data = g_new0 (CertificateManagerData, 1); cfm_data->gui = glade_xml_new (EVOLUTION_GLADEDIR "/" GLADE_FILE_NAME, NULL, NULL); @@ -333,6 +533,17 @@ certificate_manager_config_control_new (void) cfm_data->contactcerts_treeview = glade_xml_get_widget (cfm_data->gui, "contactcerts-treeview"); cfm_data->authoritycerts_treeview = glade_xml_get_widget (cfm_data->gui, "authoritycerts-treeview"); + cfm_data->view_your_button = glade_xml_get_widget (cfm_data->gui, "your-view-button"); + cfm_data->backup_your_button = glade_xml_get_widget (cfm_data->gui, "your-backup-button"); + cfm_data->backup_all_your_button = glade_xml_get_widget (cfm_data->gui, "your-backup-all-button"); + cfm_data->import_your_button = glade_xml_get_widget (cfm_data->gui, "your-import-button"); + cfm_data->delete_your_button = glade_xml_get_widget (cfm_data->gui, "your-delete-button"); + + cfm_data->view_ca_button = glade_xml_get_widget (cfm_data->gui, "authority-view-button"); + cfm_data->edit_ca_button = glade_xml_get_widget (cfm_data->gui, "authority-edit-button"); + cfm_data->import_ca_button = glade_xml_get_widget (cfm_data->gui, "authority-import-button"); + cfm_data->delete_ca_button = glade_xml_get_widget (cfm_data->gui, "authority-delete-button"); + initialize_yourcerts_ui(cfm_data); initialize_contactcerts_ui(cfm_data); initialize_authoritycerts_ui(cfm_data); diff --git a/smime/gui/smime-ui.glade b/smime/gui/smime-ui.glade index a3475d21b6..338e927470 100644 --- a/smime/gui/smime-ui.glade +++ b/smime/gui/smime-ui.glade @@ -1173,6 +1173,7 @@ <child> <widget class="GtkButton" id="your-view-button"> <property name="visible">True</property> + <property name="sensitive">False</property> <property name="can_default">True</property> <property name="can_focus">True</property> <property name="label" translatable="yes">View</property> @@ -1184,6 +1185,7 @@ <child> <widget class="GtkButton" id="your-backup-button"> <property name="visible">True</property> + <property name="sensitive">False</property> <property name="can_default">True</property> <property name="can_focus">True</property> <property name="relief">GTK_RELIEF_NORMAL</property> @@ -1325,6 +1327,7 @@ <child> <widget class="GtkButton" id="your-delete-button"> <property name="visible">True</property> + <property name="sensitive">False</property> <property name="can_default">True</property> <property name="can_focus">True</property> <property name="label">gtk-delete</property> @@ -1429,6 +1432,7 @@ <child> <widget class="GtkButton" id="contact-view-button"> <property name="visible">True</property> + <property name="sensitive">False</property> <property name="can_default">True</property> <property name="can_focus">True</property> <property name="label" translatable="yes">View</property> @@ -1440,6 +1444,7 @@ <child> <widget class="GtkButton" id="contact-edit-button"> <property name="visible">True</property> + <property name="sensitive">False</property> <property name="can_default">True</property> <property name="can_focus">True</property> <property name="relief">GTK_RELIEF_NORMAL</property> @@ -1516,6 +1521,7 @@ <child> <widget class="GtkButton" id="contact-delete-button"> <property name="visible">True</property> + <property name="sensitive">False</property> <property name="can_default">True</property> <property name="can_focus">True</property> <property name="label">gtk-delete</property> @@ -1620,6 +1626,7 @@ <child> <widget class="GtkButton" id="authority-view-button"> <property name="visible">True</property> + <property name="sensitive">False</property> <property name="can_default">True</property> <property name="can_focus">True</property> <property name="label" translatable="yes">View</property> @@ -1631,6 +1638,7 @@ <child> <widget class="GtkButton" id="authority-edit-button"> <property name="visible">True</property> + <property name="sensitive">False</property> <property name="can_default">True</property> <property name="can_focus">True</property> <property name="relief">GTK_RELIEF_NORMAL</property> @@ -1707,6 +1715,7 @@ <child> <widget class="GtkButton" id="authority-delete-button"> <property name="visible">True</property> + <property name="sensitive">False</property> <property name="can_default">True</property> <property name="can_focus">True</property> <property name="label">gtk-delete</property> @@ -1750,4 +1759,214 @@ </child> </widget> +<widget class="GtkDialog" id="dialog1"> + <property name="title" translatable="yes">dialog1</property> + <property name="type">GTK_WINDOW_TOPLEVEL</property> + <property name="window_position">GTK_WIN_POS_NONE</property> + <property name="modal">False</property> + <property name="resizable">True</property> + <property name="destroy_with_parent">False</property> + <property name="has_separator">True</property> + + <child internal-child="vbox"> + <widget class="GtkVBox" id="dialog-vbox2"> + <property name="visible">True</property> + <property name="homogeneous">False</property> + <property name="spacing">0</property> + + <child internal-child="action_area"> + <widget class="GtkHButtonBox" id="dialog-action_area2"> + <property name="visible">True</property> + <property name="layout_style">GTK_BUTTONBOX_END</property> + + <child> + <widget class="GtkButton" id="cancelbutton1"> + <property name="visible">True</property> + <property name="can_default">True</property> + <property name="can_focus">True</property> + <property name="label">gtk-cancel</property> + <property name="use_stock">True</property> + <property name="relief">GTK_RELIEF_NORMAL</property> + <property name="response_id">-6</property> + </widget> + </child> + + <child> + <widget class="GtkButton" id="okbutton1"> + <property name="visible">True</property> + <property name="can_default">True</property> + <property name="can_focus">True</property> + <property name="label">gtk-ok</property> + <property name="use_stock">True</property> + <property name="relief">GTK_RELIEF_NORMAL</property> + <property name="response_id">-5</property> + </widget> + </child> + </widget> + <packing> + <property name="padding">0</property> + <property name="expand">False</property> + <property name="fill">True</property> + <property name="pack_type">GTK_PACK_END</property> + </packing> + </child> + + <child> + <widget class="GtkLabel" id="label64"> + <property name="visible">True</property> + <property name="label" translatable="yes">You have been asked to trust a new Certificate Authority (CA).</property> + <property name="use_underline">False</property> + <property name="use_markup">False</property> + <property name="justify">GTK_JUSTIFY_LEFT</property> + <property name="wrap">False</property> + <property name="selectable">False</property> + <property name="xalign">0</property> + <property name="yalign">0.5</property> + <property name="xpad">0</property> + <property name="ypad">0</property> + </widget> + <packing> + <property name="padding">0</property> + <property name="expand">False</property> + <property name="fill">False</property> + </packing> + </child> + + <child> + <widget class="GtkLabel" id="label65"> + <property name="visible">True</property> + <property name="label" translatable="yes">Do you want to trust "%s" for the following purposes?</property> + <property name="use_underline">False</property> + <property name="use_markup">False</property> + <property name="justify">GTK_JUSTIFY_LEFT</property> + <property name="wrap">False</property> + <property name="selectable">False</property> + <property name="xalign">0</property> + <property name="yalign">0.5</property> + <property name="xpad">0</property> + <property name="ypad">0</property> + </widget> + <packing> + <property name="padding">0</property> + <property name="expand">False</property> + <property name="fill">False</property> + </packing> + </child> + + <child> + <widget class="GtkVBox" id="vbox7"> + <property name="visible">True</property> + <property name="homogeneous">False</property> + <property name="spacing">0</property> + + <child> + <widget class="GtkCheckButton" id="checkbutton1"> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="label" translatable="yes">Trust this CA to identify web sites.</property> + <property name="use_underline">True</property> + <property name="relief">GTK_RELIEF_NORMAL</property> + <property name="active">False</property> + <property name="inconsistent">False</property> + <property name="draw_indicator">True</property> + </widget> + <packing> + <property name="padding">0</property> + <property name="expand">False</property> + <property name="fill">False</property> + </packing> + </child> + + <child> + <widget class="GtkCheckButton" id="checkbutton2"> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="label" translatable="yes">Trust this CA to identify email users.</property> + <property name="use_underline">True</property> + <property name="relief">GTK_RELIEF_NORMAL</property> + <property name="active">False</property> + <property name="inconsistent">False</property> + <property name="draw_indicator">True</property> + </widget> + <packing> + <property name="padding">0</property> + <property name="expand">False</property> + <property name="fill">False</property> + </packing> + </child> + + <child> + <widget class="GtkCheckButton" id="checkbutton3"> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="label" translatable="yes">Trust this CA to identify software developers.</property> + <property name="use_underline">True</property> + <property name="relief">GTK_RELIEF_NORMAL</property> + <property name="active">False</property> + <property name="inconsistent">False</property> + <property name="draw_indicator">True</property> + </widget> + <packing> + <property name="padding">0</property> + <property name="expand">False</property> + <property name="fill">False</property> + </packing> + </child> + </widget> + <packing> + <property name="padding">0</property> + <property name="expand">False</property> + <property name="fill">False</property> + </packing> + </child> + + <child> + <widget class="GtkLabel" id="label66"> + <property name="visible">True</property> + <property name="label" translatable="yes">Before trusting this CA for any purpose, you should examine its certificate and its policy and procedures (if available).</property> + <property name="use_underline">False</property> + <property name="use_markup">False</property> + <property name="justify">GTK_JUSTIFY_LEFT</property> + <property name="wrap">True</property> + <property name="selectable">False</property> + <property name="xalign">0</property> + <property name="yalign">0.5</property> + <property name="xpad">0</property> + <property name="ypad">0</property> + </widget> + <packing> + <property name="padding">0</property> + <property name="expand">False</property> + <property name="fill">False</property> + </packing> + </child> + + <child> + <widget class="GtkAlignment" id="alignment5"> + <property name="visible">True</property> + <property name="xalign">0</property> + <property name="yalign">0.5</property> + <property name="xscale">0</property> + <property name="yscale">1</property> + + <child> + <widget class="GtkButton" id="button1"> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="label" translatable="yes">View Certificate</property> + <property name="use_underline">True</property> + <property name="relief">GTK_RELIEF_NORMAL</property> + </widget> + </child> + </widget> + <packing> + <property name="padding">0</property> + <property name="expand">False</property> + <property name="fill">False</property> + </packing> + </child> + </widget> + </child> +</widget> + </glade-interface> diff --git a/smime/lib/Makefile.am b/smime/lib/Makefile.am index 4af28f2994..260238ba35 100644 --- a/smime/lib/Makefile.am +++ b/smime/lib/Makefile.am @@ -15,10 +15,12 @@ INCLUDES = \ -DLIBGNOME_DISABLE_DEPRECATED \ -DLIBGNOMEUI_DISABLE_DEPRECATED \ $(EVOLUTION_ADDRESSBOOK_CFLAGS) \ - $(CAMEL_CFLAGS) + $(CERT_UI_CFLAGS) noinst_LTLIBRARIES = libessmime.la libessmime_la_SOURCES = \ e-cert.c \ - e-cert.h + e-cert.h \ + e-cert-db.c \ + e-cert-db.h diff --git a/smime/lib/e-cert-db.c b/smime/lib/e-cert-db.c new file mode 100644 index 0000000000..8e98124a14 --- /dev/null +++ b/smime/lib/e-cert-db.c @@ -0,0 +1,822 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ +/* e-cert-db.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 of most + 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 "e-cert-db.h" + +#include "nss.h" +#include "pk11func.h" +#include "certdb.h" +#include <e-util/e-dialog-utils.h> +#include <gtk/gtkmessagedialog.h> +#include <libgnome/gnome-i18n.h> +#include <fcntl.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <unistd.h> + +struct _ECertDBPrivate { +}; + +#define PARENT_TYPE G_TYPE_OBJECT +static GObjectClass *parent_class; + +static CERTDERCerts* e_cert_db_get_certs_from_package (PRArenaPool *arena, char *data, guint32 length); + + + +static void +e_cert_db_dispose (GObject *object) +{ + ECertDB *ec = E_CERT_DB (object); + + if (!ec->priv) + return; + + /* XXX free instance specific data */ + + g_free (ec->priv); + ec->priv = NULL; + + if (G_OBJECT_CLASS (parent_class)->dispose) + G_OBJECT_CLASS (parent_class)->dispose (object); +} + +static void +e_cert_db_class_init (ECertDBClass *klass) +{ + GObjectClass *object_class; + char *evolution_dir_path; + gboolean success; + + object_class = G_OBJECT_CLASS(klass); + + parent_class = g_type_class_ref (PARENT_TYPE); + + object_class->dispose = e_cert_db_dispose; + + evolution_dir_path = g_build_path ("/", g_get_home_dir (), ".evolution", NULL); + + /* we initialize NSS here to make sure it only happens once */ + success = (SECSuccess == NSS_InitReadWrite (evolution_dir_path)); + if (!success) { + success = (SECSuccess == NSS_Init (evolution_dir_path)); + if (success) + g_warning ("opening cert databases read-only"); + } + if (!success) { + success = (SECSuccess == NSS_NoDB_Init (evolution_dir_path)); + if (success) + g_warning ("initializing security library without cert databases."); + } + g_free (evolution_dir_path); + + if (!success) { + g_warning ("Failed all methods for initializing NSS"); + } +} + +static void +e_cert_db_init (ECertDB *ec) +{ + ec->priv = g_new0 (ECertDBPrivate, 1); +} + +GType +e_cert_db_get_type (void) +{ + static GType cert_type = 0; + + if (!cert_type) { + static const GTypeInfo cert_info = { + sizeof (ECertDBClass), + NULL, /* base_init */ + NULL, /* base_finalize */ + (GClassInitFunc) e_cert_db_class_init, + NULL, /* class_finalize */ + NULL, /* class_data */ + sizeof (ECertDB), + 0, /* n_preallocs */ + (GInstanceInitFunc) e_cert_db_init, + }; + + cert_type = g_type_register_static (PARENT_TYPE, "ECertDB", &cert_info, 0); + } + + return cert_type; +} + + + +GStaticMutex init_mutex = G_STATIC_MUTEX_INIT; +static ECertDB *cert_db = NULL; + +ECertDB* +e_cert_db_peek (void) +{ + g_static_mutex_lock (&init_mutex); + if (!cert_db) + cert_db = g_object_new (E_TYPE_CERT_DB, NULL); + g_static_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 char *nickname, + GError **error) +{ + // nsNSSShutDownPreventionLock locker; + CERTCertificate *cert = NULL; + + //PR_LOG(gPIPNSSLog, PR_LOG_DEBUG, ("Getting \"%s\"\n", asciiname)); +#if 0 + // what it should be, but for now... + if (aToken) { + cert = PK11_FindCertFromNickname(asciiname, NULL); + } else { + cert = CERT_FindCertByNickname(CERT_GetDefaultCertDB(), asciiname); + } +#endif + cert = PK11_FindCertFromNickname((char*)nickname, NULL); + if (!cert) { + cert = CERT_FindCertByNickname(CERT_GetDefaultCertDB(), (char*)nickname); + } + + + if (cert) { + // PR_LOG(gPIPNSSLog, PR_LOG_DEBUG, ("got it\n")); + ECert *ecert = e_cert_new (cert); + return ecert; + } + else { + /* XXX gerror */ + return NULL; + } +} + +ECert* +e_cert_db_find_cert_by_key (ECertDB *certdb, + const char *db_key, + GError **error) +{ +#if 0 + // nsNSSShutDownPreventionLock locker; + SECItem keyItem = {siBuffer, NULL, 0}; + SECItem *dummy; + CERTIssuerAndSN issuerSN; + unsigned long moduleID,slotID; + CERTCertificate *cert; + + if (!db_key) { + /* XXX gerror */ + 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; + } + + /* XXX gerror */ + return NULL; +#endif +} + +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 char *nickname, + GError **error) +{ +} + +ECert* +e_cert_db_find_email_signing_cert (ECertDB *certdb, + const char *nickname, + GError **error) +{ +} + +ECert* +e_cert_db_find_cert_by_email_address (ECertDB *certdb, + const char *email, + GError **error) +{ + /* nsNSSShutDownPreventionLock locker; */ + ECert *cert; + CERTCertificate *any_cert = CERT_FindCertByNicknameOrEmailAddr(CERT_GetDefaultCertDB(), + (char*)email); + CERTCertList *certlist; + + if (!any_cert) { + /* XXX gerror */ + 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) { + /* XXX gerror */ + /* XXX free any_cert */ + return NULL; + } + + if (SECSuccess != CERT_FilterCertListByUsage(certlist, certUsageEmailRecipient, PR_FALSE)) { + /* XXX gerror */ + /* XXX free 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 */ + return NULL; + } + + cert = e_cert_new (CERT_LIST_HEAD(certlist)->cert); + + return cert; +} + +static gboolean +_confirm_download_ca_cert (ECert *cert, guint32 *trustBits, gboolean *allow) +{ + /* right now just allow it and set the trustBits to 0 */ + *trustBits = 0; + *allow = TRUE; + return TRUE; +} + +static gboolean +handle_ca_cert_download(GList *certs, GError **error) +{ + ECert *certToShow; + SECItem der; + 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 char* cert0SubjectName; + const char* cert0IssuerName; + const char* cert1SubjectName; + const char* 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) { + /* XXX gerror */ + return FALSE; + } + + if (!e_cert_get_raw_der (certToShow, (char**)&der.data, &der.len)) { + /* XXX gerror */ + return FALSE; + } + + { + /*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"); + return FALSE; + } + } + +#if 0 + CERTCertificateCleaner tmpCertCleaner(tmpCert); +#endif + + if (tmpCert->isperm) { + e_notice (NULL, GTK_MESSAGE_WARNING, _("Certificate already exists")); + /* XXX gerror */ + return FALSE; + } + else { + guint32 trustBits; + gboolean allow; + char *nickname; + SECStatus srv; + + if (!_confirm_download_ca_cert (certToShow, &trustBits, &allow)) { + /* XXX gerror */ + return FALSE; + } + + if (!allow) { + /* XXX gerror */ + 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())); + +#if 0 + nsNSSCertTrust trust; + trust.SetValidCA(); + trust.AddCATrust(trustBits & nsIX509CertDB::TRUSTED_SSL, + trustBits & nsIX509CertDB::TRUSTED_EMAIL, + trustBits & nsIX509CertDB::TRUSTED_OBJSIGN); +#endif + + srv = CERT_AddTempCertToPerm(tmpCert, + nickname, + /*XXX trust.GetTrust()*/ 0); + + if (srv != SECSuccess) { + /* XXX gerror */ + 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. */ + nsNSSCertTrust defaultTrust; + defaultTrust.SetValidCA(); + defaultTrust.AddCATrust(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(char*,nickname.get()), + defaultTrust.GetTrust()); + CERT_DestroyCertificate(tmpCert2); + } +#endif + 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; + SECStatus srv = SECSuccess; + 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. */ +#if 0 + nsNSSCertTrust trust(0, 0, 0); + srv = CERT_ChangeCertTrust(CERT_GetDefaultCertDB(), + cert, trust.GetTrust()); +#endif + } + +#if 0 + PR_LOG(gPIPNSSLog, PR_LOG_DEBUG, ("cert deleted: %d", srv)); +#endif + return (srv) ? FALSE : TRUE; +} + +/* importing certificates */ +gboolean +e_cert_db_import_certs (ECertDB *certdb, + char *data, guint32 length, + ECertType cert_type, + 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); + int i; + gboolean rv; + + if (!certCollection) { + /* XXX gerror */ + 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 ((char*)currItem->data, currItem->len); + if (!cert) { + /* XXX gerror */ + 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(certs, error); + break; + default: + // We only deal with import CA certs in this method currently. + /* XXX gerror */ + 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, + char *data, guint32 length, + GError **error) +{ +} + +gboolean +e_cert_db_import_user_cert (ECertDB *certdb, + char *data, guint32 length, + GError **error) +{ +#if 0 + /* nsNSSShutDownPreventionLock locker;*/ + PK11SlotInfo *slot; + char * nickname = NULL; + gboolean rv = FALSE; + int numCACerts; + SECItem *CACerts; + CERTDERCerts * collectArgs; + CERTCertificate * cert=NULL; + + collectArgs = e_cert_db_get_certs_from_package(data, length); + if (!collectArgs) { + goto loser; + } + + cert = CERT_NewTempCertificate(CERT_GetDefaultCertDB(), collectArgs->rawCerts, + (char *)NULL, PR_FALSE, PR_TRUE); + if (!cert) { + goto loser; + } + + slot = PK11_KeyForCertExists(cert, NULL, NULL); + if ( slot == NULL ) { + 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 { + g_assert_not_reached (); + /* nickname = default_nickname(cert, NULL); */ + } + + /* user wants to import the cert */ + slot = PK11_ImportCertForKey(cert, nickname, NULL); + if (!slot) { + 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 ( cert ) { + CERT_DestroyCertificate(cert); + } + return rv; +#endif +} + +gboolean +e_cert_db_import_server_cert (ECertDB *certdb, + char *data, guint32 length, + GError **error) +{ +} + +gboolean +e_cert_db_import_certs_from_file (ECertDB *cert_db, + const char *file_path, + ECertType cert_type, + GError **error) +{ + gboolean rv; + int fd; + struct stat sb; + char *buf; + int bytes_read; + + switch (cert_type) { + case E_CERT_CA: + case E_CERT_CONTACT: + case E_CERT_SITE: + /* good */ + break; + + default: + /* not supported (yet) */ + /* XXX gerror */ + return FALSE; + } + + fd = open (file_path, O_RDONLY); + if (fd == -1) { + /* XXX gerror */ + return FALSE; + } + + if (-1 == fstat (fd, &sb)) { + /* XXX gerror */ + close (fd); + return FALSE; + } + + buf = g_malloc (sb.st_size); + if (!buf) { + /* XXX gerror */ + close (fd); + return FALSE; + } + + bytes_read = read (fd, buf, sb.st_size); + + close (fd); + + if (bytes_read != sb.st_size) { + /* XXX gerror */ + 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, error); + break; + + case E_CERT_SITE: + rv = e_cert_db_import_server_cert (cert_db, buf, bytes_read, error); + break; + + case E_CERT_CONTACT: + rv = e_cert_db_import_email_cert (cert_db, buf, bytes_read, error); + break; + + default: + rv = FALSE; + break; + } + } + + g_free (buf); + return rv; +} + +gboolean +e_cert_db_import_pkcs12_file (ECertDB *cert_db, + const char *file_path, + GError **error) +{ +} + +gboolean +e_cert_db_export_pkcs12_file (ECertDB *cert_db, + const char *file_path, + GList *certs, + GError **error) +{ +} + + + +static SECStatus PR_CALLBACK +collect_certs(void *arg, SECItem **certs, int 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, + char *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, + (void *)collectArgs); + + if (sec_rv != SECSuccess) + return NULL; + + return collectArgs; +} diff --git a/smime/lib/e-cert-db.h b/smime/lib/e-cert-db.h new file mode 100644 index 0000000000..ffc381587a --- /dev/null +++ b/smime/lib/e-cert-db.h @@ -0,0 +1,128 @@ +/* -*- 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_CERT_DB_H_ +#define _E_CERT_DB_H_ + +#include <glib-object.h> +#include "e-cert.h" +#include <cert.h> + +#define E_TYPE_CERT_DB (e_cert_db_get_type ()) +#define E_CERT_DB(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), E_TYPE_CERT_DB, ECertDB)) +#define E_CERT_DB_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), E_TYPE_CERT_DB, ECertDBClass)) +#define E_IS_CERT_DB(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), E_TYPE_CERT_DB)) +#define E_IS_CERT_DB_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), E_TYPE_CERT_DB)) +#define E_CERT_DB_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), E_TYPE_CERT_DB, ECertDBClass)) + +typedef struct _ECertDB ECertDB; +typedef struct _ECertDBClass ECertDBClass; +typedef struct _ECertDBPrivate ECertDBPrivate; + +struct _ECertDB { + GObject parent; + + ECertDBPrivate *priv; +}; + +struct _ECertDBClass { + GObjectClass parent_class; + + /* Padding for future expansion */ + void (*_ecert_reserved0) (void); + void (*_ecert_reserved1) (void); + void (*_ecert_reserved2) (void); + void (*_ecert_reserved3) (void); + void (*_ecert_reserved4) (void); +}; + +GType e_cert_db_get_type (void); + +/* single instance */ +ECertDB* e_cert_db_peek (void); + +void e_cert_db_shutdown (void); + +/* searching for certificates */ +ECert* e_cert_db_find_cert_by_nickname (ECertDB *certdb, + const char *nickname, + GError **error); + +ECert* e_cert_db_find_cert_by_key (ECertDB *certdb, + const char *db_key, + GError **error); + +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 char *nickname, + GError **error); + +ECert* e_cert_db_find_email_signing_cert (ECertDB *certdb, + const char *nickname, + GError **error); + +ECert* e_cert_db_find_cert_by_email_address (ECertDB *certdb, + const char *nickname, + GError **error); + +/* deleting certificates */ +gboolean e_cert_db_delete_cert (ECertDB *certdb, + ECert *cert); + +/* importing certificates */ +gboolean e_cert_db_import_certs (ECertDB *certdb, + char *data, guint32 length, + ECertType cert_type, + GError **error); + +gboolean e_cert_db_import_email_cert (ECertDB *certdb, + char *data, guint32 length, + GError **error); + +gboolean e_cert_db_import_user_cert (ECertDB *certdb, + char *data, guint32 length, + GError **error); + +gboolean e_cert_db_import_server_cert (ECertDB *certdb, + char *data, guint32 length, + GError **error); + +gboolean e_cert_db_import_certs_from_file (ECertDB *cert_db, + const char *file_path, + ECertType cert_type, + GError **error); + +gboolean e_cert_db_import_pkcs12_file (ECertDB *cert_db, + const char *file_path, + GError **error); + +gboolean e_cert_db_export_pkcs12_file (ECertDB *cert_db, + const char *file_path, + GList *certs, + GError **error); + + +#endif /* _E_CERT_DB_H_ */ diff --git a/smime/lib/e-cert.c b/smime/lib/e-cert.c index 5636730401..7db638b884 100644 --- a/smime/lib/e-cert.c +++ b/smime/lib/e-cert.c @@ -20,12 +20,54 @@ * 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 "e-cert.h" +#include "pk11func.h" +#include "certdb.h" struct _ECertPrivate { CERTCertificate *cert; + + /* pointers we cache since the nss implementation allocs the + string */ char *org_name; char *cn; + + gboolean delete; }; #define PARENT_TYPE G_TYPE_OBJECT @@ -42,11 +84,23 @@ e_cert_dispose (GObject *object) if (ec->priv->org_name) PORT_Free (ec->priv->org_name); if (ec->priv->cn) - PORT_Free (ec->priv->org_name); + PORT_Free (ec->priv->cn); + + if (ec->priv->delete) { + printf ("attempting to delete cert marked for deletion\n"); + if (e_cert_get_cert_type (ec) == E_CERT_USER) { + PK11_DeleteTokenCertAndKey(ec->priv->cert, NULL); + } else if (!PK11_IsReadOnly(ec->priv->cert->slot)) { + /* If the list of built-ins does contain a non-removable + copy of this certificate, our call will not remove + the certificate permanently, but rather remove all trust. */ + SEC_DeletePermCertificate(ec->priv->cert); + } + } g_free (ec->priv); ec->priv = NULL; - + if (G_OBJECT_CLASS (parent_class)->dispose) G_OBJECT_CLASS (parent_class)->dispose (object); } @@ -115,9 +169,47 @@ e_cert_new (CERTCertificate *cert) return ecert; } +ECert* +e_cert_new_from_der (char *data, guint32 len) +{ + CERTCertificate *cert = CERT_DecodeCertFromPackage (data, len); + + if (!cert) + return NULL; + + if (cert->dbhandle == NULL) + cert->dbhandle = CERT_GetDefaultCertDB(); + + return e_cert_new (cert); +} + +CERTCertificate* +e_cert_get_internal_cert (ECert *cert) +{ + /* XXX should this refcnt it? */ + return cert->priv->cert; +} + +gboolean +e_cert_get_raw_der (ECert *cert, char **data, guint32 *len) +{ + /* XXX do we really need to check if cert->priv->cert is NULL + here? it should always be non-null if we have the + ECert.. */ + if (cert->priv->cert) { + *data = (char*)cert->priv->cert->derCert.data; + *len = (guint32)cert->priv->cert->derCert.len; + return TRUE; + } + + *len = 0; + return FALSE; + +} + const char* e_cert_get_nickname (ECert *cert) { @@ -141,8 +233,46 @@ e_cert_get_cn (ECert *cert) return cert->priv->cn; } +const char* +e_cert_get_issuer_name (ECert *cert) +{ + return cert->priv->cert->issuerName; +} + +const char* +e_cert_get_subject_name (ECert *cert) +{ + return cert->priv->cert->subjectName; +} + gboolean -e_cert_is_ca_cert (ECert *cert) +e_cert_mark_for_deletion (ECert *cert) +{ + // nsNSSShutDownPreventionLock locker; + +#if 0 + // make sure user is logged in to the token + nsCOMPtr<nsIInterfaceRequestor> ctx = new PipUIContext(); +#endif + + if (PK11_NeedLogin(cert->priv->cert->slot) + && !PK11_NeedUserInit(cert->priv->cert->slot) + && !PK11_IsInternal(cert->priv->cert->slot)) { + if (SECSuccess != PK11_Authenticate(cert->priv->cert->slot, PR_TRUE, NULL)) { + return FALSE; + } + } + + cert->priv->delete = TRUE; + + return TRUE; +} + +ECertType +e_cert_get_cert_type (ECert *cert) { - return CERT_IsCACert (cert->priv->cert, NULL); + if (CERT_IsCACert (cert->priv->cert, NULL)) + return E_CERT_CA; + else /* XXX more here */ + return E_CERT_USER; } diff --git a/smime/lib/e-cert.h b/smime/lib/e-cert.h index 86517dad79..06b44f23ad 100644 --- a/smime/lib/e-cert.h +++ b/smime/lib/e-cert.h @@ -37,6 +37,13 @@ typedef struct _ECert ECert; typedef struct _ECertClass ECertClass; typedef struct _ECertPrivate ECertPrivate; +typedef enum { + E_CERT_CA, + E_CERT_CONTACT, + E_CERT_SITE, + E_CERT_USER +} ECertType; + struct _ECert { GObject parent; @@ -57,11 +64,20 @@ struct _ECertClass { GType e_cert_get_type (void); ECert* e_cert_new (CERTCertificate *cert); +ECert* e_cert_new_from_der (char *data, guint32 len); + +CERTCertificate* e_cert_get_internal_cert (ECert *cert); + +gboolean e_cert_get_raw_der (ECert *cert, char **data, guint32 *len); +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_cn (ECert *cert); +const char* e_cert_get_subject_name (ECert *cert); +const char* e_cert_get_issuer_name (ECert *cert); + +gboolean e_cert_mark_for_deletion (ECert *cert); -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_cn (ECert *cert); +ECertType e_cert_get_cert_type (ECert *cert); -gboolean e_cert_is_ca_cert (ECert *cert); #endif /* _E_CERT_H_ */ diff --git a/smime/tests/Makefile.am b/smime/tests/Makefile.am index c07d048824..0fd1064b00 100644 --- a/smime/tests/Makefile.am +++ b/smime/tests/Makefile.am @@ -7,6 +7,7 @@ INCLUDES= \ TEST_LIBS= \ $(top_builddir)/smime/lib/libessmime.la \ + -L/home/toshok/src/mozilla/mozilla/dist/lib \ $(CERT_UI_LIBS) diff --git a/smime/tests/import-cert.c b/smime/tests/import-cert.c index 63eb5a63f3..76e8dc6ccd 100644 --- a/smime/tests/import-cert.c +++ b/smime/tests/import-cert.c @@ -1,4 +1,5 @@ +#include <libgnomeui/gnome-ui-init.h> #include "e-cert-db.h" int @@ -6,19 +7,12 @@ main (int argc, char **argv) { ECertDB *db; - g_type_init (); - - if (SECSuccess != NSS_InitReadWrite ("/home/toshok/.mozilla/default/xuvq7jx3.slt")) { - g_error ("NSS_InitReadWrite failed"); - } + gnome_program_init (); - STAN_LoadDefaultNSS3TrustDomain(); + g_type_init (); db = e_cert_db_peek (); - printf ("default_trust_domain = %p\n", STAN_GetDefaultTrustDomain()); - printf ("default_crypto_context = %p\n", STAN_GetDefaultCryptoContext()); - if (!e_cert_db_import_certs_from_file (db, "ca.crt", E_CERT_CA, NULL /* XXX */)) { g_warning ("CA cert import failed"); } |