2bc6dea34fb5b52c6993d3b0e760eec238977b8c
[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("opening ssh-agent socket %s failed: %s:", path, strerror(errno));
141 return -1;
142 }
143
144 addr.sun_family = AF_UNIX;
145 addr.sun_path[UNIX_PATH_MAX - 1] = '\0';
146 strncpy(addr.sun_path, path, UNIX_PATH_MAX - 1);
147
148 if (connect(s, (struct sockaddr*)&addr, SUN_LEN(&addr)) != 0)
149 {
150 DBG1("connecting to ssh-agent socket failed: %s", strerror(errno));
151 close(s);
152 return -1;
153 }
154 return s;
155 }
156
157 /**
158 * Get the first usable key from the agent
159 */
160 static bool read_key(private_agent_private_key_t *this, public_key_t *pubkey)
161 {
162 int len, count;
163 char buf[2048];
164 chunk_t blob, key, type, n;
165
166 len = htonl(1);
167 buf[0] = SSH_AGENT_ID_REQUEST;
168 if (write(this->socket, &len, sizeof(len)) != sizeof(len) ||
169 write(this->socket, &buf, 1) != 1)
170 {
171 DBG1("writing to ssh-agent failed");
172 return FALSE;
173 }
174
175 blob = chunk_create(buf, sizeof(buf));
176 blob.len = read(this->socket, blob.ptr, blob.len);
177
178 if (blob.len < sizeof(u_int32_t) + sizeof(u_char) ||
179 read_uint32(&blob) != blob.len ||
180 read_byte(&blob) != SSH_AGENT_ID_RESPONSE)
181 {
182 DBG1("received invalid ssh-agent identity response");
183 return FALSE;
184 }
185 count = read_uint32(&blob);
186
187 while (blob.len)
188 {
189 key = read_string(&blob);
190 if (!key.len)
191 {
192 break;
193 }
194 this->key = key;
195 type = read_string(&key);
196 if (!type.len || !strneq("ssh-rsa", type.ptr, type.len))
197 {
198 break;
199 }
200 read_string(&key);
201 n = read_string(&key);
202 if (n.len <= 512/8)
203 {
204 break;;
205 }
206 if (pubkey && !private_key_belongs_to(&this->public.interface, pubkey))
207 {
208 continue;
209 }
210 this->key_size = n.len;
211 if (n.ptr[0] == 0)
212 {
213 this->key_size--;
214 }
215 this->key = chunk_clone(this->key);
216 return TRUE;
217 }
218 this->key = chunk_empty;
219 return FALSE;
220 }
221
222 /**
223 * Implementation of agent_private_key.destroy.
224 */
225 static bool sign(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("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("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("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("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("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("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("received invalid ssh-agent signature response");
289 return FALSE;
290 }
291 *signature = chunk_clone(blob);
292 return TRUE;
293 }
294
295 /**
296 * Implementation of agent_private_key.destroy.
297 */
298 static key_type_t get_type(private_agent_private_key_t *this)
299 {
300 return KEY_RSA;
301 }
302
303 /**
304 * Implementation of agent_private_key.destroy.
305 */
306 static bool decrypt(private_agent_private_key_t *this,
307 chunk_t crypto, chunk_t *plain)
308 {
309 DBG1("private key decryption not supported by ssh-agent");
310 return FALSE;
311 }
312
313 /**
314 * Implementation of agent_private_key.destroy.
315 */
316 static size_t get_keysize(private_agent_private_key_t *this)
317 {
318 return this->key_size;
319 }
320
321 /**
322 * Implementation of agent_private_key.get_public_key.
323 */
324 static public_key_t* get_public_key(private_agent_private_key_t *this)
325 {
326 chunk_t key, n, e;
327
328 key = this->key;
329 read_string(&key);
330 e = read_string(&key);
331 n = read_string(&key);
332
333 return lib->creds->create(lib->creds, CRED_PUBLIC_KEY, KEY_RSA,
334 BUILD_RSA_MODULUS, n, BUILD_RSA_PUB_EXP, e, BUILD_END);
335 }
336
337 /**
338 * Implementation of private_key_t.get_encoding
339 */
340 static bool get_encoding(private_agent_private_key_t *this,
341 key_encoding_type_t type, chunk_t *encoding)
342 {
343 return FALSE;
344 }
345
346 /**
347 * Implementation of private_key_t.get_fingerprint
348 */
349 static bool get_fingerprint(private_agent_private_key_t *this,
350 key_encoding_type_t type, chunk_t *fp)
351 {
352 chunk_t n, e, key;
353
354 if (lib->encoding->get_cache(lib->encoding, type, this, fp))
355 {
356 return TRUE;
357 }
358 key = this->key;
359 read_string(&key);
360 e = read_string(&key);
361 n = read_string(&key);
362
363 return lib->encoding->encode(lib->encoding, type, this, fp,
364 KEY_PART_RSA_MODULUS, n, KEY_PART_RSA_PUB_EXP, e, KEY_PART_END);
365 }
366
367 /**
368 * Implementation of agent_private_key.get_ref.
369 */
370 static private_agent_private_key_t* get_ref(private_agent_private_key_t *this)
371 {
372 ref_get(&this->ref);
373 return this;
374 }
375
376 /**
377 * Implementation of agent_private_key.destroy.
378 */
379 static void destroy(private_agent_private_key_t *this)
380 {
381 if (ref_put(&this->ref))
382 {
383 close(this->socket);
384 free(this->key.ptr);
385 lib->encoding->clear_cache(lib->encoding, this);
386 free(this);
387 }
388 }
389
390 /**
391 * See header.
392 */
393 agent_private_key_t *agent_private_key_open(key_type_t type, va_list args)
394 {
395 private_agent_private_key_t *this;
396 public_key_t *pubkey = NULL;
397 char *path = NULL;
398
399 while (TRUE)
400 {
401 switch (va_arg(args, builder_part_t))
402 {
403 case BUILD_AGENT_SOCKET:
404 path = va_arg(args, char*);
405 continue;
406 case BUILD_PUBLIC_KEY:
407 pubkey = va_arg(args, public_key_t*);
408 continue;
409 case BUILD_END:
410 break;
411 default:
412 return NULL;
413 }
414 break;
415 }
416 if (!path)
417 {
418 return FALSE;
419 }
420
421 this = malloc_thing(private_agent_private_key_t);
422
423 this->public.interface.get_type = (key_type_t (*)(private_key_t *this))get_type;
424 this->public.interface.sign = (bool (*)(private_key_t *this, signature_scheme_t scheme, chunk_t data, chunk_t *signature))sign;
425 this->public.interface.decrypt = (bool (*)(private_key_t *this, chunk_t crypto, chunk_t *plain))decrypt;
426 this->public.interface.get_keysize = (size_t (*) (private_key_t *this))get_keysize;
427 this->public.interface.get_public_key = (public_key_t* (*)(private_key_t *this))get_public_key;
428 this->public.interface.belongs_to = private_key_belongs_to;
429 this->public.interface.equals = private_key_equals;
430 this->public.interface.get_fingerprint = (bool(*)(private_key_t*, key_encoding_type_t type, chunk_t *fp))get_fingerprint;
431 this->public.interface.get_encoding = (bool(*)(private_key_t*, key_encoding_type_t type, chunk_t *encoding))get_encoding;
432 this->public.interface.get_ref = (private_key_t* (*)(private_key_t *this))get_ref;
433 this->public.interface.destroy = (void (*)(private_key_t *this))destroy;
434
435 this->socket = open_connection(path);
436 if (this->socket < 0)
437 {
438 free(this);
439 return NULL;
440 }
441 this->key = chunk_empty;
442 this->ref = 1;
443
444 if (!read_key(this, pubkey))
445 {
446 destroy(this);
447 return NULL;
448 }
449 return &this->public;
450 }
451