]> git.ipfire.org Git - people/ms/strongswan.git/blob - src/libstrongswan/plugins/agent/agent_private_key.c
credentials: Added void *params to public_key encrypt() and private_key decrypt(...
[people/ms/strongswan.git] / src / libstrongswan / plugins / agent / agent_private_key.c
1 /*
2 * Copyright (C) 2013-2019 Tobias Brunner
3 * Copyright (C) 2008-2009 Martin Willi
4 * HSR Hochschule fuer Technik Rapperswil
5 *
6 * This program is free software; you can redistribute it and/or modify it
7 * under the terms of the GNU General Public License as published by the
8 * Free Software Foundation; either version 2 of the License, or (at your
9 * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
10 *
11 * This program is distributed in the hope that it will be useful, but
12 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
13 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14 * for more details.
15 */
16
17 #include "agent_private_key.h"
18
19 #include <stdlib.h>
20 #include <unistd.h>
21 #include <sys/types.h>
22 #include <sys/socket.h>
23 #include <sys/un.h>
24 #include <arpa/inet.h>
25 #include <errno.h>
26
27 #include <library.h>
28 #include <utils/chunk.h>
29 #include <utils/debug.h>
30
31 #ifndef UNIX_PATH_MAX
32 #define UNIX_PATH_MAX 108
33 #endif /* UNIX_PATH_MAX */
34
35 typedef struct private_agent_private_key_t private_agent_private_key_t;
36 typedef enum agent_msg_type_t agent_msg_type_t;
37
38 /**
39 * Private data of a agent_private_key_t object.
40 */
41 struct private_agent_private_key_t {
42 /**
43 * Public interface for this signer.
44 */
45 agent_private_key_t public;
46
47 /**
48 * Path to the UNIX socket
49 */
50 char *path;
51
52 /**
53 * public key encoded in SSH format
54 */
55 chunk_t key;
56
57 /**
58 * public key
59 */
60 public_key_t *pubkey;
61
62 /**
63 * keysize in bytes
64 */
65 size_t key_size;
66
67 /**
68 * reference count
69 */
70 refcount_t ref;
71 };
72
73 /**
74 * Message types for ssh-agent protocol
75 */
76 enum agent_msg_type_t {
77 SSH_AGENT_FAILURE = 5,
78 SSH_AGENT_SUCCESS = 6,
79 SSH_AGENT_ID_REQUEST = 11,
80 SSH_AGENT_ID_RESPONSE = 12,
81 SSH_AGENT_SIGN_REQUEST = 13,
82 SSH_AGENT_SIGN_RESPONSE = 14,
83 };
84
85 /**
86 * Flags for signatures
87 */
88 enum agent_signature_flags_t {
89 SSH_AGENT_FLAG_SHA2_256 = 2,
90 SSH_AGENT_FLAG_SHA2_512 = 4,
91 };
92
93 /**
94 * read a byte from a blob
95 */
96 static u_char read_byte(chunk_t *blob)
97 {
98 u_char val;
99
100 if (blob->len < sizeof(u_char))
101 {
102 return 0;
103 }
104 val = *(blob->ptr);
105 *blob = chunk_skip(*blob, sizeof(u_char));
106 return val;
107 }
108
109 /**
110 * read a uint32_t from a blob
111 */
112 static uint32_t read_uint32(chunk_t *blob)
113 {
114 uint32_t val;
115
116 if (blob->len < sizeof(uint32_t))
117 {
118 return 0;
119 }
120 val = ntohl(*(uint32_t*)blob->ptr);
121 *blob = chunk_skip(*blob, sizeof(uint32_t));
122 return val;
123 }
124
125 /**
126 * read a ssh-agent "string" length/value from a blob
127 */
128 static chunk_t read_string(chunk_t *blob)
129 {
130 int len;
131 chunk_t str;
132
133 len = read_uint32(blob);
134 if (len > blob->len)
135 {
136 return chunk_empty;
137 }
138 str = chunk_create(blob->ptr, len);
139 *blob = chunk_skip(*blob, + len);
140 return str;
141 }
142
143 /**
144 * open socket connection to the ssh-agent
145 */
146 static int open_connection(char *path)
147 {
148 struct sockaddr_un addr;
149 int s;
150
151 s = socket(AF_UNIX, SOCK_STREAM, 0);
152 if (s == -1)
153 {
154 DBG1(DBG_LIB, "opening ssh-agent socket %s failed: %s:", path,
155 strerror(errno));
156 return -1;
157 }
158
159 addr.sun_family = AF_UNIX;
160 addr.sun_path[UNIX_PATH_MAX - 1] = '\0';
161 strncpy(addr.sun_path, path, UNIX_PATH_MAX - 1);
162
163 if (connect(s, (struct sockaddr*)&addr, SUN_LEN(&addr)) != 0)
164 {
165 DBG1(DBG_LIB, "connecting to ssh-agent socket failed: %s",
166 strerror(errno));
167 close(s);
168 return -1;
169 }
170 return s;
171 }
172
173 /**
174 * Get the first usable key from the agent
175 */
176 static bool read_key(private_agent_private_key_t *this, public_key_t *pubkey)
177 {
178 int socket, len;
179 char buf[2048];
180 chunk_t blob, key;
181 bool success = FALSE;
182
183 socket = open_connection(this->path);
184 if (socket < 0)
185 {
186 return FALSE;
187 }
188
189 len = htonl(1);
190 buf[0] = SSH_AGENT_ID_REQUEST;
191 if (write(socket, &len, sizeof(len)) != sizeof(len) ||
192 write(socket, &buf, 1) != 1)
193 {
194 DBG1(DBG_LIB, "writing to ssh-agent failed");
195 goto done;
196 }
197
198 blob = chunk_create(buf, sizeof(buf));
199 blob.len = read(socket, blob.ptr, blob.len);
200
201 if (blob.len < sizeof(uint32_t) + sizeof(u_char) ||
202 read_uint32(&blob) != blob.len ||
203 read_byte(&blob) != SSH_AGENT_ID_RESPONSE)
204 {
205 DBG1(DBG_LIB, "received invalid ssh-agent identity response");
206 goto done;
207 }
208 read_uint32(&blob);
209
210 while (blob.len)
211 {
212 key = read_string(&blob);
213 if (!key.len)
214 {
215 break;
216 }
217 this->pubkey = lib->creds->create(lib->creds, CRED_PUBLIC_KEY, KEY_ANY,
218 BUILD_BLOB_SSHKEY, key, BUILD_END);
219 if (!this->pubkey)
220 {
221 continue;
222 }
223 if (pubkey && !private_key_belongs_to(&this->public.key, pubkey))
224 {
225 this->pubkey->destroy(this->pubkey);
226 this->pubkey = NULL;
227 continue;
228 }
229 this->key = chunk_clone(key);
230 success = TRUE;
231 break;
232 }
233 done:
234 close(socket);
235 return success;
236 }
237
238 static bool scheme_supported(private_agent_private_key_t *this,
239 signature_scheme_t scheme, uint32_t *flags,
240 char **prefix)
241 {
242 switch (this->pubkey->get_type(this->pubkey))
243 {
244 case KEY_RSA:
245 switch (scheme)
246 {
247 case SIGN_RSA_EMSA_PKCS1_SHA1:
248 *prefix = "ssh-rsa";
249 return TRUE;
250 case SIGN_RSA_EMSA_PKCS1_SHA2_256:
251 *flags |= SSH_AGENT_FLAG_SHA2_256;
252 *prefix = "rsa-sha2-256";
253 return TRUE;
254 case SIGN_RSA_EMSA_PKCS1_SHA2_512:
255 *flags |= SSH_AGENT_FLAG_SHA2_512;
256 *prefix = "rsa-sha2-512";
257 return TRUE;
258 default:
259 break;
260 }
261 return FALSE;
262 case KEY_ED25519:
263 *prefix = "ssh-ed25519";
264 return scheme == SIGN_ED25519;
265 case KEY_ED448:
266 *prefix = "ssh-ed448";
267 return scheme == SIGN_ED448;
268 case KEY_ECDSA:
269 return scheme == SIGN_ECDSA_256 ||
270 scheme == SIGN_ECDSA_384 ||
271 scheme == SIGN_ECDSA_521;
272 default:
273 return FALSE;
274 }
275 }
276
277 METHOD(private_key_t, sign, bool,
278 private_agent_private_key_t *this, signature_scheme_t scheme, void *params,
279 chunk_t data, chunk_t *signature)
280 {
281 key_type_t type;
282 uint32_t len, flags = 0;
283 char buf[2048], *prefix = NULL;
284 chunk_t blob;
285 int socket;
286 bool success = FALSE;
287
288 if (!scheme_supported(this, scheme, &flags, &prefix))
289 {
290 DBG1(DBG_LIB, "signature scheme %N not supported by ssh-agent",
291 signature_scheme_names, scheme);
292 return FALSE;
293 }
294
295 socket = open_connection(this->path);
296 if (socket < 0)
297 {
298 return FALSE;
299 }
300
301 len = htonl(1 + sizeof(uint32_t) * 3 + this->key.len + data.len);
302 buf[0] = SSH_AGENT_SIGN_REQUEST;
303 if (write(socket, &len, sizeof(len)) != sizeof(len) ||
304 write(socket, &buf, 1) != 1)
305 {
306 DBG1(DBG_LIB, "writing to ssh-agent failed");
307 goto done;
308 }
309
310 len = htonl(this->key.len);
311 if (write(socket, &len, sizeof(len)) != sizeof(len) ||
312 write(socket, this->key.ptr, this->key.len) != this->key.len)
313 {
314 DBG1(DBG_LIB, "writing to ssh-agent failed");
315 goto done;
316 }
317
318 len = htonl(data.len);
319 if (write(socket, &len, sizeof(len)) != sizeof(len) ||
320 write(socket, data.ptr, data.len) != data.len)
321 {
322 DBG1(DBG_LIB, "writing to ssh-agent failed");
323 goto done;
324 }
325
326 flags = htonl(flags);
327 if (write(socket, &flags, sizeof(flags)) != sizeof(flags))
328 {
329 DBG1(DBG_LIB, "writing to ssh-agent failed");
330 goto done;
331 }
332
333 blob = chunk_create(buf, sizeof(buf));
334 blob.len = read(socket, blob.ptr, blob.len);
335 if (blob.len < sizeof(uint32_t) + sizeof(u_char) ||
336 read_uint32(&blob) != blob.len ||
337 read_byte(&blob) != SSH_AGENT_SIGN_RESPONSE)
338 {
339 DBG1(DBG_LIB, "received invalid ssh-agent signature response");
340 goto done;
341 }
342 /* parse length */
343 blob = read_string(&blob);
344 /* verify type */
345 if (prefix && !chunk_equals(read_string(&blob), chunk_from_str(prefix)))
346 {
347 DBG1(DBG_LIB, "ssh-agent didn't return requested %s signature", prefix);
348 goto done;
349 }
350 type = this->pubkey->get_type(this->pubkey);
351 if (type == KEY_RSA || type == KEY_ED25519 || type == KEY_ED448)
352 { /* for RSA/EdDSA, the signature has no special encoding */
353 blob = read_string(&blob);
354 if (blob.len)
355 {
356 *signature = chunk_clone(blob);
357 success = TRUE;
358 }
359 }
360 else
361 { /* parse ECDSA signatures */
362 blob = read_string(&blob);
363 if (blob.len)
364 {
365 chunk_t r, s;
366
367 r = read_string(&blob);
368 s = read_string(&blob);
369 if (r.len && s.len)
370 {
371 *signature = chunk_cat("cc", r, s);
372 success = TRUE;
373 }
374 }
375 }
376 if (!success)
377 {
378 DBG1(DBG_LIB, "received invalid ssh-agent signature response");
379 }
380
381 done:
382 close(socket);
383 return success;
384 }
385
386 METHOD(private_key_t, get_type, key_type_t,
387 private_agent_private_key_t *this)
388 {
389 return this->pubkey->get_type(this->pubkey);
390 }
391
392 METHOD(private_key_t, decrypt, bool,
393 private_agent_private_key_t *this, encryption_scheme_t scheme,
394 void *params, chunk_t crypto, chunk_t *plain)
395 {
396 DBG1(DBG_LIB, "private key decryption not supported by ssh-agent");
397 return FALSE;
398 }
399
400 METHOD(private_key_t, get_keysize, int,
401 private_agent_private_key_t *this)
402 {
403 return this->pubkey->get_keysize(this->pubkey);
404 }
405
406 /**
407 * Private data for RSA scheme enumerator
408 */
409 typedef struct {
410 enumerator_t public;
411 int index;
412 bool reverse;
413 } scheme_enumerator_t;
414
415 static signature_params_t rsa_schemes[] = {
416 { .scheme = SIGN_RSA_EMSA_PKCS1_SHA2_256 },
417 { .scheme = SIGN_RSA_EMSA_PKCS1_SHA2_512 },
418 };
419
420 METHOD(enumerator_t, enumerate_rsa_scheme, bool,
421 scheme_enumerator_t *this, va_list args)
422 {
423 signature_params_t **params;
424
425 VA_ARGS_VGET(args, params);
426
427 if ((this->reverse && --this->index >= 0) ||
428 (!this->reverse && ++this->index < countof(rsa_schemes)))
429 {
430 *params = &rsa_schemes[this->index];
431 return TRUE;
432 }
433 return FALSE;
434 }
435
436 /**
437 * Create an enumerator for the supported RSA signature schemes
438 */
439 static enumerator_t *create_rsa_enumerator(private_agent_private_key_t *this)
440 {
441 scheme_enumerator_t *enumerator;
442
443 INIT(enumerator,
444 .public = {
445 .enumerate = enumerator_enumerate_default,
446 .venumerate = _enumerate_rsa_scheme,
447 .destroy = (void*)free,
448 },
449 .index = -1,
450 .reverse = FALSE,
451 );
452 /* propose SHA-512 first for larger keys */
453 if (get_keysize(this) > 3072)
454 {
455 enumerator->index = countof(rsa_schemes);
456 enumerator->reverse = TRUE;
457 }
458 return &enumerator->public;
459 }
460
461 METHOD(private_key_t, supported_signature_schemes, enumerator_t*,
462 private_agent_private_key_t *this)
463 {
464 key_type_t type = get_type(this);
465
466 switch (type)
467 {
468 case KEY_RSA:
469 return create_rsa_enumerator(this);
470 case KEY_ED25519:
471 case KEY_ED448:
472 case KEY_ECDSA:
473 return signature_schemes_for_key(type, get_keysize(this));
474 default:
475 break;
476 }
477 return enumerator_create_empty();
478 }
479
480 METHOD(private_key_t, get_public_key, public_key_t*,
481 private_agent_private_key_t *this)
482 {
483 return this->pubkey->get_ref(this->pubkey);
484 }
485
486 METHOD(private_key_t, get_encoding, bool,
487 private_agent_private_key_t *this, cred_encoding_type_t type,
488 chunk_t *encoding)
489 {
490 return FALSE;
491 }
492
493 METHOD(private_key_t, get_fingerprint, bool,
494 private_agent_private_key_t *this, cred_encoding_type_t type, chunk_t *fp)
495 {
496 return this->pubkey->get_fingerprint(this->pubkey, type, fp);
497 }
498
499 METHOD(private_key_t, get_ref, private_key_t*,
500 private_agent_private_key_t *this)
501 {
502 ref_get(&this->ref);
503 return &this->public.key;
504 }
505
506 METHOD(private_key_t, destroy, void,
507 private_agent_private_key_t *this)
508 {
509 if (ref_put(&this->ref))
510 {
511 chunk_free(&this->key);
512 DESTROY_IF(this->pubkey);
513 free(this->path);
514 free(this);
515 }
516 }
517
518 /**
519 * See header.
520 */
521 agent_private_key_t *agent_private_key_open(key_type_t type, va_list args)
522 {
523 private_agent_private_key_t *this;
524 public_key_t *pubkey = NULL;
525 char *path = NULL;
526
527 while (TRUE)
528 {
529 switch (va_arg(args, builder_part_t))
530 {
531 case BUILD_AGENT_SOCKET:
532 path = va_arg(args, char*);
533 continue;
534 case BUILD_PUBLIC_KEY:
535 pubkey = va_arg(args, public_key_t*);
536 continue;
537 case BUILD_END:
538 break;
539 default:
540 return NULL;
541 }
542 break;
543 }
544 if (!path)
545 {
546 return NULL;
547 }
548
549 INIT(this,
550 .public = {
551 .key = {
552 .get_type = _get_type,
553 .supported_signature_schemes = _supported_signature_schemes,
554 .sign = _sign,
555 .decrypt = _decrypt,
556 .get_keysize = _get_keysize,
557 .get_public_key = _get_public_key,
558 .belongs_to = private_key_belongs_to,
559 .equals = private_key_equals,
560 .get_fingerprint = _get_fingerprint,
561 .has_fingerprint = private_key_has_fingerprint,
562 .get_encoding = _get_encoding,
563 .get_ref = _get_ref,
564 .destroy = _destroy,
565 },
566 },
567 .path = strdup(path),
568 .ref = 1,
569 );
570
571 if (!read_key(this, pubkey))
572 {
573 destroy(this);
574 return NULL;
575 }
576 return &this->public;
577 }