Line data Source code
1 : /*
2 : * gnome-keyring
3 : *
4 : * Copyright (C) 2014 Stef Walter
5 : * Copyright (C) 2018 Red Hat, Inc.
6 : *
7 : * This program is free software; you can redistribute it and/or modify
8 : * it under the terms of the GNU Lesser General Public License as
9 : * published by the Free Software Foundation; either version 2.1 of
10 : * the License, or (at your option) any later version.
11 : *
12 : * This program is distributed in the hope that it will be useful, but
13 : * WITHOUT ANY WARRANTY; without even the implied warranty of
14 : * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 : * Lesser General Public License for more details.
16 : *
17 : * You should have received a copy of the GNU Lesser General Public
18 : * License along with this program; if not, see
19 : * <http://www.gnu.org/licenses/>.
20 : *
21 : * Author: Stef Walter <stef@thewalter.net>, Daiki Ueno
22 : */
23 :
24 : #include "config.h"
25 :
26 : #include <string.h>
27 :
28 : #include "gkd-ssh-agent-util.h"
29 :
30 : gboolean
31 0 : _gkd_ssh_agent_read_packet (GSocketConnection *connection,
32 : EggBuffer *buffer,
33 : GCancellable *cancellable,
34 : GError **error)
35 : {
36 : GInputStream *stream;
37 : guint32 packet_size;
38 : gsize bytes_read;
39 :
40 0 : stream = g_io_stream_get_input_stream (G_IO_STREAM (connection));
41 :
42 0 : egg_buffer_reset (buffer);
43 0 : egg_buffer_resize (buffer, 4);
44 :
45 0 : if (!g_input_stream_read_all (stream, buffer->buf, 4, &bytes_read, cancellable, error))
46 0 : return FALSE;
47 :
48 0 : if (bytes_read < 4) {
49 0 : g_set_error (error, G_IO_ERROR, G_IO_ERROR_CONNECTION_CLOSED,
50 : "connection closed by peer");
51 0 : return FALSE;
52 : }
53 :
54 0 : if (!egg_buffer_get_uint32 (buffer, 0, NULL, &packet_size) ||
55 0 : packet_size < 1) {
56 0 : g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
57 : "invalid packet size %u",
58 : packet_size);
59 0 : return FALSE;
60 : }
61 :
62 0 : egg_buffer_resize (buffer, packet_size + 4);
63 0 : if (!g_input_stream_read_all (stream, buffer->buf + 4, packet_size, &bytes_read, cancellable, error))
64 0 : return FALSE;
65 :
66 0 : return TRUE;
67 : }
68 :
69 : gboolean
70 0 : _gkd_ssh_agent_write_packet (GSocketConnection *connection,
71 : EggBuffer *buffer,
72 : GCancellable *cancellable,
73 : GError **error)
74 : {
75 : GOutputStream *stream;
76 : gsize bytes_written;
77 :
78 0 : stream = g_io_stream_get_output_stream (G_IO_STREAM (connection));
79 0 : if (!egg_buffer_set_uint32 (buffer, 0, buffer->len - 4)) {
80 0 : g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
81 : "cannot read packet length");
82 0 : return FALSE;
83 : }
84 0 : return g_output_stream_write_all (stream, buffer->buf, buffer->len, &bytes_written, cancellable, error);
85 : }
86 :
87 : gboolean
88 0 : _gkd_ssh_agent_call (GSocketConnection *connection,
89 : EggBuffer*req,
90 : EggBuffer *resp,
91 : GCancellable *cancellable,
92 : GError **error)
93 : {
94 0 : return _gkd_ssh_agent_write_packet (connection, req, cancellable, error) &&
95 0 : _gkd_ssh_agent_read_packet (connection, resp, cancellable, error);
96 : }
97 :
98 : GBytes *
99 0 : _gkd_ssh_agent_parse_public_key (GBytes *input,
100 : gchar **comment)
101 : {
102 : const guchar *at;
103 : guchar *decoded;
104 : gsize n_decoded;
105 : gint state;
106 : guint save;
107 : const guchar *data;
108 : gsize n_data;
109 : const guchar *keytype;
110 : gsize n_keytype;
111 :
112 0 : g_return_val_if_fail (input, NULL);
113 :
114 0 : data = g_bytes_get_data (input, &n_data);
115 :
116 : /* Look for a key line */
117 : for (;;) {
118 : /* Eat space at the front */
119 0 : while (n_data > 0 && g_ascii_isspace (data[0])) {
120 0 : ++data;
121 0 : --n_data;
122 : }
123 :
124 : /* Not a comment or blank line? Then parse... */
125 0 : if (data[0] != '#')
126 0 : break;
127 :
128 : /* Skip to the next line */
129 0 : at = memchr (data, '\n', n_data);
130 0 : if (!at)
131 0 : return NULL;
132 0 : at += 1;
133 0 : n_data -= (at - data);
134 0 : data = at;
135 : }
136 :
137 : /* Limit to use only the first line */
138 0 : at = memchr (data, '\n', n_data);
139 0 : if (at != NULL)
140 0 : n_data = at - data;
141 :
142 0 : keytype = data;
143 :
144 : /* Find the first space */
145 0 : at = memchr (data, ' ', n_data);
146 0 : if (!at) {
147 0 : g_message ("SSH public key missing space");
148 0 : return NULL;
149 : }
150 :
151 0 : n_keytype = at - data;
152 :
153 : /* Skip more whitespace */
154 0 : n_data -= (at - data);
155 0 : data = at;
156 0 : while (n_data > 0 && (data[0] == ' ' || data[0] == '\t')) {
157 0 : ++data;
158 0 : --n_data;
159 : }
160 :
161 : /* Find the next whitespace, or the end */
162 0 : at = memchr (data, ' ', n_data);
163 0 : if (at == NULL)
164 0 : at = data + n_data;
165 :
166 : /* Check if the chunk is the base64 key */
167 0 : if ((at - data) % 4 != 0) {
168 0 : g_message ("SSH public key missing key data");
169 0 : return NULL;
170 : }
171 :
172 : /* Decode the base64 key */
173 0 : save = state = 0;
174 0 : decoded = g_malloc (n_data * 3 / 4);
175 0 : n_decoded = g_base64_decode_step ((gchar*)data, at - data, decoded, &state, &save);
176 :
177 0 : if (!n_decoded) {
178 0 : g_free (decoded);
179 0 : return NULL;
180 : }
181 :
182 : /* Check if the key type is prefixed to the decoded blob */
183 0 : if (!(n_decoded > n_keytype + 4 &&
184 0 : egg_buffer_decode_uint32 (decoded) == n_keytype &&
185 0 : memcmp (keytype, decoded + 4, n_keytype) == 0)) {
186 0 : g_message ("SSH public key missing key type");
187 0 : g_free (decoded);
188 0 : return NULL;
189 : }
190 :
191 : /* Skip more whitespace */
192 0 : n_data -= (at - data);
193 0 : data = at;
194 0 : while (n_data > 0 && (data[0] == ' ' || data[0] == '\t')) {
195 0 : ++data;
196 0 : --n_data;
197 : }
198 :
199 : /* If there's data left, its the comment */
200 0 : if (comment)
201 0 : *comment = n_data ? g_strndup ((gchar*)data, n_data) : g_strdup ("");
202 :
203 0 : return g_bytes_new_take (decoded, n_decoded);
204 : }
205 :
206 : gchar *
207 0 : _gkd_ssh_agent_canon_error (gchar *str)
208 : {
209 0 : gchar *start = str;
210 0 : gchar *end = str + strlen (str) + 1;
211 :
212 : for (;;) {
213 0 : start = strchr (start, '\r');
214 0 : if (!start)
215 0 : break;
216 0 : memmove (start, start + 1, end - (start + 1));
217 : }
218 :
219 0 : return str;
220 : }
|