Annotation of embedaddon/strongswan/src/libstrongswan/plugins/agent/agent_private_key.c, revision 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>