/pidgin/main: 4879ef4db7cf: Move Tls Certificate parsing code in...

Mike Ruprecht cmaiku at gmail.com
Thu Apr 7 13:36:21 EDT 2016


Changeset: 4879ef4db7cf4e06134f1e43fb47b3bdf758722a
Author:	 Mike Ruprecht <cmaiku at gmail.com>
Date:	 2016-04-07 01:29 -0500
Branch:	 purple-ssl-to-gio
URL: https://hg.pidgin.im/pidgin/main/rev/4879ef4db7cf

Description:

Move Tls Certificate parsing code into tls-certificate-info.[ch]

This patch moves the TLS certificate parsing code into a new file,
separate from the TLS trust and storage code (tls-certificate.[ch])
upon Masca's suggestion.

diffstat:

 finch/gntcertmgr.c               |    3 +-
 finch/gntrequest.c               |    2 +-
 libpurple/Makefile.am            |    2 +
 libpurple/tls-certificate-info.c |  779 +++++++++++++++++++++++++++++++++++++++
 libpurple/tls-certificate-info.h |  105 +++++
 libpurple/tls-certificate.c      |  751 -------------------------------------
 libpurple/tls-certificate.h      |   64 ---
 pidgin/gtkcertmgr.c              |    3 +-
 pidgin/gtkrequest.c              |    2 +-
 9 files changed, 892 insertions(+), 819 deletions(-)

diffs (truncated from 1808 to 300 lines):

diff --git a/finch/gntcertmgr.c b/finch/gntcertmgr.c
--- a/finch/gntcertmgr.c
+++ b/finch/gntcertmgr.c
@@ -23,10 +23,11 @@
 #include <internal.h>
 #include "finch.h"
 
-#include "tls-certificate.h"
 #include "debug.h"
 #include "notify.h"
 #include "request.h"
+#include "tls-certificate.h"
+#include "tls-certificate-info.h"
 
 #include "gntcertmgr.h"
 
diff --git a/finch/gntrequest.c b/finch/gntrequest.c
--- a/finch/gntrequest.c
+++ b/finch/gntrequest.c
@@ -34,7 +34,7 @@
 #include "finch.h"
 #include "gntrequest.h"
 #include "debug.h"
-#include "tls-certificate.h"
+#include "tls-certificate-info.h"
 #include "util.h"
 
 typedef struct
diff --git a/libpurple/Makefile.am b/libpurple/Makefile.am
--- a/libpurple/Makefile.am
+++ b/libpurple/Makefile.am
@@ -115,6 +115,7 @@ purple_coresources = \
 	theme-loader.c \
 	theme-manager.c \
 	tls-certificate.c \
+	tls-certificate-info.c \
 	trie.c \
 	upnp.c \
 	util.c \
@@ -193,6 +194,7 @@ purple_coreheaders = \
 	theme-loader.h \
 	theme-manager.h \
 	tls-certificate.h \
+	tls-certificate-info.h \
 	trie.h \
 	upnp.h \
 	util.h \
