Coverage Report

Created: 2023-11-19 06:33

/src/strongswan/src/libstrongswan/plugins/x509/x509_ocsp_response.c
Line
Count
Source (jump to first uncovered line)
1
/*
2
 * Copyright (C) 2017-2019 Tobias Brunner
3
 * Copyright (C) 2008-2009 Martin Willi
4
 * Copyright (C) 2007-2023 Andreas Steffen
5
 * Copyright (C) 2003 Christoph Gysin, Simon Zwahlen
6
 *
7
 * Copyright (C) secunet Security Networks AG
8
 *
9
 * This program is free software; you can redistribute it and/or modify it
10
 * under the terms of the GNU General Public License as published by the
11
 * Free Software Foundation; either version 2 of the License, or (at your
12
 * option) any later version.  See <http://www.fsf.org/copyleft/gpl.txt>.
13
 *
14
 * This program is distributed in the hope that it will be useful, but
15
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
16
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
17
 * for more details.
18
 */
19
20
#include "x509_ocsp_response.h"
21
22
#include <time.h>
23
24
#include <asn1/oid.h>
25
#include <asn1/asn1.h>
26
#include <asn1/asn1_parser.h>
27
#include <utils/identification.h>
28
#include <collections/linked_list.h>
29
#include <utils/debug.h>
30
31
#include <library.h>
32
#include <credentials/certificates/x509.h>
33
#include <credentials/certificates/crl.h>
34
#include <credentials/certificates/ocsp_single_response.h>
35
36
/**
37
 * how long do we use an OCSP response without a nextUpdate
38
 */
39
0
#define OCSP_DEFAULT_LIFETIME 30
40
41
/* defined in wincrypt.h */
42
#ifdef OCSP_RESPONSE
43
# undef OCSP_RESPONSE
44
#endif
45
46
typedef struct private_x509_ocsp_response_t private_x509_ocsp_response_t;
47
48
/**
49
 * Private data of a ocsp_t object.
50
 */
51
struct private_x509_ocsp_response_t {
52
  /**
53
   * Public interface for this ocsp object.
54
   */
55
  x509_ocsp_response_t public;
56
57
  /**
58
   * complete encoded OCSP response
59
   */
60
  chunk_t encoding;
61
62
  /**
63
   * data for signature verification
64
   */
65
  chunk_t tbsResponseData;
66
67
  /**
68
   * signature scheme
69
   */
70
  signature_params_t *scheme;
71
72
  /**
73
   * signature
74
   */
75
  chunk_t signature;
76
77
  /**
78
   * OCSP response status
79
   */
80
  ocsp_status_t ocsp_status;
81
82
  /**
83
   * name or keyid of the responder
84
   */
85
  identification_t *responderId;
86
87
  /**
88
   * time of response production
89
   */
90
  time_t producedAt;
91
92
  /**
93
   * latest nextUpdate in this OCSP response
94
   */
95
  time_t usableUntil;
96
97
  /**
98
   * list of included certificates
99
   */
100
  linked_list_t *certs;
101
102
  /**
103
   * Linked list of OCSP responses, single_response_t
104
   */
105
  linked_list_t *responses;
106
107
  /**
108
   * Nonce required for ocsp request and response
109
   */
110
  chunk_t nonce;
111
112
  /**
113
   * Signer certificate, included in response
114
   */
115
  certificate_t *cert;
116
117
  /**
118
   * Signer private key to sign response
119
   */
120
  private_key_t *key;
121
122
  /**
123
   * reference counter
124
   */
125
  refcount_t ref;
126
};
127
128
/* our OCSP response version implementation */
129
0
#define OCSP_BASIC_RESPONSE_VERSION 1
130
131
METHOD(ocsp_response_t, get_status, cert_validation_t,
132
  private_x509_ocsp_response_t *this, x509_t *subject, x509_t *issuer,
133
  time_t *revocation_time, crl_reason_t *revocation_reason,
134
  time_t *this_update, time_t *next_update)
135
0
{
136
0
  enumerator_t *enumerator;
137
0
  ocsp_single_response_t *response;
138
0
  cert_validation_t status = VALIDATION_FAILED;
139
0
  certificate_t *issuercert = &issuer->interface;
140
141
0
  enumerator = this->responses->create_enumerator(this->responses);
142
0
  while (enumerator->enumerate(enumerator, &response))
143
0
  {
144
0
    hasher_t *hasher;
145
0
    identification_t *id;
146
0
    cred_encoding_type_t type;
147
0
    chunk_t hash, fingerprint;
148
149
    /* check serial first, is cheaper */
150
0
    if (!chunk_equals(subject->get_serial(subject), response->serialNumber))
151
0
    {
152
0
      continue;
153
0
    }
154
    /* check issuerKeyHash if available */
155
0
    if (response->issuerKeyHash.ptr)
156
0
    {
157
0
      public_key_t *public;
158
159
0
      public = issuercert->get_public_key(issuercert);
160
0
      if (!public)
161
0
      {
162
0
        continue;
163
0
      }
164
0
      switch (response->hashAlgorithm)
165
0
      {
166
0
        case OID_SHA1:
167
0
          type = KEYID_PUBKEY_SHA1;
168
0
          break;
169
0
        default:
170
0
          public->destroy(public);
171
0
          continue;
172
0
      }
173
0
      if (!public->get_fingerprint(public, type, &fingerprint) ||
174
0
        !chunk_equals(response->issuerKeyHash, fingerprint))
175
0
      {
176
0
        public->destroy(public);
177
0
        continue;
178
0
      }
179
0
      public->destroy(public);
180
0
    }
181
    /* check issuerNameHash, if available */
182
0
    else if (response->issuerNameHash.ptr)
183
0
    {
184
0
      id = issuercert->get_subject(issuercert);
185
0
      hasher = lib->crypto->create_hasher(lib->crypto,
186
0
              hasher_algorithm_from_oid(response->hashAlgorithm));
187
0
      if (!hasher ||
188
0
        !hasher->allocate_hash(hasher, id->get_encoding(id), &hash))
189
0
      {
190
0
        DESTROY_IF(hasher);
191
0
        continue;
192
0
      }
193
0
      hasher->destroy(hasher);
194
0
      if (!chunk_equals(hash, response->issuerNameHash))
195
0
      {
196
0
        free(hash.ptr);
197
0
        continue;
198
0
      }
199
0
      free(hash.ptr);
200
0
    }
201
0
    else
202
0
    {
203
0
      continue;
204
0
    }
205
    /* got a match */
206
0
    status = response->status;
207
0
    *revocation_time = response->revocationTime;
208
0
    *revocation_reason = response->revocationReason;
209
0
    *this_update = response->thisUpdate;
210
0
    *next_update = response->nextUpdate;
211
212
0
    break;
213
0
  }
214
0
  enumerator->destroy(enumerator);
215
0
  return status;
216
0
}
217
218
METHOD(ocsp_response_t, create_cert_enumerator, enumerator_t*,
219
  private_x509_ocsp_response_t *this)
