/* * Copyright (C) 2013-2018 Tobias Brunner * HSR Hochschule fuer Technik Rapperswil * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the * Free Software Foundation; either version 2 of the License, or (at your * option) any later version. See . * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * for more details. */ #define _GNU_SOURCE /* for fmemopen() */ #include #include #include #include "sshkey_builder.h" #include #include #include #include #define ECDSA_PREFIX "ecdsa-sha2-" /** * Parse an EC domain parameter identifier as defined in RFC 5656 */ static chunk_t parse_ec_identifier(chunk_t identifier) { chunk_t oid = chunk_empty; if (chunk_equals(identifier, chunk_from_str("nistp256"))) { oid = asn1_build_known_oid(OID_PRIME256V1); } else if (chunk_equals(identifier, chunk_from_str("nistp384"))) { oid = asn1_build_known_oid(OID_SECT384R1); } else if (chunk_equals(identifier, chunk_from_str("nistp521"))) { oid = asn1_build_known_oid(OID_SECT521R1); } else { char ascii[64]; if (snprintf(ascii, sizeof(ascii), "%.*s", (int)identifier.len, identifier.ptr) < sizeof(ascii)) { oid = asn1_wrap(ASN1_OID, "m", asn1_oid_from_string(ascii)); } } return oid; } /** * Load a generic public key from an SSH key blob */ static sshkey_public_key_t *parse_public_key(chunk_t blob) { bio_reader_t *reader; chunk_t format; reader = bio_reader_create(blob); if (!reader->read_data32(reader, &format)) { DBG1(DBG_LIB, "invalid key format in SSH key"); reader->destroy(reader); return NULL; } if (chunk_equals(format, chunk_from_str("ssh-rsa"))) { chunk_t n, e; if (!reader->read_data32(reader, &e) || !reader->read_data32(reader, &n)) { DBG1(DBG_LIB, "invalid RSA key in SSH key"); reader->destroy(reader); return NULL; } reader->destroy(reader); return lib->creds->create(lib->creds, CRED_PUBLIC_KEY, KEY_RSA, BUILD_RSA_MODULUS, n, BUILD_RSA_PUB_EXP, e, BUILD_END); } else if (chunk_equals(format, chunk_from_str("ssh-ed25519"))) { chunk_t ed_key; if (!reader->read_data32(reader, &ed_key)) { DBG1(DBG_LIB, "invalid Ed25519 key in SSH key"); reader->destroy(reader); return NULL; } reader->destroy(reader); return lib->creds->create(lib->creds, CRED_PUBLIC_KEY, KEY_ED25519, BUILD_EDDSA_PUB, ed_key, BUILD_END); } else if (chunk_equals(format, chunk_from_str("ssh-ed448"))) { chunk_t ed_key; if (!reader->read_data32(reader, &ed_key)) { DBG1(DBG_LIB, "invalid Ed448 key in SSH key"); reader->destroy(reader); return NULL; } reader->destroy(reader); return lib->creds->create(lib->creds, CRED_PUBLIC_KEY, KEY_ED448, BUILD_EDDSA_PUB, ed_key, BUILD_END); } else if (format.len > strlen(ECDSA_PREFIX) && strpfx(format.ptr, ECDSA_PREFIX)) { chunk_t ec_blob, identifier, q, oid, encoded; sshkey_public_key_t *key; ec_blob = reader->peek(reader); reader->destroy(reader); reader = bio_reader_create(ec_blob); if (!reader->read_data32(reader, &identifier) || !reader->read_data32(reader, &q)) { DBG1(DBG_LIB, "invalid ECDSA key in SSH key"); reader->destroy(reader); return NULL; } oid = parse_ec_identifier(identifier); if (!oid.ptr) { DBG1(DBG_LIB, "invalid ECDSA key identifier in SSH key"); reader->destroy(reader); return NULL; } reader->destroy(reader); /* build key from subjectPublicKeyInfo */ encoded = asn1_wrap(ASN1_SEQUENCE, "mm", asn1_wrap(ASN1_SEQUENCE, "mm", asn1_build_known_oid(OID_EC_PUBLICKEY), oid), asn1_bitstring("c", q)); key = lib->creds->create(lib->creds, CRED_PUBLIC_KEY, KEY_ECDSA, BUILD_BLOB_ASN1_DER, encoded, BUILD_END); chunk_free(&encoded); return key; } DBG1(DBG_LIB, "unsupported SSH key format %.*s", (int)format.len, format.ptr); reader->destroy(reader); return NULL; } /** * Load SSH key from a FILE stream, closes the stream */ static sshkey_public_key_t *load_from_stream(FILE *file) { sshkey_public_key_t *public = NULL; chunk_t blob = chunk_empty; enumerator_t *enumerator; char line[1024], *token; while (!public && fgets(line, sizeof(line), file)) { /* the format is: ssh- */ if (!strpfx(line, "ssh-rsa") && !strpfx(line, ECDSA_PREFIX) && !strpfx(line, "ssh-ed25519") && !strpfx(line, "ssh-ed448")) { continue; } enumerator = enumerator_create_token(line, " ", " "); if (enumerator->enumerate(enumerator, &token) && enumerator->enumerate(enumerator, &token)) { blob = chunk_from_base64(chunk_from_str(token), NULL); } enumerator->destroy(enumerator); if (blob.ptr) { public = parse_public_key(blob); chunk_free(&blob); } } fclose(file); return public; } /** * Load SSH key from a blob of data (most likely the content of a file) */ static sshkey_public_key_t *load_from_blob(chunk_t blob) { FILE *stream; stream = fmemopen(blob.ptr, blob.len, "r"); if (!stream) { return NULL; } return load_from_stream(stream); } /** * Load SSH key from file */ static sshkey_public_key_t *load_from_file(char *file) { FILE *stream; stream = fopen(file, "r"); if (!stream) { DBG1(DBG_LIB, " opening '%s' failed: %s", file, strerror(errno)); return NULL; } return load_from_stream(stream); } /** * See header. */ sshkey_public_key_t *sshkey_public_key_load(key_type_t type, va_list args) { chunk_t sshkey = chunk_empty, blob = chunk_empty; char *file = NULL; while (TRUE) { switch (va_arg(args, builder_part_t)) { case BUILD_BLOB_SSHKEY: sshkey = va_arg(args, chunk_t); continue; case BUILD_FROM_FILE: file = va_arg(args, char*); continue; case BUILD_BLOB: blob = va_arg(args, chunk_t); continue; case BUILD_END: break; default: return NULL; } break; } if (sshkey.ptr) { return parse_public_key(sshkey); } if (file) { return load_from_file(file); } if (blob.ptr) { return load_from_blob(blob); } return NULL; } /** * See header. */ certificate_t *sshkey_certificate_load(certificate_type_t type, va_list args) { certificate_t *cert; public_key_t *key; identification_t *subject = NULL; char *file = NULL; while (TRUE) { switch (va_arg(args, builder_part_t)) { case BUILD_FROM_FILE: file = va_arg(args, char*); continue; case BUILD_SUBJECT: subject = va_arg(args, identification_t*); continue; case BUILD_END: break; default: return NULL; } break; } if (!file || !subject) { return NULL; } key = (public_key_t*)load_from_file(file); if (!key) { return NULL; } cert = lib->creds->create(lib->creds, CRED_CERTIFICATE, CERT_TRUSTED_PUBKEY, BUILD_PUBLIC_KEY, key, BUILD_SUBJECT, subject, BUILD_END); key->destroy(key); return cert; }