support for hash and URL encoded certificate payloads in charon

This commit is contained in:
Tobias Brunner
2008-04-18 11:24:45 +00:00
parent eed87e1d76
commit 6439267a8c
23 changed files with 760 additions and 160 deletions
+124 -50
View File
@@ -1,4 +1,5 @@
/*
* Copyright (C) 2008 Tobias Brunner
* Copyright (C) 2007 Martin Willi
* Hochschule fuer Technik Rapperswil
*
@@ -29,6 +30,8 @@ ENUM(auth_item_names, AUTHN_CA_CERT, AUTHZ_AC_GROUP,
"AUTHN_CA_CERT_NAME",
"AUTHN_IM_CERT",
"AUTHN_SUBJECT_CERT",
"AUTHN_IM_HASH_URL",
"AUTHN_SUBJECT_HASH_URL",
"AUTHZ_PUBKEY",
"AUTHZ_PSK",
"AUTHZ_EAP",
@@ -69,14 +72,38 @@ struct item_t {
};
/**
* implements item_enumerator_t.enumerate
* enumerator for auth_info_wrapper_t.create_cert_enumerator()
*/
static bool item_filter(void *data, item_t **item, auth_item_t *type,
void *unused, void **value)
typedef struct {
/** implements enumerator_t */
enumerator_t public;
/** inner enumerator from linked_list_t */
enumerator_t *inner;
/** the current item */
item_t *item;
} item_enumerator_t;
/**
* enumerate function for item_enumerator_t
*/
static bool enumerate(item_enumerator_t *this, auth_item_t *type, void **value)
{
*type = (*item)->type;
*value = (*item)->value;
return TRUE;
if (this->inner->enumerate(this->inner, &this->item))
{
*type = this->item->type;
*value = this->item->value;
return TRUE;
}
return FALSE;
}
/**
* destroy function for item_enumerator_t
*/
static void item_enumerator_destroy(item_enumerator_t *this)
{
this->inner->destroy(this->inner);
free(this);
}
/**
@@ -84,8 +111,26 @@ static bool item_filter(void *data, item_t **item, auth_item_t *type,
*/
static enumerator_t* create_item_enumerator(private_auth_info_t *this)
{
return enumerator_create_filter(this->items->create_enumerator(this->items),
(void*)item_filter, NULL, NULL);
item_enumerator_t *enumerator;
enumerator = malloc_thing(item_enumerator_t);
enumerator->item = NULL;
enumerator->inner = this->items->create_enumerator(this->items);
enumerator->public.enumerate = (void*)enumerate;
enumerator->public.destroy = (void*)item_enumerator_destroy;
return &enumerator->public;
}
static void destroy_item_value(item_t *item);
/**
* Implementation of auth_info_t.replace_item.
*/
static void replace_item(item_enumerator_t *enumerator, auth_item_t type, void *value)
{
destroy_item_value(enumerator->item);
enumerator->item->type = type;
enumerator->item->value = value;
}
/**
@@ -136,6 +181,12 @@ static void add_item(private_auth_info_t *this, auth_item_t type, void *value)
item->value = key->get_ref(key);
break;
}
case AUTHN_IM_HASH_URL:
case AUTHN_SUBJECT_HASH_URL:
{
item->value = strdup(value);
break;
}
case AUTHN_CA_CERT:
case AUTHN_IM_CERT:
case AUTHN_SUBJECT_CERT:
@@ -200,6 +251,8 @@ static bool complies(private_auth_info_t *this, auth_info_t *constraints)
case AUTHN_CA_CERT_NAME:
case AUTHN_IM_CERT:
case AUTHN_SUBJECT_CERT:
case AUTHN_IM_HASH_URL:
case AUTHN_SUBJECT_HASH_URL:
{ /* skip non-authorization tokens */
continue;
}
@@ -362,6 +415,16 @@ static bool equals(private_auth_info_t *this, private_auth_info_t *other)
}
continue;
}
case AUTHN_IM_HASH_URL:
case AUTHN_SUBJECT_HASH_URL:
{
if (streq(i1->value, i2->value))
{
found = TRUE;
break;
}
continue;
}
case AUTHN_CA_CERT:
case AUTHN_IM_CERT:
case AUTHN_SUBJECT_CERT:
@@ -418,6 +481,57 @@ static bool equals(private_auth_info_t *this, private_auth_info_t *other)
return equal;
}
/**
* Destroy the value associated with an item
*/
static void destroy_item_value(item_t *item)
{
switch (item->type)
{
case AUTHZ_PUBKEY:
{
public_key_t *key = (public_key_t*)item->value;
key->destroy(key);
break;
}
case AUTHZ_PSK:
{
shared_key_t *key = (shared_key_t*)item->value;
key->destroy(key);
break;
}
case AUTHN_CA_CERT:
case AUTHN_IM_CERT:
case AUTHN_SUBJECT_CERT:
case AUTHZ_CA_CERT:
case AUTHZ_IM_CERT:
case AUTHZ_SUBJECT_CERT:
{
certificate_t *cert = (certificate_t*)item->value;
cert->destroy(cert);
break;
}
case AUTHN_IM_HASH_URL:
case AUTHN_SUBJECT_HASH_URL:
case AUTHZ_CRL_VALIDATION:
case AUTHZ_OCSP_VALIDATION:
case AUTHZ_EAP:
{
free(item->value);
break;
}
case AUTHN_CA_CERT_KEYID:
case AUTHN_CA_CERT_NAME:
case AUTHZ_CA_CERT_NAME:
case AUTHZ_AC_GROUP:
{
identification_t *id = (identification_t*)item->value;
id->destroy(id);
break;
}
}
}
/**
* Implementation of auth_info_t.destroy
*/
@@ -427,48 +541,7 @@ static void destroy(private_auth_info_t *this)
while (this->items->remove_last(this->items, (void**)&item) == SUCCESS)
{
switch (item->type)
{
case AUTHZ_PUBKEY:
{
public_key_t *key = (public_key_t*)item->value;
key->destroy(key);
break;
}
case AUTHZ_PSK:
{
shared_key_t *key = (shared_key_t*)item->value;
key->destroy(key);
break;
}
case AUTHN_CA_CERT:
case AUTHN_IM_CERT:
case AUTHN_SUBJECT_CERT:
case AUTHZ_CA_CERT:
case AUTHZ_IM_CERT:
case AUTHZ_SUBJECT_CERT:
{
certificate_t *cert = (certificate_t*)item->value;
cert->destroy(cert);
break;
}
case AUTHZ_CRL_VALIDATION:
case AUTHZ_OCSP_VALIDATION:
case AUTHZ_EAP:
{
free(item->value);
break;
}
case AUTHN_CA_CERT_KEYID:
case AUTHN_CA_CERT_NAME:
case AUTHZ_CA_CERT_NAME:
case AUTHZ_AC_GROUP:
{
identification_t *id = (identification_t*)item->value;
id->destroy(id);
break;
}
}
destroy_item_value(item);
free(item);
}
this->items->destroy(this->items);
@@ -484,6 +557,7 @@ auth_info_t *auth_info_create()
this->public.add_item = (void(*)(auth_info_t*, auth_item_t type, void *value))add_item;
this->public.get_item = (bool(*)(auth_info_t*, auth_item_t type, void **value))get_item;
this->public.replace_item = (void(*)(enumerator_t*,auth_item_t,void*))replace_item;
this->public.create_item_enumerator = (enumerator_t*(*)(auth_info_t*))create_item_enumerator;
this->public.complies = (bool(*)(auth_info_t*, auth_info_t *))complies;
this->public.merge = (void(*)(auth_info_t*, auth_info_t *other))merge;
+13
View File
@@ -1,4 +1,5 @@
/*
* Copyright (C) 2008 Tobias Brunner
* Copyright (C) 2007 Martin Willi
* Hochschule fuer Technik Rapperswil
*
@@ -69,6 +70,10 @@ enum auth_item_t {
AUTHN_IM_CERT,
/** certificate for trustchain verification, value is certificate_t* */
AUTHN_SUBJECT_CERT,
/** intermediate certificate supplied as hash and url */
AUTHN_IM_HASH_URL,
/** end-entity certificate supplied as hash and url */
AUTHN_SUBJECT_HASH_URL,
/*
* item provided to authorization process
@@ -126,6 +131,14 @@ struct auth_info_t {
*/
bool (*get_item)(auth_info_t *this, auth_item_t type, void **value);
/**
* Replace an item.
*
* @param type new auth_info type
* @param value pointer to the new value
*/
void (*replace_item)(enumerator_t *this, auth_item_t type, void *value);
/**
* Create an enumerator over all items.
*
@@ -1,4 +1,5 @@
/*
* Copyright (C) 2008 Tobias Brunner
* Copyright (C) 2008 Martin Willi
* Hochschule fuer Technik Rapperswil
*
@@ -15,6 +16,8 @@
* $Id$
*/
#include <daemon.h>
#include "auth_info_wrapper.h"
typedef struct private_auth_info_wrapper_t private_auth_info_wrapper_t;
@@ -43,6 +46,8 @@ typedef struct {
enumerator_t public;
/** inner enumerator from auth_info */
enumerator_t *inner;
/** wrapped auth info */
auth_info_t *auth;
/** enumerated cert type */
certificate_type_t cert;
/** enumerated key type */
@@ -51,6 +56,52 @@ typedef struct {
identification_t *id;
} wrapper_enumerator_t;
/**
* Tries to fetch a certificate that was supplied as hash and URL (replaces the
* item's type and value in place).
*/
static bool fetch_cert(wrapper_enumerator_t *enumerator, auth_item_t *type, void **value)
{
char *url = (char*)*value;
if (!url)
{
/* fetching the certificate previously failed */
return FALSE;
}
chunk_t data;
certificate_t *cert;
DBG1(DBG_CFG, "fetching certificate from '%s' ...", url);
if (lib->fetcher->fetch(lib->fetcher, url, &data) != SUCCESS)
{
DBG1(DBG_CFG, "fetching certificate from '%s' failed", url);
/* we set the item to NULL, so we can skip it */
enumerator->auth->replace_item(enumerator->inner, *type, NULL);
return FALSE;
}
cert = lib->creds->create(lib->creds, CRED_CERTIFICATE, CERT_X509,
BUILD_BLOB_ASN1_DER, data, BUILD_END);
if (!cert)
{
DBG1(DBG_CFG, "parsing fetched certificate failed");
/* we set the item to NULL, so we can skip it */
enumerator->auth->replace_item(enumerator->inner, *type, NULL);
return FALSE;
}
DBG1(DBG_CFG, "fetched certificate \"%D\"", cert->get_subject(cert));
charon->credentials->cache_cert(charon->credentials, cert);
*type = (*type == AUTHN_IM_HASH_URL) ? AUTHN_IM_CERT : AUTHN_SUBJECT_CERT;
*value = cert;
enumerator->auth->replace_item(enumerator->inner, *type, cert);
return TRUE;
}
/**
* enumerate function for wrapper_enumerator_t
*/
@@ -62,8 +113,16 @@ static bool enumerate(wrapper_enumerator_t *this, certificate_t **cert)
while (this->inner->enumerate(this->inner, &type, &current))
{
if (type != AUTHN_SUBJECT_CERT &&
type != AUTHN_IM_CERT)
if (type == AUTHN_IM_HASH_URL ||
type == AUTHN_SUBJECT_HASH_URL)
{
if (!fetch_cert(this, &type, (void**)&current))
{
continue;
}
}
else if (type != AUTHN_SUBJECT_CERT &&
type != AUTHN_IM_CERT)
{
continue;
}
@@ -117,6 +176,7 @@ static enumerator_t *create_enumerator(private_auth_info_wrapper_t *this,
return NULL;
}
enumerator = malloc_thing(wrapper_enumerator_t);
enumerator->auth = this->auth;
enumerator->cert = cert;
enumerator->key = key;
enumerator->id = id;
+108 -24
View File
@@ -1,4 +1,5 @@
/*
* Copyright (C) 2008 Tobias Brunner
* Copyright (C) 2005-2007 Martin Willi
* Copyright (C) 2005 Jan Hutter
* Hochschule fuer Technik Rapperswil
@@ -17,6 +18,7 @@
*/
#include <stddef.h>
#include <ctype.h>
#include <daemon.h>
@@ -75,6 +77,11 @@ struct private_cert_payload_t {
* The contained cert data value.
*/
chunk_t data;
/**
* TRUE if the hash and URL data is invalid
*/
bool invalid_hash_and_url;
};
/**
@@ -123,6 +130,42 @@ encoding_rule_t cert_payload_encodings[] = {
*/
static status_t verify(private_cert_payload_t *this)
{
if (this->encoding == ENC_X509_HASH_AND_URL ||
this->encoding == ENC_X509_HASH_AND_URL_BUNDLE)
{
/* rough verification of hash and URL encoded certificates */
if (this->data.len <= 20)
{
DBG1(DBG_ENC, "invalid payload length for hash and URL (%d), ignore",
this->data.len);
this->invalid_hash_and_url = TRUE;
return SUCCESS;
}
int i = 20; /* skipping the hash */
for (; i < this->data.len; ++i)
{
if (this->data.ptr[i] == '\0')
{
/* null terminated, fine */
return SUCCESS;
}
else if (!isprint(this->data.ptr[i]))
{
DBG1(DBG_ENC, "non printable characters in URL of hash and URL"
" encoded certificate payload, ignore");
this->invalid_hash_and_url = TRUE;
return SUCCESS;
}
}
/* URL is not null terminated, correct that */
chunk_t data = chunk_alloc(this->data.len + 1);
memcpy(data.ptr, this->data.ptr, this->data.len);
data.ptr[this->data.len] = '\0';
chunk_free(&this->data);
this->data = data;
}
return SUCCESS;
}
@@ -168,40 +211,59 @@ static size_t get_length(private_cert_payload_t *this)
return this->payload_length;
}
/**
* Implementation of cert_payload_t.get_cert_encoding.
*/
static cert_encoding_t get_cert_encoding(private_cert_payload_t *this)
{
return this->encoding;
}
/**
* Implementation of cert_payload_t.get_cert.
*/
static certificate_t* get_cert(private_cert_payload_t *this)
static certificate_t *get_cert(private_cert_payload_t *this)
{
certificate_type_t type;
switch (this->encoding)
if (this->encoding != ENC_X509_SIGNATURE)
{
case ENC_X509_SIGNATURE:
type = CERT_X509;
break;
case ENC_PKCS7_WRAPPED_X509:
case ENC_PGP:
case ENC_DNS_SIGNED_KEY:
case ENC_KERBEROS_TOKEN:
case ENC_CRL:
case ENC_ARL:
case ENC_SPKI:
case ENC_X509_ATTRIBUTE:
case ENC_RAW_RSA_KEY:
case ENC_X509_HASH_AND_URL:
case ENC_X509_HASH_AND_URL_BUNDLE:
case ENC_OCSP_CONTENT:
default:
DBG1(DBG_ENC, "certificate encoding %N not supported",
cert_encoding_names, this->encoding);
return NULL;
return NULL;
}
return lib->creds->create(lib->creds, CRED_CERTIFICATE, type,
return lib->creds->create(lib->creds, CRED_CERTIFICATE, CERT_X509,
BUILD_BLOB_ASN1_DER, chunk_clone(this->data),
BUILD_END);
}
/**
* Implementation of cert_payload_t.get_hash.
*/
static chunk_t get_hash(private_cert_payload_t *this)
{
chunk_t hash = chunk_empty;
if ((this->encoding != ENC_X509_HASH_AND_URL &&
this->encoding != ENC_X509_HASH_AND_URL_BUNDLE) ||
this->invalid_hash_and_url)
{
return hash;
}
hash.ptr = this->data.ptr;
hash.len = 20;
return hash;
}
/**
* Implementation of cert_payload_t.get_url.
*/
static char *get_url(private_cert_payload_t *this)
{
if ((this->encoding != ENC_X509_HASH_AND_URL &&
this->encoding != ENC_X509_HASH_AND_URL_BUNDLE) ||
this->invalid_hash_and_url)
{
return NULL;
}
return (char*)this->data.ptr + 20;
}
/**
* Implementation of payload_t.destroy and cert_payload_t.destroy.
*/
@@ -228,12 +290,16 @@ cert_payload_t *cert_payload_create()
this->public.destroy = (void (*) (cert_payload_t*))destroy;
this->public.get_cert = (certificate_t* (*) (cert_payload_t*))get_cert;
this->public.get_cert_encoding = (cert_encoding_t (*) (cert_payload_t*))get_cert_encoding;
this->public.get_hash = (chunk_t (*) (cert_payload_t*))get_hash;
this->public.get_url = (char* (*) (cert_payload_t*))get_url;
this->critical = FALSE;
this->next_payload = NO_PAYLOAD;
this->payload_length = CERT_PAYLOAD_HEADER_LENGTH;
this->data = chunk_empty;
this->encoding = 0;
this->invalid_hash_and_url = FALSE;
return &this->public;
}
@@ -261,3 +327,21 @@ cert_payload_t *cert_payload_create_from_cert(certificate_t *cert)
return &this->public;
}
/*
* Described in header
*/
cert_payload_t *cert_payload_create_from_hash_and_url(chunk_t hash, char *url)
{
private_cert_payload_t *this = (private_cert_payload_t*)cert_payload_create();
chunk_t url_chunk;
this->encoding = ENC_X509_HASH_AND_URL;
url_chunk.ptr = url;
url_chunk.len = strlen(url) + 1;
this->data = chunk_cat("cc", hash, url_chunk);
this->payload_length = CERT_PAYLOAD_HEADER_LENGTH + this->data.len;
return &this->public;
}
@@ -1,4 +1,5 @@
/*
* Copyright (C) 2008 Tobias Brunner
* Copyright (C) 2005-2007 Martin Willi
* Copyright (C) 2005 Jan Hutter
* Hochschule fuer Technik Rapperswil
@@ -79,6 +80,32 @@ struct cert_payload_t {
*/
certificate_t *(*get_cert)(cert_payload_t *this);
/**
* Get the encoding of the certificate.
*
* @return encoding
*/
cert_encoding_t (*get_cert_encoding)(cert_payload_t *this);
/**
* Get the hash if this is a hash and URL encoded certificate.
*
* This function returns internal data, do not free.
*
* @return hash
*/
chunk_t (*get_hash)(cert_payload_t *this);
/**
* Get the URL if this is a hash and URL encoded certificate.
*
* This function returns internal data, do not free.
*
* @return url
*/
char *(*get_url)(cert_payload_t *this);
/**
* Destroys the cert_payload object.
*/
@@ -101,4 +128,13 @@ cert_payload_t *cert_payload_create(void);
*/
cert_payload_t *cert_payload_create_from_cert(certificate_t *cert);
/**
* Creates a certificate payload with hash and URL encoding of a certificate.
*
* @param hash hash of the DER encoded certificate (get's cloned)
* @param url the URL to locate the certificate (get's cloned)
* @return cert_payload_t object
*/
cert_payload_t *cert_payload_create_from_hash_and_url(chunk_t hash, char *url);
#endif /* CERT_PAYLOAD_H_ @} */
+100 -8
View File
@@ -1,4 +1,5 @@
/*
* Copyright (C) 2008 Tobias Brunner
* Copyright (C) 2008 Martin Willi
* Hochschule fuer Technik Rapperswil
*
@@ -20,6 +21,7 @@
#include <utils/mutex.h>
#include <utils/linked_list.h>
#include <crypto/hashers/hasher.h>
#include <daemon.h>
@@ -77,6 +79,16 @@ struct ca_section_t {
* OCSP URIs
*/
linked_list_t *ocsp;
/**
* Hashes of certificates issued by this CA
*/
linked_list_t *hashes;
/**
* Base URI used for certificates from this CA
*/
char *certuribase;
};
/**
@@ -90,6 +102,8 @@ static ca_section_t *ca_section_create(char *name, certificate_t *cert)
ca->crl = linked_list_create();
ca->ocsp = linked_list_create();
ca->cert = cert;
ca->hashes = linked_list_create();
ca->certuribase = NULL;
return ca;
}
@@ -100,6 +114,8 @@ static void ca_section_destroy(ca_section_t *this)
{
this->crl->destroy_function(this->crl, free);
this->ocsp->destroy_function(this->ocsp, free);
this->hashes->destroy_offset(this->hashes, offsetof(identification_t, destroy));
free(this->certuribase);
free(this->name);
free(this);
}
@@ -161,6 +177,39 @@ static enumerator_t *create_inner_cdp(ca_section_t *section, cdp_data_t *data)
return enumerator;
}
/**
* inner enumerator constructor for hash and URL
*/
static enumerator_t *create_inner_cdp_hashandurl(ca_section_t *section, cdp_data_t *data)
{
enumerator_t *enumerator = NULL, *hash_enum;
identification_t *current;
if (!data->id || !section->certuribase)
{
return NULL;
}
hash_enum = section->hashes->create_enumerator(section->hashes);
while (hash_enum->enumerate(hash_enum, &current))
{
if (current->matches(current, data->id))
{
chunk_t hash = current->get_encoding(current);
char *hash_str = chunk_to_hex(hash, FALSE);
char *url = malloc(strlen(section->certuribase) + 40 + 1);
strcpy(url, section->certuribase);
strncat(url, hash_str, 40);
free(hash_str);
enumerator = enumerator_create_single(url, free);
break;
}
}
hash_enum->destroy(hash_enum);
return enumerator;
}
/**
* Implementation of credential_set_t.create_cdp_enumerator.
*/
@@ -170,7 +219,8 @@ static enumerator_t *create_cdp_enumerator(private_stroke_ca_t *this,
cdp_data_t *data;
switch (type)
{ /* we serve CRLs and OCSP responders */
{ /* we serve CRLs, OCSP responders and URLs for hash and URL */
case CERT_X509:
case CERT_X509_CRL:
case CERT_X509_OCSP_RESPONSE:
case CERT_ANY:
@@ -185,8 +235,8 @@ static enumerator_t *create_cdp_enumerator(private_stroke_ca_t *this,
this->mutex->lock(this->mutex);
return enumerator_create_nested(this->sections->create_enumerator(this->sections),
(void*)create_inner_cdp, data,
(void*)cdp_data_destroy);
(type == CERT_X509) ? (void*)create_inner_cdp_hashandurl : (void*)create_inner_cdp,
data, (void*)cdp_data_destroy);
}
/**
* Implementation of stroke_ca_t.add.
@@ -221,6 +271,10 @@ static void add(private_stroke_ca_t *this, stroke_msg_t *msg)
{
ca->ocsp->insert_last(ca->ocsp, strdup(msg->add_ca.ocspuri2));
}
if (msg->add_ca.certuribase)
{
ca->certuribase = strdup(msg->add_ca.certuribase);
}
this->mutex->lock(this->mutex);
this->sections->insert_last(this->sections, ca);
this->mutex->unlock(this->mutex);
@@ -284,6 +338,42 @@ static void list_uris(linked_list_t *list, char *label, FILE *out)
enumerator->destroy(enumerator);
}
/**
* Implementation of stroke_ca_t.check_for_hash_and_url.
*/
static void check_for_hash_and_url(private_stroke_ca_t *this, certificate_t* cert)
{
ca_section_t *section;
enumerator_t *enumerator;
hasher_t *hasher = lib->crypto->create_hasher(lib->crypto, HASH_SHA1);
if (hasher == NULL)
{
DBG1(DBG_IKE, "unable to use hash and URL, SHA1 not supported");
return;
}
this->mutex->lock(this->mutex);
enumerator = this->sections->create_enumerator(this->sections);
while (enumerator->enumerate(enumerator, (void**)&section))
{
if (section->certuribase && cert->issued_by(cert, section->cert))
{
chunk_t hash, encoded = cert->get_encoding(cert);
hasher->allocate_hash(hasher, encoded, &hash);
section->hashes->insert_last(section->hashes,
identification_create_from_encoding(ID_CERT_DER_SHA1, hash));
chunk_free(&hash);
chunk_free(&encoded);
break;
}
}
enumerator->destroy(enumerator);
this->mutex->unlock(this->mutex);
hasher->destroy(hasher);
}
/**
* Implementation of stroke_ca_t.list.
*/
@@ -307,19 +397,20 @@ static void list(private_stroke_ca_t *this, stroke_msg_t *msg, FILE *out)
first = FALSE;
}
fprintf(out, "\n");
fprintf(out, " authname: \"%D\"\n", cert->get_subject(cert));
fprintf(out, " authname: \"%D\"\n", cert->get_subject(cert));
/* list authkey and keyid */
if (public)
{
fprintf(out, " authkey: %D\n",
fprintf(out, " authkey: %D\n",
public->get_id(public, ID_PUBKEY_SHA1));
fprintf(out, " keyid: %D\n",
fprintf(out, " keyid: %D\n",
public->get_id(public, ID_PUBKEY_INFO_SHA1));
public->destroy(public);
}
list_uris(section->crl, " crluris: ", out);
list_uris(section->ocsp, " ocspuris: ", out);
list_uris(section->crl, " crluris: ", out);
list_uris(section->ocsp, " ocspuris: ", out);
fprintf(out, " certuribase: '%s'\n", section->certuribase);
}
enumerator->destroy(enumerator);
this->mutex->unlock(this->mutex);
@@ -350,6 +441,7 @@ stroke_ca_t *stroke_ca_create(stroke_cred_t *cred)
this->public.add = (void(*)(stroke_ca_t*, stroke_msg_t *msg))add;
this->public.del = (void(*)(stroke_ca_t*, stroke_msg_t *msg))del;
this->public.list = (void(*)(stroke_ca_t*, stroke_msg_t *msg, FILE *out))list;
this->public.check_for_hash_and_url = (void(*)(stroke_ca_t*, certificate_t*))check_for_hash_and_url;
this->public.destroy = (void(*)(stroke_ca_t*))destroy;
this->sections = linked_list_create();
+8
View File
@@ -1,4 +1,5 @@
/*
* Copyright (C) 2008 Tobias Brunner
* Copyright (C) 2008 Martin Willi
* Hochschule fuer Technik Rapperswil
*
@@ -60,6 +61,13 @@ struct stroke_ca_t {
*/
void (*list)(stroke_ca_t *this, stroke_msg_t *msg, FILE *out);
/**
* Check if a certificate can be made available through hash and URL.
*
* @param cert peer certificate
*/
void (*check_for_hash_and_url)(stroke_ca_t *this, certificate_t* cert);
/**
* Destroy a stroke_ca instance.
*/
+8 -1
View File
@@ -42,6 +42,11 @@ struct private_stroke_config_t {
*/
mutex_t *mutex;
/**
* ca sections
*/
stroke_ca_t *ca;
/**
* credentials
*/
@@ -435,6 +440,7 @@ static peer_cfg_t *build_peer_cfg(private_stroke_config_t *this,
cert = this->cred->load_peer(this->cred, msg->add_conn.me.cert);
if (cert)
{
this->ca->check_for_hash_and_url(this->ca, cert);
me = update_peerid(cert, me);
cert->destroy(cert);
}
@@ -805,7 +811,7 @@ static void destroy(private_stroke_config_t *this)
/*
* see header file
*/
stroke_config_t *stroke_config_create(stroke_cred_t *cred)
stroke_config_t *stroke_config_create(stroke_ca_t *ca, stroke_cred_t *cred)
{
private_stroke_config_t *this = malloc_thing(private_stroke_config_t);
@@ -818,6 +824,7 @@ stroke_config_t *stroke_config_create(stroke_cred_t *cred)
this->list = linked_list_create();
this->mutex = mutex_create(MUTEX_RECURSIVE);
this->ca = ca;
this->cred = cred;
return &this->public;
+2 -1
View File
@@ -25,6 +25,7 @@
#include <config/backend.h>
#include <stroke_msg.h>
#include "stroke_ca.h"
#include "stroke_cred.h"
typedef struct stroke_config_t stroke_config_t;
@@ -62,6 +63,6 @@ struct stroke_config_t {
/**
* Create a stroke_config instance.
*/
stroke_config_t *stroke_config_create(stroke_cred_t *cred);
stroke_config_t *stroke_config_create(stroke_ca_t *ca, stroke_cred_t *cred);
#endif /* STROKE_CONFIG_H_ @}*/
+9 -7
View File
@@ -250,13 +250,15 @@ static void stroke_add_ca(private_stroke_socket_t *this,
pop_string(msg, &msg->add_ca.crluri2);
pop_string(msg, &msg->add_ca.ocspuri);
pop_string(msg, &msg->add_ca.ocspuri2);
pop_string(msg, &msg->add_ca.certuribase);
DBG2(DBG_CFG, "ca %s", msg->add_ca.name);
DBG2(DBG_CFG, " cacert=%s", msg->add_ca.cacert);
DBG2(DBG_CFG, " crluri=%s", msg->add_ca.crluri);
DBG2(DBG_CFG, " crluri2=%s", msg->add_ca.crluri2);
DBG2(DBG_CFG, " ocspuri=%s", msg->add_ca.ocspuri);
DBG2(DBG_CFG, " ocspuri2=%s", msg->add_ca.ocspuri2);
DBG2(DBG_CFG, "ca %s", msg->add_ca.name);
DBG2(DBG_CFG, " cacert=%s", msg->add_ca.cacert);
DBG2(DBG_CFG, " crluri=%s", msg->add_ca.crluri);
DBG2(DBG_CFG, " crluri2=%s", msg->add_ca.crluri2);
DBG2(DBG_CFG, " ocspuri=%s", msg->add_ca.ocspuri);
DBG2(DBG_CFG, " ocspuri2=%s", msg->add_ca.ocspuri2);
DBG2(DBG_CFG, " certuribase=%s", msg->add_ca.certuribase);
DBG1(DBG_CFG, "received stroke: add ca '%s'", msg->add_ca.name);
@@ -588,7 +590,7 @@ stroke_socket_t *stroke_socket_create()
this->cred = stroke_cred_create();
this->attribute = stroke_attribute_create();
this->ca = stroke_ca_create(this->cred);
this->config = stroke_config_create(this->cred);
this->config = stroke_config_create(this->ca, this->cred);
this->control = stroke_control_create();
this->list = stroke_list_create();
+5
View File
@@ -79,6 +79,11 @@ enum ike_extension_t {
* peer supports MOBIKE (RFC4555)
*/
EXT_MOBIKE = (1<<1),
/**
* peer supports HTTP cert lookups as specified in RFC4306
*/
EXT_HASH_AND_URL = (1<<2),
};
/**
+51 -1
View File
@@ -1,4 +1,5 @@
/*
* Copyright (C) 2008 Tobias Brunner
* Copyright (C) 2006-2008 Martin Willi
* Hochschule fuer Technik Rapperswil
*
@@ -47,6 +48,55 @@ struct private_ike_cert_post_t {
bool initiator;
};
/**
* Generates the cert payload, if possible with hash and url
*/
static cert_payload_t *build_cert_payload(private_ike_cert_post_t *this, certificate_t *cert)
{
cert_payload_t *payload = NULL;
if (this->ike_sa->supports_extension(this->ike_sa, EXT_HASH_AND_URL))
{
/* ok, our peer sent us a HTTP_CERT_LOOKUP_SUPPORTED Notify */
hasher_t *hasher = lib->crypto->create_hasher(lib->crypto, HASH_SHA1);
if (hasher != NULL)
{
chunk_t hash, encoded = cert->get_encoding(cert);
enumerator_t *enumerator;
char *url;
hasher->allocate_hash(hasher, encoded, &hash);
identification_t *id = identification_create_from_encoding(ID_CERT_DER_SHA1, hash);
enumerator = charon->credentials->create_cdp_enumerator(charon->credentials, CERT_X509, id);
if (enumerator->enumerate(enumerator, &url))
{
/* if we have an URL available we send that to our peer */
payload = cert_payload_create_from_hash_and_url(hash, url);
}
enumerator->destroy(enumerator);
id->destroy(id);
chunk_free(&hash);
chunk_free(&encoded);
hasher->destroy(hasher);
}
else
{
DBG1(DBG_IKE, "unable to use hash and URL, SHA1 not supported");
}
}
if (!payload)
{
/* our peer does not support hash and URL or we do not have an URL
* to send to our peer, just create a normal cert payload */
payload = cert_payload_create_from_cert(cert);
}
return payload;
}
/**
* add certificates to message
*/
@@ -81,7 +131,7 @@ static void build_certs(private_ike_cert_post_t *this, message_t *message)
{
break;
}
payload = cert_payload_create_from_cert(cert);
payload = build_cert_payload(this, cert);
if (!payload)
{
break;
+179 -50
View File
@@ -1,4 +1,5 @@
/*
* Copyright (C) 2008 Tobias Brunner
* Copyright (C) 2006-2007 Martin Willi
* Hochschule fuer Technik Rapperswil
*
@@ -45,6 +46,11 @@ struct private_ike_cert_pre_t {
* Are we the initiator?
*/
bool initiator;
/**
* Did we send a HTTP_CERT_LOOKUP_SUPPORTED Notify?
*/
bool http_cert_lookup_supported_sent;
};
/**
@@ -62,52 +68,108 @@ static void process_certreqs(private_ike_cert_pre_t *this, message_t *message)
iterator = message->get_payload_iterator(message);
while (iterator->iterate(iterator, (void**)&payload))
{
if (payload->get_type(payload) == CERTIFICATE_REQUEST)
switch(payload->get_type(payload))
{
certreq_payload_t *certreq = (certreq_payload_t*)payload;
chunk_t keyid;
enumerator_t *enumerator;
this->ike_sa->set_condition(this->ike_sa, COND_CERTREQ_SEEN, TRUE);
if (certreq->get_cert_type(certreq) != CERT_X509)
case CERTIFICATE_REQUEST:
{
DBG1(DBG_IKE, "cert payload %N not supported - ignored",
certificate_type_names, certreq->get_cert_type(certreq));
continue;
}
enumerator = certreq->create_keyid_enumerator(certreq);
while (enumerator->enumerate(enumerator, &keyid))
{
identification_t *id;
certificate_t *cert;
certreq_payload_t *certreq = (certreq_payload_t*)payload;
chunk_t keyid;
enumerator_t *enumerator;
id = identification_create_from_encoding(
ID_PUBKEY_INFO_SHA1, keyid);
cert = charon->credentials->get_cert(charon->credentials,
CERT_X509, KEY_ANY, id, TRUE);
if (cert)
this->ike_sa->set_condition(this->ike_sa, COND_CERTREQ_SEEN, TRUE);
if (certreq->get_cert_type(certreq) != CERT_X509)
{
DBG1(DBG_IKE, "received cert request for \"%D\"",
cert->get_subject(cert));
auth->add_item(auth, AUTHN_CA_CERT, cert);
cert->destroy(cert);
ca_found = TRUE;
DBG1(DBG_IKE, "cert payload %N not supported - ignored",
certificate_type_names, certreq->get_cert_type(certreq));
break;
}
else
enumerator = certreq->create_keyid_enumerator(certreq);
while (enumerator->enumerate(enumerator, &keyid))
{
DBG1(DBG_IKE, "received cert request for unknown ca "
"with keyid %D", id);
auth->add_item(auth, AUTHN_CA_CERT_KEYID, id);
identification_t *id;
certificate_t *cert;
id = identification_create_from_encoding(
ID_PUBKEY_INFO_SHA1, keyid);
cert = charon->credentials->get_cert(charon->credentials,
CERT_X509, KEY_ANY, id, TRUE);
if (cert)
{
DBG1(DBG_IKE, "received cert request for \"%D\"",
cert->get_subject(cert));
auth->add_item(auth, AUTHN_CA_CERT, cert);
cert->destroy(cert);
ca_found = TRUE;
}
else
{
DBG1(DBG_IKE, "received cert request for unknown ca "
"with keyid %D", id);
auth->add_item(auth, AUTHN_CA_CERT_KEYID, id);
}
id->destroy(id);
}
id->destroy(id);
enumerator->destroy(enumerator);
break;
}
enumerator->destroy(enumerator);
case NOTIFY:
{
notify_payload_t *notify = (notify_payload_t*)payload;
/* we only handle one type of notify here */
if (notify->get_notify_type(notify) == HTTP_CERT_LOOKUP_SUPPORTED)
{
this->ike_sa->enable_extension(this->ike_sa, EXT_HASH_AND_URL);
}
break;
}
default:
/* ignore other payloads here, these are handled elsewhere */
break;
}
}
iterator->destroy(iterator);
}
/**
* tries to extract a certificate from the cert payload or the credential
* manager (based on the hash of a hash and URL encoded cert).
* Note: the returned certificate (if any) has to be destroyed
*/
static certificate_t *try_get_cert(cert_payload_t *cert_payload)
{
certificate_t *cert = NULL;
switch (cert_payload->get_cert_encoding(cert_payload))
{
case ENC_X509_SIGNATURE:
{
cert = cert_payload->get_cert(cert_payload);
break;
}
case ENC_X509_HASH_AND_URL:
{
identification_t *id;
chunk_t hash = cert_payload->get_hash(cert_payload);
if (!hash.ptr)
{
/* invalid hash and URL data (logged elsewhere) */
break;
}
id = identification_create_from_encoding(ID_CERT_DER_SHA1, hash);
cert = charon->credentials->get_cert(charon->credentials,
CERT_X509, KEY_ANY, id, FALSE);
id->destroy(id);
break;
}
default:
{
break;
}
}
return cert;
}
/**
* import certificates
*/
@@ -125,28 +187,85 @@ static void process_certs(private_ike_cert_pre_t *this, message_t *message)
{
if (payload->get_type(payload) == CERTIFICATE)
{
certificate_t *cert;
cert_payload_t *cert_payload = (cert_payload_t*)payload;
cert = cert_payload->get_cert(cert_payload);
if (cert)
cert_encoding_t type = cert_payload->get_cert_encoding(cert_payload);
switch (type)
{
if (first)
{ /* the first certificate MUST be an end entity one */
DBG1(DBG_IKE, "received end entity cert \"%D\"",
cert->get_subject(cert));
auth->add_item(auth, AUTHN_SUBJECT_CERT, cert);
first = FALSE;
}
else
case ENC_X509_SIGNATURE:
case ENC_X509_HASH_AND_URL:
{
DBG1(DBG_IKE, "received issuer cert \"%D\"",
cert->get_subject(cert));
auth->add_item(auth, AUTHN_IM_CERT, cert);
if (type == ENC_X509_HASH_AND_URL &&
!this->http_cert_lookup_supported_sent)
{
DBG1(DBG_IKE, "received hash and URL encoded cert, but"
" we don't accept them, ignore");
break;
}
certificate_t *cert = try_get_cert(cert_payload);
if (cert)
{
/* we've got a certificate from the payload or the cache */
if (first)
{ /* the first certificate MUST be an end entity one */
DBG1(DBG_IKE, "received end entity cert \"%D\"",
cert->get_subject(cert));
auth->add_item(auth, AUTHN_SUBJECT_CERT, cert);
first = FALSE;
}
else
{
DBG1(DBG_IKE, "received issuer cert \"%D\"",
cert->get_subject(cert));
auth->add_item(auth, AUTHN_IM_CERT, cert);
}
cert->destroy(cert);
}
else if (type == ENC_X509_HASH_AND_URL)
{
/* we received a hash and URL encoded certificate that
* we haven't fetched yet, we store the URL and fetch
* it later */
char *url = cert_payload->get_url(cert_payload);
if (!url)
{
DBG1(DBG_IKE, "received invalid hash and URL encoded"
" cert, ignore");
break;
}
if (first)
{ /* the first certificate MUST be an end entity one */
DBG1(DBG_IKE, "received hash and URL for end"
" entity cert \"%s\"", url);
auth->add_item(auth, AUTHN_SUBJECT_HASH_URL, url);
first = FALSE;
}
else
{
DBG1(DBG_IKE, "received hash and URL for issuer"
" cert \"%s\"", url);
auth->add_item(auth, AUTHN_IM_HASH_URL, url);
}
}
break;
}
case ENC_PKCS7_WRAPPED_X509:
case ENC_PGP:
case ENC_DNS_SIGNED_KEY:
case ENC_KERBEROS_TOKEN:
case ENC_CRL:
case ENC_ARL:
case ENC_SPKI:
case ENC_X509_ATTRIBUTE:
case ENC_RAW_RSA_KEY:
case ENC_X509_HASH_AND_URL_BUNDLE:
case ENC_OCSP_CONTENT:
default:
DBG1(DBG_ENC, "certificate encoding %N not supported",
cert_encoding_names, cert_payload->get_cert_encoding(cert_payload));
}
cert->destroy(cert);
}
}
iterator->destroy(iterator);
@@ -238,6 +357,15 @@ static void build_certreqs(private_ike_cert_pre_t *this, message_t *message)
}
enumerator->destroy(enumerator);
}
/* if we've added at least one certreq, we notify our peer that we support
* hash and URL for the requested certificates */
if (lib->settings->get_bool(lib->settings, "charon.hash_and_url", FALSE) &&
message->get_payload(message, CERTIFICATE_REQUEST))
{
message->add_notify(message, FALSE, HTTP_CERT_LOOKUP_SUPPORTED, chunk_empty);
this->http_cert_lookup_supported_sent = TRUE;
}
}
/**
@@ -342,6 +470,7 @@ ike_cert_pre_t *ike_cert_pre_create(ike_sa_t *ike_sa, bool initiator)
this->ike_sa = ike_sa;
this->initiator = initiator;
this->http_cert_lookup_supported_sent = FALSE;
return &this->public;
}
@@ -4,6 +4,7 @@
* Copyright (C) 2002 Mario Strasser
* Copyright (C) 2000-2006 Andreas Steffen
* Copyright (C) 2006-2008 Martin Willi
* Copyright (C) 2008 Tobias Brunner
* Hochschule fuer Technik Rapperswil
*
* This program is free software; you can redistribute it and/or modify it
@@ -68,6 +69,11 @@ struct private_x509_cert_t {
* X.509 certificate encoding in ASN.1 DER format
*/
chunk_t encoding;
/**
* SHA1 hash of the DER encoding of this X.509 certificate
*/
chunk_t encoding_hash;
/**
* X.509 certificate body over which signature is computed
@@ -904,6 +910,12 @@ static id_match_t has_subject(private_x509_cert_t *this, identification_t *subje
identification_t *current;
enumerator_t *enumerator;
id_match_t match, best;
if (this->encoding_hash.ptr && subject->get_type(subject) == ID_CERT_DER_SHA1 &&
chunk_equals(this->encoding_hash, subject->get_encoding(subject)))
{
return ID_MATCH_PERFECT;
}
best = this->subject->matches(this->subject, subject);
enumerator = this->subjectAltNames->create_enumerator(this->subjectAltNames);
@@ -1152,6 +1164,7 @@ static void destroy(private_x509_cert_t *this)
DESTROY_IF(this->public_key);
DESTROY_IF(this->authKeyIdentifier);
chunk_free(&this->encoding);
chunk_free(&this->encoding_hash);
free(this);
}
}
@@ -1184,6 +1197,7 @@ static private_x509_cert_t* create_empty(void)
this->public.interface.create_ocsp_uri_enumerator = (enumerator_t* (*)(x509_t*))create_ocsp_uri_enumerator;
this->encoding = chunk_empty;
this->encoding_hash = chunk_empty;
this->public_key = NULL;
this->subject = NULL;
this->issuer = NULL;
@@ -1218,6 +1232,18 @@ static private_x509_cert_t *create_from_chunk(chunk_t chunk)
{
this->flags |= X509_SELF_SIGNED;
}
hasher_t *hasher = lib->crypto->create_hasher(lib->crypto, HASH_SHA1);
if (hasher != NULL)
{
hasher->allocate_hash(hasher, this->encoding, &this->encoding_hash);
hasher->destroy(hasher);
}
else
{
DBG1(" unable to create hash of certificate, SHA1 not supported");
}
return this;
}
+6 -3
View File
@@ -50,11 +50,12 @@ ENUM_BEGIN(id_type_names, ID_ANY, ID_KEY_ID,
"ID_DER_ASN1_DN",
"ID_DER_ASN1_GN",
"ID_KEY_ID");
ENUM_NEXT(id_type_names, ID_DER_ASN1_GN_URI, ID_PUBKEY_SHA1, ID_KEY_ID,
ENUM_NEXT(id_type_names, ID_DER_ASN1_GN_URI, ID_CERT_DER_SHA1, ID_KEY_ID,
"ID_DER_ASN1_GN_URI",
"ID_PUBKEY_INFO_SHA1",
"ID_PUBKEY_SHA1");
ENUM_END(id_type_names, ID_PUBKEY_SHA1);
"ID_PUBKEY_SHA1",
"ID_CERT_DER_SHA1");
ENUM_END(id_type_names, ID_CERT_DER_SHA1);
/**
* X.501 acronyms for well known object identifiers (OIDs)
@@ -941,6 +942,7 @@ static int print(FILE *stream, const struct printf_info *info,
case ID_KEY_ID:
case ID_PUBKEY_INFO_SHA1:
case ID_PUBKEY_SHA1:
case ID_CERT_DER_SHA1:
return fprintf(stream, "%#B", &this->encoded);
case ID_DER_ASN1_GN_URI:
{
@@ -1175,6 +1177,7 @@ identification_t *identification_create_from_encoding(id_type_t type, chunk_t en
case ID_DER_ASN1_GN_URI:
case ID_PUBKEY_INFO_SHA1:
case ID_PUBKEY_SHA1:
case ID_CERT_DER_SHA1:
default:
break;
}
+5
View File
@@ -137,6 +137,11 @@ enum id_type_t {
* SHA1 hash over PKCS#1 subjectPublicKey
*/
ID_PUBKEY_SHA1,
/**
* SHA1 hash of the binary DER encoding of a certificate
*/
ID_CERT_DER_SHA1,
};
/**
+1 -1
View File
@@ -1,5 +1,4 @@
/* automatic handling of confread struct arguments
* Copyright (C) 2007 Tobias Brunner
* Copyright (C) 2006 Andreas Steffen
* Hochschule fuer Technik Rapperswil, Switzerland
*
@@ -233,6 +232,7 @@ static const token_info_t token_info[] =
{ ARG_STR, offsetof(starter_ca_t, crluri2), NULL },
{ ARG_STR, offsetof(starter_ca_t, ocspuri), NULL },
{ ARG_STR, offsetof(starter_ca_t, ocspuri2), NULL },
{ ARG_STR, offsetof(starter_ca_t, certuribase), NULL },
/* end keywords */
{ ARG_MISC, 0, NULL /* KW_HOST */ },
+1 -2
View File
@@ -1,6 +1,4 @@
/* strongSwan IPsec config file parser
* Copyright (C) 2007 Tobias Brunner
* Hochschule fuer Technik Rapperswil
* Copyright (C) 2001-2002 Mathieu Lafon
* Arkoon Network Security
*
@@ -154,6 +152,7 @@ struct starter_ca {
char *crluri2;
char *ocspuri;
char *ocspuri2;
char *certuribase;
bool strict;
+5
View File
@@ -858,6 +858,11 @@ synonym for
.TP
.B ocspuri2
defines an alternative OCSP URI. Currently used by IKEv2 only.
.B certuribase
defines the base URI for the Hash and URL feature supported by IKEv2.
Instead of exchanging complete certificates, IKEv2 allows to send an URI
that resolves to the DER encoded certificate. The certificate URIs are built
by appending the SHA1 hash of the DER encoded certificates to this base URI.
.SH "CONFIG SECTIONS"
At present, the only
.B config
+2 -2
View File
@@ -1,5 +1,4 @@
/* strongSwan keywords
* Copyright (C) 2007 Tobias Brunner
* Copyright (C) 2005 Andreas Steffen
* Hochschule fuer Technik Rapperswil, Switzerland
*
@@ -105,9 +104,10 @@ typedef enum {
KW_CRLURI2,
KW_OCSPURI,
KW_OCSPURI2,
KW_CERTURIBASE,
#define KW_CA_FIRST KW_CA_SETUP
#define KW_CA_LAST KW_OCSPURI2
#define KW_CA_LAST KW_CERTURIBASE
/* end keywords */
KW_HOST,
+1 -1
View File
@@ -1,6 +1,5 @@
%{
/* strongSwan keywords
* Copyright (C) 2007 Tobias Brunner
* Copyright (C) 2005 Andreas Steffen
* Hochschule fuer Technik Rapperswil, Switzerland
*
@@ -89,6 +88,7 @@ crluri2, KW_CRLURI2
ocspuri, KW_OCSPURI
ocspuri1, KW_OCSPURI
ocspuri2, KW_OCSPURI2
certuribase, KW_CERTURIBASE
left, KW_LEFT
leftnexthop, KW_LEFTNEXTHOP
leftsubnet, KW_LEFTSUBNET
+7 -7
View File
@@ -1,5 +1,4 @@
/* Stroke for charon is the counterpart to whack from pluto
* Copyright (C) 2007 Tobias Brunner
* Copyright (C) 2006 Martin Willi
* Hochschule fuer Technik Rapperswil
*
@@ -308,12 +307,13 @@ int starter_stroke_add_ca(starter_ca_t *ca)
msg.type = STR_ADD_CA;
msg.length = offsetof(stroke_msg_t, buffer);
msg.add_ca.name = push_string(&msg, ca->name);
msg.add_ca.cacert = push_string(&msg, ca->cacert);
msg.add_ca.crluri = push_string(&msg, ca->crluri);
msg.add_ca.crluri2 = push_string(&msg, ca->crluri2);
msg.add_ca.ocspuri = push_string(&msg, ca->ocspuri);
msg.add_ca.ocspuri2 = push_string(&msg, ca->ocspuri2);
msg.add_ca.name = push_string(&msg, ca->name);
msg.add_ca.cacert = push_string(&msg, ca->cacert);
msg.add_ca.crluri = push_string(&msg, ca->crluri);
msg.add_ca.crluri2 = push_string(&msg, ca->crluri2);
msg.add_ca.ocspuri = push_string(&msg, ca->ocspuri);
msg.add_ca.ocspuri2 = push_string(&msg, ca->ocspuri2);
msg.add_ca.certuribase = push_string(&msg, ca->certuribase);
return send_stroke_msg(&msg);
}
+1
View File
@@ -233,6 +233,7 @@ struct stroke_msg_t {
char *crluri2;
char *ocspuri;
char *ocspuri2;
char *certuribase;
} add_ca;
/* data for STR_LOGLEVEL */