]> git.ipfire.org Git - thirdparty/strongswan.git/blame - src/libstrongswan/plugins/sshkey/sshkey_builder.c
Update copyright headers after acquisition by secunet
[thirdparty/strongswan.git] / src / libstrongswan / plugins / sshkey / sshkey_builder.c
CommitLineData
cc4408ab 1/*
7de07293 2 * Copyright (C) 2013-2018 Tobias Brunner
19ef2aec
TB
3 *
4 * Copyright (C) secunet Security Networks AG
cc4408ab
TB
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
09417da4 17#define _GNU_SOURCE /* for fmemopen() */
075e8036
TB
18#include <unistd.h>
19#include <stdio.h>
20#include <errno.h>
21
cc4408ab
TB
22#include "sshkey_builder.h"
23
dd9e3668
TB
24#include <asn1/oid.h>
25#include <asn1/asn1.h>
cc4408ab
TB
26#include <bio/bio_reader.h>
27#include <utils/debug.h>
28
dd9e3668
TB
29#define ECDSA_PREFIX "ecdsa-sha2-"
30
31/**
32 * Parse an EC domain parameter identifier as defined in RFC 5656
33 */
34static chunk_t parse_ec_identifier(chunk_t identifier)
35{
36 chunk_t oid = chunk_empty;
37
38 if (chunk_equals(identifier, chunk_from_str("nistp256")))
39 {
40 oid = asn1_build_known_oid(OID_PRIME256V1);
41 }
42 else if (chunk_equals(identifier, chunk_from_str("nistp384")))
43 {
44 oid = asn1_build_known_oid(OID_SECT384R1);
45 }
46 else if (chunk_equals(identifier, chunk_from_str("nistp521")))
47 {
48 oid = asn1_build_known_oid(OID_SECT521R1);
49 }
50 else
51 {
52 char ascii[64];
53
54 if (snprintf(ascii, sizeof(ascii), "%.*s", (int)identifier.len,
55 identifier.ptr) < sizeof(ascii))
56 {
57 oid = asn1_wrap(ASN1_OID, "m", asn1_oid_from_string(ascii));
58 }
59 }
60 return oid;
61}
62
cc4408ab
TB
63/**
64 * Load a generic public key from an SSH key blob
65 */
66static sshkey_public_key_t *parse_public_key(chunk_t blob)
67{
68 bio_reader_t *reader;
69 chunk_t format;
70
71 reader = bio_reader_create(blob);
72 if (!reader->read_data32(reader, &format))
73 {
74 DBG1(DBG_LIB, "invalid key format in SSH key");
75 reader->destroy(reader);
76 return NULL;
77 }
78 if (chunk_equals(format, chunk_from_str("ssh-rsa")))
79 {
80 chunk_t n, e;
81
82 if (!reader->read_data32(reader, &e) ||
83 !reader->read_data32(reader, &n))
84 {
85 DBG1(DBG_LIB, "invalid RSA key in SSH key");
86 reader->destroy(reader);
87 return NULL;
88 }
89 reader->destroy(reader);
90 return lib->creds->create(lib->creds, CRED_PUBLIC_KEY, KEY_RSA,
91 BUILD_RSA_MODULUS, n, BUILD_RSA_PUB_EXP, e, BUILD_END);
92 }
7de07293
TB
93 else if (chunk_equals(format, chunk_from_str("ssh-ed25519")))
94 {
e3e0fe41 95 chunk_t ed_key;
7de07293 96
e3e0fe41 97 if (!reader->read_data32(reader, &ed_key))
7de07293
TB
98 {
99 DBG1(DBG_LIB, "invalid Ed25519 key in SSH key");
100 reader->destroy(reader);
101 return NULL;
102 }
103 reader->destroy(reader);
104 return lib->creds->create(lib->creds, CRED_PUBLIC_KEY, KEY_ED25519,
e3e0fe41 105 BUILD_EDDSA_PUB, ed_key, BUILD_END);
7de07293
TB
106 }
107 else if (chunk_equals(format, chunk_from_str("ssh-ed448")))
108 {
e3e0fe41 109 chunk_t ed_key;
7de07293 110
e3e0fe41 111 if (!reader->read_data32(reader, &ed_key))
7de07293
TB
112 {
113 DBG1(DBG_LIB, "invalid Ed448 key in SSH key");
114 reader->destroy(reader);
115 return NULL;
116 }
117 reader->destroy(reader);
118 return lib->creds->create(lib->creds, CRED_PUBLIC_KEY, KEY_ED448,
e3e0fe41 119 BUILD_EDDSA_PUB, ed_key, BUILD_END);
7de07293 120 }
dd9e3668 121 else if (format.len > strlen(ECDSA_PREFIX) &&
d27f225d 122 strpfx(format.ptr, ECDSA_PREFIX))
dd9e3668
TB
123 {
124 chunk_t ec_blob, identifier, q, oid, encoded;
125 sshkey_public_key_t *key;
126
127 ec_blob = reader->peek(reader);
128 reader->destroy(reader);
129 reader = bio_reader_create(ec_blob);
130 if (!reader->read_data32(reader, &identifier) ||
131 !reader->read_data32(reader, &q))
132 {
133 DBG1(DBG_LIB, "invalid ECDSA key in SSH key");
134 reader->destroy(reader);
135 return NULL;
136 }
137 oid = parse_ec_identifier(identifier);
138 if (!oid.ptr)
139 {
140 DBG1(DBG_LIB, "invalid ECDSA key identifier in SSH key");
141 reader->destroy(reader);
142 return NULL;
143 }
144 reader->destroy(reader);
145 /* build key from subjectPublicKeyInfo */
146 encoded = asn1_wrap(ASN1_SEQUENCE, "mm",
147 asn1_wrap(ASN1_SEQUENCE, "mm",
148 asn1_build_known_oid(OID_EC_PUBLICKEY), oid),
149 asn1_bitstring("c", q));
150 key = lib->creds->create(lib->creds, CRED_PUBLIC_KEY,
151 KEY_ECDSA, BUILD_BLOB_ASN1_DER, encoded, BUILD_END);
152 chunk_free(&encoded);
153 return key;
154 }
cc4408ab
TB
155 DBG1(DBG_LIB, "unsupported SSH key format %.*s", (int)format.len,
156 format.ptr);
157 reader->destroy(reader);
158 return NULL;
159}
160
075e8036
TB
161/**
162 * Load SSH key from a FILE stream, closes the stream
163 */
164static sshkey_public_key_t *load_from_stream(FILE *file)
165{
166 sshkey_public_key_t *public = NULL;
167 chunk_t blob = chunk_empty;
168 enumerator_t *enumerator;
169 char line[1024], *token;
170
171 while (!public && fgets(line, sizeof(line), file))
7de07293
TB
172 { /* the format is: ssh-<key-type> <key(base64)> <identifier> */
173 if (!strpfx(line, "ssh-rsa") && !strpfx(line, ECDSA_PREFIX) &&
174 !strpfx(line, "ssh-ed25519") && !strpfx(line, "ssh-ed448"))
075e8036
TB
175 {
176 continue;
177 }
178 enumerator = enumerator_create_token(line, " ", " ");
179 if (enumerator->enumerate(enumerator, &token) &&
180 enumerator->enumerate(enumerator, &token))
181 {
182 blob = chunk_from_base64(chunk_from_str(token), NULL);
183 }
184 enumerator->destroy(enumerator);
185 if (blob.ptr)
186 {
187 public = parse_public_key(blob);
188 chunk_free(&blob);
189 }
190 }
191 fclose(file);
192 return public;
193}
194
195/**
71c9565a 196 * Load SSH key from a blob of data (most likely the content of a file)
075e8036 197 */
71c9565a 198static sshkey_public_key_t *load_from_blob(chunk_t blob)
075e8036
TB
199{
200 FILE *stream;
201
71c9565a 202 stream = fmemopen(blob.ptr, blob.len, "r");
075e8036
TB
203 if (!stream)
204 {
075e8036
TB
205 return NULL;
206 }
207 return load_from_stream(stream);
208}
209
210/**
211 * Load SSH key from file
212 */
213static sshkey_public_key_t *load_from_file(char *file)
214{
215 FILE *stream;
216
217 stream = fopen(file, "r");
218 if (!stream)
219 {
220 DBG1(DBG_LIB, " opening '%s' failed: %s", file, strerror(errno));
221 return NULL;
222 }
223 return load_from_stream(stream);
224}
225
cc4408ab
TB
226/**
227 * See header.
228 */
229sshkey_public_key_t *sshkey_public_key_load(key_type_t type, va_list args)
230{
71c9565a 231 chunk_t sshkey = chunk_empty, blob = chunk_empty;
075e8036 232 char *file = NULL;
cc4408ab
TB
233
234 while (TRUE)
235 {
236 switch (va_arg(args, builder_part_t))
237 {
238 case BUILD_BLOB_SSHKEY:
71c9565a 239 sshkey = va_arg(args, chunk_t);
cc4408ab 240 continue;
075e8036
TB
241 case BUILD_FROM_FILE:
242 file = va_arg(args, char*);
243 continue;
71c9565a
TB
244 case BUILD_BLOB:
245 blob = va_arg(args, chunk_t);
075e8036 246 continue;
cc4408ab
TB
247 case BUILD_END:
248 break;
249 default:
250 return NULL;
251 }
252 break;
253 }
71c9565a 254 if (sshkey.ptr)
cc4408ab 255 {
71c9565a 256 return parse_public_key(sshkey);
cc4408ab 257 }
075e8036
TB
258 if (file)
259 {
260 return load_from_file(file);
261 }
71c9565a 262 if (blob.ptr)
075e8036 263 {
71c9565a 264 return load_from_blob(blob);
075e8036 265 }
cc4408ab
TB
266 return NULL;
267}
1cda6921
TB
268
269/**
270 * See header.
271 */
272certificate_t *sshkey_certificate_load(certificate_type_t type, va_list args)
273{
274 certificate_t *cert;
275 public_key_t *key;
276 identification_t *subject = NULL;
277 char *file = NULL;
278
279 while (TRUE)
280 {
281 switch (va_arg(args, builder_part_t))
282 {
283 case BUILD_FROM_FILE:
284 file = va_arg(args, char*);
285 continue;
286 case BUILD_SUBJECT:
287 subject = va_arg(args, identification_t*);
288 continue;
289 case BUILD_END:
290 break;
291 default:
292 return NULL;
293 }
294 break;
295 }
296 if (!file || !subject)
297 {
298 return NULL;
299 }
300 key = (public_key_t*)load_from_file(file);
301 if (!key)
302 {
303 return NULL;
304 }
305 cert = lib->creds->create(lib->creds, CRED_CERTIFICATE,
306 CERT_TRUSTED_PUBKEY, BUILD_PUBLIC_KEY, key,
307 BUILD_SUBJECT, subject, BUILD_END);
308 key->destroy(key);
309 return cert;
310}