220
0
{
221
0
  return this->certs->create_enumerator(this->certs);
222
0
}
223
224
CALLBACK(filter, bool,
225
  void *data, enumerator_t *orig, va_list args)
226
0
{
227
0
  ocsp_single_response_t *response;
228
0
  cert_validation_t *status;
229
0
  crl_reason_t *revocationReason;
230
0
  chunk_t *serialNumber;
231
0
  time_t *revocationTime;
232
233
0
  VA_ARGS_VGET(args, serialNumber, status, revocationTime, revocationReason);
234
235
0
  if (orig->enumerate(orig, &response))
236
0
  {
237
0
    if (serialNumber)
238
0
    {
239
0
      *serialNumber = response->serialNumber;
240
0
    }
241
0
    if (status)
242
0
    {
243
0
      *status = response->status;
244
0
    }
245
0
    if (revocationTime)
246
0
    {
247
0
      *revocationTime = response->revocationTime;
248
0
    }
249
0
    if (revocationReason)
250
0
    {
251
0
      *revocationReason = response->revocationReason;
252
0
    }
253
0
    return TRUE;
254
0
  }
255
0
  return FALSE;
256
0
}
257
258
METHOD(ocsp_response_t, create_response_enumerator, enumerator_t*,
259
  private_x509_ocsp_response_t *this)
260
0
{
261
0
  return enumerator_create_filter(
262
0
        this->responses->create_enumerator(this->responses),
263
0
        filter, NULL, NULL);
264
0
}
265
266
METHOD(ocsp_response_t, get_nonce, chunk_t,
267
  private_x509_ocsp_response_t *this)
268
0
{
269
0
  return this->nonce;
270
0
}
271
272
/**
273
 * Build singleResponse
274
 */
275
static chunk_t build_singleResponse(private_x509_ocsp_response_t *this,
276
                  ocsp_single_response_t *response)
277
0
{
278
0
  chunk_t certID, certStatus, nextUpdate = chunk_empty;
279
280
0
  certID = asn1_wrap(ASN1_SEQUENCE, "mmmm",
281
0
        asn1_algorithmIdentifier(
282
0
          hasher_algorithm_to_oid(response->hashAlgorithm)),
283
0
        asn1_simple_object(ASN1_OCTET_STRING, response->issuerNameHash),
284
0
        asn1_simple_object(ASN1_OCTET_STRING, response->issuerKeyHash),
285
0
        asn1_integer("c", response->serialNumber));
286
287
0
  switch (response->status)
288
0
  {
289
0
    case VALIDATION_GOOD:
290
0
      certStatus = asn1_wrap(ASN1_CONTEXT_S_0, "c", chunk_empty);
291
0
      break;
292
0
    case VALIDATION_REVOKED:
293
0
    case VALIDATION_ON_HOLD:
294
0
      certStatus = asn1_wrap(ASN1_CONTEXT_C_1, "mm",
295
0
              asn1_from_time(&response->revocationTime,
296
0
                       ASN1_GENERALIZEDTIME),
297
0
              asn1_wrap(ASN1_CONTEXT_C_0, "m",
298
0
                asn1_simple_object(ASN1_ENUMERATED,
299
0
                 chunk_from_chars(response->revocationReason))));
300
0
      break;
301
0
    case VALIDATION_FAILED:
302
0
    default:
303
0
      certStatus = asn1_wrap(ASN1_CONTEXT_S_2, "c", chunk_empty);
304
0
  }
305
306
0
  if (response->nextUpdate != 0)
307
0
  {
308
0
    nextUpdate = asn1_wrap(ASN1_CONTEXT_C_0, "m",
309
0
            asn1_from_time(&response->nextUpdate,
310
0
                     ASN1_GENERALIZEDTIME));
311
0
  }
312
313
0
  return asn1_wrap(ASN1_SEQUENCE, "mmmm",
314
0
        certID,
315
0
        certStatus,
316
0
        asn1_from_time(&response->thisUpdate, ASN1_GENERALIZEDTIME),
317
0
        nextUpdate);
318
0
}
319
320
/**
321
 * ASN.1 definition of singleResponse
322
 */