diff --git a/libpurple/tls-certificate-info.c b/libpurple/tls-certificate-info.c
new file mode 100644
--- /dev/null
+++ b/libpurple/tls-certificate-info.c
@@ -0,0 +1,779 @@
+/*
+ *
+ * purple
+ *
+ * Purple is the legal property of its developers, whose names are too numerous
+ * to list here.  Please refer to the COPYRIGHT file distributed with this
+ * source distribution.
+ *
+ * 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., 51 Franklin Street, Fifth Floor, Boston, MA  02111-1301  USA
+ */
+
+#include "internal.h"
+#include "tls-certificate-info.h"
+#include "ciphers/sha1hash.h"
+#include "debug.h"
+#include "util.h"
+
+#define DER_TYPE_CLASS(type)		(type & 0xc0)
+
+#define DER_TYPE_CLASS_UNIVERSAL	0x00
+#define DER_TYPE_CLASS_APPLICATION	0x40
+#define DER_TYPE_CLASS_CONTEXT_SPECIFIC	0x80
+#define DER_TYPE_CLASS_PRIVATE		0xc0
+
+#define DER_TYPE_TAG(type) (type & 0x1f)
+
+#define DER_TYPE_IS_CONSTRUCTED(type) ((type & 0x20) ? TRUE : FALSE)
+
+#define DER_TYPE_TAG_IS_LONG_FORM(type) (DER_TYPE_TAG(type) == 0x1f)
+
+#define DER_LENGTH_IS_LONG_FORM(byte) ((byte & 0x80) ? TRUE : FALSE)
+#define DER_LENGTH_LONG_FORM_SIZE(byte) (byte & 0x7f)
+
+typedef struct {
+	guint8 type_class;
+	gboolean constructed;
+	guint type;
+	GBytes *content;
+	GSList *children;
+} DerNodeData;
+
+static void der_node_data_children_list_free(GSList *children);
+
+static void
+der_node_data_free(DerNodeData *node_data)
+{
+	g_return_if_fail(node_data != NULL);
+
+	g_clear_pointer(&node_data->content, g_bytes_unref);
+	g_clear_pointer(&node_data->children,
+			der_node_data_children_list_free);
+
+	g_free(node_data);
+}
+
+static void
+der_node_data_children_list_free(GSList *children)
+{
+	g_return_if_fail(children != NULL);
+
+	g_slist_free_full(children, (GDestroyNotify)der_node_data_free);
+}
+
+/* Parses DER encoded data into a GSList of DerNodeData instances */
+static GSList *
+der_parse(GBytes *data_bytes)
+{
+	const guint8 *data;
+	gsize size = 0;
+	gsize offset = 0;
+	GSList *nodes = NULL;
+	DerNodeData *node = NULL;
+
+	data = g_bytes_get_data(data_bytes, &size);
+
+	/* Parse data */
+	while (offset < size) {
+		guint8 byte;
+		gsize length;
+
+		/* Parse type */
+
+		byte = *(data + offset++);
+		node = g_new0(DerNodeData, 1);
+		node->type_class = DER_TYPE_CLASS(byte);
+		node->constructed = DER_TYPE_IS_CONSTRUCTED(byte);
+
+		if (DER_TYPE_TAG_IS_LONG_FORM(byte)) {
+			/* Long-form type encoding */
+			/* TODO: Handle long-form encoding.
+			 * Maiku: The certificates I tested didn't do this.
+			 */
+			g_return_val_if_reached(NULL);
+		} else {
+			/* Short-form type encoding */
+			node->type = DER_TYPE_TAG(byte);
+		}
+
+		/* Parse content length */
+
+		if (offset >= size) {
+			purple_debug_error("tls-certificate",
+					"Not enough remaining data when "
+					"parsing DER chunk length byte: "
+					"read (%" G_GSIZE_FORMAT ") "
+					"available: ""(%" G_GSIZE_FORMAT ")",
+					offset, size);
+			break;
+		}
+
+		byte = *(data + offset++);
+
+		if (DER_LENGTH_IS_LONG_FORM(byte)) {
+			/* Long-form length encoding */
+			guint num_len_bytes = DER_LENGTH_LONG_FORM_SIZE(byte);
+			guint i;
+
+			/* Guard against overflowing the integer */
+			if (num_len_bytes > sizeof(guint)) {
+				purple_debug_error("tls-certificate",
+						"Number of long-form length "
+						"bytes greater than guint "
+						"size: %u > %" G_GSIZE_FORMAT,
+						num_len_bytes, sizeof(guint));
+				break;
+			}
+
+			/* Guard against reading past the end of the buffer */
+			if (offset + num_len_bytes > size) {
+				purple_debug_error("tls-certificate",
+						"Not enough remaining data "
+						"when parsing DER chunk "
+						"long-form length bytes: "
+						"read (%" G_GSIZE_FORMAT ") "
+						"available: ""(%"
+						G_GSIZE_FORMAT ")",
+						offset, size);
+				break;
+			}
+
+			length = 0;
+
+			for (i = 0; i < num_len_bytes; ++i) {
+				length = length << 8;
+				length |= *(data + offset++);
+			}
+		} else {
+			/* Short-form length encoding */
+			length = byte;
+		}
+		
+		/* Parse content */
+
+		if (offset + length > size) {
+			purple_debug_error("tls-certificate",
+					"Not enough remaining data when "
+					"parsing DER chunk content: "
+					"content size (%" G_GSIZE_FORMAT ") "
+					"available: ""(%" G_GSIZE_FORMAT ")",
+					length, size - offset);
+			break;
+		}
+
+		node->content = g_bytes_new_from_bytes(data_bytes,
+				offset, length);
+		offset += length;
+
+		/* Maybe recurse */
+		if (node->constructed) {
+			node->children = der_parse(node->content);
+
+			if (node->children == NULL) {
+				/* No children on a constructed type
+				 * should an error. If this happens, it
+				 * outputs debug info inside der_parse().
+				 */
+				break;
+			}
+		}
+
+		nodes = g_slist_append(nodes, node);
+		node = NULL;
+	}
+
+	if (node != NULL) {
+		/* There was an error. Free parsing data. */
+		der_node_data_free(node);
+		g_clear_pointer(&nodes, der_node_data_children_list_free);
+		/* FIXME: Report error to calling function ala GError? */
+	}
+
+	return nodes;
+}
+
+static gchar *
+der_parse_string(DerNodeData *node)
+{
+	const gchar *str;
+	gsize length = 0;
+
+	g_return_val_if_fail(node != NULL, NULL);
+	g_return_val_if_fail(node->content != NULL, NULL);
+
+	str = g_bytes_get_data(node->content, &length);
+	return g_strndup(str, length);
+}
+
+typedef struct {
+	gchar *oid;
+	gchar *value;
+} DerOIDValue;
+
+static void
+der_oid_value_free(DerOIDValue *data)
+{
+	g_return_if_fail(data != NULL);
+
+	g_clear_pointer(&data->oid, g_free);
+	g_clear_pointer(&data->value, g_free);
+
+	g_free(data);
+}
+
+static void
+der_oid_value_slist_free(GSList *list)
+{
+	g_return_if_fail(list != NULL);
+
+	g_slist_free_full(list, (GDestroyNotify)der_oid_value_free);
+}
+
+static const gchar *
+der_oid_value_slist_get_value_by_oid(GSList *list, const gchar *oid)
+{
+	for (; list != NULL; list = g_slist_next(list)) {
+		DerOIDValue *value = list->data;



More information about the Commits mailing list