|
Packit |
b00eeb |
/*
|
|
Packit |
b00eeb |
* gnome-keyring
|
|
Packit |
b00eeb |
*
|
|
Packit |
b00eeb |
* Copyright (C) 2011 Collabora Ltd.
|
|
Packit |
b00eeb |
*
|
|
Packit |
b00eeb |
* This program is free software; you can redistribute it and/or modify
|
|
Packit |
b00eeb |
* it under the terms of the GNU Lesser General Public License as
|
|
Packit |
b00eeb |
* published by the Free Software Foundation; either version 2.1 of
|
|
Packit |
b00eeb |
* the License, or (at your option) any later version.
|
|
Packit |
b00eeb |
*
|
|
Packit |
b00eeb |
* This program is distributed in the hope that it will be useful, but
|
|
Packit |
b00eeb |
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
Packit |
b00eeb |
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
Packit |
b00eeb |
* Lesser General Public License for more details.
|
|
Packit |
b00eeb |
*
|
|
Packit |
b00eeb |
* You should have received a copy of the GNU Lesser General Public
|
|
Packit |
b00eeb |
* License along with this program; if not, see <http://www.gnu.org/licenses/>.
|
|
Packit |
b00eeb |
*
|
|
Packit |
b00eeb |
* Author: Stef Walter <stefw@collabora.co.uk>
|
|
Packit |
b00eeb |
*/
|
|
Packit |
b00eeb |
|
|
Packit |
b00eeb |
#include "config.h"
|
|
Packit |
b00eeb |
|
|
Packit |
b00eeb |
#include "gcr-openssh.h"
|
|
Packit |
b00eeb |
#include "gcr-internal.h"
|
|
Packit |
b00eeb |
#include "gcr-types.h"
|
|
Packit |
b00eeb |
|
|
Packit |
b00eeb |
#include "gcr/gcr-oids.h"
|
|
Packit |
b00eeb |
|
|
Packit |
b00eeb |
#include "egg/egg-asn1x.h"
|
|
Packit |
b00eeb |
#include "egg/egg-asn1-defs.h"
|
|
Packit |
b00eeb |
#include "egg/egg-buffer.h"
|
|
Packit |
b00eeb |
#include "egg/egg-decimal.h"
|
|
Packit |
b00eeb |
|
|
Packit |
b00eeb |
#include <p11-kit/pkcs11.h>
|
|
Packit |
b00eeb |
|
|
Packit |
b00eeb |
#include <string.h>
|
|
Packit |
b00eeb |
|
|
Packit |
b00eeb |
typedef struct {
|
|
Packit |
b00eeb |
GcrOpensshPubCallback callback;
|
|
Packit |
b00eeb |
gpointer user_data;
|
|
Packit |
b00eeb |
} OpensshPubClosure;
|
|
Packit |
b00eeb |
|
|
Packit |
b00eeb |
static void
|
|
Packit |
b00eeb |
skip_spaces (const gchar ** line,
|
|
Packit |
b00eeb |
gsize *n_line)
|
|
Packit |
b00eeb |
{
|
|
Packit |
b00eeb |
while (*n_line > 0 && (*line)[0] == ' ') {
|
|
Packit |
b00eeb |
(*line)++;
|
|
Packit |
b00eeb |
(*n_line)--;
|
|
Packit |
b00eeb |
}
|
|
Packit |
b00eeb |
}
|
|
Packit |
b00eeb |
|
|
Packit |
b00eeb |
static gboolean
|
|
Packit |
b00eeb |
next_word (const gchar **line,
|
|
Packit |
b00eeb |
gsize *n_line,
|
|
Packit |
b00eeb |
const gchar **word,
|
|
Packit |
b00eeb |
gsize *n_word)
|
|
Packit |
b00eeb |
{
|
|
Packit |
b00eeb |
const gchar *beg;
|
|
Packit |
b00eeb |
const gchar *end;
|
|
Packit |
b00eeb |
const gchar *at;
|
|
Packit |
b00eeb |
gboolean quotes;
|
|
Packit |
b00eeb |
|
|
Packit |
b00eeb |
skip_spaces (line, n_line);
|
|
Packit |
b00eeb |
|
|
Packit |
b00eeb |
if (!*n_line) {
|
|
Packit |
b00eeb |
*word = NULL;
|
|
Packit |
b00eeb |
*n_word = 0;
|
|
Packit |
b00eeb |
return FALSE;
|
|
Packit |
b00eeb |
}
|
|
Packit |
b00eeb |
|
|
Packit |
b00eeb |
beg = at = *line;
|
|
Packit |
b00eeb |
end = beg + *n_line;
|
|
Packit |
b00eeb |
quotes = FALSE;
|
|
Packit |
b00eeb |
|
|
Packit |
b00eeb |
do {
|
|
Packit |
b00eeb |
switch (*at) {
|
|
Packit |
b00eeb |
case '"':
|
|
Packit |
b00eeb |
quotes = !quotes;
|
|
Packit |
b00eeb |
at++;
|
|
Packit |
b00eeb |
break;
|
|
Packit |
b00eeb |
case ' ':
|
|
Packit |
b00eeb |
if (!quotes)
|
|
Packit |
b00eeb |
end = at;
|
|
Packit |
b00eeb |
else
|
|
Packit |
b00eeb |
at++;
|
|
Packit |
b00eeb |
break;
|
|
Packit |
b00eeb |
default:
|
|
Packit |
b00eeb |
at++;
|
|
Packit |
b00eeb |
break;
|
|
Packit |
b00eeb |
}
|
|
Packit |
b00eeb |
} while (at < end);
|
|
Packit |
b00eeb |
|
|
Packit |
b00eeb |
*word = beg;
|
|
Packit |
b00eeb |
*n_word = end - beg;
|
|
Packit |
b00eeb |
(*line) += *n_word;
|
|
Packit |
b00eeb |
(*n_line) -= *n_word;
|
|
Packit |
b00eeb |
return TRUE;
|
|
Packit |
b00eeb |
}
|
|
Packit |
b00eeb |
|
|
Packit |
b00eeb |
static gboolean
|
|
Packit |
b00eeb |
match_word (const gchar *word,
|
|
Packit |
b00eeb |
gsize n_word,
|
|
Packit |
b00eeb |
const gchar *matches)
|
|
Packit |
b00eeb |
{
|
|
Packit |
b00eeb |
gsize len = strlen (matches);
|
|
Packit |
b00eeb |
if (len != n_word)
|
|
Packit |
b00eeb |
return FALSE;
|
|
Packit |
b00eeb |
return memcmp (word, matches, n_word) == 0;
|
|
Packit |
b00eeb |
}
|
|
Packit |
b00eeb |
|
|
Packit |
b00eeb |
static gulong
|
|
Packit |
b00eeb |
keytype_to_algo (const gchar *algo,
|
|
Packit |
b00eeb |
gsize length)
|
|
Packit |
b00eeb |
{
|
|
Packit |
b00eeb |
if (!algo)
|
|
Packit |
b00eeb |
return G_MAXULONG;
|
|
Packit |
b00eeb |
else if (match_word (algo, length, "ssh-rsa"))
|
|
Packit |
b00eeb |
return CKK_RSA;
|
|
Packit |
b00eeb |
else if (match_word (algo, length, "ssh-dss"))
|
|
Packit |
b00eeb |
return CKK_DSA;
|
|
Packit |
b00eeb |
else if (length >= 6 && strncmp (algo, "ecdsa-", 6) == 0)
|
|
Packit |
b00eeb |
return CKK_ECDSA;
|
|
Packit |
b00eeb |
return G_MAXULONG;
|
|
Packit |
b00eeb |
}
|
|
Packit |
b00eeb |
|
|
Packit |
b00eeb |
static gboolean
|
|
Packit |
b00eeb |
read_decimal_mpi (const gchar *decimal,
|
|
Packit |
b00eeb |
gsize n_decimal,
|
|
Packit |
b00eeb |
GckBuilder *builder,
|
|
Packit |
b00eeb |
gulong attribute_type)
|
|
Packit |
b00eeb |
{
|
|
Packit |
b00eeb |
gpointer data;
|
|
Packit |
b00eeb |
gsize n_data;
|
|
Packit |
b00eeb |
|
|
Packit |
b00eeb |
data = egg_decimal_decode (decimal, n_decimal, &n_data);
|
|
Packit |
b00eeb |
if (data == NULL)
|
|
Packit |
b00eeb |
return FALSE;
|
|
Packit |
b00eeb |
|
|
Packit |
b00eeb |
gck_builder_add_data (builder, attribute_type, data, n_data);
|
|
Packit |
b00eeb |
g_free (data);
|
|
Packit |
b00eeb |
return TRUE;
|
|
Packit |
b00eeb |
}
|
|
Packit |
b00eeb |
|
|
Packit |
b00eeb |
static gint
|
|
Packit |
b00eeb |
atoin (const char *p, gint digits)
|
|
Packit |
b00eeb |
{
|
|
Packit |
b00eeb |
gint ret = 0, base = 1;
|
|
Packit |
b00eeb |
while(--digits >= 0) {
|
|
Packit |
b00eeb |
if (p[digits] < '0' || p[digits] > '9')
|
|
Packit |
b00eeb |
return -1;
|
|
Packit |
b00eeb |
ret += (p[digits] - '0') * base;
|
|
Packit |
b00eeb |
base *= 10;
|
|
Packit |
b00eeb |
}
|
|
Packit |
b00eeb |
return ret;
|
|
Packit |
b00eeb |
}
|
|
Packit |
b00eeb |
|
|
Packit |
b00eeb |
static GcrDataError
|
|
Packit |
b00eeb |
parse_v1_public_line (const gchar *line,
|
|
Packit |
b00eeb |
gsize length,
|
|
Packit |
b00eeb |
GBytes *backing,
|
|
Packit |
b00eeb |
GcrOpensshPubCallback callback,
|
|
Packit |
b00eeb |
gpointer user_data)
|
|
Packit |
b00eeb |
{
|
|
Packit |
b00eeb |
const gchar *word_bits, *word_exponent, *word_modulus, *word_options, *outer;
|
|
Packit |
b00eeb |
gsize len_bits, len_exponent, len_modulus, len_options, n_outer;
|
|
Packit |
b00eeb |
GckBuilder builder = GCK_BUILDER_INIT;
|
|
Packit |
b00eeb |
GckAttributes *attrs;
|
|
Packit |
b00eeb |
gchar *label, *options;
|
|
Packit |
b00eeb |
GBytes *bytes;
|
|
Packit |
b00eeb |
gint bits;
|
|
Packit |
b00eeb |
|
|
Packit |
b00eeb |
g_assert (line);
|
|
Packit |
b00eeb |
|
|
Packit |
b00eeb |
outer = line;
|
|
Packit |
b00eeb |
n_outer = length;
|
|
Packit |
b00eeb |
options = NULL;
|
|
Packit |
b00eeb |
label = NULL;
|
|
Packit |
b00eeb |
|
|
Packit |
b00eeb |
/* Eat space at the front */
|
|
Packit |
b00eeb |
skip_spaces (&line, &length);
|
|
Packit |
b00eeb |
|
|
Packit |
b00eeb |
/* Blank line or comment */
|
|
Packit |
b00eeb |
if (length == 0 || line[0] == '#')
|
|
Packit |
b00eeb |
return GCR_ERROR_UNRECOGNIZED;
|
|
Packit |
b00eeb |
|
|
Packit |
b00eeb |
/*
|
|
Packit |
b00eeb |
* If the line starts with a digit, then no options:
|
|
Packit |
b00eeb |
*
|
|
Packit |
b00eeb |
* 2048 35 25213680043....93533757 Label
|
|
Packit |
b00eeb |
*
|
|
Packit |
b00eeb |
* If the line doesn't start with a digit, then have options:
|
|
Packit |
b00eeb |
*
|
|
Packit |
b00eeb |
* option,option 2048 35 25213680043....93533757 Label
|
|
Packit |
b00eeb |
*/
|
|
Packit |
b00eeb |
if (g_ascii_isdigit (line[0])) {
|
|
Packit |
b00eeb |
word_options = NULL;
|
|
Packit |
b00eeb |
len_options = 0;
|
|
Packit |
b00eeb |
} else {
|
|
Packit |
b00eeb |
if (!next_word (&line, &length, &word_options, &len_options))
|
|
Packit |
b00eeb |
return GCR_ERROR_UNRECOGNIZED;
|
|
Packit |
b00eeb |
}
|
|
Packit |
b00eeb |
|
|
Packit |
b00eeb |
if (!next_word (&line, &length, &word_bits, &len_bits) ||
|
|
Packit |
b00eeb |
!next_word (&line, &length, &word_exponent, &len_exponent) ||
|
|
Packit |
b00eeb |
!next_word (&line, &length, &word_modulus, &len_modulus))
|
|
Packit |
b00eeb |
return GCR_ERROR_UNRECOGNIZED;
|
|
Packit |
b00eeb |
|
|
Packit |
b00eeb |
bits = atoin (word_bits, len_bits);
|
|
Packit |
b00eeb |
if (bits <= 0)
|
|
Packit |
b00eeb |
return GCR_ERROR_UNRECOGNIZED;
|
|
Packit |
b00eeb |
|
|
Packit |
b00eeb |
if (!read_decimal_mpi (word_exponent, len_exponent, &builder, CKA_PUBLIC_EXPONENT) ||
|
|
Packit |
b00eeb |
!read_decimal_mpi (word_modulus, len_modulus, &builder, CKA_MODULUS)) {
|
|
Packit |
b00eeb |
gck_builder_clear (&builder);
|
|
Packit |
b00eeb |
return GCR_ERROR_UNRECOGNIZED;
|
|
Packit |
b00eeb |
}
|
|
Packit |
b00eeb |
|
|
Packit |
b00eeb |
gck_builder_add_ulong (&builder, CKA_KEY_TYPE, CKK_RSA);
|
|
Packit |
b00eeb |
gck_builder_add_ulong (&builder, CKA_CLASS, CKO_PUBLIC_KEY);
|
|
Packit |
b00eeb |
|
|
Packit |
b00eeb |
skip_spaces (&line, &length);
|
|
Packit |
b00eeb |
if (length > 0) {
|
|
Packit |
b00eeb |
label = g_strndup (line, length);
|
|
Packit |
b00eeb |
g_strstrip (label);
|
|
Packit |
b00eeb |
gck_builder_add_string (&builder, CKA_LABEL, label);
|
|
Packit |
b00eeb |
}
|
|
Packit |
b00eeb |
|
|
Packit |
b00eeb |
if (word_options)
|
|
Packit |
b00eeb |
options = g_strndup (word_options, len_options);
|
|
Packit |
b00eeb |
|
|
Packit |
b00eeb |
attrs = gck_attributes_ref_sink (gck_builder_end (&builder));
|
|
Packit |
b00eeb |
|
|
Packit |
b00eeb |
if (callback != NULL) {
|
|
Packit |
b00eeb |
bytes = g_bytes_new_with_free_func (outer, n_outer,
|
|
Packit |
b00eeb |
(GDestroyNotify)g_bytes_unref,
|
|
Packit |
b00eeb |
g_bytes_ref (backing));
|
|
Packit |
b00eeb |
(callback) (attrs, label, options, bytes, user_data);
|
|
Packit |
b00eeb |
g_bytes_unref (bytes);
|
|
Packit |
b00eeb |
}
|
|
Packit |
b00eeb |
|
|
Packit |
b00eeb |
gck_attributes_unref (attrs);
|
|
Packit |
b00eeb |
g_free (options);
|
|
Packit |
b00eeb |
g_free (label);
|
|
Packit |
b00eeb |
return GCR_SUCCESS;
|
|
Packit |
b00eeb |
}
|
|
Packit |
b00eeb |
|
|
Packit |
b00eeb |
static gboolean
|
|
Packit |
b00eeb |
read_buffer_mpi_to_der (EggBuffer *buffer,
|
|
Packit |
b00eeb |
gsize *offset,
|
|
Packit |
b00eeb |
GckBuilder *builder,
|
|
Packit |
b00eeb |
gulong attribute_type)
|
|
Packit |
b00eeb |
{
|
|
Packit |
b00eeb |
const guchar *data, *data_value;
|
|
Packit |
b00eeb |
GBytes *der_data = NULL;
|
|
Packit |
b00eeb |
gsize len, data_len;
|
|
Packit |
b00eeb |
GNode *asn = NULL;
|
|
Packit |
b00eeb |
gboolean rv = FALSE;
|
|
Packit |
b00eeb |
|
|
Packit |
b00eeb |
if (!egg_buffer_get_byte_array (buffer, *offset, offset, &data, &len))
|
|
Packit |
b00eeb |
return FALSE;
|
|
Packit |
b00eeb |
|
|
Packit |
b00eeb |
asn = egg_asn1x_create (pk_asn1_tab, "ECPoint");
|
|
Packit |
b00eeb |
if (!asn)
|
|
Packit |
b00eeb |
return FALSE;
|
|
Packit |
b00eeb |
|
|
Packit |
b00eeb |
egg_asn1x_set_string_as_raw (asn, (guchar *)data, len, NULL);
|
|
Packit |
b00eeb |
der_data = egg_asn1x_encode (asn, g_realloc);
|
|
Packit |
b00eeb |
if (!der_data)
|
|
Packit |
b00eeb |
goto out;
|
|
Packit |
b00eeb |
|
|
Packit |
b00eeb |
data_value = g_bytes_get_data (der_data, &data_len);
|
|
Packit |
b00eeb |
gck_builder_add_data (builder, attribute_type, data_value, data_len);
|
|
Packit |
b00eeb |
rv = TRUE;
|
|
Packit |
b00eeb |
out:
|
|
Packit |
b00eeb |
g_bytes_unref (der_data);
|
|
Packit |
b00eeb |
egg_asn1x_destroy (asn);
|
|
Packit |
b00eeb |
return rv;
|
|
Packit |
b00eeb |
}
|
|
Packit |
b00eeb |
|
|
Packit |
b00eeb |
static gboolean
|
|
Packit |
b00eeb |
read_buffer_mpi (EggBuffer *buffer,
|
|
Packit |
b00eeb |
gsize *offset,
|
|
Packit |
b00eeb |
GckBuilder *builder,
|
|
Packit |
b00eeb |
gulong attribute_type)
|
|
Packit |
b00eeb |
{
|
|
Packit |
b00eeb |
const guchar *data;
|
|
Packit |
b00eeb |
gsize len;
|
|
Packit |
b00eeb |
|
|
Packit |
b00eeb |
if (!egg_buffer_get_byte_array (buffer, *offset, offset, &data, &len))
|
|
Packit |
b00eeb |
return FALSE;
|
|
Packit |
b00eeb |
|
|
Packit |
b00eeb |
gck_builder_add_data (builder, attribute_type, data, len);
|
|
Packit |
b00eeb |
return TRUE;
|
|
Packit |
b00eeb |
}
|
|
Packit |
b00eeb |
|
|
Packit |
b00eeb |
static gboolean
|
|
Packit |
b00eeb |
read_v2_public_dsa (EggBuffer *buffer,
|
|
Packit |
b00eeb |
gsize *offset,
|
|
Packit |
b00eeb |
GckBuilder *builder)
|
|
Packit |
b00eeb |
{
|
|
Packit |
b00eeb |
if (!read_buffer_mpi (buffer, offset, builder, CKA_PRIME) ||
|
|
Packit |
b00eeb |
!read_buffer_mpi (buffer, offset, builder, CKA_SUBPRIME) ||
|
|
Packit |
b00eeb |
!read_buffer_mpi (buffer, offset, builder, CKA_BASE) ||
|
|
Packit |
b00eeb |
!read_buffer_mpi (buffer, offset, builder, CKA_VALUE)) {
|
|
Packit |
b00eeb |
return FALSE;
|
|
Packit |
b00eeb |
}
|
|
Packit |
b00eeb |
|
|
Packit |
b00eeb |
gck_builder_add_ulong (builder, CKA_KEY_TYPE, CKK_DSA);
|
|
Packit |
b00eeb |
gck_builder_add_ulong (builder, CKA_CLASS, CKO_PUBLIC_KEY);
|
|
Packit |
b00eeb |
|
|
Packit |
b00eeb |
return TRUE;
|
|
Packit |
b00eeb |
}
|
|
Packit |
b00eeb |
|
|
Packit |
b00eeb |
static gboolean
|
|
Packit |
b00eeb |
read_v2_public_rsa (EggBuffer *buffer,
|
|
Packit |
b00eeb |
gsize *offset,
|
|
Packit |
b00eeb |
GckBuilder *builder)
|
|
Packit |
b00eeb |
{
|
|
Packit |
b00eeb |
if (!read_buffer_mpi (buffer, offset, builder, CKA_PUBLIC_EXPONENT) ||
|
|
Packit |
b00eeb |
!read_buffer_mpi (buffer, offset, builder, CKA_MODULUS)) {
|
|
Packit |
b00eeb |
return FALSE;
|
|
Packit |
b00eeb |
}
|
|
Packit |
b00eeb |
|
|
Packit |
b00eeb |
gck_builder_add_ulong (builder, CKA_KEY_TYPE, CKK_RSA);
|
|
Packit |
b00eeb |
gck_builder_add_ulong (builder, CKA_CLASS, CKO_PUBLIC_KEY);
|
|
Packit |
b00eeb |
|
|
Packit |
b00eeb |
return TRUE;
|
|
Packit |
b00eeb |
}
|
|
Packit |
b00eeb |
|
|
Packit |
b00eeb |
static gboolean
|
|
Packit |
b00eeb |
read_v2_public_ecdsa (EggBuffer *buffer,
|
|
Packit |
b00eeb |
gsize *offset,
|
|
Packit |
b00eeb |
GckBuilder *builder)
|
|
Packit |
b00eeb |
{
|
|
Packit |
b00eeb |
gconstpointer data;
|
|
Packit |
b00eeb |
GBytes *bytes;
|
|
Packit |
b00eeb |
GNode *asn;
|
|
Packit |
b00eeb |
GNode *node;
|
|
Packit |
b00eeb |
gchar *curve;
|
|
Packit |
b00eeb |
GQuark oid;
|
|
Packit |
b00eeb |
gsize len;
|
|
Packit |
b00eeb |
|
|
Packit |
b00eeb |
/* The named curve */
|
|
Packit |
b00eeb |
if (!egg_buffer_get_string (buffer, *offset, offset,
|
|
Packit |
b00eeb |
&curve, (EggBufferAllocator)g_realloc))
|
|
Packit |
b00eeb |
return FALSE;
|
|
Packit |
b00eeb |
|
|
Packit |
b00eeb |
if (g_strcmp0 (curve, "nistp256") == 0) {
|
|
Packit |
b00eeb |
oid = GCR_OID_EC_SECP256R1;
|
|
Packit |
b00eeb |
} else if (g_strcmp0 (curve, "nistp384") == 0) {
|
|
Packit |
b00eeb |
oid = GCR_OID_EC_SECP384R1;
|
|
Packit |
b00eeb |
} else if (g_strcmp0 (curve, "nistp521") == 0) {
|
|
Packit |
b00eeb |
oid = GCR_OID_EC_SECP521R1;
|
|
Packit |
b00eeb |
} else {
|
|
Packit |
b00eeb |
g_free (curve);
|
|
Packit |
b00eeb |
g_message ("unknown or unsupported curve in ssh public key");
|
|
Packit |
b00eeb |
return FALSE;
|
|
Packit |
b00eeb |
}
|
|
Packit |
b00eeb |
|
|
Packit |
b00eeb |
g_free (curve);
|
|
Packit |
b00eeb |
|
|
Packit |
b00eeb |
asn = egg_asn1x_create (pk_asn1_tab, "ECParameters");
|
|
Packit |
b00eeb |
g_return_val_if_fail (asn != NULL, FALSE);
|
|
Packit |
b00eeb |
|
|
Packit |
b00eeb |
node = egg_asn1x_node (asn, "namedCurve", NULL);
|
|
Packit |
b00eeb |
if (!egg_asn1x_set_choice (asn, node))
|
|
Packit |
b00eeb |
g_return_val_if_reached (FALSE);
|
|
Packit |
b00eeb |
|
|
Packit |
b00eeb |
if (!egg_asn1x_set_oid_as_quark (node, oid))
|
|
Packit |
b00eeb |
g_return_val_if_reached (FALSE);
|
|
Packit |
b00eeb |
|
|
Packit |
b00eeb |
bytes = egg_asn1x_encode (asn, g_realloc);
|
|
Packit |
b00eeb |
g_return_val_if_fail (bytes != NULL, FALSE);
|
|
Packit |
b00eeb |
egg_asn1x_destroy (asn);
|
|
Packit |
b00eeb |
|
|
Packit |
b00eeb |
data = g_bytes_get_data (bytes, &len;;
|
|
Packit |
b00eeb |
gck_builder_add_data (builder, CKA_EC_PARAMS, data, len);
|
|
Packit |
b00eeb |
g_bytes_unref (bytes);
|
|
Packit |
b00eeb |
|
|
Packit |
b00eeb |
/* need to convert to DER encoded OCTET STRING */
|
|
Packit |
b00eeb |
if (!read_buffer_mpi_to_der (buffer, offset, builder, CKA_EC_POINT))
|
|
Packit |
b00eeb |
return FALSE;
|
|
Packit |
b00eeb |
|
|
Packit |
b00eeb |
gck_builder_add_ulong (builder, CKA_KEY_TYPE, CKK_ECDSA);
|
|
Packit |
b00eeb |
gck_builder_add_ulong (builder, CKA_CLASS, CKO_PUBLIC_KEY);
|
|
Packit |
b00eeb |
|
|
Packit |
b00eeb |
return TRUE;
|
|
Packit |
b00eeb |
}
|
|
Packit |
b00eeb |
|
|
Packit |
b00eeb |
static gboolean
|
|
Packit |
b00eeb |
read_v2_public_key (gulong algo,
|
|
Packit |
b00eeb |
gconstpointer data,
|
|
Packit |
b00eeb |
gsize n_data,
|
|
Packit |
b00eeb |
GckBuilder *builder)
|
|
Packit |
b00eeb |
{
|
|
Packit |
b00eeb |
EggBuffer buffer;
|
|
Packit |
b00eeb |
gboolean ret;
|
|
Packit |
b00eeb |
gsize offset;
|
|
Packit |
b00eeb |
gchar *stype;
|
|
Packit |
b00eeb |
int alg;
|
|
Packit |
b00eeb |
|
|
Packit |
b00eeb |
egg_buffer_init_static (&buffer, data, n_data);
|
|
Packit |
b00eeb |
offset = 0;
|
|
Packit |
b00eeb |
|
|
Packit |
b00eeb |
/* The string algorithm */
|
|
Packit |
b00eeb |
if (!egg_buffer_get_string (&buffer, offset, &offset,
|
|
Packit |
b00eeb |
&stype, (EggBufferAllocator)g_realloc))
|
|
Packit |
b00eeb |
return FALSE;
|
|
Packit |
b00eeb |
|
|
Packit |
b00eeb |
alg = keytype_to_algo (stype, stype ? strlen (stype) : 0);
|
|
Packit |
b00eeb |
g_free (stype);
|
|
Packit |
b00eeb |
|
|
Packit |
b00eeb |
if (alg != algo) {
|
|
Packit |
b00eeb |
g_message ("invalid or mis-matched algorithm in ssh public key: %s", stype);
|
|
Packit |
b00eeb |
egg_buffer_uninit (&buffer);
|
|
Packit |
b00eeb |
return FALSE;
|
|
Packit |
b00eeb |
}
|
|
Packit |
b00eeb |
|
|
Packit |
b00eeb |
switch (algo) {
|
|
Packit |
b00eeb |
case CKK_RSA:
|
|
Packit |
b00eeb |
ret = read_v2_public_rsa (&buffer, &offset, builder);
|
|
Packit |
b00eeb |
break;
|
|
Packit |
b00eeb |
case CKK_DSA:
|
|
Packit |
b00eeb |
ret = read_v2_public_dsa (&buffer, &offset, builder);
|
|
Packit |
b00eeb |
break;
|
|
Packit |
b00eeb |
case CKK_ECDSA:
|
|
Packit |
b00eeb |
ret = read_v2_public_ecdsa (&buffer, &offset, builder);
|
|
Packit |
b00eeb |
break;
|
|
Packit |
b00eeb |
default:
|
|
Packit |
b00eeb |
g_assert_not_reached ();
|
|
Packit |
b00eeb |
break;
|
|
Packit |
b00eeb |
}
|
|
Packit |
b00eeb |
|
|
Packit |
b00eeb |
egg_buffer_uninit (&buffer);
|
|
Packit |
b00eeb |
return ret;
|
|
Packit |
b00eeb |
}
|
|
Packit |
b00eeb |
|
|
Packit |
b00eeb |
static gboolean
|
|
Packit |
b00eeb |
decode_v2_public_key (gulong algo,
|
|
Packit |
b00eeb |
const gchar *data,
|
|
Packit |
b00eeb |
gsize n_data,
|
|
Packit |
b00eeb |
GckBuilder *builder)
|
|
Packit |
b00eeb |
{
|
|
Packit |
b00eeb |
gpointer decoded;
|
|
Packit |
b00eeb |
gsize n_decoded;
|
|
Packit |
b00eeb |
gboolean ret;
|
|
Packit |
b00eeb |
guint save;
|
|
Packit |
b00eeb |
gint state;
|
|
Packit |
b00eeb |
|
|
Packit |
b00eeb |
/* Decode the base64 key */
|
|
Packit |
b00eeb |
save = state = 0;
|
|
Packit |
b00eeb |
decoded = g_malloc (n_data * 3 / 4);
|
|
Packit |
b00eeb |
n_decoded = g_base64_decode_step ((gchar*)data, n_data, decoded, &state, &save);
|
|
Packit |
b00eeb |
|
|
Packit |
b00eeb |
if (!n_decoded) {
|
|
Packit |
b00eeb |
g_free (decoded);
|
|
Packit |
b00eeb |
return FALSE;
|
|
Packit |
b00eeb |
}
|
|
Packit |
b00eeb |
|
|
Packit |
b00eeb |
/* Parse the actual key */
|
|
Packit |
b00eeb |
ret = read_v2_public_key (algo, decoded, n_decoded, builder);
|
|
Packit |
b00eeb |
|
|
Packit |
b00eeb |
g_free (decoded);
|
|
Packit |
b00eeb |
|
|
Packit |
b00eeb |
return ret;
|
|
Packit |
b00eeb |
}
|
|
Packit |
b00eeb |
|
|
Packit |
b00eeb |
static GcrDataError
|
|
Packit |
b00eeb |
parse_v2_public_line (const gchar *line,
|
|
Packit |
b00eeb |
gsize length,
|
|
Packit |
b00eeb |
GBytes *backing,
|
|
Packit |
b00eeb |
GcrOpensshPubCallback callback,
|
|
Packit |
b00eeb |
gpointer user_data)
|
|
Packit |
b00eeb |
{
|
|
Packit |
b00eeb |
const gchar *word_options, *word_algo, *word_key;
|
|
Packit |
b00eeb |
gsize len_options, len_algo, len_key;
|
|
Packit |
b00eeb |
GckBuilder builder = GCK_BUILDER_INIT;
|
|
Packit |
b00eeb |
GckAttributes *attrs;
|
|
Packit |
b00eeb |
gchar *options;
|
|
Packit |
b00eeb |
gchar *label = NULL;
|
|
Packit |
b00eeb |
const gchar *outer = line;
|
|
Packit |
b00eeb |
gsize n_outer = length;
|
|
Packit |
b00eeb |
GBytes *bytes;
|
|
Packit |
b00eeb |
gulong algo;
|
|
Packit |
b00eeb |
|
|
Packit |
b00eeb |
g_assert (line);
|
|
Packit |
b00eeb |
|
|
Packit |
b00eeb |
/* Eat space at the front */
|
|
Packit |
b00eeb |
skip_spaces (&line, &length);
|
|
Packit |
b00eeb |
|
|
Packit |
b00eeb |
/* Blank line or comment */
|
|
Packit |
b00eeb |
if (length == 0 || line[0] == '#')
|
|
Packit |
b00eeb |
return GCR_ERROR_UNRECOGNIZED;
|
|
Packit |
b00eeb |
|
|
Packit |
b00eeb |
if (!next_word (&line, &length, &word_algo, &len_algo))
|
|
Packit |
b00eeb |
return GCR_ERROR_UNRECOGNIZED;
|
|
Packit |
b00eeb |
|
|
Packit |
b00eeb |
/*
|
|
Packit |
b00eeb |
* If the first word is not the algorithm, then we have options:
|
|
Packit |
b00eeb |
*
|
|
Packit |
b00eeb |
* option,option ssh-rsa AAAAB3NzaC1yc2EAAAABIwAAAI...EAz8Ji= Label here
|
|
Packit |
b00eeb |
*
|
|
Packit |
b00eeb |
* If the first word is the algorithm, then we have no options:
|
|
Packit |
b00eeb |
*
|
|
Packit |
b00eeb |
* ssh-rsa AAAAB3NzaC1yc2EAAAABIwAAAI...EAz8Ji= Label here
|
|
Packit |
b00eeb |
*/
|
|
Packit |
b00eeb |
algo = keytype_to_algo (word_algo, len_algo);
|
|
Packit |
b00eeb |
if (algo == G_MAXULONG) {
|
|
Packit |
b00eeb |
word_options = word_algo;
|
|
Packit |
b00eeb |
len_options = len_algo;
|
|
Packit |
b00eeb |
if (!next_word (&line, &length, &word_algo, &len_algo))
|
|
Packit |
b00eeb |
return GCR_ERROR_UNRECOGNIZED;
|
|
Packit |
b00eeb |
algo = keytype_to_algo (word_algo, len_algo);
|
|
Packit |
b00eeb |
if (algo == G_MAXULONG)
|
|
Packit |
b00eeb |
return GCR_ERROR_UNRECOGNIZED;
|
|
Packit |
b00eeb |
} else {
|
|
Packit |
b00eeb |
word_options = NULL;
|
|
Packit |
b00eeb |
len_options = 0;
|
|
Packit |
b00eeb |
}
|
|
Packit |
b00eeb |
|
|
Packit |
b00eeb |
/* Must have at least two words */
|
|
Packit |
b00eeb |
if (!next_word (&line, &length, &word_key, &len_key))
|
|
Packit |
b00eeb |
return GCR_ERROR_FAILURE;
|
|
Packit |
b00eeb |
|
|
Packit |
b00eeb |
if (!decode_v2_public_key (algo, word_key, len_key, &builder)) {
|
|
Packit |
b00eeb |
gck_builder_clear (&builder);
|
|
Packit |
b00eeb |
return GCR_ERROR_FAILURE;
|
|
Packit |
b00eeb |
}
|
|
Packit |
b00eeb |
|
|
Packit |
b00eeb |
if (word_options)
|
|
Packit |
b00eeb |
options = g_strndup (word_options, len_options);
|
|
Packit |
b00eeb |
else
|
|
Packit |
b00eeb |
options = NULL;
|
|
Packit |
b00eeb |
|
|
Packit |
b00eeb |
/* The remainder of the line is the label */
|
|
Packit |
b00eeb |
skip_spaces (&line, &length);
|
|
Packit |
b00eeb |
if (length > 0) {
|
|
Packit |
b00eeb |
label = g_strndup (line, length);
|
|
Packit |
b00eeb |
g_strstrip (label);
|
|
Packit |
b00eeb |
gck_builder_add_string (&builder, CKA_LABEL, label);
|
|
Packit |
b00eeb |
}
|
|
Packit |
b00eeb |
|
|
Packit |
b00eeb |
attrs = gck_attributes_ref_sink (gck_builder_end (&builder));
|
|
Packit |
b00eeb |
|
|
Packit |
b00eeb |
if (callback != NULL) {
|
|
Packit |
b00eeb |
bytes = g_bytes_new_with_free_func (outer, n_outer,
|
|
Packit |
b00eeb |
(GDestroyNotify)g_bytes_unref,
|
|
Packit |
b00eeb |
g_bytes_ref (backing));
|
|
Packit |
b00eeb |
(callback) (attrs, label, options, bytes, user_data);
|
|
Packit |
b00eeb |
g_bytes_unref (bytes);
|
|
Packit |
b00eeb |
}
|
|
Packit |
b00eeb |
|
|
Packit |
b00eeb |
gck_attributes_unref (attrs);
|
|
Packit |
b00eeb |
g_free (options);
|
|
Packit |
b00eeb |
g_free (label);
|
|
Packit |
b00eeb |
return GCR_SUCCESS;
|
|
Packit |
b00eeb |
}
|
|
Packit |
b00eeb |
|
|
Packit |
b00eeb |
guint
|
|
Packit |
b00eeb |
_gcr_openssh_pub_parse (GBytes *data,
|
|
Packit |
b00eeb |
GcrOpensshPubCallback callback,
|
|
Packit |
b00eeb |
gpointer user_data)
|
|
Packit |
b00eeb |
{
|
|
Packit |
b00eeb |
const gchar *line;
|
|
Packit |
b00eeb |
const gchar *end;
|
|
Packit |
b00eeb |
gsize length;
|
|
Packit |
b00eeb |
gboolean last;
|
|
Packit |
b00eeb |
GcrDataError res;
|
|
Packit |
b00eeb |
guint num_parsed;
|
|
Packit |
b00eeb |
|
|
Packit |
b00eeb |
g_return_val_if_fail (data != NULL, FALSE);
|
|
Packit |
b00eeb |
|
|
Packit |
b00eeb |
line = g_bytes_get_data (data, NULL);
|
|
Packit |
b00eeb |
length = g_bytes_get_size (data);
|
|
Packit |
b00eeb |
last = FALSE;
|
|
Packit |
b00eeb |
num_parsed = 0;
|
|
Packit |
b00eeb |
|
|
Packit |
b00eeb |
for (;;) {
|
|
Packit |
b00eeb |
end = memchr (line, '\n', length);
|
|
Packit |
b00eeb |
if (end == NULL) {
|
|
Packit |
b00eeb |
end = line + length;
|
|
Packit |
b00eeb |
last = TRUE;
|
|
Packit |
b00eeb |
}
|
|
Packit |
b00eeb |
|
|
Packit |
b00eeb |
if (line != end) {
|
|
Packit |
b00eeb |
res = parse_v2_public_line (line, end - line, data, callback, user_data);
|
|
Packit |
b00eeb |
if (res == GCR_ERROR_UNRECOGNIZED)
|
|
Packit |
b00eeb |
res = parse_v1_public_line (line, end - line, data, callback, user_data);
|
|
Packit |
b00eeb |
if (res == GCR_SUCCESS)
|
|
Packit |
b00eeb |
num_parsed++;
|
|
Packit |
b00eeb |
}
|
|
Packit |
b00eeb |
|
|
Packit |
b00eeb |
if (last)
|
|
Packit |
b00eeb |
break;
|
|
Packit |
b00eeb |
|
|
Packit |
b00eeb |
end++;
|
|
Packit |
b00eeb |
length -= (end - line);
|
|
Packit |
b00eeb |
line = end;
|
|
Packit |
b00eeb |
}
|
|
Packit |
b00eeb |
|
|
Packit |
b00eeb |
return num_parsed;
|
|
Packit |
b00eeb |
}
|