Blame egg/egg-dn.c

Packit b00eeb
/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- */
Packit b00eeb
/* egg-asn1.c - ASN.1 helper routines
Packit b00eeb
Packit b00eeb
   Copyright (C) 2007 Stefan Walter
Packit b00eeb
Packit b00eeb
   The Gnome Keyring Library is free software; you can redistribute it and/or
Packit b00eeb
   modify it under the terms of the GNU Library General Public License as
Packit b00eeb
   published by the Free Software Foundation; either version 2 of the
Packit b00eeb
   License, or (at your option) any later version.
Packit b00eeb
Packit b00eeb
   The Gnome Keyring Library is distributed in the hope that it will be useful,
Packit b00eeb
   but WITHOUT ANY WARRANTY; without even the implied warranty of
Packit b00eeb
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
Packit b00eeb
   Library General Public License for more details.
Packit b00eeb
Packit b00eeb
   You should have received a copy of the GNU Library General Public
Packit b00eeb
   License along with the Gnome Library; see the file COPYING.LIB.  If not,
Packit b00eeb
   see <http://www.gnu.org/licenses/>.
Packit b00eeb
Packit b00eeb
   Author: Stef Walter <stef@memberwebs.com>
Packit b00eeb
*/
Packit b00eeb
Packit b00eeb
#include "config.h"
Packit b00eeb
Packit b00eeb
#include "egg-asn1-defs.h"
Packit b00eeb
#include "egg-asn1x.h"
Packit b00eeb
#include "egg-dn.h"
Packit b00eeb
#include "egg-oid.h"
Packit b00eeb
Packit b00eeb
#include <string.h>
Packit b00eeb
Packit b00eeb
static const char HEXC[] = "0123456789ABCDEF";
Packit b00eeb
Packit b00eeb
static gchar*
Packit b00eeb
dn_print_hex_value (GBytes *val)
Packit b00eeb
{
Packit b00eeb
	const gchar *data = g_bytes_get_data (val, NULL);
Packit b00eeb
	gsize size = g_bytes_get_size (val);
Packit b00eeb
	GString *result = g_string_sized_new (size * 2 + 1);
Packit b00eeb
	gsize i;
Packit b00eeb
Packit b00eeb
	g_string_append_c (result, '#');
Packit b00eeb
	for (i = 0; i < size; ++i) {
Packit b00eeb
		g_string_append_c (result, HEXC[data[i] >> 4 & 0xf]);
Packit b00eeb
		g_string_append_c (result, HEXC[data[i] & 0xf]);
Packit b00eeb
	}
Packit b00eeb
Packit b00eeb
	return g_string_free (result, FALSE);
Packit b00eeb
}
Packit b00eeb
Packit b00eeb
static gchar*
Packit b00eeb
dn_print_oid_value_parsed (GQuark oid,
Packit b00eeb
                           guint flags,
Packit b00eeb
                           GNode *val)
Packit b00eeb
{
Packit b00eeb
	GNode *asn1, *node;
Packit b00eeb
	GBytes *value;
Packit b00eeb
	const gchar *data;
Packit b00eeb
	gsize size;
Packit b00eeb
	gchar *result;
Packit b00eeb
Packit b00eeb
	g_assert (val != NULL);
Packit b00eeb
Packit b00eeb
	asn1 = egg_asn1x_create_quark (pkix_asn1_tab, oid);
Packit b00eeb
	g_return_val_if_fail (asn1, NULL);
Packit b00eeb
Packit b00eeb
	if (!egg_asn1x_get_any_into (val, asn1)) {
Packit b00eeb
		g_message ("couldn't decode value for OID: %s: %s",
Packit b00eeb
		           g_quark_to_string (oid), egg_asn1x_message (asn1));
Packit b00eeb
		egg_asn1x_destroy (asn1);
Packit b00eeb
		return NULL;
Packit b00eeb
	}
Packit b00eeb
Packit b00eeb
	/*
Packit b00eeb
	 * If it's a choice element, then we have to read depending
Packit b00eeb
	 * on what's there.
Packit b00eeb
	 */
Packit b00eeb
	if (flags & EGG_OID_IS_CHOICE)
Packit b00eeb
		node = egg_asn1x_get_choice (asn1);
Packit b00eeb
	else
Packit b00eeb
		node = asn1;
Packit b00eeb
Packit b00eeb
	value = egg_asn1x_get_value_raw (node);
Packit b00eeb
	data = g_bytes_get_data (value, &size);
Packit b00eeb
Packit b00eeb
	/*
Packit b00eeb
	 * Now we make sure it's UTF-8.
Packit b00eeb
	 */
Packit b00eeb
Packit b00eeb
	if (!value) {
Packit b00eeb
		g_message ("couldn't read value for OID: %s", g_quark_to_string (oid));
Packit b00eeb
		result = NULL;
Packit b00eeb
Packit b00eeb
	} else if (!g_utf8_validate (data, size, NULL)) {
Packit b00eeb
		result = dn_print_hex_value (value);
Packit b00eeb
Packit b00eeb
	} else {
Packit b00eeb
		result = g_strndup (data, size);
Packit b00eeb
	}
Packit b00eeb
Packit b00eeb
	g_bytes_unref (value);
Packit b00eeb
	egg_asn1x_destroy (asn1);
Packit b00eeb
Packit b00eeb
	return result;
Packit b00eeb
}
Packit b00eeb
Packit b00eeb
static gchar*
Packit b00eeb
dn_print_oid_value (GQuark oid,
Packit b00eeb
                    guint flags,
Packit b00eeb
                    GNode *val)
