Logo Search packages:      
Sourcecode: strongswan version File versions  Download package

ocsp.c

/* Support of the Online Certificate Status Protocol (OCSP)
 * Copyright (C) 2003 Christoph Gysin, Simon Zwahlen
 * Zuercher Hochschule Winterthur
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by the
 * Free Software Foundation; either version 2 of the License, or (at your
 * option) any later version.  See <http://www.fsf.org/copyleft/gpl.txt>.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
 * for more details.
 *
 */

#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>

#include <freeswan.h>
#include <freeswan/ipsec_policy.h>

#include "constants.h"
#include "defs.h"
#include "log.h"
#include "x509.h"
#include "crl.h"
#include "ca.h"
#include "rnd.h"
#include "asn1.h"
#include "certs.h"
#include "smartcard.h"
#include "oid.h"
#include "whack.h"
#include "pkcs1.h"
#include "keys.h"
#include "fetch.h"
#include "ocsp.h"

#define NONCE_LENGTH          16

static const char *const cert_status_names[] = {
    "good",
    "revoked",
    "unknown",
    "undefined"
};


static const char *const response_status_names[] = {
    "successful",
    "malformed request",
    "internal error",
    "try later",
    "signature required",
    "unauthorized"
};

/* response container */
typedef struct response response_t;

struct response {
    chunk_t  tbs;
    chunk_t  responder_id_name;
    chunk_t  responder_id_key;
    time_t   produced_at;
    chunk_t  responses;
    chunk_t  nonce;
    int      algorithm;
    chunk_t  signature;
};

const response_t empty_response = {
    { NULL, 0 }   ,     /* tbs */
    { NULL, 0 }   ,     /* responder_id_name */
    { NULL, 0 }   ,     /* responder_id_key */
    UNDEFINED_TIME,     /* produced_at */
    { NULL, 0 }   ,     /* single_response */
    { NULL, 0 }   ,     /* nonce */
    OID_UNKNOWN   ,     /* signature_algorithm */
    { NULL, 0 }         /* signature */
};

/* single response container */
typedef struct single_response single_response_t;

struct single_response {
    single_response_t *next;
    int               hash_algorithm;
    chunk_t           issuer_name_hash;
    chunk_t           issuer_key_hash;
    chunk_t           serialNumber;
    cert_status_t     status;
    time_t            revocationTime;
    crl_reason_t      revocationReason;
    time_t            thisUpdate;
    time_t            nextUpdate;
};

const single_response_t empty_single_response = {
      NULL            , /* *next */
    OID_UNKNOWN       , /* hash_algorithm */
    { NULL, 0 }       , /* issuer_name_hash */
    { NULL, 0 }       , /* issuer_key_hash */
    { NULL, 0 }       , /* serial_number */
    CERT_UNDEFINED    , /* status */
    UNDEFINED_TIME    , /* revocationTime */
    REASON_UNSPECIFIED, /* revocationReason */
    UNDEFINED_TIME    , /* this_update */
    UNDEFINED_TIME      /* next_update */
};


/* list of single requests */
typedef struct request_list request_list_t;
struct request_list {
    chunk_t request;
    request_list_t *next;
};

/* some OCSP specific prefabricated ASN.1 constants */

static u_char ASN1_nonce_oid_str[] = {
    0x06, 0x09, 0x2B, 0x06, 0x01, 0x05, 0x05, 0x07, 0x30, 0x01, 0x02
};

static const chunk_t ASN1_nonce_oid = strchunk(ASN1_nonce_oid_str);

static u_char ASN1_response_oid_str[] = {
    0x06, 0x09, 0x2B, 0x06, 0x01, 0x05, 0x05, 0x07, 0x30, 0x01, 0x04
};

static const chunk_t ASN1_response_oid = strchunk(ASN1_response_oid_str);

static u_char ASN1_response_content_str[] = {
    0x04, 0x0D,
        0x30, 0x0B,
            0x06, 0x09, 0x2B, 0x06, 0x01, 0x05, 0x05, 0x07, 0x30, 0x01, 0x01
};

static const chunk_t ASN1_response_content = strchunk(ASN1_response_content_str);

/* default OCSP uri */
static chunk_t ocsp_default_uri;

/* ocsp cache: pointer to first element */
static ocsp_location_t *ocsp_cache = NULL;

/* static temporary storage for ocsp requestor information */
static x509cert_t *ocsp_requestor_cert = NULL;

static smartcard_t *ocsp_requestor_sc = NULL;

static const struct RSA_private_key *ocsp_requestor_pri = NULL;

/* asn.1 definitions for parsing */

static const asn1Object_t ocspResponseObjects[] = {
  { 0, "OCSPResponse",                  ASN1_SEQUENCE,     ASN1_NONE }, /*  0 */
  { 1,   "responseStatus",              ASN1_ENUMERATED,   ASN1_BODY }, /*  1 */
  { 1,   "responseBytesContext",        ASN1_CONTEXT_C_0,  ASN1_OPT  }, /*  2 */
  { 2,     "responseBytes",             ASN1_SEQUENCE,     ASN1_NONE }, /*  3 */
  { 3,       "responseType",            ASN1_OID,          ASN1_BODY }, /*  4 */
  { 3,       "response",                ASN1_OCTET_STRING, ASN1_BODY }, /*  5 */
  { 1,   "end opt",                     ASN1_EOC,          ASN1_END  }  /*  6 */
};

#define OCSP_RESPONSE_STATUS  1
#define OCSP_RESPONSE_TYPE    4
#define OCSP_RESPONSE         5
#define OCSP_RESPONSE_ROOF    7

