Annotation of embedaddon/strongswan/src/libstrongswan/plugins/agent/agent_private_key.c, revision 1.1.1.1
1.1 misho 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: 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: }
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>