323
static const asn1Object_t singleResponseObjects[] = {
324
  { 0, "singleResponse",        ASN1_SEQUENCE,      ASN1_BODY }, /*  0 */
325
  { 1,   "certID",          ASN1_SEQUENCE,      ASN1_NONE }, /*  1 */
326
  { 2,     "algorithm",       ASN1_EOC,       ASN1_RAW  }, /*  2 */
327
  { 2,     "issuerNameHash",      ASN1_OCTET_STRING,    ASN1_BODY }, /*  3 */
328
  { 2,     "issuerKeyHash",     ASN1_OCTET_STRING,    ASN1_BODY }, /*  4 */
329
  { 2,     "serialNumber",      ASN1_INTEGER,     ASN1_BODY }, /*  5 */
330
  { 1,   "certStatusGood",      ASN1_CONTEXT_S_0,   ASN1_OPT  }, /*  6 */
331
  { 1,   "end opt",         ASN1_EOC,       ASN1_END  }, /*  7 */
332
  { 1,   "certStatusRevoked",     ASN1_CONTEXT_C_1,   ASN1_OPT  }, /*  8 */
333
  { 2,     "revocationTime",      ASN1_GENERALIZEDTIME, ASN1_BODY }, /*  9 */
334
  { 2,     "revocationReason",    ASN1_CONTEXT_C_0,   ASN1_OPT  }, /* 10 */
335
  { 3,       "crlReason",       ASN1_ENUMERATED,    ASN1_BODY }, /* 11 */
336
  { 2,     "end opt",         ASN1_EOC,       ASN1_END  }, /* 12 */
337
  { 1,   "end opt",         ASN1_EOC,       ASN1_END  }, /* 13 */
338
  { 1,   "certStatusUnknown",     ASN1_CONTEXT_S_2,   ASN1_OPT  }, /* 14 */
339
  { 1,   "end opt",         ASN1_EOC,       ASN1_END  }, /* 15 */
340
  { 1,   "thisUpdate",        ASN1_GENERALIZEDTIME, ASN1_BODY }, /* 16 */
341
  { 1,   "nextUpdateContext",     ASN1_CONTEXT_C_0,   ASN1_OPT  }, /* 17 */
342
  { 2,     "nextUpdate",        ASN1_GENERALIZEDTIME, ASN1_BODY }, /* 18 */
343
  { 1,   "end opt",         ASN1_EOC,       ASN1_END  }, /* 19 */
344
  { 1,   "singleExtensionsContext", ASN1_CONTEXT_C_1,   ASN1_OPT  }, /* 20 */
345
  { 2,     "singleExtensions",    ASN1_SEQUENCE,      ASN1_LOOP }, /* 21 */
346
  { 3,       "extension",       ASN1_SEQUENCE,      ASN1_NONE }, /* 22 */
347
  { 4,         "extnID",        ASN1_OID,       ASN1_BODY }, /* 23 */
348
  { 4,         "critical",      ASN1_BOOLEAN,     ASN1_BODY |
349
                                ASN1_DEF  }, /* 24 */
350
  { 4,         "extnValue",     ASN1_OCTET_STRING,    ASN1_BODY }, /* 25 */
351
  { 2,     "end loop",        ASN1_EOC,       ASN1_END  }, /* 26 */
352
  { 1,   "end opt",         ASN1_EOC,       ASN1_END  }, /* 27 */
353
  { 0, "exit",            ASN1_EOC,       ASN1_EXIT }
354
};
355
356
0
#define SINGLE_RESPONSE_ALGORITHM          2
357
0
#define SINGLE_RESPONSE_ISSUER_NAME_HASH       3
358
0
#define SINGLE_RESPONSE_ISSUER_KEY_HASH        4
359
0
#define SINGLE_RESPONSE_SERIAL_NUMBER        5
360
0
#define SINGLE_RESPONSE_CERT_STATUS_GOOD       6
361
0
#define SINGLE_RESPONSE_CERT_STATUS_REVOKED      8
362
0
#define SINGLE_RESPONSE_CERT_STATUS_REVOCATION_TIME  9
363
0
#define SINGLE_RESPONSE_CERT_STATUS_CRL_REASON    11
364
0
#define SINGLE_RESPONSE_CERT_STATUS_UNKNOWN     14
365
0
#define SINGLE_RESPONSE_THIS_UPDATE         16
366
0
#define SINGLE_RESPONSE_NEXT_UPDATE         18
367
#define SINGLE_RESPONSE_EXT_ID            23
368
#define SINGLE_RESPONSE_CRITICAL          24
369
#define SINGLE_RESPONSE_EXT_VALUE         25
370
371
/**
372
 * Parse a single OCSP response
373
 */
374
static bool parse_singleResponse(private_x509_ocsp_response_t *this,
375
                 chunk_t blob, int level0)
376
0
{
377
0
  asn1_parser_t *parser;
378
0
  chunk_t object;
379
0
  int objectID;
380
0
  bool success = FALSE;
381
382
0
  ocsp_single_response_t *response;
383
384
0
  response = ocsp_single_response_create();
385
386
  /* if nextUpdate is missing, we give it a short lifetime */
387
0
  response->nextUpdate = this->producedAt + OCSP_DEFAULT_LIFETIME;
388
389
0
  parser = asn1_parser_create(singleResponseObjects, blob);
390
0
  parser->set_top_level(parser, level0);
391
392
0
  while (parser->iterate(parser, &objectID, &object))
393
0
  {
394
0
    switch (objectID)
395
0
    {
396
0
      case SINGLE_RESPONSE_ALGORITHM:
397
0
        response->hashAlgorithm = asn1_parse_algorithmIdentifier(object,
398
0
                      parser->get_level(parser)+1, NULL);
399
0
        break;
400
0
      case SINGLE_RESPONSE_ISSUER_NAME_HASH:
401
0
        response->issuerNameHash = chunk_clone(object);
402
0
        break;
403
0
      case SINGLE_RESPONSE_ISSUER_KEY_HASH:
404
0
        response->issuerKeyHash = chunk_clone(object);
405
0
        break;
406
0
      case SINGLE_RESPONSE_SERIAL_NUMBER:
407
0
        response->serialNumber = chunk_clone(chunk_skip_zero(object));
408
0
        break;
409
0
      case SINGLE_RESPONSE_CERT_STATUS_GOOD:
410
0
        response->status = VALIDATION_GOOD;
411
0
        break;
412
0
      case SINGLE_RESPONSE_CERT_STATUS_REVOKED:
413
0
        response->status = VALIDATION_REVOKED;
414
0
        break;
415
0
      case SINGLE_RESPONSE_CERT_STATUS_REVOCATION_TIME:
416
0
        response->revocationTime = asn1_to_time(&object, ASN1_GENERALIZEDTIME);
417
0
        break;
418
0
      case SINGLE_RESPONSE_CERT_STATUS_CRL_REASON:
419
0
        if (object.len == 1)
420
0
        {
421
0
          response->revocationReason = *object.ptr;
422
0
        }
423
0
        break;
424
0
      case SINGLE_RESPONSE_CERT_STATUS_UNKNOWN:
425
0
        response->status = VALIDATION_FAILED;
426
0
        break;
427
0
      case SINGLE_RESPONSE_THIS_UPDATE:
428
0
        response->thisUpdate = asn1_to_time(&object, ASN1_GENERALIZEDTIME);
429
0
        break;
430
0
      case SINGLE_RESPONSE_NEXT_UPDATE:
431
0
        response->nextUpdate = asn1_to_time(&object, ASN1_GENERALIZEDTIME);
432
0
        if (response->nextUpdate > this->usableUntil)
433
0
        {
434
0
          this->usableUntil = response->nextUpdate;
435
0
        }
436
0
        break;
437
0
    }
438
0
  }
439
0
  success = parser->success(parser);
440
0
  parser->destroy(parser);
441
0
  if (success)
442
0
  {
443
0
    if (this->usableUntil == UNDEFINED_TIME)
444
0
    {
445
0
      this->usableUntil = this->producedAt + OCSP_DEFAULT_LIFETIME;
446
0
    }
447
0
    this->responses->insert_last(this->responses, response);
448
0
  }
449
0
  else
450
0
  {
451
0
    response->destroy(response);
452
0
  }
453
0
  return success;
454
0
}
455
456
/**
457
 * Build responses
458
 */