static const asn1Object_t basicResponseObjects[] = {
  { 0, "BasicOCSPResponse",             ASN1_SEQUENCE,        ASN1_NONE }, /*  0 */
  { 1,   "tbsResponseData",             ASN1_SEQUENCE,        ASN1_OBJ  }, /*  1 */
  { 2,     "versionContext",            ASN1_CONTEXT_C_0,     ASN1_NONE |
                                                ASN1_DEF  }, /*  2 */
  { 3,       "version",                 ASN1_INTEGER,         ASN1_BODY }, /*  3 */
  { 2,     "responderIdContext",        ASN1_CONTEXT_C_1,     ASN1_OPT  }, /*  4 */
  { 3,       "responderIdByName",       ASN1_SEQUENCE,        ASN1_OBJ  }, /*  5 */
  { 2,     "end choice",                ASN1_EOC,             ASN1_END  }, /*  6 */
  { 2,     "responderIdContext",        ASN1_CONTEXT_C_2,     ASN1_OPT  }, /*  7 */
  { 3,       "responderIdByKey",        ASN1_OCTET_STRING,    ASN1_BODY }, /*  8 */
  { 2,     "end choice",                ASN1_EOC,             ASN1_END  }, /*  9 */
  { 2,     "producedAt",                ASN1_GENERALIZEDTIME, ASN1_BODY }, /* 10 */
  { 2,     "responses",                 ASN1_SEQUENCE,        ASN1_OBJ  }, /* 11 */
  { 2,     "responseExtensionsContext", ASN1_CONTEXT_C_1,     ASN1_OPT  }, /* 12 */
  { 3,       "responseExtensions",      ASN1_SEQUENCE,        ASN1_LOOP }, /* 13 */
  { 4,         "extension",             ASN1_SEQUENCE,        ASN1_NONE }, /* 14 */
  { 5,           "extnID",              ASN1_OID,             ASN1_BODY }, /* 15 */
  { 5,           "critical",            ASN1_BOOLEAN,         ASN1_BODY |
                                                ASN1_DEF  }, /* 16 */
  { 5,           "extnValue",           ASN1_OCTET_STRING,    ASN1_BODY }, /* 17 */
  { 4,         "end loop",              ASN1_EOC,             ASN1_END  }, /* 18 */
  { 2,     "end opt",                   ASN1_EOC,             ASN1_END  }, /* 19 */
  { 1,   "signatureAlgorithm",          ASN1_EOC,             ASN1_RAW  }, /* 20 */
  { 1,   "signature",                   ASN1_BIT_STRING,      ASN1_BODY }, /* 21 */
  { 1,   "certsContext",                ASN1_CONTEXT_C_0,     ASN1_OPT  }, /* 22 */
  { 2,     "certs",                     ASN1_SEQUENCE,        ASN1_LOOP }, /* 23 */
  { 3,       "certificate",             ASN1_SEQUENCE,        ASN1_OBJ  }, /* 24 */
  { 2,     "end loop",                  ASN1_EOC,           ASN1_END  }, /* 25 */
  { 1,   "end opt",                     ASN1_EOC,             ASN1_END  }  /* 26 */
};

#define BASIC_RESPONSE_TBS_DATA            1
#define BASIC_RESPONSE_VERSION             3
#define BASIC_RESPONSE_ID_BY_NAME    5
#define BASIC_RESPONSE_ID_BY_KEY     8
#define BASIC_RESPONSE_PRODUCED_AT  10
#define BASIC_RESPONSE_RESPONSES    11
#define BASIC_RESPONSE_EXT_ID       15
#define BASIC_RESPONSE_CRITICAL           16
#define BASIC_RESPONSE_EXT_VALUE    17
#define BASIC_RESPONSE_ALGORITHM    20
#define BASIC_RESPONSE_SIGNATURE    21
#define BASIC_RESPONSE_CERTIFICATE  24
#define BASIC_RESPONSE_ROOF         27

static const asn1Object_t responsesObjects[] = {
  { 0, "responses",                   ASN1_SEQUENCE,          ASN1_LOOP }, /*  0 */
  { 1,   "singleResponse",            ASN1_EOC,               ASN1_RAW  }, /*  1 */
  { 0, "end loop",                    ASN1_EOC,               ASN1_END  }  /*  2 */
};

#define RESPONSES_SINGLE_RESPONSE    1
#define RESPONSES_ROOF               3

static const asn1Object_t singleResponseObjects[] = {
  { 0, "singleResponse",            ASN1_SEQUENCE,          ASN1_BODY }, /*  0 */
  { 1,   "certID",                  ASN1_SEQUENCE,          ASN1_NONE }, /*  1 */
  { 2,     "algorithm",             ASN1_EOC,               ASN1_RAW  }, /*  2 */
  { 2,     "issuerNameHash",        ASN1_OCTET_STRING,      ASN1_BODY }, /*  3 */
  { 2,     "issuerKeyHash",         ASN1_OCTET_STRING,      ASN1_BODY }, /*  4 */
  { 2,     "serialNumber",          ASN1_INTEGER,           ASN1_BODY }, /*  5 */
  { 1,   "certStatusGood",          ASN1_CONTEXT_S_0,       ASN1_OPT  }, /*  6 */
  { 1,   "end opt",                 ASN1_EOC,               ASN1_END  }, /*  7 */
  { 1,   "certStatusRevoked",       ASN1_CONTEXT_C_1,       ASN1_OPT  }, /*  8 */
  { 2,     "revocationTime",        ASN1_GENERALIZEDTIME,   ASN1_BODY }, /*  9 */
  { 2,     "revocationReason",      ASN1_CONTEXT_C_0,       ASN1_OPT  }, /* 10 */
  { 3,       "crlReason",           ASN1_ENUMERATED,        ASN1_BODY }, /* 11 */
  { 2,     "end opt",               ASN1_EOC,               ASN1_END  }, /* 12 */
  { 1,   "end opt",                 ASN1_EOC,               ASN1_END  }, /* 13 */
  { 1,   "certStatusUnknown",       ASN1_CONTEXT_S_2,       ASN1_OPT  }, /* 14 */
  { 1,   "end opt",                 ASN1_EOC,               ASN1_END  }, /* 15 */
  { 1,   "thisUpdate",              ASN1_GENERALIZEDTIME,   ASN1_BODY }, /* 16 */
  { 1,   "nextUpdateContext",       ASN1_CONTEXT_C_0,       ASN1_OPT  }, /* 17 */
  { 2,     "nextUpdate",            ASN1_GENERALIZEDTIME,   ASN1_BODY }, /* 18 */
  { 1,   "end opt",                 ASN1_EOC,               ASN1_END  }, /* 19 */
  { 1,   "singleExtensionsContext", ASN1_CONTEXT_C_1,       ASN1_OPT  }, /* 20 */
  { 2,     "singleExtensions",      ASN1_SEQUENCE,          ASN1_LOOP }, /* 21 */
  { 3,       "extension",           ASN1_SEQUENCE,          ASN1_NONE }, /* 22 */
  { 4,         "extnID",            ASN1_OID,               ASN1_BODY }, /* 23 */
  { 4,         "critical",          ASN1_BOOLEAN,           ASN1_BODY |
                                              ASN1_DEF  }, /* 24 */
  { 4,         "extnValue",         ASN1_OCTET_STRING,      ASN1_BODY }, /* 25 */
  { 2,     "end loop",              ASN1_EOC,               ASN1_END  }, /* 26 */
  { 1,   "end opt",                 ASN1_EOC,               ASN1_END  }  /* 27 */
};

