/*
* gnome-keyring
*
* Copyright (C) 2011 Collabora Ltd.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as
* published by the Free Software Foundation; either version 2.1 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this program; if not, see <http://www.gnu.org/licenses/>.
*
* Author: Stef Walter <stefw@collabora.co.uk>
*/
#include "config.h"
#include "gcr-certificate-extensions.h"
#include "gcr/gcr-oids.h"
#include "egg/egg-asn1x.h"
#include "egg/egg-asn1-defs.h"
#include "egg/egg-dn.h"
#include <glib/gi18n-lib.h>
GBytes *
_gcr_certificate_extension_find (GNode *cert,
GQuark oid,
gboolean *critical)
{
GNode *node;
gint index;
g_return_val_if_fail (cert != NULL, NULL);
/* Extensions */
for (index = 1; TRUE; ++index) {
node = egg_asn1x_node (cert, "tbsCertificate", "extensions", index, NULL);
if (node == NULL)
return NULL;
/* Dig out the OID */
if (egg_asn1x_get_oid_as_quark (egg_asn1x_node (node, "extnID", NULL)) == oid) {
if (critical) {
if (!egg_asn1x_get_boolean (egg_asn1x_node (node, "critical", NULL), critical))
g_return_val_if_reached (NULL);
}
/* Extension value */
return egg_asn1x_get_string_as_bytes (egg_asn1x_node (node, "extnValue", NULL));
}
}
g_assert_not_reached ();
}
gboolean
_gcr_certificate_extension_basic_constraints (GBytes *data,
gboolean *is_ca,
gint *path_len)
{
gboolean ret = TRUE;
GNode *asn = NULL;
GNode *node;
gulong value;
g_return_val_if_fail (data != NULL, FALSE);
asn = egg_asn1x_create_and_decode (pkix_asn1_tab, "BasicConstraints", data);
if (asn == NULL)
return FALSE;
if (path_len) {
node = egg_asn1x_node (asn, "pathLenConstraint", NULL);
if (!egg_asn1x_have (node))
*path_len = -1;
else if (!egg_asn1x_get_integer_as_ulong (node, &value))
ret = FALSE;
else
*path_len = value;
}
if (is_ca) {
node = egg_asn1x_node (asn, "cA", NULL);
if (!egg_asn1x_have (node))
*is_ca = FALSE;
else if (!egg_asn1x_get_boolean (node, is_ca))
ret = FALSE;
}
egg_asn1x_destroy (asn);
return ret;
}
GQuark *
_gcr_certificate_extension_extended_key_usage (GBytes *data)
{
GNode *asn = NULL;
GNode *node;
GArray *array;
GQuark oid;
int i;
g_return_val_if_fail (data != NULL, NULL);
asn = egg_asn1x_create_and_decode (pkix_asn1_tab, "ExtKeyUsageSyntax", data);
if (asn == NULL)
return NULL;
array = g_array_new (TRUE, TRUE, sizeof (GQuark));
for (i = 0; TRUE; ++i) {
node = egg_asn1x_node (asn, i + 1, NULL);
if (node == NULL)
break;
oid = egg_asn1x_get_oid_as_quark (node);
g_array_append_val (array, oid);
}
egg_asn1x_destroy (asn);
return (GQuark*)g_array_free (array, FALSE);
}
gpointer
_gcr_certificate_extension_subject_key_identifier (GBytes *data,
gsize *n_keyid)
{
GNode *asn = NULL;
gpointer result;
g_return_val_if_fail (data != NULL, NULL);
asn = egg_asn1x_create_and_decode (pkix_asn1_tab, "SubjectKeyIdentifier", data);
if (asn == NULL)
return NULL;
result = egg_asn1x_get_string_as_raw (asn, g_realloc, n_keyid);
egg_asn1x_destroy (asn);
return result;
}
gboolean
_gcr_certificate_extension_key_usage (GBytes *data,
gulong *key_usage)
{
GNode *asn = NULL;
gboolean ret = TRUE;
guint n_bits;
g_return_val_if_fail (data != NULL, FALSE);
asn = egg_asn1x_create_and_decode (pkix_asn1_tab, "KeyUsage", data);
if (asn == NULL)
return FALSE;
ret = egg_asn1x_get_bits_as_ulong (asn, key_usage, &n_bits);
egg_asn1x_destroy (asn);
return ret;
}
static void
general_name_parse_other (GNode *node, GcrGeneralName *general)
{
GNode *decode = NULL;
GQuark oid;
GNode *any;
general->type = GCR_GENERAL_NAME_OTHER;
general->description = _("Other Name");
general->display = NULL;
oid = egg_asn1x_get_oid_as_quark (egg_asn1x_node (node, "type-id", NULL));
any = egg_asn1x_node (node, "value", NULL);
if (any == NULL)
return;
if (oid == GCR_OID_ALT_NAME_XMPP_ADDR) {
general->description = _("XMPP Addr");
decode = egg_asn1x_get_any_as (any, pkix_asn1_tab, "UTF8String");
general->display = egg_asn1x_get_string_as_utf8 (decode, g_realloc);
} else if (oid == GCR_OID_ALT_NAME_DNS_SRV) {
general->description = _("DNS SRV");
decode = egg_asn1x_get_any_as (any, pkix_asn1_tab, "IA5String");
general->display = egg_asn1x_get_string_as_utf8 (decode, g_realloc);
}
egg_asn1x_destroy (decode);
}
static void
general_name_parse_rfc822 (GNode *node, GcrGeneralName *general)
{
general->type = GCR_GENERAL_NAME_RFC822;
general->description = _("Email");
general->display = egg_asn1x_get_string_as_utf8 (node, g_realloc);
}
static void
general_name_parse_dns (GNode *node, GcrGeneralName *general)
{
general->type = GCR_GENERAL_NAME_DNS;
general->description = _("DNS");
general->display = egg_asn1x_get_string_as_utf8 (node, g_realloc);
}
static void
general_name_parse_x400 (GNode *node, GcrGeneralName *general)
{
general->type = GCR_GENERAL_NAME_X400;
general->description = _("X400 Address");
}
static void
general_name_parse_dn (GNode *node, GcrGeneralName *general)
{
general->type = GCR_GENERAL_NAME_DNS;
general->description = _("Directory Name");
general->display = egg_dn_read (node);
}
static void
general_name_parse_edi (GNode *node, GcrGeneralName *general)
{
general->type = GCR_GENERAL_NAME_EDI;
general->description = _("EDI Party Name");
}
static void
general_name_parse_uri (GNode *node, GcrGeneralName *general)
{
general->type = GCR_GENERAL_NAME_URI;
general->description = _("URI");
general->display = egg_asn1x_get_string_as_utf8 (node, g_realloc);
}
static void
general_name_parse_ip (GNode *node, GcrGeneralName *general)
{
general->type = GCR_GENERAL_NAME_IP;
general->description = _("IP Address");
general->display = egg_asn1x_get_string_as_utf8 (node, g_realloc);
}
static void
general_name_parse_registered (GNode *node, GcrGeneralName *general)
{
general->type = GCR_GENERAL_NAME_REGISTERED_ID;
general->description = _("Registered ID");
general->display = egg_asn1x_get_oid_as_string (node);
}
GArray*
_gcr_certificate_extension_subject_alt_name (GBytes *data)
{
GNode *asn = NULL;
guint count, i;
const gchar *node_name;
GArray *names;
GcrGeneralName general;
GNode *choice;
asn = egg_asn1x_create_and_decode (pkix_asn1_tab, "SubjectAltName", data);
if (asn == NULL)
return NULL;
names = g_array_new (FALSE, TRUE, sizeof (GcrGeneralName));
count = egg_asn1x_count (asn);
for (i = 0; i < count; i++) {
choice = egg_asn1x_get_choice (egg_asn1x_node (asn, i + 1, NULL));
g_return_val_if_fail (choice, NULL);
node_name = egg_asn1x_name (choice);
g_return_val_if_fail (node_name, NULL);
memset (&general, 0, sizeof (general));
if (g_str_equal (node_name, "otherName"))
general_name_parse_other (choice, &general);
else if (g_str_equal (node_name, "rfc822Name"))
general_name_parse_rfc822 (choice, &general);
else if (g_str_equal (node_name, "dNSName"))
general_name_parse_dns (choice, &general);
else if (g_str_equal (node_name, "x400Address"))
general_name_parse_x400 (choice, &general);
else if (g_str_equal (node_name, "directoryName"))
general_name_parse_dn (choice, &general);
else if (g_str_equal (node_name, "ediPartyName"))
general_name_parse_edi (choice, &general);
else if (g_str_equal (node_name, "uniformResourceIdentifier"))
general_name_parse_uri (choice, &general);
else if (g_str_equal (node_name, "IPAddress"))
general_name_parse_ip (choice, &general);
else if (g_str_equal (node_name, "registeredID"))
general_name_parse_registered (choice, &general);
general.raw = egg_asn1x_get_element_raw (choice);
g_array_append_val (names, general);
}
egg_asn1x_destroy (asn);
return names;
}
void
_gcr_general_names_free (GArray *names)
{
GcrGeneralName *name;
guint i;
for (i = 0; names && i < names->len; i++) {
name = &g_array_index (names, GcrGeneralName, i);
g_free (name->display);
g_bytes_unref (name->raw);
}
g_array_free (names, TRUE);
}