diff options
-rw-r--r-- | smime/ChangeLog | 45 | ||||
-rw-r--r-- | smime/gui/certificate-viewer.c | 72 | ||||
-rw-r--r-- | smime/lib/e-asn1-object.c | 62 | ||||
-rw-r--r-- | smime/lib/e-asn1-object.h | 52 | ||||
-rw-r--r-- | smime/lib/e-cert.c | 741 | ||||
-rw-r--r-- | smime/lib/e-cert.h | 4 |
6 files changed, 937 insertions, 39 deletions
diff --git a/smime/ChangeLog b/smime/ChangeLog index 7794f799f6..c5b644d9a2 100644 --- a/smime/ChangeLog +++ b/smime/ChangeLog @@ -1,3 +1,48 @@ +2003-12-04 Chris Toshok <toshok@ximian.com> + + * lib/e-cert.h: add prototype for e_cert_get_asn1_struct. + + * lib/e-cert.c (e_cert_dispose): unref the asn1 object. + (get_int_value): copy and massage from mozilla source. + (process_version): same. + (process_serial_number_der): same. + (get_default_oid_format): same. + (get_oid_text): same. + (process_raw_bytes): same. + (process_sec_algorithm_id): same. + (process_subject_public_key_info): same. + (process_ns_cert_type_extensions): same. + (process_key_usage_extensions): same. + (process_extension_data): same. + (process_single_extension): same. + (process_extensions): same. + (process_name): same. + (create_tbs_certificate_asn1_struct): same. + (create_asn1_struct): same. + (e_cert_get_asn1_struct): new function. + + * lib/e-asn1-object.c (e_asn1_object_dispose): free the display + name, value, and children. + (e_asn1_object_init): assume it's a valid container unless we hear + otherwise. + (e_asn1_object_new_from_cert): nuke. + (e_asn1_object_set_valid_container): implement. + (e_asn1_object_append_child): same. + (e_asn1_object_set_display_name): same. + (e_asn1_object_set_display_value): same. + + * lib/e-asn1-object.h: add prototypes for + e_asn1_object_set_valid_container, e_asn1_object_set_display_name, + e_asn1_object_set_display_value, and e_asn1_object_append_child. + + * gui/certificate-viewer.c (populate_fields_tree): populate the + tree from the asn structure. + (hierarchy_selection_changed): blow away the old fields_tree + content and populate it again. + (fields_selection_changed): implement, set the text view's + contents to the asn1 object's display_value. + (fill_in_details): expand all nodes in the hierarchy tree. + 2003-12-03 Chris Toshok <toshok@ximian.com> * lib/Makefile.am (libessmime_la_SOURCES): add e-asn1-object.[ch] diff --git a/smime/gui/certificate-viewer.c b/smime/gui/certificate-viewer.c index 9314ac5c1e..e897597319 100644 --- a/smime/gui/certificate-viewer.c +++ b/smime/gui/certificate-viewer.c @@ -118,6 +118,32 @@ fill_in_general (CertificateViewerData *cvm_data, ECert *cert) } static void +populate_fields_tree (CertificateViewerData *cvm_data, EASN1Object *asn1, GtkTreeIter *root) +{ + GtkTreeIter new_iter; + + /* first insert a node for the current asn1 */ + gtk_tree_store_insert (cvm_data->fields_store, &new_iter, root, -1); + gtk_tree_store_set (cvm_data->fields_store, &new_iter, + 0, e_asn1_object_get_display_name (asn1), + 1, asn1, + -1); + + if (e_asn1_object_is_valid_container (asn1)) { + GList *children = e_asn1_object_get_children (asn1); + if (children) { + GList *l; + for (l = children; l; l = l->next) { + EASN1Object *subasn1 = l->data; + populate_fields_tree (cvm_data, subasn1, &new_iter); + } + } + g_list_foreach (children, (GFunc)g_object_unref, NULL); + g_list_free (children); + } +} + +static void hierarchy_selection_changed (GtkTreeSelection *selection, CertificateViewerData *cvm_data) { GtkTreeIter iter; @@ -137,14 +163,51 @@ hierarchy_selection_changed (GtkTreeSelection *selection, CertificateViewerData if (!cert) return; - /* XXX show the selected fields somehow */ - asn1_object = e_asn1_object_new_from_cert (cert); + /* display the cert's ASN1 structure */ + asn1_object = e_cert_get_asn1_struct (cert); + + /* wipe out the old model */ + cvm_data->fields_store = gtk_tree_store_new (2, G_TYPE_STRING, G_TYPE_POINTER); + gtk_tree_view_set_model (GTK_TREE_VIEW (cvm_data->fields_tree), + GTK_TREE_MODEL (cvm_data->fields_store)); + + /* populate the fields from the newly selected cert */ + populate_fields_tree (cvm_data, asn1_object, NULL); + gtk_tree_view_expand_all (GTK_TREE_VIEW (cvm_data->fields_tree)); + g_object_unref (asn1_object); + + /* and blow away the field value */ + gtk_text_buffer_set_text (gtk_text_view_get_buffer (GTK_TEXT_VIEW (cvm_data->field_text)), + "", 0); } } static void fields_selection_changed (GtkTreeSelection *selection, CertificateViewerData *cvm_data) { + GtkTreeIter iter; + GtkTreeModel *model; + + if (gtk_tree_selection_get_selected (selection, + &model, + &iter)) { + EASN1Object *asn1_object; + const char *value; + + gtk_tree_model_get (model, + &iter, + 1, &asn1_object, + -1); + + value = e_asn1_object_get_display_value (asn1_object); + + if (value) + gtk_text_buffer_set_text (gtk_text_view_get_buffer (GTK_TEXT_VIEW (cvm_data->field_text)), + value, strlen (value)); + else + gtk_text_buffer_set_text (gtk_text_view_get_buffer (GTK_TEXT_VIEW (cvm_data->field_text)), + "", 0); + } } static void @@ -168,10 +231,7 @@ fill_in_details (CertificateViewerData *cvm_data, ECert *cert) g_signal_connect (selection, "changed", G_CALLBACK (hierarchy_selection_changed), cvm_data); /* hook up all the fields tree foo */ - cvm_data->fields_store = gtk_tree_store_new (1, G_TYPE_STRING); cvm_data->fields_tree = glade_xml_get_widget (cvm_data->gui, "cert-fields-treeview"); - gtk_tree_view_set_model (GTK_TREE_VIEW (cvm_data->fields_tree), - GTK_TREE_MODEL (cvm_data->fields_store)); gtk_tree_view_insert_column_with_attributes (GTK_TREE_VIEW (cvm_data->fields_tree), -1, "Field", gtk_cell_renderer_text_new(), @@ -203,6 +263,8 @@ fill_in_details (CertificateViewerData *cvm_data, ECert *cert) root = &new_iter; } + + gtk_tree_view_expand_all (GTK_TREE_VIEW (cvm_data->hierarchy_tree)); } GtkWidget* diff --git a/smime/lib/e-asn1-object.c b/smime/lib/e-asn1-object.c index 47cf591b48..b7528dcd22 100644 --- a/smime/lib/e-asn1-object.c +++ b/smime/lib/e-asn1-object.c @@ -79,6 +79,21 @@ static GObjectClass *parent_class; static void e_asn1_object_dispose (GObject *object) { + EASN1Object *obj = E_ASN1_OBJECT (object); + if (obj->priv) { + + if (obj->priv->display_name) + g_free (obj->priv->display_name); + + if (obj->priv->value) + g_free (obj->priv->value); + + g_list_foreach (obj->priv->children, (GFunc)g_object_unref, NULL); + g_list_free (obj->priv->children); + + g_free (obj->priv); + obj->priv = NULL; + } } static void @@ -97,6 +112,8 @@ static void e_asn1_object_init (EASN1Object *asn1) { asn1->priv = g_new0 (EASN1ObjectPrivate, 1); + + asn1->priv->valid_container = TRUE; } GType @@ -296,31 +313,18 @@ e_asn1_object_new_from_der (char *data, guint32 len) } EASN1Object* -e_asn1_object_new_from_cert (ECert *cert) -{ - char *data; - guint32 len; - EASN1Object *obj; - - if (!e_cert_get_raw_der (cert, &data, &len)) - return NULL; - - obj = g_object_new (E_TYPE_ASN1_OBJECT, NULL); - if (!build_from_der (obj, data, data + len)) { - g_object_unref (obj); - return NULL; - } - - return obj; -} - -EASN1Object* e_asn1_object_new (void) { return E_ASN1_OBJECT (g_object_new (E_TYPE_ASN1_OBJECT, NULL)); } +void +e_asn1_object_set_valid_container (EASN1Object *obj, gboolean flag) +{ + obj->priv->valid_container = flag; +} + gboolean e_asn1_object_is_valid_container (EASN1Object *obj) { @@ -349,12 +353,32 @@ e_asn1_object_get_children (EASN1Object *obj) return children; } +void +e_asn1_object_append_child (EASN1Object *parent, EASN1Object *child) +{ + parent->priv->children = g_list_append (parent->priv->children, g_object_ref (child)); +} + +void +e_asn1_object_set_display_name (EASN1Object *obj, const char *name) +{ + g_free (obj->priv->display_name); + obj->priv->display_name = g_strdup (name); +} + const char* e_asn1_object_get_display_name (EASN1Object *obj) { return obj->priv->display_name; } +void +e_asn1_object_set_display_value (EASN1Object *obj, const char *value) +{ + g_free (obj->priv->value); + obj->priv->value = g_strdup (value); +} + const char* e_asn1_object_get_display_value (EASN1Object *obj) { diff --git a/smime/lib/e-asn1-object.h b/smime/lib/e-asn1-object.h index ebdb7de15e..76e2530fcc 100644 --- a/smime/lib/e-asn1-object.h +++ b/smime/lib/e-asn1-object.h @@ -25,8 +25,6 @@ #include <glib-object.h> -#include "e-cert.h" - #include <nspr.h> #define E_TYPE_ASN1_OBJECT (e_asn1_object_get_type ()) @@ -41,9 +39,32 @@ typedef struct _EASN1ObjectClass EASN1ObjectClass; typedef struct _EASN1ObjectPrivate EASN1ObjectPrivate; enum { - E_ASN1_OBJECT_TYPE_APPLICATION, - E_ASN1_OBJECT_TYPE_CONTEXT_SPECIFIC, - E_ASN1_OBJECT_TYPE_PRIVATE + /* + * Identifiers for the possible types of object. + */ + E_ASN1_OBJECT_TYPE_END_CONTENTS = 0, + E_ASN1_OBJECT_TYPE_BOOLEAN = 1, + E_ASN1_OBJECT_TYPE_INTEGER = 2, + E_ASN1_OBJECT_TYPE_BIT_STRING = 3, + E_ASN1_OBJECT_TYPE_OCTET_STRING = 4, + E_ASN1_OBJECT_TYPE_NULL = 5, + E_ASN1_OBJECT_TYPE_OBJECT_ID = 6, + E_ASN1_OBJECT_TYPE_ENUMERATED = 10, + E_ASN1_OBJECT_TYPE_UTF8_STRING = 12, + E_ASN1_OBJECT_TYPE_SEQUENCE = 16, + E_ASN1_OBJECT_TYPE_SET = 17, + E_ASN1_OBJECT_TYPE_PRINTABLE_STRING = 19, + E_ASN1_OBJECT_TYPE_T61_STRING = 20, + E_ASN1_OBJECT_TYPE_IA5_STRING = 22, + E_ASN1_OBJECT_TYPE_UTC_TIME = 23, + E_ASN1_OBJECT_TYPE_GEN_TIME = 24, + E_ASN1_OBJECT_TYPE_VISIBLE_STRING = 26, + E_ASN1_OBJECT_TYPE_UNIVERSAL_STRING = 28, + E_ASN1_OBJECT_TYPE_BMP_STRING = 30, + E_ASN1_OBJECT_TYPE_HIGH_TAG_NUMBER = 31, + E_ASN1_OBJECT_TYPE_CONTEXT_SPECIFIC = 32, + E_ASN1_OBJECT_TYPE_APPLICATION = 33, + E_ASN1_OBJECT_TYPE_PRIVATE = 34, }; struct _EASN1Object { @@ -63,18 +84,21 @@ struct _EASN1ObjectClass { void (*_ecert_reserved4) (void); }; -EASN1Object *e_asn1_object_new_from_cert (ECert *cert); EASN1Object *e_asn1_object_new_from_der (char *data, guint32 len); EASN1Object *e_asn1_object_new (void); -gboolean e_asn1_object_is_valid_container (EASN1Object *obj); -PRUint32 e_asn1_object_get_asn1_type (EASN1Object *obj); -PRUint32 e_asn1_object_get_asn1_tag (EASN1Object *obj); -GList *e_asn1_object_get_children (EASN1Object *obj); -const char *e_asn1_object_get_display_name (EASN1Object *obj); -const char *e_asn1_object_get_display_value (EASN1Object *obj); - -void e_asn1_object_get_data (EASN1Object *obj, char **data, guint32 *len); +void e_asn1_object_set_valid_container (EASN1Object *obj, gboolean flag); +gboolean e_asn1_object_is_valid_container (EASN1Object *obj); +PRUint32 e_asn1_object_get_asn1_type (EASN1Object *obj); +PRUint32 e_asn1_object_get_asn1_tag (EASN1Object *obj); +GList *e_asn1_object_get_children (EASN1Object *obj); +void e_asn1_object_append_child (EASN1Object *parent, EASN1Object *child); +void e_asn1_object_set_display_name (EASN1Object *obj, const char *name); +const char *e_asn1_object_get_display_name (EASN1Object *obj); +void e_asn1_object_set_display_value (EASN1Object *obj, const char *value); +const char *e_asn1_object_get_display_value (EASN1Object *obj); + +void e_asn1_object_get_data (EASN1Object *obj, char **data, guint32 *len); GType e_asn1_object_get_type (void); diff --git a/smime/lib/e-cert.c b/smime/lib/e-cert.c index 2389cc29c8..173f445c22 100644 --- a/smime/lib/e-cert.c +++ b/smime/lib/e-cert.c @@ -90,6 +90,8 @@ struct _ECertPrivate { char *sha1_fingerprint; char *md5_fingerprint; + EASN1Object *asn1; + gboolean delete; }; @@ -130,6 +132,9 @@ e_cert_dispose (GObject *object) if (ec->priv->md5_fingerprint) PORT_Free (ec->priv->md5_fingerprint); + if (ec->priv->asn1) + g_object_unref (ec->priv->asn1); + if (ec->priv->delete) { printf ("attempting to delete cert marked for deletion\n"); if (e_cert_get_cert_type (ec) == E_CERT_USER) { @@ -434,6 +439,742 @@ e_cert_get_chain (ECert *ecert) return l; } +static gboolean +get_int_value (SECItem *versionItem, + unsigned long *version) +{ + SECStatus srv; + srv = SEC_ASN1DecodeInteger(versionItem,version); + if (srv != SECSuccess) { + g_warning ("could not decode version of cert"); + return FALSE; + } + return TRUE; +} + +static gboolean +process_version (SECItem *versionItem, + EASN1Object **retItem) +{ + EASN1Object *item = e_asn1_object_new (); + unsigned long version; + + e_asn1_object_set_display_name (item, _("Version")); + + /* Now to figure out what version this certificate is. */ + + if (versionItem->data) { + if (!get_int_value (versionItem, &version)) + return FALSE; + } else { + /* If there is no version present in the cert, then rfc2459 + says we default to v1 (0) */ + version = 0; + } + + switch (version){ + case 0: + e_asn1_object_set_display_value (item, _("Version 1")); + break; + case 1: + e_asn1_object_set_display_value (item, _("Version 2")); + break; + case 2: + e_asn1_object_set_display_value (item, _("Version 3")); + break; + default: + g_warning ("Bad value for cert version"); + return FALSE; + } + + *retItem = item; + return TRUE; +} + +static gboolean +process_serial_number_der (SECItem *serialItem, + EASN1Object **retItem) +{ + char *serialNumber; + EASN1Object *item = e_asn1_object_new (); + + e_asn1_object_set_display_name (item, _("Serial Number")); + + serialNumber = CERT_Hexify(serialItem, 1); + + e_asn1_object_set_display_value (item, serialNumber); + PORT_Free (serialNumber); /* XXX the right free to use? */ + + *retItem = item; + return TRUE; +} + +static gboolean +get_default_oid_format (SECItem *oid, + char **text) +{ + char buf[300]; + unsigned int len; + int written; + + unsigned long val = oid->data[0]; + unsigned int i = val % 40; + val /= 40; + written = PR_snprintf(buf, 300, "%lu %u ", val, i); + if (written < 0) + return FALSE; + len = written; + + val = 0; + for (i = 1; i < oid->len; ++i) { + /* In this loop, we have to parse a DER formatted + If the first bit is a 1, then the integer is + represented by more than one byte. If the + first bit is set then we continue on and add + the values of the later bytes until we get + a byte without the first bit set. + */ + unsigned long j; + + j = oid->data[i]; + val = (val << 7) | (j & 0x7f); + if (j & 0x80) + continue; + written = PR_snprintf(&buf[len], sizeof(buf)-len, "%lu ", val); + if (written < 0) + return FALSE; + + len += written; + if (len >= sizeof (buf)) + g_warning ("OID data to big to display in 300 chars."); + val = 0; + } + + *text = g_strdup (buf); + return TRUE; +} + +static gboolean +get_oid_text (SECItem *oid, char **text) +{ + SECOidTag oidTag = SECOID_FindOIDTag(oid); + char *temp; + + switch (oidTag) { + case SEC_OID_PKCS1_MD2_WITH_RSA_ENCRYPTION: + *text = g_strdup (_("PKCS #1 MD2 With RSA Encryption")); + break; + case SEC_OID_PKCS1_MD5_WITH_RSA_ENCRYPTION: + *text = g_strdup (_("PKCS #1 MD5 With RSA Encryption")); + break; + case SEC_OID_PKCS1_SHA1_WITH_RSA_ENCRYPTION: + *text = g_strdup (_("PKCS #1 SHA-1 With RSA Encryption")); + break; + case SEC_OID_AVA_COUNTRY_NAME: + *text = g_strdup (_("C")); + break; + case SEC_OID_AVA_COMMON_NAME: + *text = g_strdup (_("CN")); + break; + case SEC_OID_AVA_ORGANIZATIONAL_UNIT_NAME: + *text = g_strdup (_("OU")); + break; + case SEC_OID_AVA_ORGANIZATION_NAME: + *text = g_strdup (_("O")); + break; + case SEC_OID_AVA_LOCALITY: + *text = g_strdup (_("L")); + break; + case SEC_OID_AVA_DN_QUALIFIER: + *text = g_strdup (_("DN")); + break; + case SEC_OID_AVA_DC: + *text = g_strdup (_("DC")); + break; + case SEC_OID_AVA_STATE_OR_PROVINCE: + *text = g_strdup (_("ST")); + break; + case SEC_OID_PKCS1_RSA_ENCRYPTION: + *text = g_strdup (_("PKCS #1 RSA Encryption")); + break; + case SEC_OID_X509_KEY_USAGE: + *text = g_strdup (_("Certificate Key Usage")); + break; + case SEC_OID_NS_CERT_EXT_CERT_TYPE: + *text = g_strdup (_("Netscape Certificate Type")); + break; + case SEC_OID_X509_AUTH_KEY_ID: + *text = g_strdup (_("Certificate Authority Key Identifier")); + break; + case SEC_OID_RFC1274_UID: + *text = g_strdup (_("UID")); + break; + case SEC_OID_PKCS9_EMAIL_ADDRESS: + *text = g_strdup (_("E")); + break; + default: + if (!get_default_oid_format (oid, &temp)) + return FALSE; + + *text = g_strdup_printf (_("Object Identifier (%s)"), temp); + g_free (temp); + + break; + } + return TRUE; +} + + +static gboolean +process_raw_bytes (SECItem *data, char **text) +{ + /* This function is used to display some DER bytes + that we have not added support for decoding. + It prints the value of the byte out into a + string that can later be displayed as a byte + string. We place a new line after 24 bytes + to break up extermaly long sequence of bytes. + */ + GString *str = g_string_new (""); + PRUint32 i; + char buffer[5]; + for (i=0; i<data->len; i++) { + PR_snprintf(buffer, 5, "%02x ", data->data[i]); + g_string_append (str, buffer); + if ((i+1)%16 == 0) { + g_string_append (str, "\n"); + } + } + *text = g_string_free (str, FALSE); + return TRUE; +} + +static gboolean +process_sec_algorithm_id (SECAlgorithmID *algID, + EASN1Object **retSequence) +{ + EASN1Object *sequence = e_asn1_object_new (); + char *text; + + *retSequence = NULL; + + get_oid_text (&algID->algorithm, &text); + + if (!algID->parameters.len || algID->parameters.data[0] == E_ASN1_OBJECT_TYPE_NULL) { + e_asn1_object_set_display_value (sequence, text); + e_asn1_object_set_valid_container (sequence, FALSE); + } else { + EASN1Object *subitem; + + subitem = e_asn1_object_new (); + e_asn1_object_set_display_name (subitem, _("Algorithm Identifier")); + e_asn1_object_set_display_value (subitem, text); + e_asn1_object_append_child (sequence, subitem); + g_object_unref (subitem); + + g_free (text); + + subitem = e_asn1_object_new (); + e_asn1_object_set_display_name (subitem, _("Algorithm Parameters")); + process_raw_bytes (&algID->parameters, &text); + e_asn1_object_set_display_value (subitem, text); + e_asn1_object_append_child (sequence, subitem); + g_object_unref (subitem); + } + + g_free (text); + *retSequence = sequence; + return TRUE; +} + +static gboolean +process_subject_public_key_info (CERTSubjectPublicKeyInfo *spki, + EASN1Object *parentSequence) +{ + EASN1Object *spkiSequence = e_asn1_object_new(); + EASN1Object *sequenceItem; + EASN1Object *printableItem; + char *text; + + e_asn1_object_set_display_name (spkiSequence, _("Subject Public Key Info")); + + if (!process_sec_algorithm_id (&spki->algorithm, &sequenceItem)) + return FALSE; + + e_asn1_object_set_display_name (sequenceItem, _("Subject Public Key Algorithm")); + + e_asn1_object_append_child (spkiSequence, sequenceItem); + + /* The subjectPublicKey field is encoded as a bit string. + ProcessRawBytes expects the lenght to be in bytes, so + let's convert the lenght into a temporary SECItem. + */ + SECItem data; + data.data = spki->subjectPublicKey.data; + data.len = spki->subjectPublicKey.len / 8; + + process_raw_bytes (&data, &text); + printableItem = e_asn1_object_new (); + + e_asn1_object_set_display_value (printableItem, text); + e_asn1_object_set_display_name (printableItem, _("Subject's Public Key")); + e_asn1_object_append_child (spkiSequence, printableItem); + g_object_unref (printableItem); + + e_asn1_object_append_child (parentSequence, spkiSequence); + g_object_unref (spkiSequence); + + return TRUE; +} + +static gboolean +process_ns_cert_type_extensions (SECItem *extData, + GString *text) +{ + SECItem decoded; + unsigned char nsCertType; + + decoded.data = NULL; + decoded.len = 0; + if (SECSuccess != SEC_ASN1DecodeItem(NULL, &decoded, + SEC_ASN1_GET(SEC_BitStringTemplate), extData)) { + g_string_append (text, _("Error: Unable to process extension")); + return TRUE; + } + + nsCertType = decoded.data[0]; + + PORT_Free (decoded.data); /* XXX right free? */ + + if (nsCertType & NS_CERT_TYPE_SSL_CLIENT) { + g_string_append (text, _("SSL Client Certificate")); + g_string_append (text, "\n"); + } + if (nsCertType & NS_CERT_TYPE_SSL_SERVER) { + g_string_append (text, _("SSL Server Certificate")); + g_string_append (text, "\n"); + } + if (nsCertType & NS_CERT_TYPE_EMAIL) { + g_string_append (text, _("Email")); + g_string_append (text, "\n"); + } + if (nsCertType & NS_CERT_TYPE_OBJECT_SIGNING) { + g_string_append (text, _("Object Signer")); + g_string_append (text, "\n"); + } + if (nsCertType & NS_CERT_TYPE_SSL_CA) { + g_string_append (text, _("SSL Certificate Authority")); + g_string_append (text, "\n"); + } + if (nsCertType & NS_CERT_TYPE_EMAIL_CA) { + g_string_append (text, _("Email Certificate Authority")); + g_string_append (text, "\n"); + } + if (nsCertType & NS_CERT_TYPE_OBJECT_SIGNING_CA) { + g_string_append (text, _("Object Signer")); + g_string_append (text, "\n"); + } + return TRUE; +} + +static gboolean +process_key_usage_extensions (SECItem *extData, GString *text) +{ + SECItem decoded; + unsigned char keyUsage; + + decoded.data = NULL; + decoded.len = 0; + if (SECSuccess != SEC_ASN1DecodeItem(NULL, &decoded, + SEC_ASN1_GET(SEC_BitStringTemplate), extData)) { + g_string_append (text, _("Error: Unable to process extension")); + return TRUE; + } + + keyUsage = decoded.data[0]; + PORT_Free (decoded.data); /* XXX right free? */ + + if (keyUsage & KU_DIGITAL_SIGNATURE) { + g_string_append (text, _("Signing")); + g_string_append (text, "\n"); + } + if (keyUsage & KU_NON_REPUDIATION) { + g_string_append (text, _("Non-repudiation")); + g_string_append (text, "\n"); + } + if (keyUsage & KU_KEY_ENCIPHERMENT) { + g_string_append (text, _("Key Encipherment")); + g_string_append (text, "\n"); + } + if (keyUsage & KU_DATA_ENCIPHERMENT) { + g_string_append (text, _("Data Encipherment")); + g_string_append (text, "\n"); + } + if (keyUsage & KU_KEY_AGREEMENT) { + g_string_append (text, _("Key Agreement")); + g_string_append (text, "\n"); + } + if (keyUsage & KU_KEY_CERT_SIGN) { + g_string_append (text, _("Certificate Signer")); + g_string_append (text, "\n"); + } + if (keyUsage & KU_CRL_SIGN) { + g_string_append (text, _("CRL Signer")); + g_string_append (text, "\n"); + } + + return TRUE; +} + +static gboolean +process_extension_data (SECOidTag oidTag, SECItem *extData, + GString *str) +{ + gboolean rv; + switch (oidTag) { + case SEC_OID_NS_CERT_EXT_CERT_TYPE: + rv = process_ns_cert_type_extensions (extData, str); + break; + case SEC_OID_X509_KEY_USAGE: + rv = process_key_usage_extensions (extData, str); + break; + default: { + char *text; + rv = process_raw_bytes (extData, &text); + g_string_append (str, text); + g_free (text); + break; + } + } + return rv; +} + +static gboolean +process_single_extension (CERTCertExtension *extension, + EASN1Object **retExtension) +{ + GString *str = g_string_new (""); + char *text; + EASN1Object *extensionItem; + SECOidTag oidTag = SECOID_FindOIDTag(&extension->id); + + get_oid_text (&extension->id, &text); + + extensionItem = e_asn1_object_new (); + + e_asn1_object_set_display_name (extensionItem, text); + g_free (text); + + if (extension->critical.data != NULL) { + if (extension->critical.data[0]) { + g_string_append (str, _("Critical")); + } else { + g_string_append (str, _("Not Critical")); + } + } else { + g_string_append (str, _("Not Critical")); + } + g_string_append (str, "\n"); + if (!process_extension_data (oidTag, &extension->value, str)) { + g_string_free (str, TRUE); + return FALSE; + } + + e_asn1_object_set_display_value (extensionItem, str->str); + g_string_free (str, TRUE); + *retExtension = extensionItem; + return TRUE; +} + +static gboolean +process_extensions (CERTCertExtension **extensions, + EASN1Object *parentSequence) +{ + EASN1Object *extensionSequence = e_asn1_object_new (); + PRInt32 i; + + e_asn1_object_set_display_name (extensionSequence, _("Extensions")); + + for (i=0; extensions[i] != NULL; i++) { + EASN1Object *newExtension; + + if (!process_single_extension (extensions[i], + &newExtension)) + return FALSE; + + e_asn1_object_append_child (extensionSequence, newExtension); + } + e_asn1_object_append_child (parentSequence, extensionSequence); + return TRUE; +} + +static gboolean +process_name (CERTName *name, char **value) +{ + CERTRDN** rdns; + CERTRDN** rdn; + CERTAVA** avas; + CERTAVA* ava; + SECItem *decodeItem = NULL; + GString *final_string = g_string_new (""); + + char *type; + GString *avavalue; + char *temp; + CERTRDN **lastRdn; + + rdns = name->rdns; + + lastRdn = rdns; + + /* find last RDN */ + lastRdn = rdns; + while (*lastRdn) lastRdn++; + + /* The above whille loop will put us at the last member + * of the array which is a NULL pointer. So let's back + * up one spot so that we have the last non-NULL entry in + * the array in preparation for traversing the + * RDN's (Relative Distinguished Name) in reverse order. + */ + lastRdn--; + + /* + * Loop over name contents in _reverse_ RDN order appending to string + * When building the Ascii string, NSS loops over these entries in + * reverse order, so I will as well. The difference is that NSS + * will always place them in a one line string separated by commas, + * where I want each entry on a single line. I can't just use a comma + * as my delimitter because it is a valid character to have in the + * value portion of the AVA and could cause trouble when parsing. + */ + for (rdn = lastRdn; rdn >= rdns; rdn--) { + avas = (*rdn)->avas; + while ((ava = *avas++) != 0) { + if (!get_oid_text (&ava->type, &type)) + return FALSE; + + /* This function returns a string in UTF8 format. */ + decodeItem = CERT_DecodeAVAValue(&ava->value); + if(!decodeItem) { + return FALSE; + } + + avavalue = g_string_new_len ((char*)decodeItem->data, decodeItem->len); + + SECITEM_FreeItem(decodeItem, PR_TRUE); + + temp = g_strdup_printf (_("%s = %s"), type, avavalue->str); + + g_string_append (final_string, temp); + g_string_append (final_string, "\n"); + g_string_free (avavalue, TRUE); + g_free (temp); + } + } + *value = g_string_free (final_string, FALSE); + return TRUE; +} + +static gboolean +create_tbs_certificate_asn1_struct (ECert *cert, EASN1Object **seq) +{ + /* + ** TBSCertificate ::= SEQUENCE { + ** version [0] EXPLICIT Version DEFAULT v1, + ** serialNumber CertificateSerialNumber, + ** signature AlgorithmIdentifier, + ** issuer Name, + ** validity Validity, + ** subject Name, + ** subjectPublicKeyInfo SubjectPublicKeyInfo, + ** issuerUniqueID [1] IMPLICIT UniqueIdentifier OPTIONAL, + ** -- If present, version shall be v2 or v3 + ** subjectUniqueID [2] IMPLICIT UniqueIdentifier OPTIONAL, + ** -- If present, version shall be v2 or v3 + ** extensions [3] EXPLICIT Extensions OPTIONAL + ** -- If present, version shall be v3 + ** } + ** + ** This is the ASN1 structure we should be dealing with at this point. + ** The code in this method will assert this is the structure we're dealing + ** and then add more user friendly text for that field. + */ + EASN1Object *sequence = e_asn1_object_new (); + char *text; + EASN1Object *subitem; + SECItem data; + + e_asn1_object_set_display_name (sequence, _("Certificate")); + + if (!process_version (&cert->priv->cert->version, &subitem)) + return FALSE; + e_asn1_object_append_child (sequence, subitem); + g_object_unref (subitem); + + if (!process_serial_number_der (&cert->priv->cert->serialNumber, &subitem)) + return FALSE; + e_asn1_object_append_child (sequence, subitem); + g_object_unref (subitem); + + + if (!process_sec_algorithm_id (&cert->priv->cert->signature, &subitem)) + return FALSE; + e_asn1_object_set_display_name (subitem, _("Certificate Signature Algorithm")); + e_asn1_object_append_child (sequence, subitem); + g_object_unref (subitem); + + process_name (&cert->priv->cert->issuer, &text); + subitem = e_asn1_object_new (); + e_asn1_object_set_display_value (subitem, text); + g_free (text); + + e_asn1_object_set_display_name (subitem, _("Issuer")); + e_asn1_object_append_child (sequence, subitem); + g_object_unref (subitem); + +#if notyet + nsCOMPtr<nsIASN1Sequence> validitySequence = new nsNSSASN1Sequence(); + nssComponent->GetPIPNSSBundleString(NS_LITERAL_STRING("CertDumpValidity").get(), + text); + validitySequence->SetDisplayName(text); + asn1Objects->AppendElement(validitySequence, PR_FALSE); + nssComponent->GetPIPNSSBundleString(NS_LITERAL_STRING("CertDumpNotBefore").get(), + text); + nsCOMPtr<nsIX509CertValidity> validityData; + GetValidity(getter_AddRefs(validityData)); + PRTime notBefore, notAfter; + + validityData->GetNotBefore(¬Before); + validityData->GetNotAfter(¬After); + validityData = 0; + rv = ProcessTime(notBefore, text.get(), validitySequence); + if (NS_FAILED(rv)) + return rv; + + nssComponent->GetPIPNSSBundleString(NS_LITERAL_STRING("CertDumpNotAfter").get(), + text); + rv = ProcessTime(notAfter, text.get(), validitySequence); + if (NS_FAILED(rv)) + return rv; +#endif + + subitem = e_asn1_object_new (); + e_asn1_object_set_display_name (subitem, _("Subject")); + + process_name (&cert->priv->cert->subject, &text); + e_asn1_object_set_display_value (subitem, text); + g_free (text); + e_asn1_object_append_child (sequence, subitem); + g_object_unref (subitem); + + if (!process_subject_public_key_info (&cert->priv->cert->subjectPublicKeyInfo, sequence)) + return FALSE; + + /* Is there an issuerUniqueID? */ + if (cert->priv->cert->issuerID.data) { + /* The issuerID is encoded as a bit string. + The function ProcessRawBytes expects the + length to be in bytes, so let's convert the + length in a temporary SECItem + */ + data.data = cert->priv->cert->issuerID.data; + data.len = cert->priv->cert->issuerID.len / 8; + + subitem = e_asn1_object_new (); + + e_asn1_object_set_display_name (subitem, _("Issuer Unique ID")); + process_raw_bytes (&data, &text); + e_asn1_object_set_display_value (subitem, text); + g_free (text); + + e_asn1_object_append_child (sequence, subitem); + } + + if (cert->priv->cert->subjectID.data) { + /* The subjectID is encoded as a bit string. + The function ProcessRawBytes expects the + length to be in bytes, so let's convert the + length in a temporary SECItem + */ + data.data = cert->priv->cert->issuerID.data; + data.len = cert->priv->cert->issuerID.len / 8; + + subitem = e_asn1_object_new (); + + e_asn1_object_set_display_name (subitem, _("Subject Unique ID")); + process_raw_bytes (&data, &text); + e_asn1_object_set_display_value (subitem, text); + g_free (text); + + e_asn1_object_append_child (sequence, subitem); + } + if (cert->priv->cert->extensions) { + if (!process_extensions (cert->priv->cert->extensions, sequence)) + return FALSE; + } + + *seq = sequence; + + return TRUE; +} + +static gboolean +create_asn1_struct (ECert *cert) +{ + EASN1Object *sequence; + SECItem temp; + char *text; + + cert->priv->asn1 = e_asn1_object_new (); + + if (cert->priv->cert->nickname) + e_asn1_object_set_display_name (cert->priv->asn1, cert->priv->cert->nickname); + else if (cert->priv->cn) + e_asn1_object_set_display_name (cert->priv->asn1, cert->priv->cn); + else + e_asn1_object_set_display_name (cert->priv->asn1, cert->priv->cert->subjectName); + + /* This sequence will be contain the tbsCertificate, signatureAlgorithm, + and signatureValue. */ + + if (!create_tbs_certificate_asn1_struct (cert, &sequence)) + return FALSE; + e_asn1_object_append_child (cert->priv->asn1, sequence); + g_object_unref (sequence); + + if (!process_sec_algorithm_id (&cert->priv->cert->signatureWrap.signatureAlgorithm, &sequence)) + return FALSE; + e_asn1_object_set_display_name (sequence, _("Certificate Signature Algorithm")); + e_asn1_object_append_child (cert->priv->asn1, sequence); + g_object_unref (sequence); + + sequence = e_asn1_object_new (); + e_asn1_object_set_display_name (sequence, _("Certificate Signature Value")); + + /* The signatureWrap is encoded as a bit string. + The function ProcessRawBytes expects the + length to be in bytes, so let's convert the + length in a temporary SECItem */ + temp.data = cert->priv->cert->signatureWrap.signature.data; + temp.len = cert->priv->cert->signatureWrap.signature.len / 8; + process_raw_bytes (&temp, &text); + e_asn1_object_set_display_value (sequence, text); + e_asn1_object_append_child (cert->priv->asn1, sequence); + g_free (text); + + return TRUE; +} + +EASN1Object* +e_cert_get_asn1_struct (ECert *cert) +{ + if (!cert->priv->asn1) + create_asn1_struct (cert); + + return g_object_ref (cert->priv->asn1); +} + gboolean e_cert_mark_for_deletion (ECert *cert) { diff --git a/smime/lib/e-cert.h b/smime/lib/e-cert.h index ac596b285b..d18e0a9486 100644 --- a/smime/lib/e-cert.h +++ b/smime/lib/e-cert.h @@ -25,6 +25,7 @@ #include <glib-object.h> #include <cert.h> +#include "e-asn1-object.h" #define E_TYPE_CERT (e_cert_get_type ()) #define E_CERT(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), E_TYPE_CERT, ECert)) @@ -91,7 +92,8 @@ 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); -GList* e_cert_get_chain (ECert *cert); +GList* e_cert_get_chain (ECert *cert); +EASN1Object* e_cert_get_asn1_struct (ECert *cert); gboolean e_cert_mark_for_deletion (ECert *cert); |