Packit b00eeb
{
Packit b00eeb
	GBytes *der;
Packit b00eeb
	gchar *value;
Packit b00eeb
Packit b00eeb
	g_assert (val != NULL);
Packit b00eeb
Packit b00eeb
	if (flags & EGG_OID_PRINTABLE) {
Packit b00eeb
		value = dn_print_oid_value_parsed (oid, flags, val);
Packit b00eeb
		if (value != NULL)
Packit b00eeb
			return value;
Packit b00eeb
	}
Packit b00eeb
Packit b00eeb
	der = egg_asn1x_get_element_raw (val);
Packit b00eeb
	value = dn_print_hex_value (der);
Packit b00eeb
	g_bytes_unref (der);
Packit b00eeb
Packit b00eeb
	return value;
Packit b00eeb
}
Packit b00eeb
Packit b00eeb
static gchar*
Packit b00eeb
dn_parse_rdn (GNode *asn)
Packit b00eeb
{
Packit b00eeb
	const gchar *name;
Packit b00eeb
	guint flags;
Packit b00eeb
	GQuark oid;
Packit b00eeb
	GNode *value;
Packit b00eeb
	gchar *display;
Packit b00eeb
	gchar *result;
Packit b00eeb
Packit b00eeb
	g_assert (asn);
Packit b00eeb
Packit b00eeb
	oid = egg_asn1x_get_oid_as_quark (egg_asn1x_node (asn, "type", NULL));
Packit b00eeb
	g_return_val_if_fail (oid, NULL);
Packit b00eeb
Packit b00eeb
	flags = egg_oid_get_flags (oid);
Packit b00eeb
	name = egg_oid_get_name (oid);
Packit b00eeb
Packit b00eeb
	value = egg_asn1x_node (asn, "value", NULL);
Packit b00eeb
	g_return_val_if_fail (value, NULL);
Packit b00eeb
Packit b00eeb
	display = dn_print_oid_value (oid, flags, value);
Packit b00eeb
	result = g_strconcat ((flags & EGG_OID_PRINTABLE) ? name : g_quark_to_string (oid),
Packit b00eeb
	                      "=", display, NULL);
Packit b00eeb
	g_free (display);
Packit b00eeb
Packit b00eeb
	return result;
Packit b00eeb
}
Packit b00eeb
Packit b00eeb
gchar*
Packit b00eeb
egg_dn_read (GNode* asn)
Packit b00eeb
{
Packit b00eeb
	gboolean done = FALSE;
Packit b00eeb
	GString *result;
Packit b00eeb
	GNode *node;
Packit b00eeb
	gchar *rdn;
Packit b00eeb
	gint i, j;
Packit b00eeb
Packit b00eeb
	g_return_val_if_fail (asn, NULL);
Packit b00eeb
Packit b00eeb
	result = g_string_sized_new (64);
Packit b00eeb
Packit b00eeb
	/* Each (possibly multi valued) RDN */
Packit b00eeb
	for (i = 1; !done; ++i) {
Packit b00eeb
Packit b00eeb
		/* Each type=value pair of an RDN */
Packit b00eeb
		for (j = 1; TRUE; ++j) {
Packit b00eeb
			node = egg_asn1x_node (asn, i, j, NULL);
Packit b00eeb
			if (!node) {
Packit b00eeb
				done = j == 1;
Packit b00eeb
				break;
Packit b00eeb
			}
Packit b00eeb
Packit b00eeb
			rdn = dn_parse_rdn (node);
Packit b00eeb
			g_return_val_if_fail (rdn, NULL);
Packit b00eeb
Packit b00eeb
			/* Account for multi valued RDNs */
Packit b00eeb
			if (j > 1)
Packit b00eeb
				g_string_append (result, "+");
Packit b00eeb
			else if (i > 1)
Packit b00eeb
				g_string_append (result, ", ");
Packit b00eeb
Packit b00eeb
			g_string_append (result, rdn);
Packit b00eeb
			g_free (rdn);
Packit b00eeb
		}
Packit b00eeb
	}
Packit b00eeb
Packit b00eeb
	/* Returns null when string is empty */
Packit b00eeb
	return g_string_free (result, (result->len == 0));
Packit b00eeb
}
Packit b00eeb
Packit b00eeb
gchar*
Packit b00eeb
egg_dn_read_part (GNode *asn, const gchar *match)
Packit b00eeb
{
Packit b00eeb
	gboolean done = FALSE;
Packit b00eeb
	const gchar *name;
Packit b00eeb
	GNode *node;
Packit b00eeb
	GQuark oid;
Packit b00eeb
	gint i, j;
Packit b00eeb
Packit b00eeb
	g_return_val_if_fail (asn, NULL);
Packit b00eeb
	g_return_val_if_fail (match, NULL);
Packit b00eeb
Packit b00eeb
	/* Each (possibly multi valued) RDN */
Packit b00eeb
	for (i = 1; !done; ++i) {
Packit b00eeb
Packit b00eeb
		/* Each type=value pair of an RDN */
Packit b00eeb
		for (j = 1; TRUE; ++j) {
Packit b00eeb
			node = egg_asn1x_node (asn, i, j, "type", NULL);
Packit b00eeb
			if (!node) {
Packit b00eeb
				done = j == 1;
Packit b00eeb
				break;
Packit b00eeb
			}
Packit b00eeb
Packit b00eeb
			oid = egg_asn1x_get_oid_as_quark (node);
Packit b00eeb
			g_return_val_if_fail (oid, NULL);
Packit b00eeb
Packit b00eeb
			/* Does it match either the OID or the displayable? */
Packit b00eeb
			if (g_ascii_strcasecmp (g_quark_to_string (oid), match) != 0) {
Packit b00eeb
				name = egg_oid_get_name (oid);
Packit b00eeb
				if (!g_ascii_strcasecmp (name, match) == 0)
Packit b00eeb
					continue;
Packit b00eeb
			}
Packit b00eeb
Packit b00eeb
			node = egg_asn1x_node (asn, i, j, "value", NULL);
Packit b00eeb
			g_return_val_if_fail (node, NULL);
Packit b00eeb
Packit b00eeb
			return dn_print_oid_value (oid, egg_oid_get_flags (oid), node);
Packit b00eeb
		}
Packit b00eeb
	}
Packit b00eeb
Packit b00eeb
	return NULL;
Packit b00eeb
}
Packit b00eeb
Packit b00eeb
gboolean
Packit b00eeb
egg_dn_parse (GNode *asn, EggDnCallback callback, gpointer user_data)
Packit b00eeb
{
Packit b00eeb
	gboolean done = FALSE;
Packit b00eeb
	GNode *node;
Packit b00eeb
	GQuark oid;
Packit b00eeb
	guint i, j;
Packit b00eeb
Packit b00eeb
	g_return_val_if_fail (asn, FALSE);
Packit b00eeb
Packit b00eeb
	/* Each (possibly multi valued) RDN */
Packit b00eeb
	for (i = 1; !done; ++i) {
Packit b00eeb
Packit b00eeb
		/* Each type=value pair of an RDN */
Packit b00eeb
		for (j = 1; TRUE; ++j) {
Packit b00eeb
Packit b00eeb
			/* Dig out the type */
Packit b00eeb
			node = egg_asn1x_node (asn, i, j, "type", NULL);
Packit b00eeb
			if (!node) {
Packit b00eeb
				done = j == 1;
Packit b00eeb
				break;
Packit b00eeb
			}
Packit b00eeb
Packit b00eeb
			oid = egg_asn1x_get_oid_as_quark (node);
Packit b00eeb
			g_return_val_if_fail (oid, FALSE);
Packit b00eeb
Packit b00eeb
			/* Dig out the value */
Packit b00eeb
			node = egg_asn1x_node (asn, i, j, "value", NULL);
Packit b00eeb
			if (!node) {
Packit b00eeb
				done = j == 1;
Packit b00eeb
				break;
Packit b00eeb
			}
Packit b00eeb
Packit b00eeb
			if (callback)
Packit b00eeb
				(callback) (i, oid, node, user_data);
Packit b00eeb
		}
Packit b00eeb
	}
Packit b00eeb
Packit b00eeb
	return i > 1;
Packit b00eeb
}
Packit b00eeb
Packit b00eeb
gchar *
Packit b00eeb
egg_dn_print_value (GQuark oid,
Packit b00eeb
                    GNode *value)