#define SINGLE_RESPONSE_ALGORITHM                2
#define SINGLE_RESPONSE_ISSUER_NAME_HASH         3
#define SINGLE_RESPONSE_ISSUER_KEY_HASH                4
#define SINGLE_RESPONSE_SERIAL_NUMBER                  5
#define SINGLE_RESPONSE_CERT_STATUS_GOOD         6
#define SINGLE_RESPONSE_CERT_STATUS_REVOKED            8
#define SINGLE_RESPONSE_CERT_STATUS_REVOCATION_TIME    9
#define SINGLE_RESPONSE_CERT_STATUS_CRL_REASON        11
#define SINGLE_RESPONSE_CERT_STATUS_UNKNOWN           14
#define SINGLE_RESPONSE_THIS_UPDATE             16
#define SINGLE_RESPONSE_NEXT_UPDATE             18
#define SINGLE_RESPONSE_EXT_ID                        23
#define SINGLE_RESPONSE_CRITICAL                24
#define SINGLE_RESPONSE_EXT_VALUE               25
#define SINGLE_RESPONSE_ROOF                    28

/* build an ocsp location from certificate information
 * without unsharing its contents
 */
static bool
build_ocsp_location(const x509cert_t *cert, ocsp_location_t *location)
{
    static u_char digest[SHA1_DIGEST_SIZE];  /* temporary storage */

    location->uri = cert->accessLocation;

    if (location->uri.ptr == NULL)
    {
      ca_info_t *ca = get_ca_info(cert->issuer, cert->authKeySerialNumber
            , cert->authKeyID);
      if (ca != NULL && ca->ocspuri != NULL)
          setchunk(location->uri, ca->ocspuri, strlen(ca->ocspuri))
      else
          /* abort if no ocsp location uri is defined */
          return FALSE;
    }
    
    setchunk(location->authNameID, digest, SHA1_DIGEST_SIZE);
    compute_digest(cert->issuer, OID_SHA1, &location->authNameID);

    location->next = NULL;
    location->issuer = cert->issuer;
    location->authKeyID = cert->authKeyID;
    location->authKeySerialNumber = cert->authKeySerialNumber;
    
    if (cert->authKeyID.ptr == NULL) 
    {
      x509cert_t *authcert = get_authcert(cert->issuer
            , cert->authKeySerialNumber, cert->authKeyID, AUTH_CA);

      if (authcert != NULL)
      {
          location->authKeyID = authcert->subjectKeyID;
          location->authKeySerialNumber = authcert->serialNumber;
      }
    }

    location->nonce = empty_chunk;
    location->certinfo = NULL;

    return TRUE;
}

/*
 * compare two ocsp locations for equality
 */
static bool
same_ocsp_location(const ocsp_location_t *a, const ocsp_location_t *b)
{
    return ((a->authKeyID.ptr != NULL)
            ? same_keyid(a->authKeyID, b->authKeyID)
            : (same_dn(a->issuer, b->issuer)
                && same_serial(a->authKeySerialNumber, b->authKeySerialNumber)))
          && same_chunk(a->uri, b->uri);
}

/*
 * find an existing ocsp location in a chained list
 */
ocsp_location_t*
get_ocsp_location(const ocsp_location_t * loc, ocsp_location_t *chain)
{

    while (chain != NULL)
    {
      if (same_ocsp_location(loc, chain))
          return chain;
      chain = chain->next;
    }
    return NULL;
}
 
/* retrieves the status of a cert from the ocsp cache
 * returns CERT_UNDEFINED if no status is found
 */
static cert_status_t
get_ocsp_status(const ocsp_location_t *loc, chunk_t serialNumber
    ,time_t *nextUpdate, time_t *revocationTime, crl_reason_t *revocationReason)
{
    ocsp_certinfo_t *certinfo, **certinfop;
    int cmp = -1;

    /* find location */
    ocsp_location_t *location = get_ocsp_location(loc, ocsp_cache);

    if (location == NULL)
      return CERT_UNDEFINED;

    /* traverse list of certinfos in increasing order */
    certinfop = &location->certinfo;
    certinfo = *certinfop;

    while (certinfo != NULL)
    {
      cmp = cmp_chunk(serialNumber, certinfo->serialNumber);
      if (cmp <= 0)
          break;
      certinfop = &certinfo->next;
      certinfo = *certinfop;
    }

    if (cmp == 0)
    {
      *nextUpdate = certinfo->nextUpdate;
      *revocationTime = certinfo->revocationTime;
      *revocationReason = certinfo->revocationReason;
      return certinfo->status;
    }

    return CERT_UNDEFINED;
}

/*
 * verify the ocsp status of a certificate
 */
cert_status_t
verify_by_ocsp(const x509cert_t *cert, time_t *until
, time_t *revocationDate, crl_reason_t *revocationReason)
{
    cert_status_t status;
    ocsp_location_t location;
    time_t nextUpdate = 0;

    *revocationDate = UNDEFINED_TIME;
    *revocationReason = REASON_UNSPECIFIED;
    
    /* is an ocsp location defined? */
    if (!build_ocsp_location(cert, &location))
      return CERT_UNDEFINED;

    lock_ocsp_cache("verify_by_ocsp");
    status = get_ocsp_status(&location, cert->serialNumber, &nextUpdate
      , revocationDate, revocationReason);
    unlock_ocsp_cache("verify_by_ocsp");

    if (status == CERT_UNDEFINED || nextUpdate < time(NULL))
    {
      plog("ocsp status is stale or not in cache");
      add_ocsp_fetch_request(&location, cert->serialNumber);

      /* inititate fetching of ocsp status */
      wake_fetch_thread("verify_by_ocsp");
    }
    *until = nextUpdate;
    return status;
}

/*
 * check if an ocsp status is about to expire
 */
void
check_ocsp(void)
{
    ocsp_location_t *location;

    lock_ocsp_cache("check_ocsp");
    location = ocsp_cache;
    
    while (location != NULL)
    {
      char buf[BUF_LEN];
      bool first = TRUE;
      ocsp_certinfo_t *certinfo = location->certinfo;

      while (certinfo != NULL)
      {
          if (!certinfo->once)
          {
            time_t time_left = certinfo->nextUpdate - time(NULL);

            DBG(DBG_CONTROL,
                if (first)
                {
                  dntoa(buf, BUF_LEN, location->issuer);
                  DBG_log("issuer: '%s'", buf);
                  if (location->authKeyID.ptr != NULL)
                  {
                      datatot(location->authKeyID.ptr, location->authKeyID.len
                        , ':', buf, BUF_LEN);
                      DBG_log("authkey: %s", buf);
                  }
                  first = FALSE;
                }
                datatot(certinfo->serialNumber.ptr, certinfo->serialNumber.len
                  , ':', buf, BUF_LEN);
                DBG_log("serial: %s, %ld seconds left", buf, time_left)
            )

            if (time_left < 2*crl_check_interval)
                add_ocsp_fetch_request(location, certinfo->serialNumber);
          }
          certinfo = certinfo->next;
      }
      location = location->next;
    }
    unlock_ocsp_cache("check_ocsp");
}

