/* * gnome-keyring * * Copyright (C) 2014 Stef Walter * Copyright (C) 2018 Red Hat, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation; either version 2.1 of * the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this program; if not, see * . * * Author: Stef Walter , Daiki Ueno */ #include "config.h" #include #include "gcr-ssh-agent-util.h" gboolean _gcr_ssh_agent_read_packet (GSocketConnection *connection, EggBuffer *buffer, GCancellable *cancellable, GError **error) { GInputStream *stream; guint32 packet_size; gsize bytes_read; stream = g_io_stream_get_input_stream (G_IO_STREAM (connection)); egg_buffer_reset (buffer); egg_buffer_resize (buffer, 4); if (!g_input_stream_read_all (stream, buffer->buf, 4, &bytes_read, cancellable, error)) return FALSE; if (bytes_read < 4) { g_set_error (error, G_IO_ERROR, G_IO_ERROR_CONNECTION_CLOSED, "connection closed by peer"); return FALSE; } if (!egg_buffer_get_uint32 (buffer, 0, NULL, &packet_size) || packet_size < 1) { g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, "invalid packet size %u", packet_size); return FALSE; } egg_buffer_resize (buffer, packet_size + 4); if (!g_input_stream_read_all (stream, buffer->buf + 4, packet_size, &bytes_read, cancellable, error)) return FALSE; return TRUE; } gboolean _gcr_ssh_agent_write_packet (GSocketConnection *connection, EggBuffer *buffer, GCancellable *cancellable, GError **error) { GOutputStream *stream; gsize bytes_written; stream = g_io_stream_get_output_stream (G_IO_STREAM (connection)); if (!egg_buffer_set_uint32 (buffer, 0, buffer->len - 4)) { g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, "cannot read packet length"); return FALSE; } return g_output_stream_write_all (stream, buffer->buf, buffer->len, &bytes_written, cancellable, error); } gboolean _gcr_ssh_agent_call (GSocketConnection *connection, EggBuffer*req, EggBuffer *resp, GCancellable *cancellable, GError **error) { return _gcr_ssh_agent_write_packet (connection, req, cancellable, error) && _gcr_ssh_agent_read_packet (connection, resp, cancellable, error); } GBytes * _gcr_ssh_agent_parse_public_key (GBytes *input, gchar **comment) { const guchar *at; guchar *decoded; gsize n_decoded; gint state; guint save; const guchar *data; gsize n_data; const guchar *keytype; gsize n_keytype; g_return_val_if_fail (input, NULL); data = g_bytes_get_data (input, &n_data); /* Look for a key line */ for (;;) { /* Eat space at the front */ while (n_data > 0 && g_ascii_isspace (data[0])) { ++data; --n_data; } /* Not a comment or blank line? Then parse... */ if (data[0] != '#') break; /* Skip to the next line */ at = memchr (data, '\n', n_data); if (!at) return NULL; at += 1; n_data -= (at - data); data = at; } /* Limit to use only the first line */ at = memchr (data, '\n', n_data); if (at != NULL) n_data = at - data; keytype = data; /* Find the first space */ at = memchr (data, ' ', n_data); if (!at) { g_message ("SSH public key missing space"); return NULL; } n_keytype = at - data; /* Skip more whitespace */ n_data -= (at - data); data = at; while (n_data > 0 && (data[0] == ' ' || data[0] == '\t')) { ++data; --n_data; } /* Find the next whitespace, or the end */ at = memchr (data, ' ', n_data); if (at == NULL) at = data + n_data; /* Check if the chunk is the base64 key */ if ((at - data) % 4 != 0) { g_message ("SSH public key missing key data"); return NULL; } /* Decode the base64 key */ save = state = 0; decoded = g_malloc (n_data * 3 / 4); n_decoded = g_base64_decode_step ((gchar*)data, at - data, decoded, &state, &save); if (!n_decoded) { g_free (decoded); return NULL; } /* Check if the key type is prefixed to the decoded blob */ if (!(n_decoded > n_keytype + 4 && egg_buffer_decode_uint32 (decoded) == n_keytype && memcmp (keytype, decoded + 4, n_keytype) == 0)) { g_message ("SSH public key missing key type"); g_free (decoded); return NULL; } /* Skip more whitespace */ n_data -= (at - data); data = at; while (n_data > 0 && (data[0] == ' ' || data[0] == '\t')) { ++data; --n_data; } /* If there's data left, its the comment */ if (comment) *comment = n_data ? g_strndup ((gchar*)data, n_data) : g_strdup (""); return g_bytes_new_take (decoded, n_decoded); } gchar * _gcr_ssh_agent_canon_error (gchar *str) { gchar *start = str; gchar *end = str + strlen (str) + 1; for (;;) { start = strchr (start, '\r'); if (!start) break; memmove (start, start + 1, end - (start + 1)); } return str; }