459
static chunk_t build_responses(private_x509_ocsp_response_t *this)
460
0
{
461
0
  ocsp_single_response_t *response;
462
0
  chunk_t responses = chunk_empty, single_response;
463
0
  enumerator_t *enumerator;
464
465
0
  enumerator = this->responses->create_enumerator(this->responses);
466
0
  while (enumerator->enumerate(enumerator, &response))
467
0
  {
468
0
    single_response = build_singleResponse(this, response);
469
0
    responses = chunk_cat("mm", responses, single_response);
470
0
  }
471
0
  enumerator->destroy(enumerator);
472
473
0
  return asn1_wrap(ASN1_SEQUENCE, "m", responses);
474
0
}
475
476
/**
477
 * ASN.1 definition of responses
478
 */
479
static const asn1Object_t responsesObjects[] = {
480
  { 0, "responses",     ASN1_SEQUENCE,  ASN1_LOOP }, /* 0 */
481
  { 1,   "singleResponse",  ASN1_EOC,   ASN1_RAW  }, /* 1 */
482
  { 0, "end loop",      ASN1_EOC,   ASN1_END  }, /* 2 */
483
  { 0, "exit",        ASN1_EOC,   ASN1_EXIT }
484
};
485
0
#define RESPONSES_SINGLE_RESPONSE 1
486
487
/**
488
 * Parse all responses
489
 */
490
static bool parse_responses(private_x509_ocsp_response_t *this,
491
              chunk_t blob, int level0)
492
0
{
493
0
  asn1_parser_t *parser;
494
0
  chunk_t object;
495
0
  int objectID;
496
0
  bool success = FALSE;
497
498
0
  parser = asn1_parser_create(responsesObjects, blob);
499
0
  parser->set_top_level(parser, level0);
500
501
0
  while (parser->iterate(parser, &objectID, &object))
502
0
  {
503
0
    switch (objectID)
504
0
    {
505
0
      case RESPONSES_SINGLE_RESPONSE:
506
0
        if (!parse_singleResponse(this, object,
507
0
                      parser->get_level(parser)+1))
508
0
        {
509
0
          goto end;
510
0
        }
511
0
        break;
512
0
      default:
513
0
        break;
514
0
    }
515
0
  }
516
0
  success = parser->success(parser);
517
518
0
end:
519
0
  parser->destroy(parser);
520
0
  return success;
521
0
}
522
523
/**
524
 * Build tbsResponseData
525
 */
526
static chunk_t build_tbsResponseData(private_x509_ocsp_response_t *this)
527
0
{
528
0
  chunk_t responderIdByName;
529
0
  chunk_t responseExtensions = chunk_empty;
530
531
0
  responderIdByName = asn1_wrap(ASN1_CONTEXT_C_1, "c",
532
0
              this->responderId->get_encoding(this->responderId));
533
534
0
  this->producedAt = time(NULL);
535
536
0
  responseExtensions = asn1_wrap(ASN1_CONTEXT_C_1, "m",
537
0
              asn1_wrap(ASN1_SEQUENCE, "m",
538
0
                asn1_wrap(ASN1_SEQUENCE, "mm",
539
0
                  asn1_build_known_oid(OID_NONCE),
540
0
                  asn1_wrap(ASN1_OCTET_STRING, "m",
541
0
                    asn1_simple_object(ASN1_OCTET_STRING,
542
0
                              this->nonce)))));
543
544
0
  return asn1_wrap(ASN1_SEQUENCE, "mmmm",
545
0
        responderIdByName,
546
0
        asn1_from_time(&this->producedAt, ASN1_GENERALIZEDTIME),
547
0
        build_responses(this),
548
0
        responseExtensions);
549
0
}
550
551
/**
552
 * Build the signature
553
 */
554
static bool build_signature(private_x509_ocsp_response_t *this,
555
              chunk_t tbsResponseData, chunk_t *signature)
556
0
{
557
0
  if (!this->key->sign(this->key, this->scheme->scheme, this->scheme->params,
558
0
             tbsResponseData, signature))
559
0
  {
560
0
    DBG1(DBG_LIB, "creating OCSP response signature failed");
561
0
    return FALSE;
562
0
  }
563
0
  return TRUE;
564
0
}
565
566
/**
567
 * Build the basicOCSPResponse
568
 */
569
static bool build_basicOCSPResponse(private_x509_ocsp_response_t *this,
570
                  chunk_t *basicResponse)
571
0
{
572
0
  chunk_t tbsResponseData, sig_scheme, signature;
573
0
  chunk_t cert_encoding, certs = chunk_empty;
574
0
  x509_t *x509 = (x509_t*)this->cert;
575
576
0
  *basicResponse = chunk_empty;
577
578
0
  if (!signature_params_build(this->scheme, &sig_scheme))
579
0
  {
580
0
    return FALSE;
581
0
  }
582
0
  tbsResponseData = build_tbsResponseData(this);
583
584
0
  if (!build_signature(this, tbsResponseData, &signature))
585
0
  {
586
0
    free(tbsResponseData.ptr);
587
0
    free(sig_scheme.ptr);
588
0
    return FALSE;
589
0
  }
590
591
  /* don't include self-signed signer certificates */
592
0
  if (!(x509->get_flags(x509) & X509_SELF_SIGNED))
593
0
  {
594
0
    if (!this->cert->get_encoding(this->cert, CERT_ASN1_DER, &cert_encoding))
595
0
    {
596
0
      free(tbsResponseData.ptr);
597
0
      free(sig_scheme.ptr);
598
0
      free(signature.ptr);
599
0
      return FALSE;
600
0
    }
601
0
    certs = asn1_wrap(ASN1_CONTEXT_C_0, "m",
602
0
          asn1_wrap(ASN1_SEQUENCE, "m", cert_encoding));
603
0
  }
604
605
0
  *basicResponse = asn1_wrap(ASN1_SEQUENCE, "mmmm",
606
0
            tbsResponseData, sig_scheme,
607
0
            asn1_bitstring("m", signature), certs);
608
0
  return TRUE;
609
0
}
610
611
/**
612
 * ASN.1 definition of basicResponse
613
 */