/*
 *  frees the allocated memory of a certinfo struct
 */
static void
free_certinfo(ocsp_certinfo_t *certinfo)
{
    freeanychunk(certinfo->serialNumber);
    pfree(certinfo);
}

/*
 * frees all certinfos in a chained list
 */
static void
free_certinfos(ocsp_certinfo_t *chain)
{
    ocsp_certinfo_t *certinfo;

    while (chain != NULL)
    {
      certinfo = chain;
      chain = chain->next;
      free_certinfo(certinfo);
    }
}

/*
 * frees the memory allocated to an ocsp location including all certinfos
 */
static void
free_ocsp_location(ocsp_location_t* location)
{
    freeanychunk(location->issuer);
    freeanychunk(location->authNameID);
    freeanychunk(location->authKeyID);
    freeanychunk(location->authKeySerialNumber);
    freeanychunk(location->uri);
    free_certinfos(location->certinfo);
    pfree(location);
}

/*
 * free a chained list of ocsp locations
 */
void
free_ocsp_locations(ocsp_location_t **chain)
{
    while (*chain != NULL)
    {
      ocsp_location_t *location = *chain;
      *chain = location->next;
      free_ocsp_location(location);
    }
}

/*
 * free the ocsp cache
 */
void
free_ocsp_cache(void)
{
    lock_ocsp_cache("free_ocsp_cache");
    free_ocsp_locations(&ocsp_cache);
    unlock_ocsp_cache("free_ocsp_cache");
}

/*
 * frees the ocsp cache and global variables
 */
void
free_ocsp(void)
{
    pfreeany(ocsp_default_uri.ptr);
    free_ocsp_cache();
}

/*
 * list a chained list of ocsp_locations
 */
void
list_ocsp_locations(ocsp_location_t *location, bool requests, bool utc
, bool strict)
{
    bool first = TRUE;

    while (location != NULL)
    {
      ocsp_certinfo_t *certinfo = location->certinfo;

      if (certinfo != NULL)
      {
          u_char buf[BUF_LEN];

          if (first)
          {
            whack_log(RC_COMMENT, " ");
            whack_log(RC_COMMENT, "List of OCSP %s:", requests?
                "fetch requests":"responses");
            first = FALSE;
            }
          whack_log(RC_COMMENT, " ");
          if (location->issuer.ptr != NULL)
          {
            dntoa(buf, BUF_LEN, location->issuer);
            whack_log(RC_COMMENT, "       issuer:  '%s'", buf);
          }
          whack_log(RC_COMMENT, "       uri:     '%.*s'", (int)location->uri.len
            , location->uri.ptr);
          if (location->authNameID.ptr != NULL)
          {
            datatot(location->authNameID.ptr, location->authNameID.len, ':'
                , buf, BUF_LEN);
            whack_log(RC_COMMENT, "       authname: %s", buf);
          }
          if (location->authKeyID.ptr != NULL)
          {
            datatot(location->authKeyID.ptr, location->authKeyID.len, ':'
                , buf, BUF_LEN);
            whack_log(RC_COMMENT, "       authkey:  %s", buf);
          }
          if (location->authKeySerialNumber.ptr != NULL)
          {
            datatot(location->authKeySerialNumber.ptr
                , location->authKeySerialNumber.len, ':', buf, BUF_LEN);
            whack_log(RC_COMMENT, "       aserial:  %s", buf);
          }
          while (certinfo != NULL)
          {
            char thisUpdate[TIMETOA_BUF];

            strcpy(thisUpdate, timetoa(&certinfo->thisUpdate, utc));

            if (requests)
            {
                whack_log(RC_COMMENT, "%s, trials: %d", thisUpdate
                  , certinfo->trials);
            }
            else if (certinfo->once)
            {
                whack_log(RC_COMMENT, "%s, onetime use%s", thisUpdate
                  , (certinfo->nextUpdate < time(NULL))? " (expired)": "");
            }
            else
            {
                whack_log(RC_COMMENT, "%s, until %s %s", thisUpdate
                  , timetoa(&certinfo->nextUpdate, utc)
                  , check_expiry(certinfo->nextUpdate, OCSP_WARNING_INTERVAL, strict));
            }
            datatot(certinfo->serialNumber.ptr, certinfo->serialNumber.len, ':'
                , buf, BUF_LEN);
            whack_log(RC_COMMENT, "       serial:   %s, %s", buf
                , cert_status_names[certinfo->status]);
            certinfo = certinfo->next;
          }
      }
      location = location->next;
    }
}

/*
 * list the ocsp cache
 */
void
list_ocsp_cache(bool utc, bool strict)
{
    lock_ocsp_cache("list_ocsp_cache");
    list_ocsp_locations(ocsp_cache, FALSE, utc, strict);
    unlock_ocsp_cache("list_ocsp_cache");
}

static bool
get_ocsp_requestor_cert(ocsp_location_t *location)
{
    x509cert_t *cert = NULL;

    /* initialize temporary static storage */
    ocsp_requestor_cert = NULL;
    ocsp_requestor_sc   = NULL;
    ocsp_requestor_pri  = NULL;

    for (;;)
    {
      char buf[BUF_LEN];

      /* looking for a certificate from the same issuer */
      cert = get_x509cert(location->issuer, location->authKeySerialNumber
                ,location->authKeyID, cert);
      if (cert == NULL)
          break;

      DBG(DBG_CONTROL,
          dntoa(buf, BUF_LEN, cert->subject);
          DBG_log("candidate: '%s'", buf);
      )

      if (cert->smartcard)
      {
          /* look for a matching private key on a smartcard */
          smartcard_t *sc = scx_get(cert);

          if (sc != NULL)
          {
            DBG(DBG_CONTROL,
                DBG_log("matching smartcard found")
            )
            if (sc->valid)
            {
                ocsp_requestor_cert = cert;
                ocsp_requestor_sc = sc;
                return TRUE;
            }
            plog("unable to sign ocsp request without PIN");
          }
      }
      else
      {
          /* look for a matching private key in the chained list */
          const struct RSA_private_key *pri = get_x509_private_key(cert);

          if (pri != NULL)
          {
            DBG(DBG_CONTROL,
                DBG_log("matching private key found")
            )
            ocsp_requestor_cert = cert;
            ocsp_requestor_pri = pri;
            return TRUE;
          }
      }
    }
    return FALSE;
}

