Coverage Report

Created: 2024-02-29 06:05

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