changeset 19017:e6558bae2bc6

- GnuTLS plugin now uses reference counting to manage its underlying X.509 certificate data
author William Ehlhardt <williamehlhardt@gmail.com>
date Fri, 13 Jul 2007 08:10:33 +0000
parents 597d3167c1cc
children d6f902265076
files libpurple/plugins/ssl/ssl-gnutls.c
diffstat 1 files changed, 57 insertions(+), 22 deletions(-) [+]
line wrap: on
line diff
--- a/libpurple/plugins/ssl/ssl-gnutls.c	Fri Jul 13 07:30:16 2007 +0000
+++ b/libpurple/plugins/ssl/ssl-gnutls.c	Fri Jul 13 08:10:33 2007 +0000
@@ -410,6 +410,42 @@
 
 static PurpleCertificateScheme x509_gnutls;
 
+/** Refcounted GnuTLS certificate data instance */
+typedef struct {
+	gint refcount;
+	gnutls_x509_crt_t crt;
+} x509_crtdata_t;
+
+/** Helper functions for reference counting */
+static x509_crtdata_t *
+x509_crtdata_addref(x509_crtdata_t *cd)
+{
+	(cd->refcount)++;
+	return cd;
+}
+
+static void
+x509_crtdata_delref(x509_crtdata_t *cd)
+{
+	g_assert(cd->refcount > 0);
+	
+	(cd->refcount)--;
+
+	/* If the refcount reaches zero, kill the structure */
+	if (cd->refcount == 0) {
+		purple_debug_info("gnutls/x509",
+				  "Freeing unused cert data at %p\n",
+				  cd);
+		/* Kill the internal data */
+		gnutls_x509_crt_deinit( cd->crt );
+		/* And kill the struct */
+		g_free( cd );
+	}
+}
+
+/** Helper macro to retrieve the GnuTLS crt_t from a PurpleCertificate */
+#define X509_GET_GNUTLS_DATA(pcrt) ( ((x509_crtdata_t *) (pcrt->data))->crt)
+
 /** Transforms a gnutls_datum_t containing an X.509 certificate into a Certificate instance under the x509_gnutls scheme
  *
  * @param dt   Datum to transform
@@ -423,22 +459,23 @@
 x509_import_from_datum(const gnutls_datum_t dt, gnutls_x509_crt_fmt_t mode)
 {
 	/* Internal certificate data structure */
-	gnutls_x509_crt_t *certdat;
+	x509_crtdata_t *certdat;
 	/* New certificate to return */
 	PurpleCertificate * crt;
 
 	/* Allocate and prepare the internal certificate data */
-	certdat = g_new0(gnutls_x509_crt_t, 1);
-	gnutls_x509_crt_init(certdat);
-
+	certdat = g_new0(x509_crtdata_t, 1);
+	gnutls_x509_crt_init(&(certdat->crt));
+	certdat->refcount = 0;
+	
 	/* Perform the actual certificate parse */
-	/* Yes, certdat SHOULD be dereferenced */
-	gnutls_x509_crt_import(*certdat, &dt, mode);
+	/* Yes, certdat->crt should be passed as-is */
+	gnutls_x509_crt_import(certdat->crt, &dt, mode);
 	
 	/* Allocate the certificate and load it with data */
 	crt = g_new0(PurpleCertificate, 1);
 	crt->scheme = &x509_gnutls;
-	crt->data = certdat;
+	crt->data = x509_crtdata_addref(certdat);
 
 	return crt;
 }
@@ -506,7 +543,7 @@
 	g_return_val_if_fail(crt->scheme == &x509_gnutls, FALSE);
 	g_return_val_if_fail(crt->data, FALSE);
 
-	crt_dat = *( (gnutls_x509_crt_t *) crt->data);
+	crt_dat = X509_GET_GNUTLS_DATA(crt);
 
 	/* Obtain the output size required */
 	out_size = 0;
@@ -571,12 +608,10 @@
 	g_return_if_fail(crt->data != NULL);
 	g_return_if_fail(crt->scheme != NULL);
 
-	/* Destroy the GnuTLS-specific data */
-	gnutls_x509_crt_deinit( *( (gnutls_x509_crt_t *) crt->data ) );
-	g_free(crt->data);
-
-	/* TODO: Reference counting here? */
-
+	/* Use the reference counting system to free (or not) the
+	   underlying data */
+	x509_crtdata_delref((x509_crtdata_t *)crt->data);
+	
 	/* Kill the structure itself */
 	g_free(crt);
 }
@@ -608,8 +643,8 @@
 
 	/* TODO: check for more nullness? */
 
-	crt_dat = *((gnutls_x509_crt_t *) crt->data);
-	issuer_dat = *((gnutls_x509_crt_t *) issuer->data);
+	crt_dat = X509_GET_GNUTLS_DATA(crt);
+	issuer_dat = X509_GET_GNUTLS_DATA(issuer);
 
 	/* First, let's check that crt.issuer is actually issuer */
 	ret = gnutls_x509_crt_check_issuer(crt_dat, issuer_dat);
@@ -659,7 +694,7 @@
 
 	g_return_val_if_fail(crt, NULL);
 
-	crt_dat = *( (gnutls_x509_crt_t *) crt->data );
+	crt_dat = X509_GET_GNUTLS_DATA(crt);
 
 	/* Extract the fingerprint */
 	/* TODO: Errorcheck? */
@@ -686,9 +721,9 @@
 	g_return_val_if_fail(crt, NULL);
 	g_return_val_if_fail(crt->scheme == &x509_gnutls, NULL);
 
-	cert_dat = *( (gnutls_x509_crt_t *) crt->data );
+	cert_dat = X509_GET_GNUTLS_DATA(crt);
 
-	/* TODO: Not return values? */
+	/* TODO: Note return values? */
 	
 	/* Figure out the length of the Common Name */
 	/* Claim that the buffer is size 0 so GnuTLS just tells us how much
@@ -720,7 +755,7 @@
 	g_return_val_if_fail(crt->scheme == &x509_gnutls, FALSE);
 	g_return_val_if_fail(name, FALSE);
 
-	crt_dat = *( (gnutls_x509_crt_t *) crt->data );
+	crt_dat = X509_GET_GNUTLS_DATA(crt);
 
 	if (gnutls_x509_crt_check_hostname(crt_dat, name)) {
 		return TRUE;
@@ -737,7 +772,7 @@
 	g_assert(crt);
 	g_assert(crt->scheme == &x509_gnutls);
 
-	crt_dat = *( (gnutls_x509_crt_t *) crt->data );
+	crt_dat = X509_GET_GNUTLS_DATA(crt);
 
 	/* TODO: Errorcheck this? */
 	return gnutls_x509_crt_get_activation_time(crt_dat);
@@ -751,7 +786,7 @@
 	g_assert(crt);
 	g_assert(crt->scheme == &x509_gnutls);
 
-	crt_dat = *( (gnutls_x509_crt_t *) crt->data );
+	crt_dat = X509_GET_GNUTLS_DATA(crt);
 
 	/* TODO: Errorcheck this? */
 	return gnutls_x509_crt_get_expiration_time(crt_dat);