static chunk_t
generate_signature(chunk_t digest, smartcard_t *sc
    , const RSA_private_key_t *pri)
{
    chunk_t sigdata;
    u_char *pos;
    size_t siglen = 0;

    if (sc != NULL)
    {
      /* RSA signature is done on smartcard */

      if (!scx_establish_context(sc) || !scx_login(sc))
      {
          scx_release_context(sc);
          return empty_chunk;
      }

      siglen = scx_get_keylength(sc);

      if (siglen == 0)
      {
          plog("failed to get keylength from smartcard");
          scx_release_context(sc);
          return empty_chunk;
      }

      DBG(DBG_CONTROL | DBG_CRYPT,
          DBG_log("signing hash with RSA key from smartcard (slot: %d, id: %s)"
            , (int)sc->slot, sc->id)
      )

      pos = build_asn1_object(&sigdata, ASN1_BIT_STRING, 1 + siglen);
      *pos++ = 0x00;
      scx_sign_hash(sc, digest.ptr, digest.len, pos, siglen);
      if (!pkcs11_keep_state)
          scx_release_context(sc);
    }
    else
    {
      /* RSA signature is done in software */
      siglen = pri->pub.k;
      pos = build_asn1_object(&sigdata, ASN1_BIT_STRING, 1 + siglen);
      *pos++ = 0x00;
      sign_hash(pri, digest.ptr, digest.len, pos, siglen);
    }
    return sigdata;
}

/*
 * build signature into ocsp request
 * gets built only if a request cert with
 * a corresponding private key is found
 */
static chunk_t
build_signature(chunk_t tbsRequest)
{
    chunk_t sigdata, certs;
    chunk_t digest_info;

    u_char digest_buf[MAX_DIGEST_LEN];
    chunk_t digest_raw = { digest_buf, MAX_DIGEST_LEN };

    if (!compute_digest(tbsRequest, OID_SHA1, &digest_raw))
      return empty_chunk;

    /* according to PKCS#1 v2.1 digest must be packaged into
     * an ASN.1 structure for encryption
     */
    digest_info = asn1_wrap(ASN1_SEQUENCE, "cm"
      , ASN1_sha1_id
      , asn1_simple_object(ASN1_OCTET_STRING, digest_raw));

    /* generate the RSA signature */
    sigdata = generate_signature(digest_info
      , ocsp_requestor_sc
      , ocsp_requestor_pri);
    freeanychunk(digest_info);

    /* has the RSA signature generation been successful? */
    if (sigdata.ptr == NULL)
      return empty_chunk;

    /* include our certificate */
    certs = asn1_wrap(ASN1_CONTEXT_C_0, "m"
            , asn1_simple_object(ASN1_SEQUENCE
                , ocsp_requestor_cert->certificate
              )
          );

    /* build signature comprising algorithm, signature and cert */
    return asn1_wrap(ASN1_CONTEXT_C_0, "m"
            , asn1_wrap(ASN1_SEQUENCE, "cmm"
                , ASN1_sha1WithRSA_id
                , sigdata
                , certs
              )
         );
}

/* build request (into requestList)
 * no singleRequestExtensions used
 */
static chunk_t
build_request(ocsp_location_t *location, ocsp_certinfo_t *certinfo)
{
    chunk_t reqCert = asn1_wrap(ASN1_SEQUENCE, "cmmm"
            , ASN1_sha1_id
            , asn1_simple_object(ASN1_OCTET_STRING, location->authNameID)
            , asn1_simple_object(ASN1_OCTET_STRING, location->authKeyID)
            , asn1_simple_object(ASN1_INTEGER, certinfo->serialNumber));

    return asn1_wrap(ASN1_SEQUENCE, "m", reqCert);
}

/*
 * build requestList (into TBSRequest)
 */
static chunk_t
build_request_list(ocsp_location_t *location)
{
    chunk_t requestList;
    request_list_t *reqs = NULL;
    ocsp_certinfo_t *certinfo = location->certinfo;
    u_char *pos;

    size_t datalen = 0;

    /* build content */
    while (certinfo != NULL)
    {
      /* build request for every certificate in list
       * and store them in a chained list
       */
      request_list_t *req = alloc_thing(request_list_t, "ocsp request");

      req->request = build_request(location, certinfo);
      req->next = reqs;
      reqs = req;

      datalen += req->request.len;
      certinfo = certinfo->next;
    }

    pos = build_asn1_object(&requestList, ASN1_SEQUENCE
          , datalen);

    /* copy all in chained list, free list afterwards */
    while (reqs != NULL)
    {
      request_list_t *req = reqs;

      mv_chunk(&pos, req->request);
      reqs = reqs->next;
      pfree(req);
    }

    return requestList;
}

/*
 * build requestorName (into TBSRequest)
 */
static chunk_t
build_requestor_name(void)
{
    return asn1_wrap(ASN1_CONTEXT_C_1, "m"
            , asn1_simple_object(ASN1_CONTEXT_C_4
                , ocsp_requestor_cert->subject));
}

/*
 * build nonce extension (into requestExtensions)
 */
static chunk_t
build_nonce_extension(ocsp_location_t *location)
{
    /* generate a random nonce */
    location->nonce.ptr = alloc_bytes(NONCE_LENGTH, "ocsp nonce"),
    location->nonce.len = NONCE_LENGTH;
    get_rnd_bytes(location->nonce.ptr, NONCE_LENGTH);

    return asn1_wrap(ASN1_SEQUENCE, "cm"
            , ASN1_nonce_oid
            , asn1_simple_object(ASN1_OCTET_STRING, location->nonce));
}

/*
 * build requestExtensions (into TBSRequest)
 */
static chunk_t
build_request_ext(ocsp_location_t *location)
{
    return asn1_wrap(ASN1_CONTEXT_C_2, "m"
            , asn1_wrap(ASN1_SEQUENCE, "mm"
                , build_nonce_extension(location)
                , asn1_wrap(ASN1_SEQUENCE, "cc"
                  , ASN1_response_oid
                  , ASN1_response_content
                  )
              )
          );
}

/*
 * build TBSRequest (into OCSPRequest)
 */