614
static const asn1Object_t basicResponseObjects[] = {
615
  { 0, "BasicOCSPResponse",       ASN1_SEQUENCE,      ASN1_NONE }, /*  0 */
616
  { 1,   "tbsResponseData",       ASN1_SEQUENCE,      ASN1_OBJ  }, /*  1 */
617
  { 2,     "versionContext",        ASN1_CONTEXT_C_0,   ASN1_NONE |
618
                                  ASN1_DEF  }, /*  2 */
619
  { 3,       "version",         ASN1_INTEGER,     ASN1_BODY }, /*  3 */
620
  { 2,     "responderIdContext",      ASN1_CONTEXT_C_1,   ASN1_OPT  }, /*  4 */
621
  { 3,       "responderIdByName",     ASN1_SEQUENCE,      ASN1_OBJ  }, /*  5 */
622
  { 2,     "end choice",          ASN1_EOC,       ASN1_END  }, /*  6 */
623
  { 2,     "responderIdContext",      ASN1_CONTEXT_C_2,   ASN1_OPT  }, /*  7 */
624
  { 3,       "responderIdByKey",      ASN1_OCTET_STRING,    ASN1_BODY }, /*  8 */
625
  { 2,     "end choice",          ASN1_EOC,       ASN1_END  }, /*  9 */
626
  { 2,     "producedAt",          ASN1_GENERALIZEDTIME, ASN1_BODY }, /* 10 */
627
  { 2,     "responses",         ASN1_SEQUENCE,      ASN1_OBJ  }, /* 11 */
628
  { 2,     "responseExtensionsContext", ASN1_CONTEXT_C_1,   ASN1_OPT  }, /* 12 */
629
  { 3,       "responseExtensions",    ASN1_SEQUENCE,      ASN1_LOOP }, /* 13 */
630
  { 4,         "extension",       ASN1_SEQUENCE,      ASN1_NONE }, /* 14 */
631
  { 5,           "extnID",        ASN1_OID,       ASN1_BODY }, /* 15 */
632
  { 5,           "critical",        ASN1_BOOLEAN,     ASN1_BODY |
633
                                  ASN1_DEF  }, /* 16 */
634
  { 5,           "extnValue",       ASN1_OCTET_STRING,    ASN1_BODY }, /* 17 */
635
  { 3,       "end loop",          ASN1_EOC,       ASN1_END  }, /* 18 */
636
  { 2,     "end opt",           ASN1_EOC,       ASN1_END  }, /* 19 */
637
  { 1,   "signatureAlgorithm",      ASN1_EOC,       ASN1_RAW  }, /* 20 */
638
  { 1,   "signature",           ASN1_BIT_STRING,    ASN1_BODY }, /* 21 */
639
  { 1,   "certsContext",          ASN1_CONTEXT_C_0,   ASN1_OPT  }, /* 22 */
640
  { 2,     "certs",           ASN1_SEQUENCE,      ASN1_LOOP }, /* 23 */
641
  { 3,       "certificate",       ASN1_SEQUENCE,      ASN1_RAW  }, /* 24 */
642
  { 2,     "end loop",          ASN1_EOC,       ASN1_END  }, /* 25 */
643
  { 1,   "end opt",           ASN1_EOC,       ASN1_END  }, /* 26 */
644
  { 0, "exit",              ASN1_EOC,       ASN1_EXIT }
645
};
646
0
#define BASIC_RESPONSE_TBS_DATA    1
647
0
#define BASIC_RESPONSE_VERSION     3
648
0
#define BASIC_RESPONSE_ID_BY_NAME  5
649
0
#define BASIC_RESPONSE_ID_BY_KEY   8
650
0
#define BASIC_RESPONSE_PRODUCED_AT  10
651
0
#define BASIC_RESPONSE_RESPONSES  11
652
0
#define BASIC_RESPONSE_EXT_ID   15
653
0
#define BASIC_RESPONSE_CRITICAL   16
654
0
#define BASIC_RESPONSE_EXT_VALUE  17
655
0
#define BASIC_RESPONSE_ALGORITHM  20
656
0
#define BASIC_RESPONSE_SIGNATURE  21
657
0
#define BASIC_RESPONSE_CERTIFICATE  24
658
659
/**
660
 * Parse a basicOCSPResponse
661
 */
662
static bool parse_basicOCSPResponse(private_x509_ocsp_response_t *this,
663
                  chunk_t blob, int level0)
