Coverage Report

Created: 2026-07-16 06:39

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/strongswan/src/libcharon/encoding/payloads/cert_payload.c
Line
Count
Source
1
/*
2
 * Copyright (C) 2008 Tobias Brunner
3
 * Copyright (C) 2005-2010 Martin Willi
4
 * Copyright (C) 2005 Jan Hutter
5
 *
6
 * Copyright (C) secunet Security Networks AG
7
 *
8
 * This program is free software; you can redistribute it and/or modify it
9
 * under the terms of the GNU General Public License as published by the
10
 * Free Software Foundation; either version 2 of the License, or (at your
11
 * option) any later version.  See <http://www.fsf.org/copyleft/gpl.txt>.
12
 *
13
 * This program is distributed in the hope that it will be useful, but
14
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
15
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
16
 * for more details.
17
 */
18
19
#include <stddef.h>
20
#include <ctype.h>
21
22
#include <daemon.h>
23
24
#include "cert_payload.h"
25
26
ENUM(cert_encoding_names, ENC_PKCS7_WRAPPED_X509, ENC_OCSP_CONTENT,
27
  "ENC_PKCS7_WRAPPED_X509",
28
  "ENC_PGP",
29
  "ENC_DNS_SIGNED_KEY",
30
  "ENC_X509_SIGNATURE",
31
  "ENC_X509_KEY_EXCHANGE",
32
  "ENC_KERBEROS_TOKENS",
33
  "ENC_CRL",
34
  "ENC_ARL",
35
  "ENC_SPKI",
36
  "ENC_X509_ATTRIBUTE",
37
  "ENC_RAW_RSA_KEY",
38
  "ENC_X509_HASH_AND_URL",
39
  "ENC_X509_HASH_AND_URL_BUNDLE",
40
  "ENC_OCSP_CONTENT",
41
);
42
43
typedef struct private_cert_payload_t private_cert_payload_t;
44
45
/**
46
 * Private data of an cert_payload_t object.
47
 */
48
struct private_cert_payload_t {
49
50
  /**
51
   * Public cert_payload_t interface.
52
   */
53
  cert_payload_t public;
54
55
  /**
56
   * Next payload type.
57
   */
58
  uint8_t  next_payload;
59
60
  /**
61
   * Critical flag.
62
   */
63
  bool critical;
64
65
  /**
66
   * reserved bits
67
   */
68
  bool reserved[7];
69
70
  /**
71
   * Length of this payload.
72
   */
73
  uint16_t payload_length;
74
75
  /**
76
   * Encoding of the CERT Data.
77
   */
78
  uint8_t encoding;
79
80
  /**
81
   * The contained cert data value.
82
   */
83
  chunk_t data;
84
85
  /**
86
   * TRUE if the "Hash and URL" data is invalid
87
   */
88
  bool invalid_hash_and_url;
89
90
  /**
91
   * The payload type.
92
   */
93
  payload_type_t type;
94
};
95
96
/**
97
 * Encoding rules to parse or generate a CERT payload
98
 *
99
 * The defined offsets are the positions in a object of type
100
 * private_cert_payload_t.
101
 *
102
 */
103
static encoding_rule_t encodings[] = {
104
  /* 1 Byte next payload type, stored in the field next_payload */
105
  { U_INT_8,      offsetof(private_cert_payload_t, next_payload)  },
106
  /* the critical bit */
107
  { FLAG,       offsetof(private_cert_payload_t, critical)    },
108
  /* 7 Bit reserved bits, nowhere stored */
109
  { RESERVED_BIT,   offsetof(private_cert_payload_t, reserved[0]) },
110
  { RESERVED_BIT,   offsetof(private_cert_payload_t, reserved[1]) },
111
  { RESERVED_BIT,   offsetof(private_cert_payload_t, reserved[2]) },
112
  { RESERVED_BIT,   offsetof(private_cert_payload_t, reserved[3]) },
113
  { RESERVED_BIT,   offsetof(private_cert_payload_t, reserved[4]) },
114
  { RESERVED_BIT,   offsetof(private_cert_payload_t, reserved[5]) },
115
  { RESERVED_BIT,   offsetof(private_cert_payload_t, reserved[6]) },
116
  /* Length of the whole payload*/
117
  { PAYLOAD_LENGTH, offsetof(private_cert_payload_t, payload_length)},
118
  /* 1 Byte CERT type*/
119
  { U_INT_8,      offsetof(private_cert_payload_t, encoding)    },
120
  /* some cert data bytes, length is defined in PAYLOAD_LENGTH */
121
  { CHUNK_DATA,   offsetof(private_cert_payload_t, data)      }
122
};
123
124
/*
125
                           1                   2                   3
126
       0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
127
      +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
128
      ! Next Payload  !C!  RESERVED   !         Payload Length        !
129
      +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
130
      ! Cert Encoding !                                               !
131
      +-+-+-+-+-+-+-+-+                                               !
132
      ~                       Certificate Data                        ~
133
      !                                                               !
134
      +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
135
*/
136
137
METHOD(payload_t, verify, status_t,
138
  private_cert_payload_t *this)