static chunk_t
build_tbs_request(ocsp_location_t *location, bool has_requestor_cert)
{
    /* version is skipped since the default is ok */
    return asn1_wrap(ASN1_SEQUENCE, "mmm"
            , (has_requestor_cert)
                  ? build_requestor_name()
                  : empty_chunk
            , build_request_list(location)
            , build_request_ext(location));
}

/* assembles an ocsp request to given location
 * and sets nonce field in location to the sent nonce
 */
chunk_t
build_ocsp_request(ocsp_location_t *location)
{
    bool has_requestor_cert;
    chunk_t tbsRequest, signature;
    char buf[BUF_LEN];

    DBG(DBG_CONTROL,
      DBG_log("assembling ocsp request");
      dntoa(buf, BUF_LEN, location->issuer);
      DBG_log("issuer: '%s'", buf);
      if (location->authKeyID.ptr != NULL)
      {
          datatot(location->authKeyID.ptr, location->authKeyID.len, ':'
            , buf, BUF_LEN);
          DBG_log("authkey: %s", buf);
      }
    )
    lock_certs_and_keys("build_ocsp_request");

    /* looks for requestor cert and matching private key */
    has_requestor_cert = get_ocsp_requestor_cert(location);

    /* build content */
    tbsRequest = build_tbs_request(location, has_requestor_cert);

    /* sign tbsReuqest */
    signature = (has_requestor_cert)? build_signature(tbsRequest)
                            : empty_chunk;

    unlock_certs_and_keys("build_ocsp_request");

    return asn1_wrap(ASN1_SEQUENCE, "mm"
            , tbsRequest
            , signature);
}

/*
 * check if the OCSP response has a valid signature
 */
static bool
valid_ocsp_response(response_t *res)
{
    int pathlen;
    x509cert_t *authcert;

    lock_authcert_list("valid_ocsp_response");

    authcert = get_authcert(res->responder_id_name, empty_chunk
                , res->responder_id_key, AUTH_OCSP | AUTH_CA);

    if (authcert == NULL)
    {
      plog("no matching ocsp signer cert found");
      unlock_authcert_list("valid_ocsp_response");
      return FALSE;
    }
    DBG(DBG_CONTROL,
      DBG_log("ocsp signer cert found")
    )

    if (!check_signature(res->tbs, res->signature, res->algorithm
                   , res->algorithm, authcert))
    {
      plog("signature of ocsp response is invalid");
      unlock_authcert_list("valid_ocsp_response");
      return FALSE;
    }
    DBG(DBG_CONTROL,
      DBG_log("signature of ocsp response is valid")
    )


    for (pathlen = 0; pathlen < MAX_CA_PATH_LEN; pathlen++)
    {
      u_char buf[BUF_LEN];
      err_t ugh = NULL;
      time_t until;

      x509cert_t *cert = authcert;

      DBG(DBG_CONTROL,
          dntoa(buf, BUF_LEN, cert->subject);
          DBG_log("subject: '%s'",buf);
          dntoa(buf, BUF_LEN, cert->issuer);
          DBG_log("issuer:  '%s'",buf);
          if (cert->authKeyID.ptr != NULL)
          {
            datatot(cert->authKeyID.ptr, cert->authKeyID.len, ':'
                , buf, BUF_LEN);
            DBG_log("authkey:  %s", buf);
          }
      )

      ugh = check_validity(authcert, &until);

      if (ugh != NULL)
      {
          plog("%s", ugh);
          unlock_authcert_list("valid_ocsp_response");
          return FALSE;
        }
      
      DBG(DBG_CONTROL,
          DBG_log("certificate is valid")
      )
      
      authcert = get_authcert(cert->issuer, cert->authKeySerialNumber
          , cert->authKeyID, AUTH_CA);

      if (authcert == NULL)
      {
          plog("issuer cacert not found");
          unlock_authcert_list("valid_ocsp_response");
          return FALSE;
      }
      DBG(DBG_CONTROL,
          DBG_log("issuer cacert found")
      )

      if (!check_signature(cert->tbsCertificate, cert->signature
                     , cert->algorithm, cert->algorithm, authcert))
      {
          plog("certificate signature is invalid");
          unlock_authcert_list("valid_ocsp_response");
          return FALSE;
      }
      DBG(DBG_CONTROL,
          DBG_log("certificate signature is valid")
      )

      /* check if cert is self-signed */
      if (same_dn(cert->issuer, cert->subject))
      {
          DBG(DBG_CONTROL,
            DBG_log("reached self-signed root ca")
          )
          unlock_authcert_list("valid_ocsp_response");
          return TRUE;
      }
    }
    plog("maximum ca path length of %d levels exceeded", MAX_CA_PATH_LEN);
    unlock_authcert_list("valid_ocsp_response");
    return FALSE;
}

/*
 * parse a basic OCSP response
 */
static bool
parse_basic_ocsp_response(chunk_t blob, int level0, response_t *res)
{
    u_int level, version;
    u_int extn_oid = OID_UNKNOWN;
    u_char buf[BUF_LEN];
    asn1_ctx_t ctx;
    bool critical;
    chunk_t object;
    int objectID = 0;

    asn1_init(&ctx, blob, level0, FALSE, DBG_RAW);

    while (objectID < BASIC_RESPONSE_ROOF)
    {
      if (!extract_object(basicResponseObjects, &objectID, &object, &level, &ctx))
          return FALSE;
      
      switch (objectID)
      {
      case BASIC_RESPONSE_TBS_DATA:
          res->tbs = object;
          break;
      case BASIC_RESPONSE_VERSION:
          version = (object.len)? (1 + (u_int)*object.ptr) : 1;
          if (version != OCSP_BASIC_RESPONSE_VERSION)
          {
            plog("wrong ocsp basic response version (version= %i)",  version);
            return FALSE;
          }
          break;
      case BASIC_RESPONSE_ID_BY_NAME:
          res->responder_id_name = object;
          DBG(DBG_PARSING,
            dntoa(buf, BUF_LEN, object);
            DBG_log("  '%s'",buf)
          )
          break;
      case BASIC_RESPONSE_ID_BY_KEY:
          res->responder_id_key = object;
          break;
      case BASIC_RESPONSE_PRODUCED_AT:
          res->produced_at = asn1totime(&object, ASN1_GENERALIZEDTIME);
          break;
      case BASIC_RESPONSE_RESPONSES:
          res->responses = object;
          break;
      case BASIC_RESPONSE_EXT_ID:
          extn_oid = known_oid(object);
          break;
      case BASIC_RESPONSE_CRITICAL:
          critical = object.len && *object.ptr;
          DBG(DBG_PARSING,
            DBG_log("  %s",(critical)?"TRUE":"FALSE");
          )
          break;
      case BASIC_RESPONSE_EXT_VALUE:
          if (extn_oid == OID_NONCE)
            res->nonce = object;
          break;
      case BASIC_RESPONSE_ALGORITHM:
          res->algorithm = parse_algorithmIdentifier(object, level+1, NULL);
          break;
      case BASIC_RESPONSE_SIGNATURE:
          res->signature = object;
          break;
      case BASIC_RESPONSE_CERTIFICATE:
          {
            chunk_t blob;
            x509cert_t *cert = alloc_thing(x509cert_t, "ocspcert");

            clonetochunk(blob, object.ptr, object.len, "ocspcert blob");
            *cert = empty_x509cert;

            if (parse_x509cert(blob, level+1, cert)
            && cert->isOcspSigner
            && trust_authcert_candidate(cert, NULL))
            {
                add_authcert(cert, AUTH_OCSP);
            }
            else
            {
                DBG(DBG_CONTROL | DBG_PARSING,
                  DBG_log("embedded ocsp certificate rejected")
                )
                free_x509cert(cert);
            }
          }
          break;
      }
      objectID++;
    }
    return TRUE;
}