664
0
{
665
0
  asn1_parser_t *parser;
666
0
  chunk_t object;
667
0
  chunk_t responses = chunk_empty;
668
0
  int objectID;
669
0
  int extn_oid = OID_UNKNOWN;
670
0
  u_int responses_level = level0;
671
0
  certificate_t *cert;
672
0
  bool success = FALSE;
673
674
0
  parser = asn1_parser_create(basicResponseObjects, blob);
675
0
  parser->set_top_level(parser, level0);
676
677
0
  while (parser->iterate(parser, &objectID, &object))
678
0
  {
679
0
    switch (objectID)
680
0
    {
681
0
      case BASIC_RESPONSE_TBS_DATA:
682
0
        this->tbsResponseData = object;
683
0
        break;
684
0
      case BASIC_RESPONSE_VERSION:
685
0
      {
686
0
        u_int version = (object.len)? (1 + (u_int)*object.ptr) : 1;
687
688
0
        if (version != OCSP_BASIC_RESPONSE_VERSION)
689
0
        {
690
0
          DBG1(DBG_ASN, "  ocsp ResponseData version %d not "
691
0
             "supported", version);
692
0
          goto end;
693
0
        }
694
0
        break;
695
0
      }
696
0
      case BASIC_RESPONSE_ID_BY_NAME:
697
0
        this->responderId = identification_create_from_encoding(
698
0
                          ID_DER_ASN1_DN, object);
699
0
        DBG2(DBG_ASN, "  '%Y'", this->responderId);
700
0
        break;
701
0
      case BASIC_RESPONSE_ID_BY_KEY:
702
0
        this->responderId = identification_create_from_encoding(
703
0
                          ID_KEY_ID, object);
704
0
        DBG2(DBG_ASN, "  '%Y'", this->responderId);
705
0
        break;
706
0
      case BASIC_RESPONSE_PRODUCED_AT:
707
0
        this->producedAt = asn1_to_time(&object, ASN1_GENERALIZEDTIME);
708
0
        break;
709
0
      case BASIC_RESPONSE_RESPONSES:
710
0
        responses = object;
711
0
        responses_level = parser->get_level(parser)+1;
712
0
        break;
713
0
      case BASIC_RESPONSE_EXT_ID:
714
0
        extn_oid = asn1_known_oid(object);
715
0
        break;
716
0
      case BASIC_RESPONSE_CRITICAL:
717
0
        DBG2(DBG_ASN, "  %s",
718
0
           object.len && *object.ptr ? "TRUE" : "FALSE");
719
0
        break;
720
0
      case BASIC_RESPONSE_EXT_VALUE:
721
0
        if (extn_oid == OID_NONCE &&
722
0
          asn1_parse_simple_object(&object, ASN1_OCTET_STRING,
723
0
                    parser->get_level(parser)+1, "nonce"))
724
0
        {
725
0
          this->nonce = chunk_clone(object);
726
0
        }
727
0
        break;
728
0
      case BASIC_RESPONSE_ALGORITHM:
729
0
        INIT(this->scheme);
730
0
        if (!signature_params_parse(object, parser->get_level(parser)+1,
731
0
                      this->scheme))
732
0
        {
733
0
          DBG1(DBG_ASN, "  unable to parse signature algorithm");
734
0
          goto end;
735
0
        }
736
0
        break;
737
0
      case BASIC_RESPONSE_SIGNATURE:
738
0
        this->signature = chunk_skip(object, 1);
739
0
        break;
740
0
      case BASIC_RESPONSE_CERTIFICATE:
741
0
      {
742
0
        cert = lib->creds->create(lib->creds, CRED_CERTIFICATE,CERT_X509,
743
0
                      BUILD_BLOB_ASN1_DER, object,
744
0
                      BUILD_END);
745
0
        if (cert)
746
0
        {
747
0
          this->certs->insert_last(this->certs, cert);
748
0
        }
749
0
        break;
750
0
      }
751
0
    }
752
0
  }
753
0
  success = parser->success(parser);
754
755
0
end:
756
0
  parser->destroy(parser);
757
0
  if (success)
758
0
  {
759
0
    if (!this->responderId)
760
0
    {
761
0
      this->responderId = identification_create_from_encoding(ID_ANY,
762
0
                  chunk_empty);
763
0
    }
764
0
    success = parse_responses(this, responses, responses_level);
765
0
  }
766
0
  return success;
767
0
}
768
769
/**
770
 * Build the OCSPResponse
771
 *
772
 */
773
static chunk_t build_OCSPResponse(private_x509_ocsp_response_t *this)
774
0
{
775
0
  chunk_t response, responseBytes = chunk_empty;
776
777
0
  if (this->ocsp_status == OCSP_SUCCESSFUL)
778
0
  {
779
0
    if (!build_basicOCSPResponse(this, &response))
780
0
    {
781
0
      return chunk_empty;
782
0
    }
783
0
    responseBytes = asn1_wrap(ASN1_CONTEXT_C_0, "m",
784
0
              asn1_wrap(ASN1_SEQUENCE, "mm",
785
0
                asn1_build_known_oid(OID_BASIC),
786
0
                asn1_wrap(ASN1_OCTET_STRING, "m", response)));
787
0
  }
788
0
  return asn1_wrap(ASN1_SEQUENCE, "mm",
789
0
        asn1_simple_object(ASN1_ENUMERATED,
790
0
          chunk_from_chars(this->ocsp_status)),
791
0
        responseBytes);
792
0
}
793
794
/**
795
 * ASN.1 definition of ocspResponse
796
 */
797
static const asn1Object_t ocspResponseObjects[] = {
798
  { 0, "OCSPResponse",      ASN1_SEQUENCE,    ASN1_NONE }, /* 0 */
799
  { 1,   "responseStatus",    ASN1_ENUMERATED,  ASN1_BODY }, /* 1 */
800
  { 1,   "responseBytesContext",  ASN1_CONTEXT_C_0, ASN1_OPT  }, /* 2 */
801
  { 2,     "responseBytes",   ASN1_SEQUENCE,    ASN1_NONE }, /* 3 */
802
  { 3,       "responseType",    ASN1_OID,     ASN1_BODY }, /* 4 */
803
  { 3,       "response",      ASN1_OCTET_STRING,  ASN1_BODY }, /* 5 */
804
  { 1,   "end opt",       ASN1_EOC,     ASN1_END  }, /* 6 */
805
  { 0, "exit",          ASN1_EOC,     ASN1_EXIT }
806
};
807
0
#define OCSP_RESPONSE_STATUS  1
808
0
#define OCSP_RESPONSE_TYPE    4
809
0
#define OCSP_RESPONSE     5
810
811
/**
812
 * Parse OCSPResponse object
813
 */
814
static bool parse_OCSPResponse(private_x509_ocsp_response_t *this)
815
0
{
816
0
  asn1_parser_t *parser;
817
0
  chunk_t object;
818
0
  int objectID;
819
0
  int responseType = OID_UNKNOWN;
820
0
  bool success = FALSE;
821
822
0
  parser = asn1_parser_create(ocspResponseObjects, this->encoding);
823
824
0
  while (parser->iterate(parser, &objectID, &object))
825
0
  {
826
0
    switch (objectID)
827
0
    {
828
0
      case OCSP_RESPONSE_STATUS:
829
0
        this->ocsp_status = (ocsp_status_t)*object.ptr;
830
0
        switch (this->ocsp_status)
831
0
        {
832
0
          case OCSP_SUCCESSFUL:
833
0
            break;
834
0
          default:
835
0
            DBG1(DBG_LIB, "  ocsp response status: %N",
836
0
               ocsp_status_names, this->ocsp_status);
837
0
            success = TRUE;
838
0
            break;
839
0
        }
840
0
        break;
841
0
      case OCSP_RESPONSE_TYPE:
842
0
        responseType = asn1_known_oid(object);
843
0
        break;
844
0
      case OCSP_RESPONSE:
845
0
        switch (responseType)
846
0
        {
847
0
          case OID_BASIC:
848
0
            success = parse_basicOCSPResponse(this, object,
849
0
                        parser->get_level(parser)+1);
850
0
            break;
851
0
          default:
852
0
            DBG1(DBG_LIB, "  ocsp response type %#B not supported",
853
0
               &object);
854
0
            goto end;
855
0
        }
856
0
        break;
857
0
    }
858
0
  }
859
0
  success &= parser->success(parser);
860
861
0
end:
862
0
  parser->destroy(parser);
863
0
  return success;
864
0
}
865
866
METHOD(certificate_t, get_type, certificate_type_t,
867
  private_x509_ocsp_response_t *this)