Packit b00eeb
{
Packit b00eeb
	g_return_val_if_fail (oid != 0, NULL);
Packit b00eeb
	g_return_val_if_fail (value != NULL, NULL);
Packit b00eeb
Packit b00eeb
	return dn_print_oid_value (oid, egg_oid_get_flags (oid), value);
Packit b00eeb
}
Packit b00eeb
Packit b00eeb
static gboolean
Packit b00eeb
is_ascii_string (const gchar *string)
Packit b00eeb
{
Packit b00eeb
	const gchar *p = string;
Packit b00eeb
Packit b00eeb
	g_return_val_if_fail (string != NULL, FALSE);
Packit b00eeb
Packit b00eeb
	for (p = string; *p != '\0'; p++) {
Packit b00eeb
		if (!g_ascii_isspace (*p) && *p < ' ')
Packit b00eeb
			return FALSE;
Packit b00eeb
	}
Packit b00eeb
Packit b00eeb
	return TRUE;
Packit b00eeb
}
Packit b00eeb
Packit b00eeb
static gboolean
Packit b00eeb
is_printable_string (const gchar *string)
Packit b00eeb
{
Packit b00eeb
	const gchar *p = string;
Packit b00eeb
Packit b00eeb
	g_return_val_if_fail (string != NULL, FALSE);
Packit b00eeb
Packit b00eeb
	for (p = string; *p != '\0'; p++) {
Packit b00eeb
		if (!g_ascii_isalnum (*p) && !strchr (" '()+,-./:=?", *p))
Packit b00eeb
			return FALSE;
Packit b00eeb
	}
Packit b00eeb
Packit b00eeb
	return TRUE;
Packit b00eeb
}
Packit b00eeb
Packit b00eeb
void
Packit b00eeb
egg_dn_add_string_part (GNode *asn,
Packit b00eeb
                        GQuark oid,
Packit b00eeb
                        const gchar *string)