139
1.92k
{
140
1.92k
  if (this->encoding == ENC_X509_HASH_AND_URL ||
141
934
    this->encoding == ENC_X509_HASH_AND_URL_BUNDLE)
142
1.39k
  {
143
1.39k
    int i;
144
145
    /* coarse verification of "Hash and URL" encoded certificates */
146
1.39k
    if (this->data.len <= 20)
147
721
    {
148
721
      DBG1(DBG_ENC, "invalid payload length for hash-and-url (%d), ignore",
149
721
         this->data.len);
150
721
      this->invalid_hash_and_url = TRUE;
151
721
      return SUCCESS;
152
721
    }
153
1.94k
    for (i = 20; i < this->data.len; ++i)
154
1.67k
    {
155
1.67k
      if (this->data.ptr[i] == '\0')
156
199
      {
157
        /* null terminated, fine */
158
199
        return SUCCESS;
159
199
      }
160
1.47k
      else if (!isprint(this->data.ptr[i]))
161
206
      {
162
206
        DBG1(DBG_ENC, "non printable characters in url of hash-and-url"
163
206
           " encoded certificate payload, ignore");
164
206
        this->invalid_hash_and_url = TRUE;
165
206
        return SUCCESS;
166
206
      }
167
1.67k
    }
168
    /* URL is not null terminated, correct that */
169
272
    this->data = chunk_cat("mc", this->data, chunk_from_chars(0));
170
272
  }
171
800
  return SUCCESS;
172
1.92k
}
173
174
METHOD(payload_t, get_encoding_rules, int,
175
  private_cert_payload_t *this, encoding_rule_t **rules)
176
2.01k
{
177
2.01k
  *rules = encodings;
178
2.01k
  return countof(encodings);
179
2.01k
}
180
181
METHOD(payload_t, get_header_length, int,
182
  private_cert_payload_t *this)
183
25.4k
{
184
25.4k
  return 5;
185
25.4k
}
186
187
METHOD(payload_t, get_type, payload_type_t,
188
  private_cert_payload_t *this)
189
2.17k
{
190
2.17k
  return this->type;
191
2.17k
}
192
193
METHOD(payload_t, get_next_type, payload_type_t,
194
  private_cert_payload_t *this)
195
1.92k
{
196
1.92k
  return this->next_payload;
197
1.92k
}
198
199
METHOD(payload_t, set_next_type, void,
200
  private_cert_payload_t *this, payload_type_t type)
201
0
{
202
0
  this->next_payload = type;
203
0
}
204
205
METHOD(payload_t, get_length, size_t,
206
  private_cert_payload_t *this)
207
0
{
208
0
  return this->payload_length;
209
0
}
210
211
METHOD(cert_payload_t, get_cert_encoding, cert_encoding_t,
212
  private_cert_payload_t *this)
213
0
{
214
0
  return this->encoding;
215
0
}
216
217
METHOD(cert_payload_t, get_cert, certificate_t*,
218
  private_cert_payload_t *this)
219
0
{
220
0
  int type;
221
222
0
  switch (this->encoding)
223
0
  {
224
0
    case ENC_X509_SIGNATURE:
225
0
      type = CERT_X509;
226
0
      break;
227
0
    case ENC_X509_ATTRIBUTE:
228
0
      type = CERT_X509_AC;
229
0
      break;
230
0
    case ENC_CRL:
231
0
      type = CERT_X509_CRL;
232
0
      break;
233
0
    case ENC_OCSP_CONTENT:
234
0
      type = CERT_X509_OCSP_RESPONSE;
235
0
      break;
236
0
    default:
237
0
      return NULL;
238
0
  }
239
0
  return lib->creds->create(lib->creds, CRED_CERTIFICATE, type,
240
0
                BUILD_BLOB_ASN1_DER, this->data, BUILD_END);
241
0
}
242
243
METHOD(cert_payload_t, get_container, container_t*,
244
  private_cert_payload_t *this)
245
0
{
246
0
  int type;
247
248
0
  switch (this->encoding)
249
0
  {
250
0
    case ENC_PKCS7_WRAPPED_X509:
251
0
      type = CONTAINER_PKCS7;
252
0
      break;
253
0
    default:
254
0
      return NULL;
255
0
  }
256
0
  return lib->creds->create(lib->creds, CRED_CONTAINER, type,
257
0
                BUILD_BLOB_ASN1_DER, this->data, BUILD_END);
258
0
}
259
260
METHOD(cert_payload_t, get_hash, chunk_t,
261
  private_cert_payload_t *this)