868
0
{
869
0
  return CERT_X509_OCSP_RESPONSE;
870
0
}
871
872
METHOD(certificate_t, get_issuer, identification_t*,
873
  private_x509_ocsp_response_t *this)
874
0
{
875
0
  return this->responderId;
876
0
}
877
878
METHOD(certificate_t, has_issuer, id_match_t,
879
  private_x509_ocsp_response_t *this, identification_t *issuer)
880
0
{
881
0
  return this->responderId->matches(this->responderId, issuer);
882
0
}
883
884
METHOD(certificate_t, issued_by, bool,
885
  private_x509_ocsp_response_t *this, certificate_t *issuer,
886
  signature_params_t **scheme)
887
0
{
888
0
  public_key_t *key;
889
0
  bool valid;
890
0
  x509_t *x509 = (x509_t*)issuer;
891
892
0
  if (issuer->get_type(issuer) != CERT_X509)
893
0
  {
894
0
    return FALSE;
895
0
  }
896
0
  if (this->responderId->get_type(this->responderId) == ID_KEY_ID)
897
0
  {
898
0
    chunk_t fingerprint;
899
900
0
    key = issuer->get_public_key(issuer);
901
0
    if (!key ||
902
0
      !key->get_fingerprint(key, KEYID_PUBKEY_SHA1, &fingerprint) ||
903
0
      !chunk_equals(fingerprint,
904
0
              this->responderId->get_encoding(this->responderId)))
905
0
    {
906
0
      DESTROY_IF(key);
907
0
      return FALSE;
908
0
    }
909
0
    key->destroy(key);
910
0
  }
911
0
  else
912
0
  {
913
0
    if (!this->responderId->equals(this->responderId,
914
0
                     issuer->get_subject(issuer)))
915
0
    {
916
0
      return FALSE;
917
0
    }
918
0
  }
919
0
  if (!(x509->get_flags(x509) & X509_OCSP_SIGNER) &&
920
0
    !(x509->get_flags(x509) & X509_CA))
921
0
  {
922
0
    return FALSE;
923
0
  }
924
925
0
  key = issuer->get_public_key(issuer);
926
0
  if (!key)
927
0
  {
928
0
    return FALSE;
929
0
  }
930
0
  valid = key->verify(key, this->scheme->scheme, this->scheme->params,
931
0
            this->tbsResponseData, this->signature);
932
0
  key->destroy(key);
933
0
  if (valid && scheme)
934
0
  {
935
0
    *scheme = signature_params_clone(this->scheme);
936
0
  }
937
0
  return valid;
938
0
}
939
940
METHOD(certificate_t, get_public_key, public_key_t*,
941
  private_x509_ocsp_response_t *this)
942
0
{
943
0
  return NULL;
944
0
}
945
946
METHOD(certificate_t, get_validity, bool,
947
  private_x509_ocsp_response_t *this, time_t *when,
948
  time_t *not_before, time_t *not_after)
949
0
{
950
0
  time_t t = when ? *when : time(NULL);
951
952
0
  if (not_before)
953
0
  {
954
0
    *not_before = this->producedAt;
955
0
  }
956
0
  if (not_after)
957
0
  {
958
0
    *not_after = this->usableUntil;
959
0
  }
960
0
  return (t < this->usableUntil);
961
0
}
962
963
METHOD(certificate_t, get_encoding, bool,
964
  private_x509_ocsp_response_t *this, cred_encoding_type_t type,
965
  chunk_t *encoding)
966
0
{
967
0
  if (type == CERT_ASN1_DER)
968
0
  {
969
0
    *encoding = chunk_clone(this->encoding);
970
0
    return TRUE;
971
0
  }
972
0
  return lib->encoding->encode(lib->encoding, type, NULL, encoding,
973
0
        CRED_PART_X509_OCSP_RES_ASN1_DER, this->encoding, CRED_PART_END);
974
0
}
975
976
METHOD(certificate_t, equals, bool,
977
  private_x509_ocsp_response_t *this, certificate_t *other)
978
0
{
979
0
  chunk_t encoding;
980
0
  bool equal;
981
982
0
  if (this == (private_x509_ocsp_response_t*)other)
983
0
  {
984
0
    return TRUE;
985
0
  }
986
0
  if (other->get_type(other) != CERT_X509_OCSP_RESPONSE)
987
0
  {
988
0
    return FALSE;
989
0
  }
990
0
  if (other->equals == (void*)equals)
991
0
  { /* skip allocation if we have the same implementation */
992
0
    return chunk_equals(this->encoding, ((private_x509_ocsp_response_t*)other)->encoding);
993
0
  }
994
0
  if (!other->get_encoding(other, CERT_ASN1_DER, &encoding))
995
0
  {
996
0
    return FALSE;
997
0
  }
998
0
  equal = chunk_equals(this->encoding, encoding);
999
0
  free(encoding.ptr);
1000
0
  return equal;
1001
0
}
1002
1003
METHOD(certificate_t, get_ref, certificate_t*,
1004
  private_x509_ocsp_response_t *this)
1005
0
{
1006
0
  ref_get(&this->ref);
1007
0
  return &this->public.interface.certificate;
1008
0
}
1009
1010
METHOD(certificate_t, destroy, void,
1011
  private_x509_ocsp_response_t *this)
1012
0
{
1013
0
  if (ref_put(&this->ref))
1014
0
  {
1015
0
    this->certs->destroy_offset(this->certs,
1016
0
                offsetof(certificate_t, destroy));
1017
0
    this->responses->destroy_offset(this->responses,
1018
0
                offsetof(ocsp_single_response_t, destroy));
1019
0
    DESTROY_IF(this->cert);
1020
0
    DESTROY_IF(this->key);
1021
0
    DESTROY_IF(this->responderId);
1022
0
    signature_params_destroy(this->scheme);
1023
0
    free(this->nonce.ptr);
1024
0
    free(this->encoding.ptr);
1025
0
    free(this);
1026
0
  }
1027
0
}
1028
1029
/**
1030
 * create an empty but initialized OCSP response
1031
 */
