Updated agent plugin to the new builder API
[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 = chunk_from_buf(buf), 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.len = read(this->socket, blob.ptr, blob.len);
176
177 if (blob.len < sizeof(u_int32_t) + sizeof(u_char) ||
178 read_uint32(&blob) != blob.len ||
179 read_byte(&blob) != SSH_AGENT_ID_RESPONSE)
180 {
181 DBG1("received invalid ssh-agent identity response");
182 return FALSE;
183 }
184 count = read_uint32(&blob);
185
186 while (blob.len)
187 {
188 key = read_string(&blob);
189 if (!key.len)
190 {
191 break;
192 }
193 this->key = key;
194 type = read_string(&key);
195 if (!type.len || !strneq("ssh-rsa", type.ptr, type.len))
196 {
197 break;
198 }
199 read_string(&key);
200 n = read_string(&key);
201 if (n.len <= 512/8)
202 {
203 break;;
204 }
205 if (pubkey && !private_key_belongs_to(&this->public.interface, pubkey))
206 {
207 continue;
208 }
209 this->key_size = n.len;
210 if (n.ptr[0] == 0)
211 {
212 this->key_size--;
213 }
214 this->key = chunk_clone(this->key);
215 return TRUE;
216 }
217 this->key = chunk_empty;
218 return FALSE;
219 }
220
221 /**
222 * Implementation of agent_private_key.destroy.
223 */
224 static bool sign(private_agent_private_key_t *this, signature_scheme_t scheme,
225 chunk_t data, chunk_t *signature)
226 {
227 u_int32_t len, flags;
228 char buf[2048];
229 chunk_t blob = chunk_from_buf(buf);
230
231 if (scheme != SIGN_RSA_EMSA_PKCS1_SHA1)
232 {
233 DBG1("signature scheme %N not supported by ssh-agent",
234 signature_scheme_names, scheme);
235 return FALSE;
236 }
237
238 len = htonl(1 + sizeof(u_int32_t) * 3 + this->key.len + data.len);
239 buf[0] = SSH_AGENT_SIGN_REQUEST;
240 if (write(this->socket, &len, sizeof(len)) != sizeof(len) ||
241 write(this->socket, &buf, 1) != 1)
242 {
243 DBG1("writing to ssh-agent failed");
244 return FALSE;
245 }
246
247 len = htonl(this->key.len);
248 if (write(this->socket, &len, sizeof(len)) != sizeof(len) ||
249 write(this->socket, this->key.ptr, this->key.len) != this->key.len)
250 {
251 DBG1("writing to ssh-agent failed");
252 return FALSE;
253 }
254
255 len = htonl(data.len);
256 if (write(this->socket, &len, sizeof(len)) != sizeof(len) ||
257 write(this->socket, data.ptr, data.len) != data.len)
258 {
259 DBG1("writing to ssh-agent failed");
260 return FALSE;
261 }
262
263 flags = htonl(0);
264 if (write(this->socket, &flags, sizeof(flags)) != sizeof(flags))
265 {
266 DBG1("writing to ssh-agent failed");
267 return FALSE;
268 }
269
270 blob.len = read(this->socket, blob.ptr, blob.len);
271 if (blob.len < sizeof(u_int32_t) + sizeof(u_char) ||
272 read_uint32(&blob) != blob.len ||
273 read_byte(&blob) != SSH_AGENT_SIGN_RESPONSE)
274 {
275 DBG1("received invalid ssh-agent signature response");
276 return FALSE;
277 }
278 /* parse length */
279 blob = read_string(&blob);
280 /* skip sig type */
281 read_string(&blob);
282 /* parse length */
283 blob = read_string(&blob);
284 if (!blob.len)
285 {
286 DBG1("received invalid ssh-agent signature response");
287 return FALSE;
288 }
289 *signature = chunk_clone(blob);
290 return TRUE;
291 }
292
293 /**
294 * Implementation of agent_private_key.destroy.
295 */
296 static key_type_t get_type(private_agent_private_key_t *this)
297 {
298 return KEY_RSA;
299 }
300
301 /**
302 * Implementation of agent_private_key.destroy.
303 */
304 static bool decrypt(private_agent_private_key_t *this,
305 chunk_t crypto, chunk_t *plain)
306 {
307 DBG1("private key decryption not supported by ssh-agent");
308 return FALSE;
309 }
310
311 /**
312 * Implementation of agent_private_key.destroy.
313 */
314 static size_t get_keysize(private_agent_private_key_t *this)
315 {
316 return this->key_size;
317 }
318
319 /**
320 * Implementation of agent_private_key.get_public_key.
321 */
322 static public_key_t* get_public_key(private_agent_private_key_t *this)
323 {
324 chunk_t key, n, e;
325
326 key = this->key;
327 read_string(&key);
328 e = read_string(&key);
329 n = read_string(&key);
330
331 return lib->creds->create(lib->creds, CRED_PUBLIC_KEY, KEY_RSA,
332 BUILD_RSA_MODULUS, n, BUILD_RSA_PUB_EXP, e, BUILD_END);
333 }
334
335 /**
336 * Implementation of private_key_t.get_encoding
337 */
338 static bool get_encoding(private_agent_private_key_t *this,
339 key_encoding_type_t type, chunk_t *encoding)
340 {
341 return FALSE;
342 }
343
344 /**
345 * Implementation of private_key_t.get_fingerprint
346 */
347 static bool get_fingerprint(private_agent_private_key_t *this,
348 key_encoding_type_t type, chunk_t *fp)
349 {
350 chunk_t n, e, key;
351
352 if (lib->encoding->get_cache(lib->encoding, type, this, fp))
353 {
354 return TRUE;
355 }
356 key = this->key;
357 read_string(&key);
358 e = read_string(&key);
359 n = read_string(&key);
360
361 return lib->encoding->encode(lib->encoding, type, this, fp,
362 KEY_PART_RSA_MODULUS, n, KEY_PART_RSA_PUB_EXP, e, KEY_PART_END);
363 }
364
365 /**
366 * Implementation of agent_private_key.get_ref.
367 */
368 static private_agent_private_key_t* get_ref(private_agent_private_key_t *this)
369 {
370 ref_get(&this->ref);
371 return this;
372 }
373
374 /**
375 * Implementation of agent_private_key.destroy.
376 */
377 static void destroy(private_agent_private_key_t *this)
378 {
379 if (ref_put(&this->ref))
380 {
381 close(this->socket);
382 free(this->key.ptr);
383 lib->encoding->clear_cache(lib->encoding, this);
384 free(this);
385 }
386 }
387
388 /**
389 * See header.
390 */
391 agent_private_key_t *agent_private_key_open(key_type_t type, va_list args)
392 {
393 private_agent_private_key_t *this;
394 public_key_t *pubkey = NULL;
395 char *path = NULL;
396
397 while (TRUE)
398 {
399 switch (va_arg(args, builder_part_t))
400 {
401 case BUILD_AGENT_SOCKET:
402 path = va_arg(args, char*);
403 continue;
404 case BUILD_PUBLIC_KEY:
405 pubkey = va_arg(args, public_key_t*);
406 continue;
407 case BUILD_END:
408 break;
409 default:
410 return NULL;
411 }
412 break;
413 }
414 if (!path)
415 {
416 return FALSE;
417 }
418
419 this = malloc_thing(private_agent_private_key_t);
420
421 this->public.interface.get_type = (key_type_t (*)(private_key_t *this))get_type;
422 this->public.interface.sign = (bool (*)(private_key_t *this, signature_scheme_t scheme, chunk_t data, chunk_t *signature))sign;
423 this->public.interface.decrypt = (bool (*)(private_key_t *this, chunk_t crypto, chunk_t *plain))decrypt;
424 this->public.interface.get_keysize = (size_t (*) (private_key_t *this))get_keysize;
425 this->public.interface.get_public_key = (public_key_t* (*)(private_key_t *this))get_public_key;
426 this->public.interface.belongs_to = private_key_belongs_to;
427 this->public.interface.equals = private_key_equals;
428 this->public.interface.get_fingerprint = (bool(*)(private_key_t*, key_encoding_type_t type, chunk_t *fp))get_fingerprint;
429 this->public.interface.get_encoding = (bool(*)(private_key_t*, key_encoding_type_t type, chunk_t *encoding))get_encoding;
430 this->public.interface.get_ref = (private_key_t* (*)(private_key_t *this))get_ref;
431 this->public.interface.destroy = (void (*)(private_key_t *this))destroy;
432
433 this->socket = open_connection(path);
434 if (this->socket < 0)
435 {
436 free(this);
437 return NULL;
438 }
439 this->key = chunk_empty;
440 this->ref = 1;
441
442 if (!read_key(this, pubkey))
443 {
444 destroy(this);
445 return NULL;
446 }
447 return &this->public;
448 }
449