diff options
author | Stef Walter <stefw@collabora.co.uk> | 2011-10-06 12:51:08 +0200 |
---|---|---|
committer | Stef Walter <stefw@collabora.co.uk> | 2011-10-06 13:22:43 +0200 |
commit | f28d3d768f0e4057f727e8c3b93ba50c19f5db24 (patch) | |
tree | 288ef581590dfccbfddcad3c87d98ce3a4ed0a97 /gcr/gcr-parser.c | |
parent | 626eef363c0ece57713c5be5939677784e8115cc (diff) | |
download | gnome-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.c | 2934 |
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; -} |