2 * Copyright (C) 2008-2009 Martin Willi
3 * Hochschule fuer Technik Rapperswil
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>.
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
16 #include "agent_private_key.h"
20 #include <sys/types.h>
21 #include <sys/socket.h>
23 #include <arpa/inet.h>
31 #define UNIX_PATH_MAX 108
32 #endif /* UNIX_PATH_MAX */
34 typedef struct private_agent_private_key_t private_agent_private_key_t
;
35 typedef enum agent_msg_type_t agent_msg_type_t
;
38 * Private data of a agent_private_key_t object.
40 struct private_agent_private_key_t
{
42 * Public interface for this signer.
44 agent_private_key_t
public;
47 * ssh-agent unix socket connection
52 * key identity blob in ssh format
68 * Message types for ssh-agent protocol
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,
80 * read a byte from a blob
82 static u_char
read_byte(chunk_t
*blob
)
86 if (blob
->len
< sizeof(u_char
))
91 *blob
= chunk_skip(*blob
, sizeof(u_char
));
96 * read a u_int32_t from a blob
98 static u_int32_t
read_uint32(chunk_t
*blob
)
102 if (blob
->len
< sizeof(u_int32_t
))
106 val
= ntohl(*(u_int32_t
*)blob
->ptr
);
107 *blob
= chunk_skip(*blob
, sizeof(u_int32_t
));
112 * read a ssh-agent "string" length/value from a blob
114 static chunk_t
read_string(chunk_t
*blob
)
119 len
= read_uint32(blob
);
124 str
= chunk_create(blob
->ptr
, len
);
125 *blob
= chunk_skip(*blob
, + len
);
130 * open socket connection to the ssh-agent
132 static int open_connection(char *path
)
134 struct sockaddr_un addr
;
137 s
= socket(AF_UNIX
, SOCK_STREAM
, 0);
140 DBG1("opening ssh-agent socket %s failed: %s:", path
, strerror(errno
));
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);
148 if (connect(s
, (struct sockaddr
*)&addr
, SUN_LEN(&addr
)) != 0)
150 DBG1("connecting to ssh-agent socket failed: %s", strerror(errno
));
158 * Get the first usable key from the agent
160 static bool read_key(private_agent_private_key_t
*this, public_key_t
*pubkey
)
164 chunk_t blob
, key
, type
, n
;
167 buf
[0] = SSH_AGENT_ID_REQUEST
;
168 if (write(this->socket
, &len
, sizeof(len
)) != sizeof(len
) ||
169 write(this->socket
, &buf
, 1) != 1)
171 DBG1("writing to ssh-agent failed");
175 blob
= chunk_create(buf
, sizeof(buf
));
176 blob
.len
= read(this->socket
, blob
.ptr
, blob
.len
);
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
)
182 DBG1("received invalid ssh-agent identity response");
185 count
= read_uint32(&blob
);
189 key
= read_string(&blob
);
195 type
= read_string(&key
);
196 if (!type
.len
|| !strneq("ssh-rsa", type
.ptr
, type
.len
))
201 n
= read_string(&key
);
206 if (pubkey
&& !private_key_belongs_to(&this->public.interface
, pubkey
))
210 this->key_size
= n
.len
;
215 this->key
= chunk_clone(this->key
);
218 this->key
= chunk_empty
;
223 * Implementation of agent_private_key.destroy.
225 static bool sign(private_agent_private_key_t
*this, signature_scheme_t scheme
,
226 chunk_t data
, chunk_t
*signature
)
228 u_int32_t len
, flags
;
232 if (scheme
!= SIGN_RSA_EMSA_PKCS1_SHA1
)
234 DBG1("signature scheme %N not supported by ssh-agent",
235 signature_scheme_names
, scheme
);
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)
244 DBG1("writing to ssh-agent failed");
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
)
252 DBG1("writing to ssh-agent failed");
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
)
260 DBG1("writing to ssh-agent failed");
265 if (write(this->socket
, &flags
, sizeof(flags
)) != sizeof(flags
))
267 DBG1("writing to ssh-agent failed");
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
)
277 DBG1("received invalid ssh-agent signature response");
281 blob
= read_string(&blob
);
285 blob
= read_string(&blob
);
288 DBG1("received invalid ssh-agent signature response");
291 *signature
= chunk_clone(blob
);
296 * Implementation of agent_private_key.destroy.
298 static key_type_t
get_type(private_agent_private_key_t
*this)
304 * Implementation of agent_private_key.destroy.
306 static bool decrypt(private_agent_private_key_t
*this,
307 chunk_t crypto
, chunk_t
*plain
)
309 DBG1("private key decryption not supported by ssh-agent");
314 * Implementation of agent_private_key.destroy.
316 static size_t get_keysize(private_agent_private_key_t
*this)
318 return this->key_size
;
322 * Implementation of agent_private_key.get_public_key.
324 static public_key_t
* get_public_key(private_agent_private_key_t
*this)
330 e
= read_string(&key
);
331 n
= read_string(&key
);
333 return lib
->creds
->create(lib
->creds
, CRED_PUBLIC_KEY
, KEY_RSA
,
334 BUILD_RSA_MODULUS
, n
, BUILD_RSA_PUB_EXP
, e
, BUILD_END
);
338 * Implementation of private_key_t.get_encoding
340 static bool get_encoding(private_agent_private_key_t
*this,
341 key_encoding_type_t type
, chunk_t
*encoding
)
347 * Implementation of private_key_t.get_fingerprint
349 static bool get_fingerprint(private_agent_private_key_t
*this,
350 key_encoding_type_t type
, chunk_t
*fp
)
354 if (lib
->encoding
->get_cache(lib
->encoding
, type
, this, fp
))
360 e
= read_string(&key
);
361 n
= read_string(&key
);
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
);
368 * Implementation of agent_private_key.get_ref.
370 static private_agent_private_key_t
* get_ref(private_agent_private_key_t
*this)
377 * Implementation of agent_private_key.destroy.
379 static void destroy(private_agent_private_key_t
*this)
381 if (ref_put(&this->ref
))
385 lib
->encoding
->clear_cache(lib
->encoding
, this);
393 agent_private_key_t
*agent_private_key_open(key_type_t type
, va_list args
)
395 private_agent_private_key_t
*this;
396 public_key_t
*pubkey
= NULL
;
401 switch (va_arg(args
, builder_part_t
))
403 case BUILD_AGENT_SOCKET
:
404 path
= va_arg(args
, char*);
406 case BUILD_PUBLIC_KEY
:
407 pubkey
= va_arg(args
, public_key_t
*);
421 this = malloc_thing(private_agent_private_key_t
);
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
;
435 this->socket
= open_connection(path
);
436 if (this->socket
< 0)
441 this->key
= chunk_empty
;
444 if (!read_key(this, pubkey
))
449 return &this->public;