summaryrefslogtreecommitdiff
path: root/gcr/gcr-parser.c
diff options
context:
space:
mode:
authorStef Walter <stefw@collabora.co.uk>2011-10-06 12:51:08 +0200
committerStef Walter <stefw@collabora.co.uk>2011-10-06 13:22:43 +0200
commitf28d3d768f0e4057f727e8c3b93ba50c19f5db24 (patch)
tree288ef581590dfccbfddcad3c87d98ce3a4ed0a97 /gcr/gcr-parser.c
parent626eef363c0ece57713c5be5939677784e8115cc (diff)
downloadgnome-keyring-split.tar.gz
Split Gcr and Gck libraries out of gnome-keyringsplit
Diffstat (limited to 'gcr/gcr-parser.c')
-rw-r--r--gcr/gcr-parser.c2934
1 files changed, 0 insertions, 2934 deletions
diff --git a/gcr/gcr-parser.c b/gcr/gcr-parser.c
deleted file mode 100644
index ffcdd091..00000000
--- a/gcr/gcr-parser.c
+++ /dev/null
@@ -1,2934 +0,0 @@
-/*
- * gnome-keyring
- *
- * Copyright (C) 2008 Stefan Walter
- *
- * 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, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
- * 02111-1307, USA.
- */
-
-#include "config.h"
-
-#include "gck/gck.h"
-
-#include "gcr-internal.h"
-#include "gcr-marshal.h"
-#include "gcr-oids.h"
-#include "gcr-openpgp.h"
-#include "gcr-openssh.h"
-#include "gcr-parser.h"
-#include "gcr-record.h"
-#include "gcr-types.h"
-
-#include "egg/egg-armor.h"
-#include "egg/egg-asn1x.h"
-#include "egg/egg-asn1-defs.h"
-#include "egg/egg-dn.h"
-#include "egg/egg-openssl.h"
-#include "egg/egg-secure-memory.h"
-#include "egg/egg-symkey.h"
-
-#include <glib/gi18n-lib.h>
-
-#include <stdlib.h>
-#include <gcrypt.h>
-
-/**
- * SECTION:gcr-parser
- * @title: GcrParser
- * @short_description: Parser for certificate and key files
- *
- * A #GcrParser can parse various certificate and key files such as OpenSSL
- * PEM files, DER encoded certifictes, PKCS\#8 keys and so on. Each various
- * format is identified by a value in the #GcrDataFormat enumeration.
- *
- * In order to parse data, a new parser is created with gcr_parser_new() and
- * then the GcrParser::authenticate and GcrParser::parsed signals should be
- * connected to. Data is then fed to the parser via gcr_parser_parse_data()
- * or gcr_parser_parse_stream().
- *
- * During the GcrParsed::parsed signal the attributes that make up the currently
- * parsed item can be retrieved using the gcr_parser_get_parsed_attributes()
- * function.
- */
-
-/**
- * GcrParser:
- *
- * A parser for parsing various types of files or data.
- */
-
-/**
- * GcrParsed:
- *
- * A parsed item parsed by a #GcrParser.
- */
-
-/**
- * GcrParserClass:
- * @parent_class: The parent class
- * @authenticate: The default handler for the authenticate signal.
- * @parsed: The default handler for the parsed signal.
- *
- * The class for #GcrParser
- */
-
-/**
- * GCR_DATA_ERROR:
- *
- * A domain for data errors with codes from #GcrDataError
- */
-
-/**
- * GcrDataError
- * @GCR_ERROR_FAILURE: Failed to parse or serialize the data
- * @GCR_ERROR_UNRECOGNIZED: The data was unrecognized or unsupported
- * @GCR_ERROR_CANCELLED: The operation was cancelled
- * @GCR_ERROR_LOCKED: The data was encrypted or locked and could not be unlocked.
- *
- * Values responding to error codes for parsing and serializing data.
- */
-
-enum {
- PROP_0,
- PROP_PARSED_LABEL,
- PROP_PARSED_ATTRIBUTES,
- PROP_PARSED_DESCRIPTION
-};
-
-enum {
- AUTHENTICATE,
- PARSED,
- LAST_SIGNAL
-};
-
-#define SUCCESS 0
-
-static guint signals[LAST_SIGNAL] = { 0 };
-
-struct _GcrParsed {
- gint refs;
- GckAttributes *attrs;
- const gchar *description;
- gchar *label;
- gpointer data;
- gsize n_data;
- gboolean sensitive;
- GDestroyNotify destroy_func;
- GcrDataFormat format;
- struct _GcrParsed *next;
-};
-
-struct _GcrParserPrivate {
- GTree *specific_formats;
- gboolean normal_formats;
- GPtrArray *passwords;
- GcrParsed *parsed;
-};
-
-G_DEFINE_TYPE (GcrParser, gcr_parser, G_TYPE_OBJECT);
-
-typedef struct {
- gint ask_state;
- gint seen;
-} PasswordState;
-
-#define PASSWORD_STATE_INIT { 0, 0 }
-
-typedef struct _ParserFormat {
- gint format_id;
- gint (*function) (GcrParser *self, const guchar *data, gsize n_data);
-} ParserFormat;
-
-/* Forward declarations */
-static const ParserFormat parser_normal[];
-static const ParserFormat parser_formats[];
-static ParserFormat* parser_format_lookup (gint format_id);
-
-EGG_SECURE_DECLARE (parser);
-
-/* -----------------------------------------------------------------------------
- * QUARK DEFINITIONS
- */
-
-/*
- * PEM STRINGS
- * The xxxxx in: ----- BEGIN xxxxx ------
- */
-
-static GQuark PEM_CERTIFICATE;
-static GQuark PEM_RSA_PRIVATE_KEY;
-static GQuark PEM_DSA_PRIVATE_KEY;
-static GQuark PEM_ANY_PRIVATE_KEY;
-static GQuark PEM_ENCRYPTED_PRIVATE_KEY;
-static GQuark PEM_PRIVATE_KEY;
-static GQuark PEM_PKCS7;
-static GQuark PEM_PKCS12;
-
-static GQuark ARMOR_PGP_PUBLIC_KEY_BLOCK;
-static GQuark ARMOR_PGP_PRIVATE_KEY_BLOCK;
-
-static void
-init_quarks (void)
-{
- static volatile gsize quarks_inited = 0;
-
- _gcr_oids_init ();
-
- if (g_once_init_enter (&quarks_inited)) {
-
- #define QUARK(name, value) \
- name = g_quark_from_static_string(value)
-
- QUARK (PEM_CERTIFICATE, "CERTIFICATE");
- QUARK (PEM_PRIVATE_KEY, "PRIVATE KEY");
- QUARK (PEM_RSA_PRIVATE_KEY, "RSA PRIVATE KEY");
- QUARK (PEM_DSA_PRIVATE_KEY, "DSA PRIVATE KEY");
- QUARK (PEM_ANY_PRIVATE_KEY, "ANY PRIVATE KEY");
- QUARK (PEM_ENCRYPTED_PRIVATE_KEY, "ENCRYPTED PRIVATE KEY");
- QUARK (PEM_PKCS7, "PKCS7");
- QUARK (PEM_PKCS12, "PKCS12");
- QUARK (ARMOR_PGP_PRIVATE_KEY_BLOCK, "PGP PRIVATE KEY BLOCK");
- QUARK (ARMOR_PGP_PUBLIC_KEY_BLOCK, "PGP PUBLIC KEY BLOCK");
-
- #undef QUARK
-
- g_once_init_leave (&quarks_inited, 1);
- }
-}
-
-/* -----------------------------------------------------------------------------
- * INTERNAL
- */
-
-static void
-parsed_attribute (GcrParsed *parsed,
- CK_ATTRIBUTE_TYPE type,
- gconstpointer data,
- gsize n_data)
-{
- g_assert (parsed != NULL);
- g_assert (parsed->attrs != NULL);
- gck_attributes_add_data (parsed->attrs, type, data, n_data);
-}
-
-static gboolean
-parsed_asn1_number (GcrParsed *parsed,
- GNode *asn,
- const gchar *part,
- CK_ATTRIBUTE_TYPE type)
-{
- const guchar *value;
- gsize n_value;
-
- g_assert (asn);
- g_assert (parsed);
-
- value = egg_asn1x_get_integer_as_usg (egg_asn1x_node (asn, part, NULL), &n_value);
- if (value == NULL)
- return FALSE;
-
- parsed_attribute (parsed, type, value, n_value);
- return TRUE;
-}
-
-static gboolean
-parsed_asn1_element (GcrParsed *parsed,
- GNode *asn,
- const gchar *part,
- CK_ATTRIBUTE_TYPE type)
-{
- const guchar *value;
- gsize n_value;
-
- g_assert (asn);
- g_assert (parsed);
-
- value = egg_asn1x_get_raw_element (egg_asn1x_node (asn, part, NULL), &n_value);
- if (value == NULL)
- return FALSE;
-
- parsed_attribute (parsed, type, value, n_value);
- return TRUE;
-}
-
-static void
-parsed_ulong_attribute (GcrParsed *parsed,
- CK_ATTRIBUTE_TYPE type,
- gulong value)
-{
- g_assert (parsed != NULL);
- g_assert (parsed->attrs != NULL);
- gck_attributes_add_ulong (parsed->attrs, type, value);
-}
-
-static void
-parsed_boolean_attribute (GcrParsed *parsed,
- CK_ATTRIBUTE_TYPE type,
- gboolean value)
-{
- g_assert (parsed != NULL);
- g_assert (parsed->attrs != NULL);
- gck_attributes_add_boolean (parsed->attrs, type, value);
-}
-
-
-static void
-parsing_block (GcrParsed *parsed,
- gint format,
- gconstpointer data,
- gsize n_data)
-{
- g_assert (parsed != NULL);
- g_assert (data != NULL);
- g_assert (n_data != 0);
- g_assert (format);
- g_assert (parsed->data == NULL);
- g_assert (parsed->destroy_func == NULL);
-
- parsed->format = format;
- parsed->data = (gpointer)data;
- parsed->n_data = n_data;
-}
-
-static void
-parsed_description (GcrParsed *parsed,
- CK_OBJECT_CLASS klass)
-{
- g_assert (parsed != NULL);
- switch (klass) {
- case CKO_PRIVATE_KEY:
- parsed->description = _("Private Key");
- break;
- case CKO_CERTIFICATE:
- parsed->description = _("Certificate");
- break;
- case CKO_PUBLIC_KEY:
- parsed->description = _("Public Key");
- break;
- case CKO_GCR_GNUPG_RECORDS:
- parsed->description = _("PGP Key");
- break;
- default:
- parsed->description = NULL;
- break;
- }
-}
-
-static void
-parsing_object (GcrParsed *parsed,
- CK_OBJECT_CLASS klass)
-{
- g_assert (parsed != NULL);
- g_assert (parsed->attrs == NULL);
-
- if (parsed->sensitive)
- parsed->attrs = gck_attributes_new_full ((GckAllocator)egg_secure_realloc);
- else
- parsed->attrs = gck_attributes_new ();
- gck_attributes_add_ulong (parsed->attrs, CKA_CLASS, klass);
- parsed_description (parsed, klass);
-}
-
-static void
-parsed_attributes (GcrParsed *parsed,
- GckAttributes *attrs)
-{
- gulong klass;
-
- g_assert (parsed != NULL);
- g_assert (attrs != NULL);
- g_assert (parsed->attrs == NULL);
-
- parsed->attrs = gck_attributes_ref (attrs);
- if (gck_attributes_find_ulong (attrs, CKA_CLASS, &klass))
- parsed_description (parsed, klass);
-}
-
-static void
-parsed_label (GcrParsed *parsed,
- const gchar *label)
-{
- g_assert (parsed != NULL);
- g_assert (parsed->label == NULL);
- parsed->label = g_strdup (label);
-}
-
-static GcrParsed *
-push_parsed (GcrParser *self,
- gboolean sensitive)
-{
- GcrParsed *parsed = g_new0 (GcrParsed, 1);
- parsed->refs = 0;
- parsed->sensitive = sensitive;
- parsed->next = self->pv->parsed;
- self->pv->parsed = parsed;
- return parsed;
-}
-
-static void
-pop_parsed (GcrParser *self,
- GcrParsed *parsed)
-{
- g_assert (parsed == self->pv->parsed);
-
- self->pv->parsed = parsed->next;
-
- gck_attributes_unref (parsed->attrs);
- g_free (parsed->label);
- g_free (parsed);
-}
-
-static gint
-enum_next_password (GcrParser *self, PasswordState *state, const gchar **password)
-{
- gboolean result;
-
- /*
- * Next passes we look through all the passwords that the parser
- * has seen so far. This is because different parts of a encrypted
- * container (such as PKCS#12) often use the same password even
- * if with different algorithms.
- *
- * If we didn't do this and the user chooses enters a password,
- * but doesn't save it, they would get prompted for the same thing
- * over and over, dumb.
- */
-
- /* Look in our list of passwords */
- if (state->seen < self->pv->passwords->len) {
- g_assert (state->seen >= 0);
- *password = g_ptr_array_index (self->pv->passwords, state->seen);
- ++state->seen;
- return SUCCESS;
- }
-
- /* Fire off all the parsed property signals so anyone watching can update their state */
- g_object_notify (G_OBJECT (self), "parsed-description");
- g_object_notify (G_OBJECT (self), "parsed-attributes");
- g_object_notify (G_OBJECT (self), "parsed-label");
-
- g_signal_emit (self, signals[AUTHENTICATE], 0, state->ask_state, &result);
- ++state->ask_state;
-
- if (!result)
- return GCR_ERROR_CANCELLED;
-
- /* Return any passwords added */
- if (state->seen < self->pv->passwords->len) {
- g_assert (state->seen >= 0);
- *password = g_ptr_array_index (self->pv->passwords, state->seen);
- ++state->seen;
- return SUCCESS;
- }
-
- return GCR_ERROR_LOCKED;
-}
-
-static void
-parsed_fire (GcrParser *self,
- GcrParsed *parsed)
-{
- g_assert (GCR_IS_PARSER (self));
- g_assert (parsed != NULL);
- g_assert (parsed == self->pv->parsed);
-
- g_object_notify (G_OBJECT (self), "parsed-description");
- g_object_notify (G_OBJECT (self), "parsed-attributes");
- g_object_notify (G_OBJECT (self), "parsed-label");
-
- g_signal_emit (self, signals[PARSED], 0);
-}
-
-/* -----------------------------------------------------------------------------
- * RSA PRIVATE KEY
- */
-
-static gint
-parse_der_private_key_rsa (GcrParser *self, const guchar *data, gsize n_data)
-{
- gint res = GCR_ERROR_UNRECOGNIZED;
- GNode *asn = NULL;
- gulong version;
- GcrParsed *parsed;
-
- parsed = push_parsed (self, TRUE);
-
- asn = egg_asn1x_create_and_decode (pk_asn1_tab, "RSAPrivateKey", data, n_data);
- if (!asn)
- goto done;
-
- parsing_block (parsed, GCR_FORMAT_DER_PRIVATE_KEY_RSA, data, n_data);
- parsing_object (parsed, CKO_PRIVATE_KEY);
- parsed_ulong_attribute (parsed, CKA_KEY_TYPE, CKK_RSA);
- parsed_boolean_attribute (parsed, CKA_PRIVATE, CK_TRUE);
- res = GCR_ERROR_FAILURE;
-
- if (!egg_asn1x_get_integer_as_ulong (egg_asn1x_node (asn, "version", NULL), &version))
- goto done;
-
- /* We only support simple version */
- if (version != 0) {
- res = GCR_ERROR_UNRECOGNIZED;
- g_message ("unsupported version of RSA key: %lu", version);
- goto done;
- }
-
- if (!parsed_asn1_number (parsed, asn, "modulus", CKA_MODULUS) ||
- !parsed_asn1_number (parsed, asn, "publicExponent", CKA_PUBLIC_EXPONENT) ||
- !parsed_asn1_number (parsed, asn, "privateExponent", CKA_PRIVATE_EXPONENT) ||
- !parsed_asn1_number (parsed, asn, "prime1", CKA_PRIME_1) ||
- !parsed_asn1_number (parsed, asn, "prime2", CKA_PRIME_2) ||
- !parsed_asn1_number (parsed, asn, "coefficient", CKA_COEFFICIENT))
- goto done;
-
- parsed_fire (self, parsed);
- res = SUCCESS;
-
-done:
- egg_asn1x_destroy (asn);
- if (res == GCR_ERROR_FAILURE)
- g_message ("invalid RSA key");
-
- pop_parsed (self, parsed);
- return res;
-}
-
-/* -----------------------------------------------------------------------------
- * DSA PRIVATE KEY
- */
-
-static gint
-parse_der_private_key_dsa (GcrParser *self, const guchar *data, gsize n_data)
-{
- gint ret = GCR_ERROR_UNRECOGNIZED;
- GNode *asn = NULL;
- GcrParsed *parsed;
-
- parsed = push_parsed (self, TRUE);
-
- asn = egg_asn1x_create_and_decode (pk_asn1_tab, "DSAPrivateKey", data, n_data);
- if (!asn)
- goto done;
-
- parsing_block (parsed, GCR_FORMAT_DER_PRIVATE_KEY_DSA, data, n_data);
- parsing_object (parsed, CKO_PRIVATE_KEY);
- parsed_ulong_attribute (parsed, CKA_KEY_TYPE, CKK_DSA);
- parsed_boolean_attribute (parsed, CKA_PRIVATE, CK_TRUE);
- ret = GCR_ERROR_FAILURE;
-
- if (!parsed_asn1_number (parsed, asn, "p", CKA_PRIME) ||
- !parsed_asn1_number (parsed, asn, "q", CKA_SUBPRIME) ||
- !parsed_asn1_number (parsed, asn, "g", CKA_BASE) ||
- !parsed_asn1_number (parsed, asn, "priv", CKA_VALUE))
- goto done;
-
- parsed_fire (self, parsed);
- ret = SUCCESS;
-
-done:
- egg_asn1x_destroy (asn);
- if (ret == GCR_ERROR_FAILURE)
- g_message ("invalid DSA key");
-
- pop_parsed (self, parsed);
- return ret;
-}
-
-static gint
-parse_der_private_key_dsa_parts (GcrParser *self, const guchar *keydata, gsize n_keydata,
- const guchar *params, gsize n_params)
-{
- gint ret = GCR_ERROR_UNRECOGNIZED;
- GNode *asn_params = NULL;
- GNode *asn_key = NULL;
- GcrParsed *parsed;
-
- parsed = push_parsed (self, TRUE);
-
- asn_params = egg_asn1x_create_and_decode (pk_asn1_tab, "DSAParameters", params, n_params);
- asn_key = egg_asn1x_create_and_decode (pk_asn1_tab, "DSAPrivatePart", keydata, n_keydata);
- if (!asn_params || !asn_key)
- goto done;
-
- parsing_object (parsed, CKO_PRIVATE_KEY);
- parsed_ulong_attribute (parsed, CKA_KEY_TYPE, CKK_DSA);
- parsed_boolean_attribute (parsed, CKA_PRIVATE, CK_TRUE);
- ret = GCR_ERROR_FAILURE;
-
- if (!parsed_asn1_number (parsed, asn_params, "p", CKA_PRIME) ||
- !parsed_asn1_number (parsed, asn_params, "q", CKA_SUBPRIME) ||
- !parsed_asn1_number (parsed, asn_params, "g", CKA_BASE) ||
- !parsed_asn1_number (parsed, asn_key, NULL, CKA_VALUE))
- goto done;
-
- parsed_fire (self, parsed);
- ret = SUCCESS;
-
-done:
- egg_asn1x_destroy (asn_key);
- egg_asn1x_destroy (asn_params);
- if (ret == GCR_ERROR_FAILURE)
- g_message ("invalid DSA key");
-
- pop_parsed (self, parsed);
- return ret;
-}
-
-/* -----------------------------------------------------------------------------
- * PRIVATE KEY
- */
-
-static gint
-parse_der_private_key (GcrParser *self, const guchar *data, gsize n_data)
-{
- gint res;
-
- res = parse_der_private_key_rsa (self, data, n_data);
- if (res == GCR_ERROR_UNRECOGNIZED)
- res = parse_der_private_key_dsa (self, data, n_data);
-
- return res;
-}
-
-/* -----------------------------------------------------------------------------
- * PKCS8
- */
-
-static gint
-parse_der_pkcs8_plain (GcrParser *self, const guchar *data, gsize n_data)
-{
- gint ret;
- CK_KEY_TYPE key_type;
- GQuark key_algo;
- const guchar *keydata;
- gsize n_keydata;
- const guchar *params;
- gsize n_params;
- GNode *asn = NULL;
- GcrParsed *parsed;
-
- parsed = push_parsed (self, TRUE);
- ret = GCR_ERROR_UNRECOGNIZED;
-
- asn = egg_asn1x_create_and_decode (pkix_asn1_tab, "pkcs-8-PrivateKeyInfo", data, n_data);
- if (!asn)
- goto done;
-
- parsing_block (parsed, GCR_FORMAT_DER_PKCS8_PLAIN, data, n_data);
- ret = GCR_ERROR_FAILURE;
- key_type = GCK_INVALID;
-
- key_algo = egg_asn1x_get_oid_as_quark (egg_asn1x_node (asn, "privateKeyAlgorithm", "algorithm", NULL));
- if (!key_algo)
- goto done;
- else if (key_algo == GCR_OID_PKIX1_RSA)
- key_type = CKK_RSA;
- else if (key_algo == GCR_OID_PKIX1_DSA)
- key_type = CKK_DSA;
-
- if (key_type == GCK_INVALID) {
- ret = GCR_ERROR_UNRECOGNIZED;
- goto done;
- }
-
- keydata = egg_asn1x_get_raw_value (egg_asn1x_node (asn, "privateKey", NULL), &n_keydata);
- if (!keydata)
- goto done;
-
- params = egg_asn1x_get_raw_element (egg_asn1x_node (asn, "privateKeyAlgorithm", "parameters", NULL), &n_params);
-
- ret = SUCCESS;
-
-done:
- if (ret == SUCCESS) {
- switch (key_type) {
- case CKK_RSA:
- ret = parse_der_private_key_rsa (self, keydata, n_keydata);
- break;
- case CKK_DSA:
- /* Try the normal sane format */
- ret = parse_der_private_key_dsa (self, keydata, n_keydata);
-
- /* Otherwise try the two part format that everyone seems to like */
- if (ret == GCR_ERROR_UNRECOGNIZED && params && n_params)
- ret = parse_der_private_key_dsa_parts (self, keydata, n_keydata,
- params, n_params);
- break;
- default:
- g_message ("invalid or unsupported key type in PKCS#8 key");
- ret = GCR_ERROR_UNRECOGNIZED;
- break;
- };
-
- } else if (ret == GCR_ERROR_FAILURE) {
- g_message ("invalid PKCS#8 key");
- }
-
- egg_asn1x_destroy (asn);
- pop_parsed (self, parsed);
- return ret;
-}
-
-static gint
-parse_der_pkcs8_encrypted (GcrParser *self, const guchar *data, gsize n_data)
-{
- PasswordState pstate = PASSWORD_STATE_INIT;
- GNode *asn = NULL;
- gcry_cipher_hd_t cih = NULL;
- gcry_error_t gcry;
- gint ret, r;
- GQuark scheme;
- guchar *crypted = NULL;
- const guchar *params;
- gsize n_crypted, n_params;
- const gchar *password;
- GcrParsed *parsed;
- gint l;
-
- parsed = push_parsed (self, FALSE);
- ret = GCR_ERROR_UNRECOGNIZED;
-
- asn = egg_asn1x_create_and_decode (pkix_asn1_tab, "pkcs-8-EncryptedPrivateKeyInfo", data, n_data);
- if (!asn)
- goto done;
-
- parsing_block (parsed, GCR_FORMAT_DER_PKCS8_ENCRYPTED, data, n_data);
- ret = GCR_ERROR_FAILURE;
-
- /* Figure out the type of encryption */
- scheme = egg_asn1x_get_oid_as_quark (egg_asn1x_node (asn, "encryptionAlgorithm", "algorithm", NULL));
- if (!scheme)
- goto done;
-
- params = egg_asn1x_get_raw_element (egg_asn1x_node (asn, "encryptionAlgorithm", "parameters", NULL), &n_params);
-
- /* Loop to try different passwords */
- for (;;) {
-
- g_assert (cih == NULL);
-
- r = enum_next_password (self, &pstate, &password);
- if (r != SUCCESS) {
- ret = r;
- break;
- }
-
- /* Parse the encryption stuff into a cipher. */
- if (!egg_symkey_read_cipher (scheme, password, -1, params, n_params, &cih))
- break;
-
- crypted = egg_asn1x_get_string_as_raw (egg_asn1x_node (asn, "encryptedData", NULL), egg_secure_realloc, &n_crypted);
- if (!crypted)
- break;
-
- gcry = gcry_cipher_decrypt (cih, crypted, n_crypted, NULL, 0);
- gcry_cipher_close (cih);
- cih = NULL;
-
- if (gcry != 0) {
- g_warning ("couldn't decrypt pkcs8 data: %s", gcry_strerror (gcry));
- break;
- }
-
- /* Unpad the DER data */
- l = egg_asn1x_element_length (crypted, n_crypted);
- if (l > 0)
- n_crypted = l;
-
- /* Try to parse the resulting key */
- r = parse_der_pkcs8_plain (self, crypted, n_crypted);
- egg_secure_free (crypted);
- crypted = NULL;
-
- if (r != GCR_ERROR_UNRECOGNIZED) {
- ret = r;
- break;
- }
-
- /* We assume unrecognized data, is a bad encryption key */
- }
-
-done:
- if (cih)
- gcry_cipher_close (cih);
- egg_asn1x_destroy (asn);
- egg_secure_free (crypted);
-
- pop_parsed (self, parsed);
- return ret;
-}
-
-static gint
-parse_der_pkcs8 (GcrParser *self, const guchar *data, gsize n_data)
-{
- gint ret;
-
- ret = parse_der_pkcs8_plain (self, data, n_data);
- if (ret == GCR_ERROR_UNRECOGNIZED)
- ret = parse_der_pkcs8_encrypted (self, data, n_data);
-
- return ret;
-}
-
-/* -----------------------------------------------------------------------------
- * CERTIFICATE
- */
-
-static gint
-parse_der_certificate (GcrParser *self, const guchar *data, gsize n_data)
-{
- gchar *name = NULL;
- GcrParsed *parsed;
- GNode *node;
- GNode *asn;
-
- asn = egg_asn1x_create_and_decode (pkix_asn1_tab, "Certificate", data, n_data);
- if (asn == NULL)
- return GCR_ERROR_UNRECOGNIZED;
-
- parsed = push_parsed (self, FALSE);
-
- parsing_block (parsed, GCR_FORMAT_DER_CERTIFICATE_X509, data, n_data);
- parsing_object (parsed, CKO_CERTIFICATE);
- parsed_ulong_attribute (parsed, CKA_CERTIFICATE_TYPE, CKC_X_509);
-
- node = egg_asn1x_node (asn, "tbsCertificate", NULL);
- g_return_val_if_fail (node != NULL, GCR_ERROR_FAILURE);
-
- if (gcr_parser_get_parsed_label (self) == NULL)
- name = egg_dn_read_part (egg_asn1x_node (node, "subject", "rdnSequence", NULL), "CN");
-
- if (name != NULL) {
- parsed_label (parsed, name);
- g_free (name);
- }
-
- parsed_attribute (parsed, CKA_VALUE, data, n_data);
- parsed_asn1_element (parsed, node, "subject", CKA_SUBJECT);
- parsed_asn1_element (parsed, node, "issuer", CKA_ISSUER);
- parsed_asn1_number (parsed, node, "serialNumber", CKA_SERIAL_NUMBER);
- parsed_fire (self, parsed);
-
- egg_asn1x_destroy (asn);
-
- pop_parsed (self, parsed);
- return SUCCESS;
-}
-
-/* -----------------------------------------------------------------------------
- * PKCS7
- */
-
-static gint
-handle_pkcs7_signed_data (GcrParser *self, const guchar *data, gsize n_data)
-{
- GNode *asn = NULL;
- GNode *node;
- gint ret;
- const guchar *certificate;
- gsize n_certificate;
- int i;
-
- ret = GCR_ERROR_UNRECOGNIZED;
-
- asn = egg_asn1x_create_and_decode (pkix_asn1_tab, "pkcs-7-SignedData", data, n_data);
- if (!asn)
- goto done;
-
- ret = GCR_ERROR_FAILURE;
-
- for (i = 0; TRUE; ++i) {
-
- node = egg_asn1x_node (asn, "certificates", i + 1, NULL);
-
- /* No more certificates? */
- if (node == NULL)
- break;
-
- certificate = egg_asn1x_get_raw_element (node, &n_certificate);
-
- ret = parse_der_certificate (self, certificate, n_certificate);
- if (ret != SUCCESS)
- goto done;
- }
-
- /* TODO: Parse out all the CRLs */
-
- ret = SUCCESS;
-
-done:
- egg_asn1x_destroy (asn);
- return ret;
-}
-
-static gint
-parse_der_pkcs7 (GcrParser *self, const guchar *data, gsize n_data)
-{
- GNode *asn = NULL;
- GNode *node;
- gint ret;
- const guchar* content = NULL;
- gsize n_content;
- GQuark oid;
- GcrParsed *parsed;
-
- parsed = push_parsed (self, FALSE);
- ret = GCR_ERROR_UNRECOGNIZED;
-
- asn = egg_asn1x_create_and_decode (pkix_asn1_tab, "pkcs-7-ContentInfo", data, n_data);
- if (!asn)
- goto done;
-
- parsing_block (parsed, GCR_FORMAT_DER_PKCS7, data, n_data);
- ret = GCR_ERROR_FAILURE;
-
- node = egg_asn1x_node (asn, "contentType", NULL);
- if (!node)
- goto done;
-
- oid = egg_asn1x_get_oid_as_quark (node);
- g_return_val_if_fail (oid, GCR_ERROR_FAILURE);
-
- /* Outer most one must just be plain data */
- if (oid != GCR_OID_PKCS7_SIGNED_DATA) {
- g_message ("unsupported outer content type in pkcs7: %s", g_quark_to_string (oid));
- goto done;
- }
-
- content = egg_asn1x_get_raw_element (egg_asn1x_node (asn, "content", NULL), &n_content);
- if (!content)
- goto done;
-
- ret = handle_pkcs7_signed_data (self, content, n_content);
-
-done:
- egg_asn1x_destroy (asn);
- pop_parsed (self, parsed);
- return ret;
-}
-
-/* -----------------------------------------------------------------------------
- * PKCS12
- */
-
-static GNode *
-decode_pkcs12_asn1_accepting_invalid_crap (const ASN1_ARRAY_TYPE *defs,
- const gchar *identifier,
- gconstpointer data,
- gsize n_data)
-{
- GNode *asn;
-
- /*
- * Because PKCS#12 files, the bags specifically, are notorious for
- * being crappily constructed and are often break rules such as DER
- * sorting order etc.. we parse the DER in a non-strict fashion.
- *
- * The rules in DER are designed for X.509 certificates, so there is
- * only one way to represent a given certificate (although they fail
- * at that as well). But with PKCS#12 we don't have such high
- * requirements, and we can slack off on our validation.
- */
-
- asn = egg_asn1x_create (defs, identifier);
- g_return_val_if_fail (asn != NULL, NULL);
-
- /* Passing FALSE as the strictness argument */
- if (!egg_asn1x_decode_no_validate (asn, data, n_data) ||
- !egg_asn1x_validate (asn, FALSE)) {
- egg_asn1x_destroy (asn);
- asn = NULL;
- }
-
- return asn;
-}
-
-static gint
-handle_pkcs12_cert_bag (GcrParser *self, const guchar *data, gsize n_data)
-{
- GNode *asn = NULL;
- GNode *asn_content = NULL;
- guchar *certificate = NULL;
- const guchar *element;
- gsize n_certificate, n_element;
- gint ret;
-
- ret = GCR_ERROR_UNRECOGNIZED;
- asn = decode_pkcs12_asn1_accepting_invalid_crap (pkix_asn1_tab,
- "pkcs-12-CertBag",
- data, n_data);
- if (!asn)
- goto done;
-
- ret = GCR_ERROR_FAILURE;
-
- element = egg_asn1x_get_raw_element (egg_asn1x_node (asn, "certValue", NULL), &n_element);
- if (!element)
- goto done;
-
- asn_content = egg_asn1x_create_and_decode (pkix_asn1_tab, "pkcs-7-Data", element, n_element);
- if (!asn_content)
- goto done;
-
- certificate = egg_asn1x_get_string_as_raw (asn_content, NULL, &n_certificate);
- if (!certificate)
- goto done;
-
- ret = parse_der_certificate (self, certificate, n_certificate);
-
-done:
- egg_asn1x_destroy (asn_content);
- egg_asn1x_destroy (asn);
- g_free (certificate);
- return ret;
-}
-
-static gchar *
-parse_pkcs12_bag_friendly_name (GNode *asn)
-{
- guint count, i;
- GQuark oid;
- GNode *node;
- gconstpointer element;
- gsize n_element;
- GNode *asn_str;
- gchar *result;
-
- if (asn == NULL)
- return NULL;
-
- count = egg_asn1x_count (asn);
- for (i = 1; i <= count; i++) {
- oid = egg_asn1x_get_oid_as_quark (egg_asn1x_node (asn, i, "type", NULL));
- if (oid == GCR_OID_PKCS9_ATTRIBUTE_FRIENDLY) {
- node = egg_asn1x_node (asn, i, "values", 1, NULL);
- if (node != NULL) {
- element = egg_asn1x_get_raw_element (node, &n_element);
- asn_str = egg_asn1x_create_and_decode (pkix_asn1_tab, "BMPString",
- element, n_element);
- if (asn_str) {
- result = egg_asn1x_get_bmpstring_as_utf8 (asn_str);
- egg_asn1x_destroy (asn_str);
- return result;
- }
- }
- }
- }
-
- return NULL;
-}
-
-static gint
-handle_pkcs12_bag (GcrParser *self, const guchar *data, gsize n_data)
-{
- GNode *asn = NULL;
- gint ret, r;
- guint count = 0;
- GQuark oid;
- const guchar *element;
- gchar *friendly;
- gsize n_element;
- guint i;
- GcrParsed *parsed;
-
- ret = GCR_ERROR_UNRECOGNIZED;
-
- asn = decode_pkcs12_asn1_accepting_invalid_crap (pkix_asn1_tab,
- "pkcs-12-SafeContents",
- data, n_data);
- if (!asn)
- goto done;
-
- ret = GCR_ERROR_FAILURE;
-
- /* Get the number of elements in this bag */
- count = egg_asn1x_count (asn);
-
- /*
- * Now inside each bag are multiple elements. Who comes up
- * with this stuff?
- */
- for (i = 1; i <= count; i++) {
-
- oid = egg_asn1x_get_oid_as_quark (egg_asn1x_node (asn, i, "bagId", NULL));
- if (!oid)
- goto done;
-
- element = egg_asn1x_get_raw_element (egg_asn1x_node (asn, i, "bagValue", NULL), &n_element);
- if (!element)
- goto done;
-
- parsed = push_parsed (self, FALSE);
-
- friendly = parse_pkcs12_bag_friendly_name (egg_asn1x_node (asn, i, "bagAttributes", NULL));
- if (friendly != NULL)
- parsed_label (parsed, friendly);
-
- /* A normal unencrypted key */
- if (oid == GCR_OID_PKCS12_BAG_PKCS8_KEY) {
- r = parse_der_pkcs8_plain (self, element, n_element);
-
- /* A properly encrypted key */
- } else if (oid == GCR_OID_PKCS12_BAG_PKCS8_ENCRYPTED_KEY) {
- r = parse_der_pkcs8_encrypted (self, element, n_element);
-
- /* A certificate */
- } else if (oid == GCR_OID_PKCS12_BAG_CERTIFICATE) {
- r = handle_pkcs12_cert_bag (self, element, n_element);
-
- /* TODO: GCR_OID_PKCS12_BAG_CRL */
- } else {
- r = GCR_ERROR_UNRECOGNIZED;
- }
-
- pop_parsed (self, parsed);
-
- if (r == GCR_ERROR_FAILURE ||
- r == GCR_ERROR_CANCELLED ||
- r == GCR_ERROR_LOCKED) {
- ret = r;
- goto done;
- }
- }
-
- ret = SUCCESS;
-
-done:
- egg_asn1x_destroy (asn);
- return ret;
-}
-
-static gint
-handle_pkcs12_encrypted_bag (GcrParser *self, const guchar *data, gsize n_data)
-{
- PasswordState pstate = PASSWORD_STATE_INIT;
- GNode *asn = NULL;
- gcry_cipher_hd_t cih = NULL;
- gcry_error_t gcry;
- guchar *crypted = NULL;
- const guchar *params;
- gsize n_params, n_crypted;
- const gchar *password;
- GQuark scheme;
- gint ret, r;
- gint l;
-
- ret = GCR_ERROR_UNRECOGNIZED;
-
- asn = decode_pkcs12_asn1_accepting_invalid_crap (pkix_asn1_tab,
- "pkcs-7-EncryptedData",
- data, n_data);
- if (!asn)
- goto done;
-
- ret = GCR_ERROR_FAILURE;
-
- /* Check the encryption schema OID */
- scheme = egg_asn1x_get_oid_as_quark (egg_asn1x_node (asn, "encryptedContentInfo", "contentEncryptionAlgorithm", "algorithm", NULL));
- if (!scheme)
- goto done;
-
- params = egg_asn1x_get_raw_element (egg_asn1x_node (asn, "encryptedContentInfo", "contentEncryptionAlgorithm", "parameters", NULL), &n_params);
- if (!params)
- goto done;
-
- /* Loop to try different passwords */
- for (;;) {
-
- g_assert (cih == NULL);
-
- r = enum_next_password (self, &pstate, &password);
- if (r != SUCCESS) {
- ret = r;
- goto done;
- }
-
- /* Parse the encryption stuff into a cipher. */
- if (!egg_symkey_read_cipher (scheme, password, -1, params, n_params, &cih)) {
- ret = GCR_ERROR_FAILURE;
- goto done;
- }
-
- crypted = egg_asn1x_get_string_as_raw (egg_asn1x_node (asn, "encryptedContentInfo", "encryptedContent", NULL),
- egg_secure_realloc, &n_crypted);
- if (!crypted)
- goto done;
-
- gcry = gcry_cipher_decrypt (cih, crypted, n_crypted, NULL, 0);
- gcry_cipher_close (cih);
- cih = NULL;
-
- if (gcry != 0) {
- g_warning ("couldn't decrypt pkcs7 data: %s", gcry_strerror (gcry));
- goto done;
- }
-
- /* Unpad the DER data */
- l = egg_asn1x_element_length (crypted, n_crypted);
- if (l > 0)
- n_crypted = l;
-
- /* Try to parse the resulting key */
- r = handle_pkcs12_bag (self, crypted, n_crypted);
- egg_secure_free (crypted);
- crypted = NULL;
-
- if (r != GCR_ERROR_UNRECOGNIZED) {
- ret = r;
- break;
- }
-
- /* We assume unrecognized data is a bad encryption key */
- }
-
-done:
- if (cih)
- gcry_cipher_close (cih);
- egg_asn1x_destroy (asn);
- egg_secure_free (crypted);
- return ret;
-}
-
-static gint
-handle_pkcs12_safe (GcrParser *self, const guchar *data, gsize n_data)
-{
- GNode *asn = NULL;
- GNode *asn_content = NULL;
- gint ret, r;
- const guchar *bag;
- guchar *content = NULL;
- gsize n_bag, n_content;
- GQuark oid;
- guint i;
- GNode *node;
-
- ret = GCR_ERROR_UNRECOGNIZED;
-
- asn = decode_pkcs12_asn1_accepting_invalid_crap (pkix_asn1_tab,
- "pkcs-12-AuthenticatedSafe",
- data, n_data);
- if (!asn)
- goto done;
-
- ret = GCR_ERROR_FAILURE;
-
- /*
- * Inside each PKCS12 safe there are multiple bags.
- */
- for (i = 0; TRUE; ++i) {
- node = egg_asn1x_node (asn, i + 1, "contentType", NULL);
-
- /* All done? no more bags */
- if (!node)
- break;
-
- oid = egg_asn1x_get_oid_as_quark (node);
-
- node = egg_asn1x_node (asn, i + 1, "content", NULL);
- if (!node)
- goto done;
-
- bag = egg_asn1x_get_raw_element (node, &n_bag);
- g_return_val_if_fail (bag, ret);
-
- /* A non encrypted bag, just parse */
- if (oid == GCR_OID_PKCS7_DATA) {
-
- egg_asn1x_destroy (asn_content);
- asn_content = decode_pkcs12_asn1_accepting_invalid_crap (pkix_asn1_tab,
- "pkcs-7-Data",
- bag, n_bag);
- if (!asn_content)
- goto done;
-
- g_free (content);
- content = egg_asn1x_get_string_as_raw (asn_content, NULL, &n_content);
- if (!content)
- goto done;
-
- r = handle_pkcs12_bag (self, content, n_content);
-
- /* Encrypted data first needs decryption */
- } else if (oid == GCR_OID_PKCS7_ENCRYPTED_DATA) {
- r = handle_pkcs12_encrypted_bag (self, bag, n_bag);
-
- /* Hmmmm, not sure what this is */
- } else {
- g_warning ("unrecognized type of safe content in pkcs12: %s", g_quark_to_string (oid));
- r = GCR_ERROR_UNRECOGNIZED;
- }
-
- if (r == GCR_ERROR_FAILURE ||
- r == GCR_ERROR_CANCELLED ||
- r == GCR_ERROR_LOCKED) {
- ret = r;
- goto done;
- }
- }
-
- ret = SUCCESS;
-
-done:
- egg_asn1x_destroy (asn);
- egg_asn1x_destroy (asn_content);
- g_free (content);
- return ret;
-}
-
-static gint
-verify_pkcs12_safe (GcrParser *self,
- GNode *asn,
- gconstpointer content,
- gsize n_content)
-{
- PasswordState pstate = PASSWORD_STATE_INIT;
- const gchar *password;
- gcry_md_hd_t mdh = NULL;
- const guchar *mac_digest;
- gsize mac_len;
- guchar *digest = NULL;
- gsize n_digest;
- GQuark algorithm;
- GNode *mac_data;
- gconstpointer params;
- gsize n_params;
- int ret, r;
-
- ret = GCR_ERROR_FAILURE;
-
- /*
- * The MAC is optional (and outside the encryption no less). I wonder
- * what the designers (ha) of PKCS#12 were trying to achieve
- */
-
- mac_data = egg_asn1x_node (asn, "macData", NULL);
- if (mac_data == NULL)
- return SUCCESS;
-
- algorithm = egg_asn1x_get_oid_as_quark (egg_asn1x_node (mac_data, "mac",
- "digestAlgorithm", "algorithm", NULL));
- if (!algorithm)
- goto done;
-
- params = egg_asn1x_get_raw_element (mac_data, &n_params);
- if (!params)
- goto done;
-
- digest = egg_asn1x_get_string_as_raw (egg_asn1x_node (mac_data, "mac", "digest", NULL), NULL, &n_digest);
- if (!digest)
- goto done;
-
- /* Loop to try different passwords */
- for (;;) {
- g_assert (mdh == NULL);
-
- r = enum_next_password (self, &pstate, &password);
- if (r != SUCCESS) {
- ret = r;
- goto done;
- }
-
- /* Parse the encryption stuff into a cipher. */
- if (!egg_symkey_read_mac (algorithm, password, -1, params, n_params,
- &mdh, &mac_len)) {
- ret = GCR_ERROR_FAILURE;
- goto done;
- }
-
- /* If not the right length, then that's really broken */
- if (mac_len != n_digest) {
- r = GCR_ERROR_FAILURE;
-
- } else {
- gcry_md_write (mdh, content, n_content);
- mac_digest = gcry_md_read (mdh, 0);
- g_return_val_if_fail (mac_digest, GCR_ERROR_FAILURE);
- r = memcmp (mac_digest, digest, n_digest) == 0 ? SUCCESS : GCR_ERROR_LOCKED;
- }
-
- gcry_md_close (mdh);
- mdh = NULL;
-
- if (r != GCR_ERROR_LOCKED) {
- ret = r;
- break;
- }
- }
-
-done:
- if (mdh)
- gcry_md_close (mdh);
- g_free (digest);
- return ret;
-
-}
-
-static gint
-parse_der_pkcs12 (GcrParser *self, const guchar *data, gsize n_data)
-{
- GNode *asn = NULL;
- GNode *asn_content = NULL;
- gint ret;
- const guchar* element = NULL;
- guchar *content = NULL;
- gsize n_element, n_content;
- GQuark oid;
- GcrParsed *parsed;
-
- parsed = push_parsed (self, FALSE);
- ret = GCR_ERROR_UNRECOGNIZED;
-
- asn = decode_pkcs12_asn1_accepting_invalid_crap (pkix_asn1_tab, "pkcs-12-PFX",
- data, n_data);
- if (!asn)
- goto done;
-
- parsing_block (parsed, GCR_FORMAT_DER_PKCS12, data, n_data);
-
- oid = egg_asn1x_get_oid_as_quark (egg_asn1x_node (asn, "authSafe", "contentType", NULL));
- if (!oid)
- goto done;
-
- /* Outer most one must just be plain data */
- if (oid != GCR_OID_PKCS7_DATA) {
- g_message ("unsupported safe content type in pkcs12: %s", g_quark_to_string (oid));
- goto done;
- }
-
- element = egg_asn1x_get_raw_element (egg_asn1x_node (asn, "authSafe", "content", NULL), &n_element);
- if (!element)
- goto done;
-
- asn_content = decode_pkcs12_asn1_accepting_invalid_crap (pkix_asn1_tab, "pkcs-7-Data",
- element, n_element);
- if (!asn_content)
- goto done;
-
- content = egg_asn1x_get_string_as_raw (asn_content, g_realloc, &n_content);
- if (!content)
- goto done;
-
- ret = verify_pkcs12_safe (self, asn, content, n_content);
- if (ret == SUCCESS)
- ret = handle_pkcs12_safe (self, content, n_content);
-
-done:
- g_free (content);
- egg_asn1x_destroy (asn_content);
- egg_asn1x_destroy (asn);
- pop_parsed (self, parsed);
- return ret;
-}
-
-
-/* -----------------------------------------------------------------------------
- * OPENPGP
- */
-
-static void
-on_openpgp_packet (GPtrArray *records,
- const guchar *outer,
- gsize n_outer,
- gpointer user_data)
-{
- GcrParser *self = GCR_PARSER (user_data);
- GcrParsed *parsed;
- gchar *string;
-
- /*
- * If it's an openpgp packet that doesn't contain a key, then
- * just ignore it here.
- */
- if (records->len == 0)
- return;
-
- parsed = push_parsed (self, FALSE);
-
- /* All we can do is the packet bounds */
- parsing_block (parsed, GCR_FORMAT_OPENPGP_PACKET, outer, n_outer);
- parsing_object (parsed, CKO_GCR_GNUPG_RECORDS);
- string = _gcr_records_format (records);
- parsed_attribute (parsed, CKA_VALUE, string, strlen (string));
- parsed_fire (self, parsed);
- pop_parsed (self, parsed);
-
- g_free (string);
-}
-
-static gint
-parse_openpgp_packets (GcrParser *self,
- const guchar *data,
- gsize n_data)
-{
- gint num_parsed;
-
- num_parsed = _gcr_openpgp_parse (data, n_data,
- GCR_OPENPGP_PARSE_KEYS |
- GCR_OPENPGP_PARSE_ATTRIBUTES |
- GCR_OPENPGP_PARSE_SIGNATURES,
- on_openpgp_packet, self);
-
- if (num_parsed == 0)
- return GCR_ERROR_UNRECOGNIZED;
- return SUCCESS;
-}
-
-/* -----------------------------------------------------------------------------
- * ARMOR PARSING
- */
-
-static gboolean
-formats_for_armor_type (GQuark armor_type,
- gint *inner_format,
- gint *outer_format)
-{
- gint dummy;
- if (!inner_format)
- inner_format = &dummy;
- if (!outer_format)
- outer_format = &dummy;
-
- if (armor_type == PEM_RSA_PRIVATE_KEY) {
- *inner_format = GCR_FORMAT_DER_PRIVATE_KEY_RSA;
- *outer_format = GCR_FORMAT_PEM_PRIVATE_KEY_RSA;
- } else if (armor_type == PEM_DSA_PRIVATE_KEY) {
- *inner_format = GCR_FORMAT_DER_PRIVATE_KEY_DSA;
- *outer_format = GCR_FORMAT_PEM_PRIVATE_KEY_DSA;
- } else if (armor_type == PEM_ANY_PRIVATE_KEY) {
- *inner_format = GCR_FORMAT_DER_PRIVATE_KEY;
- *outer_format = GCR_FORMAT_PEM_PRIVATE_KEY;
- } else if (armor_type == PEM_PRIVATE_KEY) {
- *inner_format = GCR_FORMAT_DER_PKCS8_PLAIN;
- *outer_format = GCR_FORMAT_PEM_PKCS8_PLAIN;
- } else if (armor_type == PEM_ENCRYPTED_PRIVATE_KEY) {
- *inner_format = GCR_FORMAT_DER_PKCS8_ENCRYPTED;
- *outer_format = GCR_FORMAT_PEM_PKCS8_ENCRYPTED;
- } else if (armor_type == PEM_CERTIFICATE) {
- *inner_format = GCR_FORMAT_DER_CERTIFICATE_X509;
- *outer_format = GCR_FORMAT_PEM_CERTIFICATE_X509;
- } else if (armor_type == PEM_PKCS7) {
- *inner_format = GCR_FORMAT_DER_PKCS7;
- *outer_format = GCR_FORMAT_PEM_PKCS7;
- } else if (armor_type == PEM_PKCS12) {
- *inner_format = GCR_FORMAT_DER_PKCS12;
- *outer_format = GCR_FORMAT_PEM_PKCS12;
- } else if (armor_type == ARMOR_PGP_PRIVATE_KEY_BLOCK) {
- *inner_format = GCR_FORMAT_OPENPGP_PACKET;
- *outer_format = GCR_FORMAT_OPENPGP_ARMOR;
- } else if (armor_type == ARMOR_PGP_PUBLIC_KEY_BLOCK) {
- *inner_format = GCR_FORMAT_OPENPGP_PACKET;
- *outer_format = GCR_FORMAT_OPENPGP_ARMOR;
- } else {
- return FALSE;
- }
-
- return TRUE;
-}
-
-static gint
-handle_plain_pem (GcrParser *self,
- gint format_id,
- gint want_format,
- const guchar *data,
- gsize n_data)
-{
- ParserFormat *format;
-
- if (want_format != 0 && want_format != format_id)
- return GCR_ERROR_UNRECOGNIZED;
-
- format = parser_format_lookup (format_id);
- if (format == NULL)
- return GCR_ERROR_UNRECOGNIZED;
-
- return (format->function) (self, data, n_data);
-}
-
-static gint
-handle_encrypted_pem (GcrParser *self,
- gint format_id,
- gint want_format,
- GHashTable *headers,
- const guchar *data,
- gsize n_data)
-{
- PasswordState pstate = PASSWORD_STATE_INIT;
- const gchar *password;
- guchar *decrypted;
- gsize n_decrypted;
- const gchar *val;
- gboolean ret;
- gint res;
- gint l;
-
- g_assert (GCR_IS_PARSER (self));
- g_assert (headers);
-
- val = g_hash_table_lookup (headers, "DEK-Info");
- if (!val) {
- g_message ("missing encryption header");
- return GCR_ERROR_FAILURE;
- }
-
- res = GCR_ERROR_FAILURE;
- for (;;) {
-
- res = enum_next_password (self, &pstate, &password);
- if (res != SUCCESS)
- break;
-
- decrypted = NULL;
- n_decrypted = 0;
-
- /* Decrypt, this will result in garble if invalid password */
- ret = egg_openssl_decrypt_block (val, password, -1, data, n_data,
- &decrypted, &n_decrypted);
- if (!ret) {
- res = GCR_ERROR_FAILURE;
- break;
- }
-
- g_assert (decrypted);
-
- /* Unpad the DER data */
- l = egg_asn1x_element_length (decrypted, n_decrypted);
- if (l > 0)
- n_decrypted = l;
-
- /* Try to parse */
- res = handle_plain_pem (self, format_id, want_format,
- decrypted, n_decrypted);
- egg_secure_free (decrypted);
-
- /* Unrecognized is a bad password */
- if (res != GCR_ERROR_UNRECOGNIZED)
- break;
- }
-
- return res;
-}
-
-typedef struct {
- GcrParser *parser;
- gint result;
- gint want_format;
-} HandlePemArgs;
-
-static void
-handle_pem_data (GQuark type,
- const guchar *data,
- gsize n_data,
- const gchar *outer,
- gsize n_outer,
- GHashTable *headers,
- gpointer user_data)
-{
- HandlePemArgs *args = (HandlePemArgs*)user_data;
- gint res = GCR_ERROR_FAILURE;
- gboolean encrypted = FALSE;
- const gchar *val;
- gint inner_format;
- gint outer_format;
- GcrParsed *parsed;
-
- /* Something already failed to parse */
- if (args->result == GCR_ERROR_FAILURE)
- return;
-
- if (!formats_for_armor_type (type, &inner_format, &outer_format))
- return;
-
- parsed = push_parsed (args->parser, FALSE);
-
- /* Fill in information necessary for prompting */
- parsing_block (parsed, outer_format, outer, n_outer);
-
- /* See if it's encrypted PEM all openssl like*/
- if (headers) {
- val = g_hash_table_lookup (headers, "Proc-Type");
- if (val && strcmp (val, "4,ENCRYPTED") == 0)
- encrypted = TRUE;
- }
-
- if (encrypted)
- res = handle_encrypted_pem (args->parser, inner_format,
- args->want_format, headers,
- data, n_data);
- else
- res = handle_plain_pem (args->parser, inner_format,
- args->want_format, data, n_data);
-
- pop_parsed (args->parser, parsed);
-
- if (res != GCR_ERROR_UNRECOGNIZED) {
- if (args->result == GCR_ERROR_UNRECOGNIZED)
- args->result = res;
- else if (res > args->result)
- args->result = res;
- }
-}
-
-static gint
-handle_pem_format (GcrParser *self, gint subformat, const guchar *data, gsize n_data)
-{
- HandlePemArgs ctx = { self, GCR_ERROR_UNRECOGNIZED, subformat };
- guint found;
-
- if (n_data == 0)
- return GCR_ERROR_UNRECOGNIZED;
-
- found = egg_armor_parse (data, n_data, handle_pem_data, &ctx);
-
- if (found == 0)
- return GCR_ERROR_UNRECOGNIZED;
-
- return ctx.result;
-}
-
-
-static gint
-parse_pem (GcrParser *self, const guchar *data, gsize n_data)
-{
- return handle_pem_format (self, 0, data, n_data);
-}
-
-static gint
-parse_pem_private_key_rsa (GcrParser *self, const guchar *data, gsize n_data)
-{
- return handle_pem_format (self, GCR_FORMAT_DER_PRIVATE_KEY_RSA, data, n_data);
-}
-
-static gint
-parse_pem_private_key_dsa (GcrParser *self, const guchar *data, gsize n_data)
-{
- return handle_pem_format (self, GCR_FORMAT_DER_PRIVATE_KEY_DSA, data, n_data);
-}
-
-static gint
-parse_pem_certificate (GcrParser *self, const guchar *data, gsize n_data)
-{
- return handle_pem_format (self, GCR_FORMAT_DER_CERTIFICATE_X509, data, n_data);
-}
-
-static gint
-parse_pem_pkcs8_plain (GcrParser *self, const guchar *data, gsize n_data)
-{
- return handle_pem_format (self, GCR_FORMAT_DER_PKCS8_PLAIN, data, n_data);
-}
-
-static gint
-parse_pem_pkcs8_encrypted (GcrParser *self, const guchar *data, gsize n_data)
-{
- return handle_pem_format (self, GCR_FORMAT_DER_PKCS8_ENCRYPTED, data, n_data);
-}
-
-static gint
-parse_pem_pkcs7 (GcrParser *self, const guchar *data, gsize n_data)
-{
- return handle_pem_format (self, GCR_FORMAT_DER_PKCS7, data, n_data);
-}
-
-static gint
-parse_pem_pkcs12 (GcrParser *self, const guchar *data, gsize n_data)
-{
- return handle_pem_format (self, GCR_FORMAT_DER_PKCS12, data, n_data);
-}
-
-static gint
-parse_openpgp_armor (GcrParser *self,
- const guchar *data,
- gsize n_data)
-{
- return handle_pem_format (self, GCR_FORMAT_OPENPGP_PACKET, data, n_data);
-}
-
-/* -----------------------------------------------------------------------------
- * OPENSSH
- */
-
-static void
-on_openssh_public_key_parsed (GckAttributes *attrs,
- const gchar *label,
- const gchar *options,
- const gchar *outer,
- gsize n_outer,
- gpointer user_data)
-{
- GcrParser *self = GCR_PARSER (user_data);
- GcrParsed *parsed;
-
- parsed = push_parsed (self, FALSE);
- parsing_block (parsed, GCR_FORMAT_OPENSSH_PUBLIC, outer, n_outer);
- parsed_attributes (parsed, attrs);
- parsed_label (parsed, label);
- parsed_fire (self, parsed);
- pop_parsed (self, parsed);
-}
-
-static gint
-parse_openssh_public (GcrParser *self,
- const guchar *data,
- gsize n_data)
-{
- guint num_parsed;
-
- num_parsed = _gcr_openssh_pub_parse (data, n_data,
- on_openssh_public_key_parsed, self);
-
- if (num_parsed == 0)
- return GCR_ERROR_UNRECOGNIZED;
- return SUCCESS;
-}
-
-/* -----------------------------------------------------------------------------
- * FORMATS
- */
-
-/**
- * GcrDataFormat:
- * @GCR_FORMAT_ALL: Represents all the formats, when enabling or disabling
- * @GCR_FORMAT_INVALID: Not a valid format
- * @GCR_FORMAT_DER_PRIVATE_KEY: DER encoded private key
- * @GCR_FORMAT_DER_PRIVATE_KEY_RSA: DER encoded RSA private key
- * @GCR_FORMAT_DER_PRIVATE_KEY_DSA: DER encoded DSA private key
- * @GCR_FORMAT_DER_CERTIFICATE_X509: DER encoded X.509 certificate
- * @GCR_FORMAT_DER_PKCS7: DER encoded PKCS\#7 container file which can contain certificates
- * @GCR_FORMAT_DER_PKCS8: DER encoded PKCS\#8 file which can contain a key
- * @GCR_FORMAT_DER_PKCS8_PLAIN: Unencrypted DER encoded PKCS\#8 file which can contain a key
- * @GCR_FORMAT_DER_PKCS8_ENCRYPTED: Encrypted DER encoded PKCS\#8 file which can contain a key
- * @GCR_FORMAT_DER_PKCS12: DER encoded PKCS\#12 file which can contain certificates and/or keys
- * @GCR_FORMAT_OPENSSH_PUBLIC: OpenSSH v1 or v2 public key
- * @GCR_FORMAT_OPENPGP_PACKET: OpenPGP key packet(s)
- * @GCR_FORMAT_OPENPGP_ARMOR: OpenPGP public or private key armor encoded data
- * @GCR_FORMAT_PEM: An OpenSSL style PEM file with unspecified contents
- * @GCR_FORMAT_PEM_PRIVATE_KEY: An OpenSSL style PEM file with a private key
- * @GCR_FORMAT_PEM_PRIVATE_KEY_RSA: An OpenSSL style PEM file with a private RSA key
- * @GCR_FORMAT_PEM_PRIVATE_KEY_DSA: An OpenSSL style PEM file with a private DSA key
- * @GCR_FORMAT_PEM_CERTIFICATE_X509: An OpenSSL style PEM file with an X.509 certificate
- * @GCR_FORMAT_PEM_PKCS7: An OpenSSL style PEM file containing PKCS\#7
- * @GCR_FORMAT_PEM_PKCS8_PLAIN: Unencrypted OpenSSL style PEM file containing PKCS\#8
- * @GCR_FORMAT_PEM_PKCS8_ENCRYPTED: Encrypted OpenSSL style PEM file containing PKCS\#8
- * @GCR_FORMAT_PEM_PKCS12: An OpenSSL style PEM file containing PKCS\#12
- *
- * The various format identifiers.
- */
-
-/* In order of parsing when no formats specified */
-static const ParserFormat parser_normal[] = {
- { GCR_FORMAT_PEM, parse_pem },
- { GCR_FORMAT_DER_PRIVATE_KEY_RSA, parse_der_private_key_rsa },
- { GCR_FORMAT_DER_PRIVATE_KEY_DSA, parse_der_private_key_dsa },
- { GCR_FORMAT_DER_CERTIFICATE_X509, parse_der_certificate },
- { GCR_FORMAT_DER_PKCS7, parse_der_pkcs7 },
- { GCR_FORMAT_DER_PKCS8_PLAIN, parse_der_pkcs8_plain },
- { GCR_FORMAT_DER_PKCS8_ENCRYPTED, parse_der_pkcs8_encrypted },
- { GCR_FORMAT_DER_PKCS12, parse_der_pkcs12 },
- { GCR_FORMAT_OPENSSH_PUBLIC, parse_openssh_public },
- { GCR_FORMAT_OPENPGP_PACKET, parse_openpgp_packets },
- { GCR_FORMAT_OPENPGP_ARMOR, parse_openpgp_armor },
-};
-
-/* Must be in format_id numeric order */
-static const ParserFormat parser_formats[] = {
- { GCR_FORMAT_DER_PRIVATE_KEY, parse_der_private_key },
- { GCR_FORMAT_DER_PRIVATE_KEY_RSA, parse_der_private_key_rsa },
- { GCR_FORMAT_DER_PRIVATE_KEY_DSA, parse_der_private_key_dsa },
- { GCR_FORMAT_DER_CERTIFICATE_X509, parse_der_certificate },
- { GCR_FORMAT_DER_PKCS7, parse_der_pkcs7 },
- { GCR_FORMAT_DER_PKCS8, parse_der_pkcs8 },
- { GCR_FORMAT_DER_PKCS8_PLAIN, parse_der_pkcs8_plain },
- { GCR_FORMAT_DER_PKCS8_ENCRYPTED, parse_der_pkcs8_encrypted },
- { GCR_FORMAT_DER_PKCS12, parse_der_pkcs12 },
- { GCR_FORMAT_OPENSSH_PUBLIC, parse_openssh_public },
- { GCR_FORMAT_OPENPGP_PACKET, parse_openpgp_packets },
- { GCR_FORMAT_OPENPGP_ARMOR, parse_openpgp_armor },
- { GCR_FORMAT_PEM, parse_pem },
- { GCR_FORMAT_PEM_PRIVATE_KEY_RSA, parse_pem_private_key_rsa },
- { GCR_FORMAT_PEM_PRIVATE_KEY_DSA, parse_pem_private_key_dsa },
- { GCR_FORMAT_PEM_CERTIFICATE_X509, parse_pem_certificate },
- { GCR_FORMAT_PEM_PKCS7, parse_pem_pkcs7 },
- { GCR_FORMAT_PEM_PKCS8_PLAIN, parse_pem_pkcs8_plain },
- { GCR_FORMAT_PEM_PKCS8_ENCRYPTED, parse_pem_pkcs8_encrypted },
- { GCR_FORMAT_PEM_PKCS12, parse_pem_pkcs12 },
-};
-
-static int
-compar_id_to_parser_format (const void *a, const void *b)
-{
- const gint *format_id = a;
- const ParserFormat *format = b;
-
- g_assert (format_id);
- g_assert (format);
-
- if (format->format_id == *format_id)
- return 0;
- return (*format_id < format->format_id) ? -1 : 1;
-}
-
-static ParserFormat*
-parser_format_lookup (gint format_id)
-{
- return bsearch (&format_id, parser_formats, G_N_ELEMENTS (parser_formats),
- sizeof (parser_formats[0]), compar_id_to_parser_format);
-}
-
-static gint
-compare_pointers (gconstpointer a, gconstpointer b)
-{
- if (a == b)
- return 0;
- return a < b ? -1 : 1;
-}
-
-typedef struct _ForeachArgs {
- GcrParser *parser;
- const guchar *data;
- gsize n_data;
- gint result;
-} ForeachArgs;
-
-static gboolean
-parser_format_foreach (gpointer key, gpointer value, gpointer data)
-{
- ForeachArgs *args = data;
- ParserFormat *format = key;
- gint result;
-
- g_assert (format);
- g_assert (format->function);
- g_assert (GCR_IS_PARSER (args->parser));
-
- result = (format->function) (args->parser, args->data, args->n_data);
- if (result != GCR_ERROR_UNRECOGNIZED) {
- args->result = result;
- return TRUE;
- }
-
- /* Keep going */
- return FALSE;
-}
-
-/* -----------------------------------------------------------------------------
- * OBJECT
- */
-
-
-static GObject*
-gcr_parser_constructor (GType type, guint n_props, GObjectConstructParam *props)
-{
- GcrParser *self = GCR_PARSER (G_OBJECT_CLASS (gcr_parser_parent_class)->constructor(type, n_props, props));
- g_return_val_if_fail (self, NULL);
-
- /* Always try to parse with NULL and empty passwords first */
- gcr_parser_add_password (self, NULL);
- gcr_parser_add_password (self, "");
-
- return G_OBJECT (self);
-}
-
-static void
-gcr_parser_init (GcrParser *self)
-{
- self->pv = G_TYPE_INSTANCE_GET_PRIVATE (self, GCR_TYPE_PARSER, GcrParserPrivate);
- self->pv->passwords = g_ptr_array_new ();
- self->pv->normal_formats = TRUE;
-}
-
-static void
-gcr_parser_dispose (GObject *obj)
-{
- GcrParser *self = GCR_PARSER (obj);
- gsize i;
-
- g_assert (!self->pv->parsed);
-
- for (i = 0; i < self->pv->passwords->len; ++i)
- egg_secure_strfree (g_ptr_array_index (self->pv->passwords, i));
- g_ptr_array_set_size (self->pv->passwords, 0);
-
- G_OBJECT_CLASS (gcr_parser_parent_class)->dispose (obj);
-}
-
-static void
-gcr_parser_finalize (GObject *obj)
-{
- GcrParser *self = GCR_PARSER (obj);
-
- g_assert (!self->pv->parsed);
-
- g_ptr_array_free (self->pv->passwords, TRUE);
- self->pv->passwords = NULL;
-
- G_OBJECT_CLASS (gcr_parser_parent_class)->finalize (obj);
-}
-
-static void
-gcr_parser_set_property (GObject *obj, guint prop_id, const GValue *value,
- GParamSpec *pspec)
-{
- switch (prop_id) {
- default:
- G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, prop_id, pspec);
- break;
- }
-}
-
-static void
-gcr_parser_get_property (GObject *obj, guint prop_id, GValue *value,
- GParamSpec *pspec)
-{
- GcrParser *self = GCR_PARSER (obj);
-
- switch (prop_id) {
- case PROP_PARSED_ATTRIBUTES:
- g_value_set_boxed (value, gcr_parser_get_parsed_attributes (self));
- break;
- case PROP_PARSED_LABEL:
- g_value_set_string (value, gcr_parser_get_parsed_label (self));
- break;
- case PROP_PARSED_DESCRIPTION:
- g_value_set_string (value, gcr_parser_get_parsed_description (self));
- break;
- default:
- G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, prop_id, pspec);
- break;
- }
-}
-
-static void
-gcr_parser_class_init (GcrParserClass *klass)
-{
- GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
- gint i;
-
- gobject_class->constructor = gcr_parser_constructor;
- gobject_class->dispose = gcr_parser_dispose;
- gobject_class->finalize = gcr_parser_finalize;
- gobject_class->set_property = gcr_parser_set_property;
- gobject_class->get_property = gcr_parser_get_property;
-
- /**
- * GcrParser:parsed-attributes:
- *
- * Get the attributes that make up the currently parsed item. This is
- * generally only valid during a #GcrParser::parsed signal.
- */
- g_type_class_add_private (gobject_class, sizeof (GcrParserPrivate));
-
- g_object_class_install_property (gobject_class, PROP_PARSED_ATTRIBUTES,
- g_param_spec_boxed ("parsed-attributes", "Parsed Attributes", "Parsed PKCS#11 attributes",
- GCK_TYPE_ATTRIBUTES, G_PARAM_READABLE));
-
- /**
- * GcrParser:parsed-label:
- *
- * The label of the currently parsed item. This is generally
- * only valid during a #GcrParser::parsed signal.
- */
- g_object_class_install_property (gobject_class, PROP_PARSED_LABEL,
- g_param_spec_string ("parsed-label", "Parsed Label", "Parsed item label",
- "", G_PARAM_READABLE));
-
- /**
- * GcrParser:parsed-description:
- *
- * The description of the type of the currently parsed item. This is generally
- * only valid during a #GcrParser::parsed signal.
- */
- g_object_class_install_property (gobject_class, PROP_PARSED_DESCRIPTION,
- g_param_spec_string ("parsed-description", "Parsed Description", "Parsed item description",
- "", G_PARAM_READABLE));
-
- /**
- * GcrParser::authenticate:
- * @count: The number of times this item has been authenticated.
- *
- * This signal is emitted when an item needs to be unlocked or decrypted before
- * it can be parsed. The @count argument specifies the number of times
- * the signal has been emitted for a given item. This can be used to
- * display a message saying the previous password was incorrect.
- *
- * Typically the gcr_parser_add_password() function is called in
- * response to this signal.
- *
- * If %FALSE is returned, then the authentication was not handled. If
- * no handlers return %TRUE then the item is not parsed and an error
- * with the code %GCR_ERROR_CANCELLED will be raised.
- *
- * Returns: Whether the authentication was handled.
- */
- signals[AUTHENTICATE] = g_signal_new ("authenticate", GCR_TYPE_PARSER,
- G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (GcrParserClass, authenticate),
- g_signal_accumulator_true_handled, NULL, _gcr_marshal_BOOLEAN__INT,
- G_TYPE_BOOLEAN, 1, G_TYPE_POINTER);
-
- /**
- * GcrParser::parsed:
- *
- * This signal is emitted when an item is sucessfully parsed. To access
- * the information about the item use the gcr_parser_get_parsed_label(),
- * gcr_parser_get_parsed_attributes() and gcr_parser_get_parsed_description()
- * functions.
- */
- signals[PARSED] = g_signal_new ("parsed", GCR_TYPE_PARSER,
- G_SIGNAL_RUN_FIRST, G_STRUCT_OFFSET (GcrParserClass, parsed),
- NULL, NULL, g_cclosure_marshal_VOID__VOID,
- G_TYPE_NONE, 0);
-
- init_quarks ();
- _gcr_initialize_library ();
-
- /* Check that the format tables are in order */
- for (i = 1; i < G_N_ELEMENTS (parser_formats); ++i)
- g_assert (parser_formats[i].format_id >= parser_formats[i - 1].format_id);
-}
-
-/* -----------------------------------------------------------------------------
- * PUBLIC
- */
-
-/**
- * gcr_parser_new:
- *
- * Create a new #GcrParser
- *
- * Returns: (transfer full): a newly allocated #GcrParser
- */
-GcrParser *
-gcr_parser_new (void)
-{
- return g_object_new (GCR_TYPE_PARSER, NULL);
-}
-
-/**
- * gcr_parser_add_password:
- * @self: The parser
- * @password: (allow-none): a password to try
- *
- * Add a password to the set of passwords to try when parsing locked or encrypted
- * items. This is usually called from the GcrParser::authenticate signal.
- */
-void
-gcr_parser_add_password (GcrParser *self, const gchar *password)
-{
- g_return_if_fail (GCR_IS_PARSER (self));
- g_ptr_array_add (self->pv->passwords, egg_secure_strdup (password));
-}
-
-/**
- * gcr_parser_parse_data:
- * @self: The parser
- * @data: (array length=n_data): the data to parse
- * @n_data: The length of the data
- * @error: A location to raise an error on failure.
- *
- * Parse the data. The GcrParser::parsed and GcrParser::authenticate signals
- * may fire during the parsing.
- *
- * Returns: Whether the data was parsed successfully or not.
- */
-gboolean
-gcr_parser_parse_data (GcrParser *self,
- const guchar *data,
- gsize n_data,
- GError **error)
-{
- ForeachArgs args = { self, data, n_data, GCR_ERROR_UNRECOGNIZED };
- const gchar *message = NULL;
- gint i;
-
- g_return_val_if_fail (GCR_IS_PARSER (self), FALSE);
- g_return_val_if_fail (data || !n_data, FALSE);
- g_return_val_if_fail (!error || !*error, FALSE);
-
- if (data && n_data) {
- /* Just the specific formats requested */
- if (self->pv->specific_formats) {
- g_tree_foreach (self->pv->specific_formats, parser_format_foreach, &args);
-
- /* All the 'normal' formats */
- } else if (self->pv->normal_formats) {
- for (i = 0; i < G_N_ELEMENTS (parser_normal); ++i) {
- if (parser_format_foreach ((gpointer)(parser_normal + i),
- (gpointer)(parser_normal + i), &args))
- break;
- }
- }
- }
-
- switch (args.result) {
- case SUCCESS:
- return TRUE;
- case GCR_ERROR_CANCELLED:
- message = _("The operation was cancelled");
- break;
- case GCR_ERROR_UNRECOGNIZED:
- message = _("Unrecognized or unsupported data.");
- break;
- case GCR_ERROR_FAILURE:
- message = _("Could not parse invalid or corrupted data.");
- break;
- case GCR_ERROR_LOCKED:
- message = _("The data is locked");
- break;
- default:
- g_assert_not_reached ();
- break;
- };
-
- g_set_error_literal (error, GCR_DATA_ERROR, args.result, message);
- return FALSE;
-}
-
-/**
- * gcr_parser_format_enable:
- * @self: The parser
- * @format: The format identifier
- *
- * Enable parsing of the given format. Use %GCR_FORMAT_ALL to enable all the formats.
- */
-void
-gcr_parser_format_enable (GcrParser *self,
- GcrDataFormat format)
-{
- const ParserFormat *form;
- guint i;
-
- g_return_if_fail (GCR_IS_PARSER (self));
-
- if (!self->pv->specific_formats)
- self->pv->specific_formats = g_tree_new (compare_pointers);
-
- if (format != -1) {
- form = parser_format_lookup (format);
- g_return_if_fail (form);
- g_tree_insert (self->pv->specific_formats,
- (gpointer)form, (gpointer)form);
- } else {
- for (i = 0; i < G_N_ELEMENTS (parser_formats); i++) {
- form = &parser_formats[i];
- g_tree_insert (self->pv->specific_formats, (gpointer)form,
- (gpointer)form);
- }
- }
-}
-
-/**
- * gcr_parser_format_disable:
- * @self: The parser
- * @format: The format identifier
- *
- * Disable parsing of the given format. Use %GCR_FORMAT_ALL to disable all the formats.
- */
-void
-gcr_parser_format_disable (GcrParser *self,
- GcrDataFormat format)
-{
- ParserFormat *form;
-
- g_return_if_fail (GCR_IS_PARSER (self));
-
- if (format == -1) {
- if (self->pv->specific_formats)
- g_tree_destroy (self->pv->specific_formats);
- self->pv->specific_formats = NULL;
- self->pv->normal_formats = FALSE;
- }
-
- if (!self->pv->specific_formats)
- return;
-
- form = parser_format_lookup (format);
- g_return_if_fail (form);
-
- g_tree_remove (self->pv->specific_formats, form);
-}
-
-/**
- * gcr_parser_format_supported:
- * @self: The parser
- * @format: The format identifier
- *
- * Check whether the given format is supported by the parser.
- *
- * Returns: Whether the format is supported.
- */
-gboolean
-gcr_parser_format_supported (GcrParser *self,
- GcrDataFormat format)
-{
- g_return_val_if_fail (GCR_IS_PARSER (self), FALSE);
- g_return_val_if_fail (format != GCR_FORMAT_ALL, FALSE);
- g_return_val_if_fail (format != GCR_FORMAT_INVALID, FALSE);
- return parser_format_lookup (format) ? TRUE : FALSE;
-}
-
-/**
- * gcr_parser_get_parsed:
- * @self: a parser
- *
- * Get the currently parsed item
- *
- * Returns: (transfer none): the currently parsed item
- */
-GcrParsed *
-gcr_parser_get_parsed (GcrParser *self)
-{
- g_return_val_if_fail (GCR_IS_PARSER (self), NULL);
- return self->pv->parsed;
-}
-
-GType
-gcr_parsed_get_type (void)
-{
- static volatile gsize initialized = 0;
- static GType type = 0;
- if (g_once_init_enter (&initialized)) {
- type = g_boxed_type_register_static ("GcrParsed",
- (GBoxedCopyFunc)gcr_parsed_ref,
- (GBoxedFreeFunc)gcr_parsed_unref);
- g_once_init_leave (&initialized, 1);
- }
- return type;
-}
-
-/**
- * gcr_parsed_ref:
- * @parsed: a parsed item
- *
- * Add a reference to a parsed item. An item may not be shared across threads
- * until it has been referenced at least once.
- *
- * Returns: (transfer full): the parsed item
- */
-GcrParsed *
-gcr_parsed_ref (GcrParsed *parsed)
-{
- GcrParsed *copy;
-
- g_return_val_if_fail (parsed != NULL, NULL);
-
- /* Already had a reference */
-#if GLIB_CHECK_VERSION (2,29,90)
- if (g_atomic_int_add (&parsed->refs, 1) >= 1)
-#else
- if (g_atomic_int_exchange_and_add (&parsed->refs, 1) >= 1)
-#endif
- return parsed;
-
- /* If this is the first reference, flatten the stack of parsed */
- copy = g_new0 (GcrParsed, 1);
- copy->refs = 1;
- copy->label = g_strdup (gcr_parsed_get_label (parsed));
- copy->attrs = gcr_parsed_get_attributes (parsed);
- if (copy->attrs)
- gck_attributes_ref (copy->attrs);
- copy->description = gcr_parsed_get_description (parsed);
- copy->next = NULL;
-
- /* Find the block of data to copy */
- while (parsed != NULL) {
- if (parsed->data != NULL) {
- if (parsed->sensitive) {
- copy->data = egg_secure_alloc (parsed->n_data);
- memcpy (copy->data, parsed->data, parsed->n_data);
- copy->destroy_func = egg_secure_free;
- } else {
- copy->data = g_memdup (parsed->data, parsed->n_data);
- copy->destroy_func = g_free;
- }
- copy->n_data = parsed->n_data;
- break;
- }
- parsed = parsed->next;
- }
-
- return copy;
-}
-
-/**
- * gcr_parsed_unref:
- * @parsed: a parsed item
- *
- * Unreferences a parsed item which was referenced with gcr_parsed_ref()
- */
-void
-gcr_parsed_unref (gpointer parsed)
-{
- GcrParsed *par = parsed;
-
- g_return_if_fail (parsed != NULL);
-
- if (g_atomic_int_dec_and_test (&par->refs)) {
- if (par->attrs)
- gck_attributes_unref (par->attrs);
- g_free (par->label);
- if (par->destroy_func)
- (par->destroy_func) (par->data);
- g_free (par);
- }
-}
-
-/**
- * gcr_parser_get_parsed_description:
- * @self: The parser
- *
- * Get a description for the type of the currently parsed item. This is generally
- * only valid during the GcrParser::parsed signal.
- *
- * Returns: (allow-none): the description for the current item; this is owned by
- * the parser and should not be freed
- */
-const gchar*
-gcr_parser_get_parsed_description (GcrParser *self)
-{
- g_return_val_if_fail (GCR_IS_PARSER (self), NULL);
- g_return_val_if_fail (self->pv->parsed != NULL, NULL);
-
- return gcr_parsed_get_description (self->pv->parsed);
-}
-
-/**
- * gcr_parsed_get_description:
- * @parsed: a parsed item
- *
- * Get the descirption for a parsed item.
- *
- * Returns: (allow-none): the description
- */
-const gchar*
-gcr_parsed_get_description (GcrParsed *parsed)
-{
- while (parsed != NULL) {
- if (parsed->description != NULL)
- return parsed->description;
- parsed = parsed->next;
- }
-
- return NULL;
-}
-
-/**
- * gcr_parser_get_parsed_attributes:
- * @self: The parser
- *
- * Get the attributes which make up the currently parsed item. This is generally
- * only valid during the GcrParser::parsed signal.
- *
- * Returns: (transfer none) (allow-none): the attributes for the current item,
- * which are owned by the parser and should not be freed
- */
-GckAttributes *
-gcr_parser_get_parsed_attributes (GcrParser *self)
-{
- g_return_val_if_fail (GCR_IS_PARSER (self), NULL);
- g_return_val_if_fail (self->pv->parsed != NULL, NULL);
-
- return gcr_parsed_get_attributes (self->pv->parsed);
-}
-
-/**
- * gcr_parsed_get_attributes:
- * @parsed: a parsed item
- *
- * Get the attributes which make up the parsed item.
- *
- * Returns: (transfer none) (allow-none): the attributes for the item; these
- * are owned by the parsed item and should not be freed
- */
-GckAttributes *
-gcr_parsed_get_attributes (GcrParsed *parsed)
-{
- while (parsed != NULL) {
- if (parsed->attrs != NULL)
- return parsed->attrs;
- parsed = parsed->next;
- }
-
- return NULL;
-}
-
-/**
- * gcr_parser_get_parsed_label:
- * @self: The parser
- *
- * Get the label of the currently parsed item. This is generally only valid
- * during the GcrParser::parsed signal.
- *
- * Returns: (allow-none): the label of the currently parsed item. The value is
- * owned by the parser and should not be freed.
- */
-const gchar*
-gcr_parser_get_parsed_label (GcrParser *self)
-{
- g_return_val_if_fail (GCR_IS_PARSER (self), NULL);
- g_return_val_if_fail (self->pv->parsed != NULL, NULL);
-
- return gcr_parsed_get_label (self->pv->parsed);
-}
-
-/**
- * gcr_parsed_get_label:
- * @parsed: a parsed item
- *
- * Get the label for the parsed item.
- *
- * Returns: (allow-none): the label for the item
- */
-const gchar*
-gcr_parsed_get_label (GcrParsed *parsed)
-{
- while (parsed != NULL) {
- if (parsed->label != NULL)
- return parsed->label;
- parsed = parsed->next;
- }
-
- return NULL;
-}
-
-/**
- * gcr_parser_get_parsed_block:
- * @self: a parser
- * @n_block: a location to place the size of the block
- *
- * Get the raw data block that represents this parsed object. This is only
- * valid during the GcrParser::parsed signal.
- *
- * Returns: (transfer none) (array length=n_block) (allow-none): the raw data
- * block of the currently parsed item; the value is owned by the parser
- * and should not be freed
- */
-const guchar *
-gcr_parser_get_parsed_block (GcrParser *self,
- gsize *n_block)
-{
- g_return_val_if_fail (GCR_IS_PARSER (self), NULL);
- g_return_val_if_fail (n_block != NULL, NULL);
- g_return_val_if_fail (self->pv->parsed != NULL, NULL);
-
- return gcr_parsed_get_data (self->pv->parsed, n_block);
-}
-
-/**
- * gcr_parsed_get_data:
- * @parsed: a parsed item
- * @n_data: location to store size of returned data
- *
- * Get the raw data block for the parsed item.
- *
- * Returns: (transfer full) (array length=n_data) (allow-none): the raw data of
- * the parsed item, or %NULL
- */
-const guchar *
-gcr_parsed_get_data (GcrParsed *parsed,
- gsize *n_data)
-{
- g_return_val_if_fail (n_data != NULL, NULL);
-
- while (parsed != NULL) {
- if (parsed->data != NULL) {
- *n_data = parsed->n_data;
- return parsed->data;
- }
- parsed = parsed->next;
- }
-
- *n_data = 0;
- return NULL;
-}
-
-/**
- * gcr_parser_get_parsed_format:
- * @self: a parser
- *
- * Get the format of the raw data block that represents this parsed object.
- * This corresponds with the data returned from gcr_parser_get_parsed_block().
- *
- * This is only valid during the GcrParser::parsed signal.
- *
- * Returns: the data format of the currently parsed item
- */
-GcrDataFormat
-gcr_parser_get_parsed_format (GcrParser *self)
-{
- g_return_val_if_fail (GCR_IS_PARSER (self), 0);
- g_return_val_if_fail (self->pv->parsed != NULL, 0);
-
- return gcr_parsed_get_format (self->pv->parsed);
-}
-
-/**
- * gcr_parsed_get_format:
- * @parsed: a parsed item
- *
- * Get the format of the parsed item.
- *
- * Returns: the data format of the item
- */
-GcrDataFormat
-gcr_parsed_get_format (GcrParsed *parsed)
-{
- while (parsed != NULL) {
- if (parsed->data != NULL)
- return parsed->format;
- parsed = parsed->next;
- }
-
- return 0;
-}
-/* ---------------------------------------------------------------------------------
- * STREAM PARSING
- */
-
-#define GCR_TYPE_PARSING (gcr_parsing_get_type ())
-#define GCR_PARSING(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GCR_TYPE_PARSING, GcrParsing))
-#define GCR_IS_PARSING(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GCR_TYPE_PARSING))
-
-typedef struct _GcrParsing {
- GObjectClass parent;
-
- GcrParser *parser;
- gboolean async;
- GCancellable *cancel;
-
- /* Failure information */
- GError *error;
- gboolean complete;
-
- /* Operation state */
- GInputStream *input;
- GByteArray *buffer;
-
- /* Async callback stuff */
- GAsyncReadyCallback callback;
- gpointer user_data;
-
-} GcrParsing;
-
-typedef struct _GcrParsingClass {
- GObjectClass parent_class;
-} GcrParsingClass;
-
-/* State forward declarations */
-static void state_cancelled (GcrParsing *self, gboolean async);
-static void state_failure (GcrParsing *self, gboolean async);
-static void state_complete (GcrParsing *self, gboolean async);
-static void state_parse_buffer (GcrParsing *self, gboolean async);
-static void state_read_buffer (GcrParsing *self, gboolean async);
-
-/* Other forward declarations */
-static GType gcr_parsing_get_type (void) G_GNUC_CONST;
-static void gcr_parsing_async_result_init (GAsyncResultIface *iface);
-
-G_DEFINE_TYPE_WITH_CODE (GcrParsing, gcr_parsing, G_TYPE_OBJECT,
- G_IMPLEMENT_INTERFACE (G_TYPE_ASYNC_RESULT, gcr_parsing_async_result_init));
-
-#define BLOCK 4096
-
-static void
-next_state (GcrParsing *self, void (*state) (GcrParsing*, gboolean))
-{
- g_assert (GCR_IS_PARSING (self));
- g_assert (state);
-
- if (self->cancel && g_cancellable_is_cancelled (self->cancel))
- state = state_cancelled;
-
- (state) (self, self->async);
-}
-
-static void
-state_complete (GcrParsing *self, gboolean async)
-{
- g_assert (GCR_IS_PARSING (self));
- g_assert (!self->complete);
- self->complete = TRUE;
- if (async && self->callback != NULL)
- (self->callback) (G_OBJECT (self->parser), G_ASYNC_RESULT (self), self->user_data);
-}
-
-static void
-state_failure (GcrParsing *self, gboolean async)
-{
- g_assert (GCR_IS_PARSING (self));
- g_assert (self->error);
- next_state (self, state_complete);
-}
-
-static void
-state_cancelled (GcrParsing *self, gboolean async)
-{
- g_assert (GCR_IS_PARSING (self));
- if (self->cancel && g_cancellable_is_cancelled (self->cancel))
- g_cancellable_cancel (self->cancel);
- if (self->error)
- g_error_free (self->error);
- self->error = g_error_new_literal (GCR_DATA_ERROR, GCR_ERROR_CANCELLED, _("The operation was cancelled"));
- next_state (self, state_failure);
-}
-
-static void
-state_parse_buffer (GcrParsing *self, gboolean async)
-{
- GError *error = NULL;
- gboolean ret;
-
- g_assert (GCR_IS_PARSING (self));
- g_assert (self->buffer);
-
- ret = gcr_parser_parse_data (self->parser, self->buffer->data, self->buffer->len, &error);
-
- if (ret == TRUE) {
- next_state (self, state_complete);
- } else {
- g_propagate_error (&self->error, error);
- next_state (self, state_failure);
- }
-}
-
-static void
-complete_read_buffer (GcrParsing *self, gssize count, GError *error)
-{
- g_assert (GCR_IS_PARSING (self));
- g_assert (self->buffer);
-
- /* A failure */
- if (count == -1) {
- g_propagate_error (&self->error, error);
- next_state (self, state_failure);
- } else {
-
- g_return_if_fail (count >= 0 && count <= BLOCK);
- g_byte_array_set_size (self->buffer, self->buffer->len - (BLOCK - count));
-
- /* Finished reading */
- if (count == 0)
- next_state (self, state_parse_buffer);
-
- /* Read the next block */
- else
- next_state (self, state_read_buffer);
- }
-
-}
-
-static void
-on_read_buffer (GObject *obj, GAsyncResult *res, gpointer user_data)
-{
- GError *error = NULL;
- gssize count;
-
- count = g_input_stream_read_finish (G_INPUT_STREAM (obj), res, &error);
- complete_read_buffer (user_data, count, error);
-}
-
-static void
-state_read_buffer (GcrParsing *self, gboolean async)
-{
- GError *error = NULL;
- gssize count;
- gsize at;
-
- g_assert (GCR_IS_PARSING (self));
- g_assert (G_IS_INPUT_STREAM (self->input));
-
- if (!self->buffer)
- self->buffer = g_byte_array_sized_new (BLOCK);
-
- at = self->buffer->len;
- g_byte_array_set_size (self->buffer, at + BLOCK);
-
- if (async) {
- g_input_stream_read_async (self->input, self->buffer->data + at,
- BLOCK, G_PRIORITY_DEFAULT, self->cancel,
- on_read_buffer, self);
- } else {
- count = g_input_stream_read (self->input, self->buffer->data + at,
- BLOCK, self->cancel, &error);
- complete_read_buffer (self, count, error);
- }
-}
-
-static void
-gcr_parsing_init (GcrParsing *self)
-{
-
-}
-
-static void
-gcr_parsing_finalize (GObject *obj)
-{
- GcrParsing *self = GCR_PARSING (obj);
-
- g_object_unref (self->parser);
- self->parser = NULL;
-
- g_object_unref (self->input);
- self->input = NULL;
-
- if (self->cancel)
- g_object_unref (self->cancel);
- self->cancel = NULL;
-
- g_clear_error (&self->error);
-
- if (self->buffer)
- g_byte_array_free (self->buffer, TRUE);
- self->buffer = NULL;
-
- G_OBJECT_CLASS (gcr_parsing_parent_class)->finalize (obj);
-}
-
-static void
-gcr_parsing_class_init (GcrParsingClass *klass)
-{
- G_OBJECT_CLASS (klass)->finalize = gcr_parsing_finalize;
-}
-
-static gpointer
-gcr_parsing_real_get_user_data (GAsyncResult *base)
-{
- g_return_val_if_fail (GCR_IS_PARSING (base), NULL);
- return GCR_PARSING (base)->user_data;
-}
-
-static GObject*
-gcr_parsing_real_get_source_object (GAsyncResult *base)
-{
- g_return_val_if_fail (GCR_IS_PARSING (base), NULL);
- return G_OBJECT (GCR_PARSING (base)->parser);
-}
-
-static void
-gcr_parsing_async_result_init (GAsyncResultIface *iface)
-{
- iface->get_source_object = gcr_parsing_real_get_source_object;
- iface->get_user_data = gcr_parsing_real_get_user_data;
-}
-
-static GcrParsing*
-gcr_parsing_new (GcrParser *parser, GInputStream *input, GCancellable *cancel)
-{
- GcrParsing *self;
-
- g_assert (GCR_IS_PARSER (parser));
- g_assert (G_IS_INPUT_STREAM (input));
-
- self = g_object_new (GCR_TYPE_PARSING, NULL);
- self->parser = g_object_ref (parser);
- self->input = g_object_ref (input);
- if (cancel)
- self->cancel = g_object_ref (cancel);
-
- return self;
-}
-
-/**
- * gcr_parser_parse_stream:
- * @self: The parser
- * @input: The input stream
- * @cancellable: An optional cancellation object
- * @error: A location to raise an error on failure
- *
- * Parse items from the data in a #GInputStream. This function may block while
- * reading from the input stream. Use gcr_parser_parse_stream_async() for
- * a non-blocking variant.
- *
- * The GcrParser::parsed and GcrParser::authenticate signals
- * may fire during the parsing.
- *
- * Returns: Whether the parsing completed successfully or not.
- */
-gboolean
-gcr_parser_parse_stream (GcrParser *self, GInputStream *input, GCancellable *cancellable,
- GError **error)
-{
- GcrParsing *parsing;
-
- g_return_val_if_fail (GCR_IS_PARSER (self), FALSE);
- g_return_val_if_fail (G_IS_INPUT_STREAM (input), FALSE);
- g_return_val_if_fail (!error || !*error, FALSE);
-
- parsing = gcr_parsing_new (self, input, cancellable);
- parsing->async = FALSE;
-
- next_state (parsing, state_read_buffer);
- g_assert (parsing->complete);
-
- return gcr_parser_parse_stream_finish (self, G_ASYNC_RESULT (parsing), error);
-}
-
-/**
- * gcr_parser_parse_stream_async:
- * @self: The parser
- * @input: The input stream
- * @cancellable: An optional cancellation object
- * @callback: Called when the operation result is ready.
- * @user_data: Data to pass to callback
- *
- * Parse items from the data in a #GInputStream. This function completes
- * asyncronously and doesn't block.
- *
- * The GcrParser::parsed and GcrParser::authenticate signals
- * may fire during the parsing.
- */
-void
-gcr_parser_parse_stream_async (GcrParser *self, GInputStream *input, GCancellable *cancellable,
- GAsyncReadyCallback callback, gpointer user_data)
-{
- GcrParsing *parsing;
-
- g_return_if_fail (GCR_IS_PARSER (self));
- g_return_if_fail (G_IS_INPUT_STREAM (input));
-
- parsing = gcr_parsing_new (self, input, cancellable);
- parsing->async = TRUE;
- parsing->callback = callback;
- parsing->user_data = user_data;
-
- next_state (parsing, state_read_buffer);
-}
-
-/**
- * gcr_parser_parse_stream_finish:
- * @self: The parser
- * @result:The operation result
- * @error: A location to raise an error on failure
- *
- * Complete an operation to parse a stream.
- *
- * Returns: Whether the parsing completed successfully or not.
- */
-gboolean
-gcr_parser_parse_stream_finish (GcrParser *self, GAsyncResult *result, GError **error)
-{
- GcrParsing *parsing;
-
- g_return_val_if_fail (GCR_IS_PARSING (result), FALSE);
- g_return_val_if_fail (!error || !*error, FALSE);
-
- parsing = GCR_PARSING (result);
- g_return_val_if_fail (parsing->complete, FALSE);
-
- if (parsing->error) {
- g_propagate_error (error, parsing->error);
- return FALSE;
- }
-
- return TRUE;
-}