Use bits instead of bytes for a private/public key
[strongswan.git] / src / libstrongswan / plugins / agent / agent_private_key.c
1 /*
2 * Copyright (C) 2008-2009 Martin Willi
3 * Hochschule fuer Technik Rapperswil
4 *
5 * This program is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License as published by the
7 * Free Software Foundation; either version 2 of the License, or (at your
8 * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
9 *
10 * This program is distributed in the hope that it will be useful, but
11 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
12 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
13 * for more details.
14 */
15
16 #include "agent_private_key.h"
17
18 #include <stdlib.h>
19 #include <unistd.h>
20 #include <sys/types.h>
21 #include <sys/socket.h>
22 #include <sys/un.h>
23 #include <arpa/inet.h>
24 #include <errno.h>
25
26 #include <library.h>
27 #include <chunk.h>
28 #include <debug.h>
29
30 #ifndef UNIX_PATH_MAX
31 #define UNIX_PATH_MAX 108
32 #endif /* UNIX_PATH_MAX */
33
34 typedef struct private_agent_private_key_t private_agent_private_key_t;
35 typedef enum agent_msg_type_t agent_msg_type_t;
36
37 /**
38 * Private data of a agent_private_key_t object.
39 */
40 struct private_agent_private_key_t {
41 /**
42 * Public interface for this signer.
43 */
44 agent_private_key_t public;
45
46 /**
47 * ssh-agent unix socket connection
48 */
49 int socket;
50
51 /**
52 * key identity blob in ssh format
53 */
54 chunk_t key;
55
56 /**
57 * keysize in bytes
58 */
59 size_t key_size;
60
61 /**
62 * reference count
63 */
64 refcount_t ref;
65 };
66
67 /**
68 * Message types for ssh-agent protocol
69 */
70 enum agent_msg_type_t {
71 SSH_AGENT_FAILURE = 5,
72 SSH_AGENT_SUCCESS = 6,
73 SSH_AGENT_ID_REQUEST = 11,
74 SSH_AGENT_ID_RESPONSE = 12,
75 SSH_AGENT_SIGN_REQUEST = 13,
76 SSH_AGENT_SIGN_RESPONSE = 14,
77 };
78
79 /**
80 * read a byte from a blob
81 */
82 static u_char read_byte(chunk_t *blob)
83 {
84 u_char val;
85
86 if (blob->len < sizeof(u_char))
87 {
88 return 0;
89 }
90 val = *(blob->ptr);
91 *blob = chunk_skip(*blob, sizeof(u_char));
92 return val;
93 }
94
95 /**
96 * read a u_int32_t from a blob
97 */
98 static u_int32_t read_uint32(chunk_t *blob)
99 {
100 u_int32_t val;
101
102 if (blob->len < sizeof(u_int32_t))
103 {
104 return 0;
105 }
106 val = ntohl(*(u_int32_t*)blob->ptr);
107 *blob = chunk_skip(*blob, sizeof(u_int32_t));
108 return val;
109 }
110
111 /**
112 * read a ssh-agent "string" length/value from a blob
113 */
114 static chunk_t read_string(chunk_t *blob)
115 {
116 int len;
117 chunk_t str;
118
119 len = read_uint32(blob);
120 if (len > blob->len)
121 {
122 return chunk_empty;
123 }
124 str = chunk_create(blob->ptr, len);
125 *blob = chunk_skip(*blob, + len);
126 return str;
127 }
128
129 /**
130 * open socket connection to the ssh-agent
131 */
132 static int open_connection(char *path)
133 {
134 struct sockaddr_un addr;
135 int s;
136
137 s = socket(AF_UNIX, SOCK_STREAM, 0);
138 if (s == -1)
139 {
140 DBG1(DBG_LIB, "opening ssh-agent socket %s failed: %s:", path,
141 strerror(errno));
142 return -1;
143 }
144
145 addr.sun_family = AF_UNIX;
146 addr.sun_path[UNIX_PATH_MAX - 1] = '\0';
147 strncpy(addr.sun_path, path, UNIX_PATH_MAX - 1);
148
149 if (connect(s, (struct sockaddr*)&addr, SUN_LEN(&addr)) != 0)
150 {
151 DBG1(DBG_LIB, "connecting to ssh-agent socket failed: %s",
152 strerror(errno));
153 close(s);
154 return -1;
155 }
156 return s;
157 }
158
159 /**
160 * Get the first usable key from the agent
161 */
162 static bool read_key(private_agent_private_key_t *this, public_key_t *pubkey)
163 {
164 int len, count;
165 char buf[2048];
166 chunk_t blob, key, type, n;
167
168 len = htonl(1);
169 buf[0] = SSH_AGENT_ID_REQUEST;
170 if (write(this->socket, &len, sizeof(len)) != sizeof(len) ||
171 write(this->socket, &buf, 1) != 1)
172 {
173 DBG1(DBG_LIB, "writing to ssh-agent failed");
174 return FALSE;
175 }
176
177 blob = chunk_create(buf, sizeof(buf));
178 blob.len = read(this->socket, blob.ptr, blob.len);
179
180 if (blob.len < sizeof(u_int32_t) + sizeof(u_char) ||
181 read_uint32(&blob) != blob.len ||
182 read_byte(&blob) != SSH_AGENT_ID_RESPONSE)
183 {
184 DBG1(DBG_LIB, "received invalid ssh-agent identity response");
185 return FALSE;
186 }
187 count = read_uint32(&blob);
188
189 while (blob.len)
190 {
191 key = read_string(&blob);
192 if (!key.len)
193 {
194 break;
195 }
196 this->key = key;
197 type = read_string(&key);
198 if (!type.len || !strneq("ssh-rsa", type.ptr, type.len))
199 {
200 break;
201 }
202 read_string(&key);
203 n = read_string(&key);
204 if (n.len <= 512/8)
205 {
206 break;;
207 }
208 if (pubkey && !private_key_belongs_to(&this->public.key, pubkey))
209 {
210 continue;
211 }
212 this->key_size = n.len;
213 if (n.ptr[0] == 0)
214 {
215 this->key_size--;
216 }
217 this->key = chunk_clone(this->key);
218 return TRUE;
219 }
220 this->key = chunk_empty;
221 return FALSE;
222 }
223
224 METHOD(private_key_t, sign, bool,
225 private_agent_private_key_t *this, signature_scheme_t scheme,
226 chunk_t data, chunk_t *signature)
227 {
228 u_int32_t len, flags;
229 char buf[2048];
230 chunk_t blob;
231
232 if (scheme != SIGN_RSA_EMSA_PKCS1_SHA1)
233 {
234 DBG1(DBG_LIB, "signature scheme %N not supported by ssh-agent",
235 signature_scheme_names, scheme);
236 return FALSE;
237 }
238
239 len = htonl(1 + sizeof(u_int32_t) * 3 + this->key.len + data.len);
240 buf[0] = SSH_AGENT_SIGN_REQUEST;
241 if (write(this->socket, &len, sizeof(len)) != sizeof(len) ||
242 write(this->socket, &buf, 1) != 1)
243 {
244 DBG1(DBG_LIB, "writing to ssh-agent failed");
245 return FALSE;
246 }
247
248 len = htonl(this->key.len);
249 if (write(this->socket, &len, sizeof(len)) != sizeof(len) ||
250 write(this->socket, this->key.ptr, this->key.len) != this->key.len)
251 {
252 DBG1(DBG_LIB, "writing to ssh-agent failed");
253 return FALSE;
254 }
255
256 len = htonl(data.len);
257 if (write(this->socket, &len, sizeof(len)) != sizeof(len) ||
258 write(this->socket, data.ptr, data.len) != data.len)
259 {
260 DBG1(DBG_LIB, "writing to ssh-agent failed");
261 return FALSE;
262 }
263
264 flags = htonl(0);
265 if (write(this->socket, &flags, sizeof(flags)) != sizeof(flags))
266 {
267 DBG1(DBG_LIB, "writing to ssh-agent failed");
268 return FALSE;
269 }
270
271 blob = chunk_create(buf, sizeof(buf));
272 blob.len = read(this->socket, blob.ptr, blob.len);
273 if (blob.len < sizeof(u_int32_t) + sizeof(u_char) ||
274 read_uint32(&blob) != blob.len ||
275 read_byte(&blob) != SSH_AGENT_SIGN_RESPONSE)
276 {
277 DBG1(DBG_LIB, "received invalid ssh-agent signature response");
278 return FALSE;
279 }
280 /* parse length */
281 blob = read_string(&blob);
282 /* skip sig type */
283 read_string(&blob);
284 /* parse length */
285 blob = read_string(&blob);
286 if (!blob.len)
287 {
288 DBG1(DBG_LIB, "received invalid ssh-agent signature response");
289 return FALSE;
290 }
291 *signature = chunk_clone(blob);
292 return TRUE;
293 }
294
295 METHOD(private_key_t, get_type, key_type_t,
296 private_agent_private_key_t *this)
297 {
298 return KEY_RSA;
299 }
300
301 METHOD(private_key_t, decrypt, bool,
302 private_agent_private_key_t *this, encryption_scheme_t scheme,
303 chunk_t crypto, chunk_t *plain)
304 {
305 DBG1(DBG_LIB, "private key decryption not supported by ssh-agent");
306 return FALSE;
307 }
308
309 METHOD(private_key_t, get_keysize, int,
310 private_agent_private_key_t *this)
311 {
312 return this->key_size * 8;
313 }
314
315 METHOD(private_key_t, get_public_key, public_key_t*,
316 private_agent_private_key_t *this)
317 {
318 chunk_t key, n, e;
319
320 key = this->key;
321 read_string(&key);
322 e = read_string(&key);
323 n = read_string(&key);
324
325 return lib->creds->create(lib->creds, CRED_PUBLIC_KEY, KEY_RSA,
326 BUILD_RSA_MODULUS, n, BUILD_RSA_PUB_EXP, e, BUILD_END);
327 }
328
329 METHOD(private_key_t, get_encoding, bool,
330 private_agent_private_key_t *this, cred_encoding_type_t type,
331 chunk_t *encoding)
332 {
333 return FALSE;
334 }
335
336 METHOD(private_key_t, get_fingerprint, bool,
337 private_agent_private_key_t *this, cred_encoding_type_t type, chunk_t *fp)
338 {
339 chunk_t n, e, key;
340
341 if (lib->encoding->get_cache(lib->encoding, type, this, fp))
342 {
343 return TRUE;
344 }
345 key = this->key;
346 read_string(&key);
347 e = read_string(&key);
348 n = read_string(&key);
349
350 return lib->encoding->encode(lib->encoding, type, this, fp,
351 CRED_PART_RSA_MODULUS, n, CRED_PART_RSA_PUB_EXP, e, CRED_PART_END);
352 }
353
354 METHOD(private_key_t, get_ref, private_key_t*,
355 private_agent_private_key_t *this)
356 {
357 ref_get(&this->ref);
358 return &this->public.key;
359 }
360
361 METHOD(private_key_t, destroy, void,
362 private_agent_private_key_t *this)
363 {
364 if (ref_put(&this->ref))
365 {
366 close(this->socket);
367 free(this->key.ptr);
368 lib->encoding->clear_cache(lib->encoding, this);
369 free(this);
370 }
371 }
372
373 /**
374 * See header.
375 */
376 agent_private_key_t *agent_private_key_open(key_type_t type, va_list args)
377 {
378 private_agent_private_key_t *this;
379 public_key_t *pubkey = NULL;
380 char *path = NULL;
381
382 while (TRUE)
383 {
384 switch (va_arg(args, builder_part_t))
385 {
386 case BUILD_AGENT_SOCKET:
387 path = va_arg(args, char*);
388 continue;
389 case BUILD_PUBLIC_KEY:
390 pubkey = va_arg(args, public_key_t*);
391 continue;
392 case BUILD_END:
393 break;
394 default:
395 return NULL;
396 }
397 break;
398 }
399 if (!path)
400 {
401 return FALSE;
402 }
403
404 INIT(this,
405 .public.key = {
406 .get_type = _get_type,
407 .sign = _sign,
408 .decrypt = _decrypt,
409 .get_keysize = _get_keysize,
410 .get_public_key = _get_public_key,
411 .belongs_to = private_key_belongs_to,
412 .equals = private_key_equals,
413 .get_fingerprint = _get_fingerprint,
414 .has_fingerprint = private_key_has_fingerprint,
415 .get_encoding = _get_encoding,
416 .get_ref = _get_ref,
417 .destroy = _destroy,
418 },
419 .ref = 1,
420 );
421
422 this->socket = open_connection(path);
423 if (this->socket < 0)
424 {
425 free(this);
426 return NULL;
427 }
428 if (!read_key(this, pubkey))
429 {
430 destroy(this);
431 return NULL;
432 }
433 return &this->public;
434 }
435