Packit b00eeb
{
Packit b00eeb
	GNode *node;
Packit b00eeb
	GNode *value;
Packit b00eeb
	GNode *val;
Packit b00eeb
	guint flags;
Packit b00eeb
Packit b00eeb
	g_return_if_fail (asn != NULL);
Packit b00eeb
	g_return_if_fail (oid != 0);
Packit b00eeb
	g_return_if_fail (string != NULL);
Packit b00eeb
Packit b00eeb
	flags = egg_oid_get_flags (oid);
Packit b00eeb
	g_return_if_fail (flags & EGG_OID_PRINTABLE);
Packit b00eeb
Packit b00eeb
	/* Add the RelativeDistinguishedName */
Packit b00eeb
	node = egg_asn1x_append (asn);
Packit b00eeb
Packit b00eeb
	/* Add the AttributeTypeAndValue */
Packit b00eeb
	node = egg_asn1x_append (node);
Packit b00eeb
Packit b00eeb
	egg_asn1x_set_oid_as_quark (egg_asn1x_node (node, "type", NULL), oid);
Packit b00eeb
Packit b00eeb
	value = egg_asn1x_create_quark (pkix_asn1_tab, oid);
Packit b00eeb
Packit b00eeb
	if (egg_asn1x_type (value) == EGG_ASN1X_CHOICE) {
Packit b00eeb
		if (is_printable_string (string))
Packit b00eeb
			val = egg_asn1x_node (value, "printableString", NULL);
Packit b00eeb
		else if (is_ascii_string (string))
Packit b00eeb
			val = egg_asn1x_node (value, "ia5String", NULL);
Packit b00eeb
		else
Packit b00eeb
			val = egg_asn1x_node (value, "utf8String", NULL);
Packit b00eeb
		egg_asn1x_set_choice (value, val);
Packit b00eeb
	} else {
Packit b00eeb
		val = value;
Packit b00eeb
	}
Packit b00eeb
Packit b00eeb
	egg_asn1x_set_string_as_utf8 (val, g_strdup (string), g_free);
Packit b00eeb
Packit b00eeb
	egg_asn1x_set_any_from (egg_asn1x_node (node, "value", NULL), value);
Packit b00eeb
	egg_asn1x_destroy (value);
Packit b00eeb
}