Branch data 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 "gcr-ssh-agent-util.h"
29 : :
30 : : gboolean
31 : 112 : _gcr_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 : 112 : stream = g_io_stream_get_input_stream (G_IO_STREAM (connection));
41 : :
42 : 112 : egg_buffer_reset (buffer);
43 : 112 : egg_buffer_resize (buffer, 4);
44 : :
45 [ + + ]: 112 : if (!g_input_stream_read_all (stream, buffer->buf, 4, &bytes_read, cancellable, error))
46 : 7 : return FALSE;
47 : :
48 [ + + ]: 105 : if (bytes_read < 4) {
49 : 8 : g_set_error (error, G_IO_ERROR, G_IO_ERROR_CONNECTION_CLOSED,
50 : : "connection closed by peer");
51 : 8 : return FALSE;
52 : : }
53 : :
54 [ + - ]: 97 : if (!egg_buffer_get_uint32 (buffer, 0, NULL, &packet_size) ||
55 [ + + ]: 97 : packet_size < 1) {
56 : 1 : g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
57 : : "invalid packet size %u",
58 : : packet_size);
59 : 1 : return FALSE;
60 : : }
61 : :
62 : 96 : egg_buffer_resize (buffer, packet_size + 4);
63 [ - + ]: 96 : if (!g_input_stream_read_all (stream, buffer->buf + 4, packet_size, &bytes_read, cancellable, error))
64 : 0 : return FALSE;
65 : :
66 : 96 : return TRUE;
67 : : }
68 : :
69 : : gboolean
70 : 98 : _gcr_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 : 98 : stream = g_io_stream_get_output_stream (G_IO_STREAM (connection));
79 [ - + ]: 98 : 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 : 98 : return g_output_stream_write_all (stream, buffer->buf, buffer->len, &bytes_written, cancellable, error);
85 : : }
86 : :
87 : : gboolean
88 : 44 : _gcr_ssh_agent_call (GSocketConnection *connection,
89 : : EggBuffer*req,
90 : : EggBuffer *resp,
91 : : GCancellable *cancellable,
92 : : GError **error)
93 : : {
94 [ + + + - ]: 87 : return _gcr_ssh_agent_write_packet (connection, req, cancellable, error) &&
95 : 43 : _gcr_ssh_agent_read_packet (connection, resp, cancellable, error);
96 : : }
97 : :
98 : : GBytes *
99 : 26 : _gcr_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 [ - + ]: 26 : g_return_val_if_fail (input, NULL);
113 : :
114 : 26 : data = g_bytes_get_data (input, &n_data);
115 : :
116 : : /* Look for a key line */
117 : : for (;;) {
118 : : /* Eat space at the front */
119 [ + - + + ]: 30 : while (n_data > 0 && g_ascii_isspace (data[0])) {
120 : 2 : ++data;
121 : 2 : --n_data;
122 : : }
123 : :
124 : : /* Not a comment or blank line? Then parse... */
125 [ + + ]: 28 : if (data[0] != '#')
126 : 26 : break;
127 : :
128 : : /* Skip to the next line */
129 : 2 : at = memchr (data, '\n', n_data);
130 [ - + ]: 2 : if (!at)
131 : 0 : return NULL;
132 : 2 : at += 1;
133 : 2 : n_data -= (at - data);
134 : 2 : data = at;
135 : : }
136 : :
137 : : /* Limit to use only the first line */
138 : 26 : at = memchr (data, '\n', n_data);
139 [ + + ]: 26 : if (at != NULL)
140 : 25 : n_data = at - data;
141 : :
142 : 26 : keytype = data;
143 : :
144 : : /* Find the first space */
145 : 26 : at = memchr (data, ' ', n_data);
146 [ - + ]: 26 : if (!at) {
147 : 0 : g_message ("SSH public key missing space");
148 : 0 : return NULL;
149 : : }
150 : :
151 : 26 : n_keytype = at - data;
152 : :
153 : : /* Skip more whitespace */
154 : 26 : n_data -= (at - data);
155 : 26 : data = at;
156 [ + - + + : 54 : while (n_data > 0 && (data[0] == ' ' || data[0] == '\t')) {
- + ]
157 : 28 : ++data;
158 : 28 : --n_data;
159 : : }
160 : :
161 : : /* Find the next whitespace, or the end */
162 : 26 : at = memchr (data, ' ', n_data);
163 [ + + ]: 26 : if (at == NULL)
164 : 20 : at = data + n_data;
165 : :
166 : : /* Check if the chunk is the base64 key */
167 [ + + ]: 26 : if ((at - data) % 4 != 0) {
168 : 1 : g_message ("SSH public key missing key data");
169 : 1 : return NULL;
170 : : }
171 : :
172 : : /* Decode the base64 key */
173 : 25 : save = state = 0;
174 : 25 : decoded = g_malloc (n_data * 3 / 4);
175 : 25 : n_decoded = g_base64_decode_step ((gchar*)data, at - data, decoded, &state, &save);
176 : :
177 [ - + ]: 25 : 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 [ + - ]: 25 : if (!(n_decoded > n_keytype + 4 &&
184 [ + - ]: 25 : egg_buffer_decode_uint32 (decoded) == n_keytype &&
185 [ - + ]: 25 : 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 : 25 : n_data -= (at - data);
193 : 25 : data = at;
194 [ + + + + : 32 : while (n_data > 0 && (data[0] == ' ' || data[0] == '\t')) {
- + ]
195 : 7 : ++data;
196 : 7 : --n_data;
197 : : }
198 : :
199 : : /* If there's data left, its the comment */
200 [ + - ]: 25 : if (comment)
201 [ + + ]: 25 : *comment = n_data ? g_strndup ((gchar*)data, n_data) : g_strdup ("");
202 : :
203 : 25 : return g_bytes_new_take (decoded, n_decoded);
204 : : }
205 : :
206 : : gchar *
207 : 1 : _gcr_ssh_agent_canon_error (gchar *str)
208 : : {
209 : 1 : gchar *start = str;
210 : 1 : gchar *end = str + strlen (str) + 1;
211 : :
212 : : for (;;) {
213 : 11 : start = strchr (start, '\r');
214 [ + + ]: 6 : if (!start)
215 : 1 : break;
216 : 5 : memmove (start, start + 1, end - (start + 1));
217 : : }
218 : :
219 : 1 : return str;
220 : : }
|