262
0
{
263
0
  chunk_t hash = chunk_empty;
264
265
0
  if ((this->encoding != ENC_X509_HASH_AND_URL &&
266
0
     this->encoding != ENC_X509_HASH_AND_URL_BUNDLE) ||
267
0
    this->invalid_hash_and_url)
268
0
  {
269
0
    return hash;
270
0
  }
271
0
  hash.ptr = this->data.ptr;
272
0
  hash.len = 20;
273
0
  return hash;
274
0
}
275
276
METHOD(cert_payload_t, get_url, char*,
277
  private_cert_payload_t *this)
278
0
{
279
0
  if ((this->encoding != ENC_X509_HASH_AND_URL &&
280
0
     this->encoding != ENC_X509_HASH_AND_URL_BUNDLE) ||
281
0
    this->invalid_hash_and_url)
282
0
  {
283
0
    return NULL;
284
0
  }
285
0
  return (char*)this->data.ptr + 20;
286
0
}
287
288
METHOD2(payload_t, cert_payload_t, destroy, void,
289
  private_cert_payload_t *this)
290
2.01k
{
291
2.01k
  free(this->data.ptr);
292
2.01k
  free(this);
293
2.01k
}
294
295
/*
296
 * Described in header
297
 */
298
cert_payload_t *cert_payload_create(payload_type_t type)
299
2.01k
{
300
2.01k
  private_cert_payload_t *this;
301
302
2.01k
  INIT(this,
303
2.01k
    .public = {
304
2.01k
      .payload_interface = {
305
2.01k
        .verify = _verify,
306
2.01k
        .get_encoding_rules = _get_encoding_rules,
307
2.01k
        .get_header_length = _get_header_length,
308
2.01k
        .get_length = _get_length,
309
2.01k
        .get_next_type = _get_next_type,
310
2.01k
        .set_next_type = _set_next_type,
311
2.01k
        .get_type = _get_type,
312
2.01k
        .destroy = _destroy,
313
2.01k
      },
314
2.01k
      .get_cert = _get_cert,
315
2.01k
      .get_container = _get_container,
316
2.01k
      .get_cert_encoding = _get_cert_encoding,
317
2.01k
      .get_hash = _get_hash,
318
2.01k
      .get_url = _get_url,
319
2.01k
      .destroy = _destroy,
320
2.01k
    },
321
2.01k
    .next_payload = PL_NONE,
322
2.01k
    .payload_length = get_header_length(this),
323
2.01k
    .type = type,
324
2.01k
  );
325
2.01k
  return &this->public;
326
2.01k
}
327
328
/*
329
 * Described in header
330
 */
331
cert_payload_t *cert_payload_create_from_cert(payload_type_t type,
332
                        certificate_t *cert)
333
0
{
334
0
  private_cert_payload_t *this;
335
336
0
  this = (private_cert_payload_t*)cert_payload_create(type);
337
0
  switch (cert->get_type(cert))
338
0
  {
339
0
    case CERT_X509:
340
0
      this->encoding = ENC_X509_SIGNATURE;
341
0
      break;
342
0
    case CERT_X509_AC:
343
0
      this->encoding = ENC_X509_ATTRIBUTE;
344
0
      break;
345
0
    case CERT_X509_OCSP_RESPONSE:
346
0
      this->encoding = ENC_OCSP_CONTENT;
347
0
      break;
348
0
    default:
349
0
      DBG1(DBG_ENC, "embedding %N certificate in payload failed",
350
0
         certificate_type_names, cert->get_type(cert));
351
0
      free(this);
352
0
      return NULL;
353
0
  }
354
0
  if (!cert->get_encoding(cert, CERT_ASN1_DER, &this->data))
355
0
  {
356
0
    DBG1(DBG_ENC, "encoding certificate for cert payload failed");
357
0
    free(this);
358
0
    return NULL;
359
0
  }
360
0
  this->payload_length = get_header_length(this) + this->data.len;
361
362
0
  return &this->public;
363
0
}
364
365
/*
366
 * Described in header
367
 */
368
cert_payload_t *cert_payload_create_from_hash_and_url(chunk_t hash, char *url)
369
0
{
370
0
  private_cert_payload_t *this;
371
372
0
  this = (private_cert_payload_t*)cert_payload_create(PLV2_CERTIFICATE);
373
0
  this->encoding = ENC_X509_HASH_AND_URL;
374
0
  this->data = chunk_cat("cc", hash, chunk_create(url, strlen(url)));
375
0
  this->payload_length = get_header_length(this) + this->data.len;
376
377
0
  return &this->public;
378
0
}
379
380
/*
381
 * Described in header
382
 */
383
cert_payload_t *cert_payload_create_custom(payload_type_t type,
384
                    cert_encoding_t encoding, chunk_t data)
385
0
{
386
0
  private_cert_payload_t *this;
387
388
0
  this = (private_cert_payload_t*)cert_payload_create(type);
389
0
  this->encoding = encoding;
390
0
  this->data = data;
391
0
  this->payload_length = get_header_length(this) + this->data.len;
392
393
0
  return &this->public;
394
0
}