cpw.ljfisher.ssl_client_auth: 5638b081: Add/update functions for storing and ret..

lucas.fisher at gmail.com lucas.fisher at gmail.com
Sat Nov 26 00:31:13 EST 2011


----------------------------------------------------------------------
Revision: 5638b0816f76baf11ef6b7947f549eea3f4a9d63
Parent:   ddbd181593de4c07803827a162542f6804bd05ac
Author:   lucas.fisher at gmail.com
Date:     11/26/11 00:28:47
Branch:   im.pidgin.cpw.ljfisher.ssl_client_auth
URL: http://d.pidgin.im/viewmtn/revision/info/5638b0816f76baf11ef6b7947f549eea3f4a9d63

Changelog: 

Add/update functions for storing and retrieving certificate chains via certificate pools.
Update gtkcertmgr and gnutls plugin to use new functions.

Changes against parent ddbd181593de4c07803827a162542f6804bd05ac

  patched  libpurple/certificate.c
  patched  libpurple/certificate.h
  patched  libpurple/plugins/ssl/ssl-gnutls.c
  patched  pidgin/gtkcertmgr.c

-------------- next part --------------
============================================================
--- libpurple/plugins/ssl/ssl-gnutls.c	1158d7567b13746d298ad56f7f70e4ea500112ac
+++ libpurple/plugins/ssl/ssl-gnutls.c	1c206f454b6f9a30a81925f7a6a5ab8cf8162050
@@ -2345,6 +2345,7 @@ ssl_gnutls_set_client_auth(gnutls_certif
 	GList *crts = NULL;
 	int numcrts = 0;
 	GList *item = NULL;
+	gchar *id = NULL;
 	int idx = 0;
 
 	g_return_val_if_fail(pcrt, FALSE);
@@ -2363,7 +2364,12 @@ ssl_gnutls_set_client_auth(gnutls_certif
 		client_auth_key = X509_GET_GNUTLS_KEYDATA(pkey);
 #endif
 
-		crts = purple_certificate_build_chain(user_pool, pcrt, NULL);
+		id = purple_certificate_get_unique_id(pcrt);
+		crts = purple_certificate_pool_retrieve_chain(user_pool, id, NULL);
+		if (NULL == crts) {
+			purple_debug_error("gnutls/ssl", "Failed to get cert chain %s for auth\n", id);
+			return FALSE;
+		}
 
 		numcrts = g_list_length(crts);
 		cert_list = g_new0(gnutls_x509_crt_t, numcrts);
============================================================
--- libpurple/certificate.c	a403631b58a637e98dd9ba217836ce9f2a638bef
+++ libpurple/certificate.c	bdad0852368c201807a3fb9f8cb5025499d40482
@@ -600,6 +600,8 @@ purple_certificate_pool_delete(PurpleCer
 purple_certificate_pool_delete(PurpleCertificatePool *pool, const gchar *id)
 {
 	gboolean ret = FALSE;
+	int issuer_idx = 1;
+	gchar *issuer_id = NULL;
 
 	g_return_val_if_fail(pool, FALSE);
 	g_return_val_if_fail(id, FALSE);
@@ -607,6 +609,16 @@ purple_certificate_pool_delete(PurpleCer
 
 	ret = (pool->delete_cert)(id);
 
+	/* delete certs from a possible cert chain */
+	issuer_id = g_strdup_printf("%s.%d", id, issuer_idx);
+	while ((pool->cert_in_pool)(issuer_id)) { 
+		(pool->delete_cert)(issuer_id);
+		/* No error if this fails since we don't know how many certs in chain */
+		g_free(issuer_id);
+		issuer_idx += 1;
+		issuer_id = g_strdup_printf("%s.%d", id, issuer_idx);
+	}
+
 	/* Signal that the certificate was deleted if success */
 	if (ret) {
 		purple_signal_emit(pool, "certificate-deleted",
@@ -638,49 +650,133 @@ purple_certificate_pool_destroy_idlist(G
 	g_list_free(idlist);
 }
 
+static gboolean
+is_valid_crt_chain(GList *crts)
+{
+	PurpleCertificate *crt = NULL;
+	PurpleCertificate *last_crt = NULL;
+	GList *item = NULL;
+	gchar *unique_id = NULL;
+	gchar *issuer_unique_id = NULL;
+	gboolean good = TRUE;
+
+
+	/* Check if certs are in the correct order */
+	item = g_list_first(crts);
+	last_crt = (PurpleCertificate*)item->data;
+	g_return_val_if_fail(NULL != last_crt, FALSE);
+	item = g_list_next(item);
+	while (NULL != item && good) {
+		crt = (PurpleCertificate*)item->data;
+		g_return_val_if_fail(NULL != crt, FALSE);
+
+		unique_id = purple_certificate_get_unique_id(crt);
+		issuer_unique_id = purple_certificate_get_issuer_unique_id(last_crt);
+
+		if (0 != g_strcmp0(unique_id, issuer_unique_id)) {
+			purple_debug_error("certificate", "Broken certificate chain: %s %s\n",
+				unique_id, issuer_unique_id);
+			good = FALSE;
+		}
+
+		g_free(unique_id);
+		g_free(issuer_unique_id);
+		last_crt = crt;
+		item = g_list_next(item);
+	}
+
+	return good;
+}
+
+gboolean
+purple_certificate_pool_store_chain(PurpleCertificatePool *pool, const gchar *id, GList *crts)
+{
+	PurpleCertificate *crt = NULL;
+	int issuer_idx = 1;
+	GList *item = NULL;
+	gchar *newid = NULL;
+
+	g_return_val_if_fail(NULL != pool, FALSE);
+	g_return_val_if_fail(NULL != id, FALSE);
+	g_return_val_if_fail(NULL != crts, FALSE);
+	g_return_val_if_fail(is_valid_crt_chain(crts), FALSE);
+
+	item = g_list_first(crts);
+	crt = (PurpleCertificate*)item->data;
+	g_return_val_if_fail(NULL != crt, FALSE);
+
+	if (!purple_certificate_pool_store(pool, id, crt)) {
+		return FALSE;
+	}
+
+	item = g_list_next(item);
+	while (NULL != item) {
+		crt = (PurpleCertificate*)item->data;
+		g_return_val_if_fail(NULL != crt, FALSE);
+
+		newid = g_strdup_printf("%s.%d", id, issuer_idx);
+		if (!purple_certificate_pool_store(pool, newid, crt)) {
+			purple_debug_error("certificate",
+				"Failed to store chain cert using id %s\n", newid);
+			g_free(newid);
+			/* TODO: delete any certs we added before error */
+			return FALSE;
+		}
+
+		issuer_idx += 1;
+		g_free(newid);
+		item = g_list_next(item);
+	}
+
+	return TRUE;	
+}
+
 GList*
-purple_certificate_build_chain(PurpleCertificatePool *pool, PurpleCertificate *crt, gboolean *complete)
+purple_certificate_pool_retrieve_chain(PurpleCertificatePool *pool, const gchar *id, gboolean *complete)
 {
+	PurpleCertificate *crt = NULL;
 	PurpleCertificate *issuer = NULL;
-	gchar *issuer_id = NULL;
-	gchar *crt_id = NULL;
 	GList *chain = NULL;
+	int issuer_idx = 1;
+	gboolean found_self_signed = FALSE;
 
 	g_return_val_if_fail(NULL != pool, NULL);
-	g_return_val_if_fail(NULL != crt, NULL);
+	g_return_val_if_fail(NULL != id, NULL);
 
 	if (NULL != complete)
 		*complete = FALSE;
 
+	crt = purple_certificate_pool_retrieve(pool, id);
+	if (NULL == crt)
+		return NULL;
+
 	chain = g_list_append(chain, crt);
 
-	crt_id = purple_certificate_get_unique_id(crt);
-	for (;;) {
-		issuer_id = purple_certificate_get_issuer_unique_id(crt);
-		if (NULL != crt_id && NULL != issuer_id) {
-			if (0 == g_strcmp0(crt_id, issuer_id)) {
-				/* found the last cert */
-				if (NULL != complete)
-					*complete = TRUE;
-				break;
+	issuer_idx = 1;
+	do {
+		gchar *issuer_id = NULL;
+		gchar *crt_unique_id = NULL;
+		gchar *issuer_unique_id = NULL;
+
+		issuer_id = g_strdup_printf("%s.%d", id, issuer_idx);
+		issuer = purple_certificate_pool_retrieve(pool, issuer_id);
+		if (NULL != issuer) {
+			chain = g_list_append(chain, issuer);
+			crt_unique_id = purple_certificate_get_unique_id(crt);
+			issuer_unique_id = purple_certificate_get_issuer_unique_id(crt);
+			if (0 == g_strcmp0(crt_unique_id, issuer_unique_id)) {
+				found_self_signed = TRUE;
 			}
-			issuer = purple_certificate_pool_retrieve(pool, issuer_id);
-			if (NULL != issuer) {
-				purple_debug_info("cert", "add %s to cert chain\n", issuer_id);
-				chain = g_list_append(chain, issuer);
-				crt = issuer;
-				crt_id = issuer_id;
-			}
-			else {
-				purple_debug_info("cert",
-					"issuer %s not found\n", issuer_id);
-				break;
-			}
 		}
-		else
-			break;
-	}
+		issuer_idx += 1;
+		g_free(issuer_id);
+		g_free(crt_unique_id);
+		g_free(issuer_unique_id);
+	} while (NULL != issuer && !found_self_signed);
 
+	if (NULL != complete)
+		*complete = found_self_signed;
+
 	return chain;
 }
 
============================================================
--- libpurple/certificate.h	66e876816e6e2706f19d083fb59c7fc1e42e7e2c
+++ libpurple/certificate.h	efda21c46ae5738ec427a6480b25505552303e9b
@@ -562,20 +562,6 @@ purple_certificate_get_times(PurpleCerti
 gboolean
 purple_certificate_get_times(PurpleCertificate *crt, time_t *activation, time_t *expiration);
 
-/**
- * Helper to build the certificate chain. Assumes the given pool stores certificates
- * using purple_certificate_get_unique_id() as the id.
- *
- * @param pool Pool to search for issuer certificates
- * @param crt End user certificate. 
- * @param complete TRUE if the complete certificate chain is returned.
- * @returns The certificate chain. The first certificate will be crt followed by
- *          its issuer's certificate, and so on until the root CA is found or
- *          no more issuer's certificate can be found. The chain is NOT
- *          guaranteed to be complete.
- */
-GList*
-purple_certificate_build_chain(PurpleCertificatePool *pool, PurpleCertificate *crt, gboolean *complete);
 /*@}*/
 
 /*****************************************************************************/
@@ -651,6 +637,51 @@ purple_certificate_pool_store(PurpleCert
 purple_certificate_pool_store(PurpleCertificatePool *pool, const gchar *id, PurpleCertificate *crt);
 
 /**
+ * Add a certificate chain to the pool.
+ * The certificate list must be ordered with the end
+ * user certificate first. Each additional certificate must be the issuer
+ * of the prior certificate in the list.
+ *
+ * Each certificate in the chain will be stored indvidually in the pool
+ * with an id dervied from the given id. While the end user certificate
+ * will be stored and may be retrieved individually using the id given
+ * here, the remaining certificates will have other ids and should
+ * no be retrieved via purple_certificate_pool_retrieve() as the ids
+ * used to store them could change in the future.
+ *
+ * @param pool   Pool to add the certificates to
+ * @param id     Id to store the chain under
+ * @param crts   List of PurpleCertificate. The first certificate must be the
+ *               end user certificate. Each following certificate should 
+ *               belong to the issuer of the prior certificate. The caller is
+ *               responsible for freeing crts.
+ * @return       TRUE if the certificate chain was successfully stored, otherwise FALSE
+ */
+gboolean
+purple_certificate_pool_store_chain(PurpleCertificatePool *pool, const gchar *id, GList *crts);
+
+/**
+ * Retrieve a certificate chain from the pool.
+ * The returned certificate list will be ordered with the end
+ * user certificate first. Each additional certificate will be the issuer
+ * of the prior certificate in the list. There is no guarantee the complete
+ * chain will be available and the @complete flag should be checked if this
+ * is necessary.
+ * 
+ * @param pool   Pool to retrieve the certificates to
+ * @param id     Id used to store the chain
+ * @param complete If not NULL set to TRUE if a complete certificate chain was
+ *                 found. A complete chain ends with a self-signed certificate.
+ * @return       List of PurpleCertificate. The first certificate will be the
+ *               end user certificate. Each following certificate will 
+ *               belong to the issuer of the prior certificate. Returns NULL
+ *               if the certificate chain cannot be found. The caller is
+ *               responsible for freeing the returned certificate list.
+ */
+GList*
+purple_certificate_pool_retrieve_chain(PurpleCertificatePool *pool, const gchar *id, gboolean *complete);
+
+/**
  * Remove a certificate from a pool
  *
  * @param pool   Pool to remove from
============================================================
--- pidgin/gtkcertmgr.c	a7d390346287ecda2e250e4c763db492c6a2b022
+++ pidgin/gtkcertmgr.c	1227ed798d68a5f71c60e446b73eb3a2a1a18b56
@@ -682,13 +682,13 @@ pkcs12_import_key_password_ok_cb(gboolea
 	gchar *id = NULL;
 	PurpleCertificate *crt;
 
-	for(i = g_list_first(data->crts); NULL != i; i = g_list_next(i)) {
-		crt = (PurpleCertificate*)i->data;
-		id = purple_certificate_get_unique_id(crt);
-		if (!purple_certificate_pool_store(um_dat->user_crts, id, crt)) {
-			purple_notify_error(um_dat, NULL, _("Failed to save imported certificate."), NULL);
-			/* TODO: deleted corresponding key stored in privatekey pool */
-		}
+	i = g_list_first(data->crts);
+	crt = (PurpleCertificate*)i->data;
+	id = purple_certificate_get_unique_id(crt);
+
+	if (!purple_certificate_pool_store_chain(um_dat->user_crts, id, data->crts)) {
+		purple_notify_error(um_dat, NULL, _("Failed to save imported certificates."), NULL);
+		purple_privatekey_pool_delete(um_dat->user_keys, id);
 	}
 
 	pkcs12_import_data_free(data);
@@ -1014,7 +1014,7 @@ user_mgmt_export_cb(GtkWidget *button, v
 		return;
 	}
 
-	chain = purple_certificate_build_chain(um_dat->user_crts, crt, NULL);
+	chain = purple_certificate_pool_retrieve_chain(um_dat->user_crts, crt, NULL);
 
 	purple_debug_info("gtkcertmgr/user_mgmt",
 		"Got chain of %d certs\n", g_list_length(chain));
@@ -1378,7 +1378,6 @@ pidgin_certmgr_show(void)
 	g_signal_connect(G_OBJECT(win), "delete_event",
 			 G_CALLBACK(certmgr_close_cb), dlg);
 
-
 	/* TODO: Retrieve the user-set window size and use it */
 	gtk_window_set_default_size(GTK_WINDOW(win), 400, 400);
 


More information about the Commits mailing list