/*
 * parse an ocsp response and return the result as a response_t struct
 */
static response_status
parse_ocsp_response(chunk_t blob, response_t * res)
{
    asn1_ctx_t ctx;
    chunk_t object;
    u_int level;
    int objectID = 0;

    response_status rStatus = STATUS_INTERNALERROR;
    u_int ocspResponseType = OID_UNKNOWN;

    asn1_init(&ctx, blob, 0, FALSE, DBG_RAW);

    while (objectID < OCSP_RESPONSE_ROOF)
    {
      if (!extract_object(ocspResponseObjects, &objectID, &object, &level, &ctx))
          return STATUS_INTERNALERROR;

      switch (objectID) {
      case OCSP_RESPONSE_STATUS:
          rStatus = (response_status) *object.ptr;

          switch (rStatus)
          {
          case STATUS_SUCCESSFUL:
            break;
          case STATUS_MALFORMEDREQUEST:
          case STATUS_INTERNALERROR:
          case STATUS_TRYLATER:
          case STATUS_SIGREQUIRED:
          case STATUS_UNAUTHORIZED:
            plog("ocsp response: server said '%s'"
                , response_status_names[rStatus]);
            return rStatus;
          default:
            return STATUS_INTERNALERROR;
          }
          break;
      case OCSP_RESPONSE_TYPE:
          ocspResponseType = known_oid(object);
          break;
      case OCSP_RESPONSE:
          {
            switch (ocspResponseType) {
            case OID_BASIC:
                if (!parse_basic_ocsp_response(object, level+1, res))
                  return STATUS_INTERNALERROR;
                break;
            default:
                DBG(DBG_CONTROL,
                  DBG_log("ocsp response is not of type BASIC");
                  DBG_dump_chunk("ocsp response OID: ", object);
                )
                return STATUS_INTERNALERROR;
            }
          }
          break;
      }
      objectID++;
    }
    return rStatus;
}

/*
 * parse a basic OCSP response
 */
static bool
parse_ocsp_single_response(chunk_t blob, int level0, single_response_t *sres)
{
    u_int level, extn_oid;
    asn1_ctx_t ctx;
    bool critical;
    chunk_t object;
    int objectID = 0;

    asn1_init(&ctx, blob, level0, FALSE, DBG_RAW);

    while (objectID < SINGLE_RESPONSE_ROOF)
    {
      if (!extract_object(singleResponseObjects, &objectID, &object, &level, &ctx))
          return FALSE;

      switch (objectID)
      {
      case SINGLE_RESPONSE_ALGORITHM:
          sres->hash_algorithm = parse_algorithmIdentifier(object, level+1, NULL);
          break;
      case SINGLE_RESPONSE_ISSUER_NAME_HASH:
          sres->issuer_name_hash = object;
          break;
      case SINGLE_RESPONSE_ISSUER_KEY_HASH:
          sres->issuer_key_hash = object;
          break;
      case SINGLE_RESPONSE_SERIAL_NUMBER:
          sres->serialNumber = object;
          break;
      case SINGLE_RESPONSE_CERT_STATUS_GOOD:
          sres->status = CERT_GOOD;
          break;
      case SINGLE_RESPONSE_CERT_STATUS_REVOKED:
          sres->status = CERT_REVOKED;
          break;
      case SINGLE_RESPONSE_CERT_STATUS_REVOCATION_TIME:
          sres->revocationTime = asn1totime(&object, ASN1_GENERALIZEDTIME);
          break;
      case SINGLE_RESPONSE_CERT_STATUS_CRL_REASON:
          sres->revocationReason = (object.len == 1)
            ? *object.ptr : REASON_UNSPECIFIED;
          break;
      case SINGLE_RESPONSE_CERT_STATUS_UNKNOWN:
          sres->status = CERT_UNKNOWN;
          break;
      case SINGLE_RESPONSE_THIS_UPDATE:
          sres->thisUpdate = asn1totime(&object, ASN1_GENERALIZEDTIME);
          break;
      case SINGLE_RESPONSE_NEXT_UPDATE:
          sres->nextUpdate = asn1totime(&object, ASN1_GENERALIZEDTIME);
          break;
      case SINGLE_RESPONSE_EXT_ID:
          extn_oid = known_oid(object);
          break;
      case SINGLE_RESPONSE_CRITICAL:
          critical = object.len && *object.ptr;
          DBG(DBG_PARSING,
            DBG_log("  %s",(critical)?"TRUE":"FALSE");
          )
      case SINGLE_RESPONSE_EXT_VALUE:
          break;
      }
      objectID++;
    }
    return TRUE;
}

/*
 * add an ocsp location to a chained list
 */
ocsp_location_t*
add_ocsp_location(const ocsp_location_t *loc, ocsp_location_t **chain)
{
    ocsp_location_t *location = alloc_thing(ocsp_location_t, "ocsp location");

    /* unshare location fields */
    clonetochunk(location->issuer
            , loc->issuer.ptr, loc->issuer.len
            , "ocsp issuer");

    clonetochunk(location->authNameID
            , loc->authNameID.ptr, loc->authNameID.len
            , "ocsp authNameID");

    if (loc->authKeyID.ptr == NULL)
      location->authKeyID = empty_chunk;
    else
      clonetochunk(location->authKeyID
            , loc->authKeyID.ptr, loc->authKeyID.len
            , "ocsp authKeyID");

    if (loc->authKeySerialNumber.ptr == NULL)
      location->authKeySerialNumber = empty_chunk;
    else
      clonetochunk(location->authKeySerialNumber
            , loc->authKeySerialNumber.ptr, loc->authKeySerialNumber.len
            , "ocsp authKeySerialNumber");

    clonetochunk(location->uri
            , loc->uri.ptr, loc->uri.len
            , "ocsp uri");

    location->certinfo = NULL;

    /* insert new ocsp location in front of chain */
    location->next = *chain;
    *chain = location;

    DBG(DBG_CONTROL,
      DBG_log("new ocsp location added")
    )

    return location;
}

