]>
Commit | Line | Data |
---|---|---|
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 | */ | |
34 | static 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 | */ | |
66 | static 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 | */ | |
164 | static 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 | 198 | static 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 | */ | |
213 | static 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 | */ | |
229 | sshkey_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 | */ | |
272 | certificate_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 | } |