1032
static private_x509_ocsp_response_t *create_empty()
1033
0
{
1034
0
  private_x509_ocsp_response_t *this;
1035
1036
0
  INIT(this,
1037
0
    .public = {
1038
0
      .interface = {
1039
0
        .certificate = {
1040
0
          .get_type = _get_type,
1041
0
          .get_subject = _get_issuer,
1042
0
          .get_issuer = _get_issuer,
1043
0
          .has_subject = _has_issuer,
1044
0
          .has_issuer = _has_issuer,
1045
0
          .issued_by = _issued_by,
1046
0
          .get_public_key = _get_public_key,
1047
0
          .get_validity = _get_validity,
1048
0
          .get_encoding = _get_encoding,
1049
0
          .equals = _equals,
1050
0
          .get_ref = _get_ref,
1051
0
          .destroy = _destroy,
1052
0
        },
1053
0
        .get_nonce = _get_nonce,
1054
0
        .get_status = _get_status,
1055
0
        .create_cert_enumerator = _create_cert_enumerator,
1056
0
        .create_response_enumerator = _create_response_enumerator,
1057
0
      },
1058
0
    },
1059
0
    .ref = 1,
1060
0
    .producedAt = UNDEFINED_TIME,
1061
0
    .usableUntil = UNDEFINED_TIME,
1062
0
    .responses = linked_list_create(),
1063
0
    .certs = linked_list_create(),
1064
0
  );
1065
1066
0
  return this;
1067
0
}
1068
1069
/**
1070
 * See header.
1071
 */
1072
x509_ocsp_response_t *x509_ocsp_response_gen(certificate_type_t type, va_list args)
1073
0
{
1074
0
  private_x509_ocsp_response_t *this;
1075
0
  private_key_t *private;
1076
0
  certificate_t *cert;
1077
0
  chunk_t nonce;
1078
0
  identification_t *subject;
1079
0
  enumerator_t *enumerator;
1080
0
  ocsp_single_response_t *response;
1081
1082
0
  this = create_empty();
1083
1084
0
  while (TRUE)
1085
0
  {
1086
0
    switch (va_arg(args, builder_part_t))
1087
0
    {
1088
0
      case BUILD_OCSP_STATUS:
1089
0
        this->ocsp_status = va_arg(args, ocsp_status_t);
1090
0
        continue;
1091
0
      case BUILD_OCSP_RESPONSES:
1092
0
        enumerator = va_arg(args, enumerator_t*);
1093
0
        while (enumerator->enumerate(enumerator, &response))
1094
0
        {
1095
0
          this->responses->insert_last(this->responses,
1096
0
                        response->get_ref(response));
1097
0
        }
1098
0
        continue;
1099
0
      case BUILD_SIGNING_CERT:
1100
0
        cert = va_arg(args, certificate_t*);
1101
0
        if (cert)
1102
0
        {
1103
0
          subject = cert->get_subject(cert);
1104
0
          this->cert = cert->get_ref(cert);
1105
0
          this->responderId = subject->clone(subject);
1106
0
        }
1107
0
        continue;
1108
0
      case BUILD_SIGNING_KEY:
1109
0
        private = va_arg(args, private_key_t*);
1110
0
        if (private)
1111
0
        {
1112
0
          this->key = private->get_ref(private);
1113
0
        }
1114
0
        continue;
1115
0
      case BUILD_SIGNATURE_SCHEME:
1116
0
        this->scheme = va_arg(args, signature_params_t*);
1117
0
        this->scheme = signature_params_clone(this->scheme);
1118
0
        continue;
1119
0
      case BUILD_NONCE:
1120
0
        nonce = va_arg(args, chunk_t);
1121
0
        this->nonce = chunk_clone(nonce);
1122
0
        continue;
1123
0
      case BUILD_END:
1124
0
        break;
1125
0
      default:
1126
0
        goto error;
1127
0
    }
1128
0
    break;
1129
0
  }
1130
1131
0
  if (this->ocsp_status == OCSP_SUCCESSFUL)
1132
0
  {
1133
0
    if (!this->key)
1134
0
    {
1135
0
      DBG1(DBG_LIB, "no OCSP signing key defined");
1136
0
      goto error;
1137
0
    }
1138
1139
    /* select signature scheme, if not already specified */
1140
0
    if (!this->scheme)
1141
0
    {
1142
0
      INIT(this->scheme,
1143
0
        .scheme = signature_scheme_from_oid(
1144
0
              hasher_signature_algorithm_to_oid(HASH_SHA256,
1145
0
                this->key->get_type(this->key))),
1146
0
      );
1147
0
    }
1148
0
    if (this->scheme->scheme == SIGN_UNKNOWN)
1149
0
    {
1150
0
      goto error;
1151
0
    }
1152
0
  }
1153
1154
0
  this->encoding = build_OCSPResponse(this);
1155
0
  return &this->public;
1156
1157
0
error:
1158
0
  destroy(this);
1159
0
  return NULL;
1160
0
}
1161
1162
/**
1163
 * load an OCSP response
1164
 */
1165
static x509_ocsp_response_t *load(chunk_t blob)
1166
0
{
1167
0
  private_x509_ocsp_response_t *this;
1168
1169
0
  this = create_empty();
1170
0
  this->encoding = chunk_clone(blob);
1171
1172
0
  if (!parse_OCSPResponse(this))
1173
0
  {
1174
0
    destroy(this);
1175
0
    return NULL;
1176
0
  }
1177
0
  return &this->public;
1178
0
}
1179
1180
/**
1181
 * See header.
1182
 */
1183
x509_ocsp_response_t *x509_ocsp_response_load(certificate_type_t type,
1184
                        va_list args)
1185
0
{
1186
0
  chunk_t blob = chunk_empty;
1187
1188
0
  while (TRUE)
1189
0
  {
1190
0
    switch (va_arg(args, builder_part_t))
1191
0
    {
1192
0
      case BUILD_BLOB_ASN1_DER:
1193
0
        blob = va_arg(args, chunk_t);
1194
0
        continue;
1195
0
      case BUILD_END:
1196
0
        break;
1197
0
      default:
1198
0
        return NULL;
1199
0
    }
1200
0
    break;
1201
0
  }
1202
0
  if (blob.ptr)
1203
0
  {
1204
0
    return load(blob);
1205
0
  }
1206
0
  return NULL;
1207
0
}