/*
 * add a certinfo struct to a chained list
 */
void
add_certinfo(ocsp_location_t *loc, ocsp_certinfo_t *info, ocsp_location_t **chain
    , bool request)
{
    ocsp_location_t *location;
    ocsp_certinfo_t *certinfo, **certinfop;
    char buf[BUF_LEN];
    time_t now;
    int cmp = -1;

    location = get_ocsp_location(loc, *chain);
    if (location == NULL)
      location = add_ocsp_location(loc, chain);

    /* traverse list of certinfos in increasing order */
    certinfop = &location->certinfo;
    certinfo = *certinfop;

    while (certinfo != NULL)
    {
      cmp = cmp_chunk(info->serialNumber, certinfo->serialNumber);
      if (cmp <= 0)
          break;
      certinfop = &certinfo->next;
      certinfo = *certinfop;
    }

    if (cmp != 0)
    {
      /* add a new certinfo entry */
      ocsp_certinfo_t *cnew = alloc_thing(ocsp_certinfo_t, "ocsp certinfo");
      clonetochunk(cnew->serialNumber, info->serialNumber.ptr
          , info->serialNumber.len, "serialNumber");
      cnew->next = certinfo;
      *certinfop = cnew;
      certinfo = cnew;
    }
      
    DBG(DBG_CONTROL,
      datatot(info->serialNumber.ptr, info->serialNumber.len, ':'
          , buf, BUF_LEN);
      DBG_log("ocsp %s for serial %s %s"
          , request?"fetch request":"certinfo"
          , buf
          , (cmp == 0)? (request?"already exists":"updated"):"added")
    )

    time(&now);
   
    if (request)
    {
      certinfo->status = CERT_UNDEFINED;
      
      if (cmp != 0)
          certinfo->thisUpdate = now;

      certinfo->nextUpdate = UNDEFINED_TIME;
    }
    else
    {
      certinfo->status = info->status;
      certinfo->revocationTime = info->revocationTime;
      certinfo->revocationReason = info->revocationReason;
      
      certinfo->thisUpdate = (info->thisUpdate != UNDEFINED_TIME)?
          info->thisUpdate : now;

      certinfo->once = (info->nextUpdate == UNDEFINED_TIME);

      certinfo->nextUpdate = (certinfo->once)?
          (now + OCSP_DEFAULT_VALID_TIME) : info->nextUpdate;
    }
}

/*
 * process received ocsp single response and add it to ocsp cache
 */
static void
process_single_response(ocsp_location_t *location, single_response_t *sres)
{
    ocsp_certinfo_t *certinfo, **certinfop;
    int cmp = -1;

    if (sres->hash_algorithm != OID_SHA1)
    {
      plog("only SHA-1 hash supported in OCSP single response");
      return;
    }
    if (!(same_chunk(sres->issuer_name_hash, location->authNameID)
    &&   same_chunk(sres->issuer_key_hash, location->authKeyID)))
    {
      plog("ocsp single response has wrong issuer");
      return;
    }
    
    /* traverse list of certinfos in increasing order */
    certinfop = &location->certinfo;
    certinfo = *certinfop;

    while (certinfo != NULL)
    {
      cmp = cmp_chunk(sres->serialNumber, certinfo->serialNumber);
      if (cmp <= 0)
          break;
      certinfop = &certinfo->next;
      certinfo = *certinfop;
    }

    if (cmp != 0)
    {
      plog("received unrequested cert status from ocsp server");
      return;
    }

    /* unlink cert from ocsp fetch request list */
    *certinfop = certinfo->next;
    
    /* update certinfo using the single response information */
    certinfo->thisUpdate = sres->thisUpdate;
    certinfo->nextUpdate = sres->nextUpdate;
    certinfo->status = sres->status;
    certinfo->revocationTime = sres->revocationTime;
    certinfo->revocationReason = sres->revocationReason;
    
    /* add or update certinfo in ocsp cache */
    lock_ocsp_cache("process_single_response");
    add_certinfo(location, certinfo, &ocsp_cache, FALSE);
    unlock_ocsp_cache("process_single_response");

    /* free certinfo unlinked from ocsp fetch request list */
    free_certinfo(certinfo);

}

/*
 *  parse and verify ocsp response and update the ocsp cache
 */
void
parse_ocsp(ocsp_location_t *location, chunk_t blob)
{
    response_t res = empty_response;

    /* parse the ocsp response without looking at the single responses yet */
    response_status status = parse_ocsp_response(blob, &res);

    if (status != STATUS_SUCCESSFUL)
    {
      plog("error in ocsp response");
      return;
    }
    /* check if there was a nonce in the request */
    if (location->nonce.ptr != NULL && res.nonce.ptr == NULL)
    {
      plog("ocsp response contains no nonce, replay attack possible");
    }
    /* check if the nonce is identical */
    if (res.nonce.ptr != NULL && !same_chunk(res.nonce, location->nonce))
    {
      plog("invalid nonce in ocsp response");
      return;
    }
    /* check if the response is signed by a trusted key */
    if (!valid_ocsp_response(&res))
    {
      plog("invalid ocsp response");
      return;
    }
    DBG(DBG_CONTROL,
      DBG_log("valid ocsp response")
    )

    /* now parse the single responses one at a time */
    {
      u_int level;
      asn1_ctx_t ctx;
      chunk_t object;
      int objectID = 0;

      asn1_init(&ctx, res.responses, 0, FALSE, DBG_RAW);

      while (objectID < RESPONSES_ROOF)
      {
          if (!extract_object(responsesObjects, &objectID, &object, &level, &ctx))
            return;
          
          if (objectID == RESPONSES_SINGLE_RESPONSE)
          {
            single_response_t sres = empty_single_response;

            if (parse_ocsp_single_response(object, level+1, &sres))
            {
                process_single_response(location, &sres);
            }
          }
          objectID++;
      }
    }
}

Generated by  Doxygen 1.6.0   Back to index