Change-Id: I700b2afd0e0fc60ce4f864e77166e3fa2e36aaaepersonal/stbuehler/wip
parent
7fb0148348
commit
d72a3c2940
@ -0,0 +1,288 @@
|
||||
|
||||
#include "gnutls_ocsp.h"
|
||||
|
||||
#include <gnutls/ocsp.h>
|
||||
#include <gnutls/crypto.h>
|
||||
|
||||
typedef struct ocsp_response_cert_entry {
|
||||
gnutls_digest_algorithm_t digest;
|
||||
gnutls_datum_t issuer_name_hash;
|
||||
gnutls_datum_t serial;
|
||||
} ocsp_response_cert_entry;
|
||||
|
||||
typedef struct ocsp_response {
|
||||
gnutls_ocsp_resp_t resp;
|
||||
gnutls_datum_t resp_data; /* DER encoded */
|
||||
|
||||
GArray* certificates;
|
||||
} ocsp_response;
|
||||
|
||||
struct liGnuTLSOCSP {
|
||||
GArray* responses;
|
||||
size_t max_serial_length;
|
||||
size_t max_hash_length;
|
||||
};
|
||||
|
||||
static void clear_entry(ocsp_response_cert_entry* entry) {
|
||||
gnutls_free(entry->issuer_name_hash.data);
|
||||
gnutls_free(entry->serial.data);
|
||||
memset(entry, 0, sizeof(*entry));
|
||||
}
|
||||
|
||||
static int get_entry(liServer *srv, ocsp_response_cert_entry* entry, gnutls_ocsp_resp_t resp, unsigned int ndx) {
|
||||
int r;
|
||||
memset(entry, 0, sizeof(*entry));
|
||||
|
||||
r = gnutls_ocsp_resp_get_single(
|
||||
resp, ndx, &entry->digest, &entry->issuer_name_hash, NULL, &entry->serial,
|
||||
NULL, NULL, NULL, NULL, NULL);
|
||||
|
||||
if (GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE == r) return r;
|
||||
|
||||
if (GNUTLS_E_SUCCESS != r) {
|
||||
ERROR(srv, "Couldn't retrieve OCSP response information for entry %u (%s): %s",
|
||||
ndx,
|
||||
gnutls_strerror_name(r), gnutls_strerror(r));
|
||||
return r;
|
||||
}
|
||||
|
||||
if (0 == entry->serial.size || GNUTLS_DIG_UNKNOWN == entry->digest || entry->issuer_name_hash.size != gnutls_hash_get_len(entry->digest)) {
|
||||
ERROR(srv, "Invalid OCSP response data in entry %u", ndx);
|
||||
return GNUTLS_E_OCSP_RESPONSE_ERROR;
|
||||
}
|
||||
|
||||
return GNUTLS_E_SUCCESS;
|
||||
}
|
||||
|
||||
static void clear_response(ocsp_response* response) {
|
||||
gnutls_free(response->resp_data.data);
|
||||
gnutls_ocsp_resp_deinit(response->resp);
|
||||
if (NULL != response->certificates) {
|
||||
guint i;
|
||||
for (i = 0; i < response->certificates->len; ++i) {
|
||||
ocsp_response_cert_entry* entry = &g_array_index(response->certificates, ocsp_response_cert_entry, i);
|
||||
clear_entry(entry);
|
||||
}
|
||||
g_array_free(response->certificates, TRUE);
|
||||
}
|
||||
memset(response, 0, sizeof(*response));
|
||||
}
|
||||
|
||||
/* pass ownership of der_data */
|
||||
static gboolean add_response(liServer *srv, liGnuTLSOCSP *ocsp, gnutls_datum_t* der_data) {
|
||||
ocsp_response response;
|
||||
int r;
|
||||
guint i;
|
||||
|
||||
memset(&response, 0, sizeof(response));
|
||||
|
||||
response.resp_data = *der_data;
|
||||
der_data->data = NULL; der_data->size = 0;
|
||||
|
||||
if (GNUTLS_E_SUCCESS != (r = gnutls_ocsp_resp_init(&response.resp))) {
|
||||
ERROR(srv, "gnutls_ocsp_resp_init (%s): %s",
|
||||
gnutls_strerror_name(r), gnutls_strerror(r));
|
||||
goto error;
|
||||
}
|
||||
|
||||
if (GNUTLS_E_SUCCESS != (r = gnutls_ocsp_resp_import(response.resp, &response.resp_data))) {
|
||||
ERROR(srv, "gnutls_ocsp_resp_import (%s): %s",
|
||||
gnutls_strerror_name(r), gnutls_strerror(r));
|
||||
goto error;
|
||||
}
|
||||
|
||||
response.certificates = g_array_new(FALSE, TRUE, sizeof(ocsp_response_cert_entry));
|
||||
for (i = 0; ; ++i) {
|
||||
ocsp_response_cert_entry entry;
|
||||
|
||||
r = get_entry(srv, &entry, response.resp, i);
|
||||
if (GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE == r) break; /* got them all */
|
||||
if (GNUTLS_E_SUCCESS != r) goto error;
|
||||
|
||||
g_array_append_vals(response.certificates, &entry, 1);
|
||||
|
||||
if (entry.serial.size > ocsp->max_serial_length) ocsp->max_serial_length = entry.serial.size;
|
||||
if (entry.issuer_name_hash.size > ocsp->max_hash_length) ocsp->max_hash_length = entry.issuer_name_hash.size;
|
||||
}
|
||||
|
||||
g_array_append_vals(ocsp->responses, &response, 1);
|
||||
return TRUE;
|
||||
|
||||
error:
|
||||
clear_response(&response);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
static int ctx_ocsp_response(gnutls_session_t session, void* ptr, gnutls_datum_t* ocsp_resp) {
|
||||
liGnuTLSOCSP* ocsp = ptr;
|
||||
guint i;
|
||||
int r;
|
||||
gnutls_datum_t serial = { NULL, 0 };
|
||||
gnutls_datum_t issuer_name = { NULL, 0 };
|
||||
char* issuer_name_hash = NULL;
|
||||
|
||||
if (0 == ocsp->responses->len) return GNUTLS_E_NO_CERTIFICATE_STATUS;
|
||||
|
||||
serial.size = ocsp->max_serial_length;
|
||||
serial.data = gnutls_malloc(serial.size);
|
||||
issuer_name_hash = gnutls_malloc(ocsp->max_hash_length);
|
||||
|
||||
{
|
||||
gnutls_datum_t const* crt_datum;
|
||||
gnutls_x509_crt_t crt = NULL;
|
||||
size_t serial_size = ocsp->max_serial_length;
|
||||
|
||||
crt_datum = gnutls_certificate_get_ours(session); /* memory is NOT owned */
|
||||
if (GNUTLS_E_SUCCESS != (r = gnutls_x509_crt_init(&crt))) goto cleanup;
|
||||
if (GNUTLS_E_SUCCESS != (r = gnutls_x509_crt_import(crt, crt_datum, GNUTLS_X509_FMT_DER))) {
|
||||
gnutls_x509_crt_deinit(crt);
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
;
|
||||
if (GNUTLS_E_SUCCESS != (r = gnutls_x509_crt_get_serial(crt, serial.data, &serial_size))) {
|
||||
gnutls_x509_crt_deinit(crt);
|
||||
goto cleanup;
|
||||
}
|
||||
serial.size = serial_size;
|
||||
|
||||
if (GNUTLS_E_SUCCESS != (r = gnutls_x509_crt_get_raw_issuer_dn(crt, &issuer_name))) {
|
||||
gnutls_x509_crt_deinit(crt);
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
gnutls_x509_crt_deinit(crt);
|
||||
}
|
||||
|
||||
for (i = 0; i < ocsp->responses->len; ++i) {
|
||||
ocsp_response* response = &g_array_index(ocsp->responses, ocsp_response, i);
|
||||
guint j;
|
||||
|
||||
for (j = 0; j < response->certificates->len; ++j) {
|
||||
ocsp_response_cert_entry* entry = &g_array_index(response->certificates, ocsp_response_cert_entry, i);
|
||||
|
||||
if (serial.size != entry->serial.size
|
||||
|| 0 != memcmp(serial.data, entry->serial.data, serial.size)) continue;
|
||||
|
||||
if (GNUTLS_E_SUCCESS != (r = gnutls_hash_fast(entry->digest, issuer_name.data, issuer_name.size, issuer_name_hash))) goto cleanup;
|
||||
|
||||
if (0 != memcmp(issuer_name_hash, entry->issuer_name_hash.data, entry->issuer_name_hash.size)) continue;
|
||||
|
||||
ocsp_resp->size = response->resp_data.size;
|
||||
ocsp_resp->data = gnutls_malloc(ocsp_resp->size);
|
||||
memcpy(ocsp_resp->data, response->resp_data.data, ocsp_resp->size);
|
||||
r = GNUTLS_E_SUCCESS;
|
||||
goto cleanup;
|
||||
}
|
||||
}
|
||||
|
||||
r = GNUTLS_E_NO_CERTIFICATE_STATUS;
|
||||
|
||||
cleanup:
|
||||
gnutls_free(issuer_name_hash);
|
||||
gnutls_free(issuer_name.data);
|
||||
gnutls_free(serial.data);
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
liGnuTLSOCSP* li_gnutls_ocsp_new(void) {
|
||||
liGnuTLSOCSP* ocsp = g_slice_new0(liGnuTLSOCSP);
|
||||
ocsp->responses = g_array_new(FALSE, TRUE, sizeof(ocsp_response));
|
||||
ocsp->max_hash_length = ocsp->max_serial_length = 0;
|
||||
return ocsp;
|
||||
}
|
||||
|
||||
void li_gnutls_ocsp_free(liGnuTLSOCSP *ocsp) {
|
||||
if (NULL != ocsp->responses) {
|
||||
guint i;
|
||||
for (i = 0; i < ocsp->responses->len; ++i) {
|
||||
ocsp_response* response = &g_array_index(ocsp->responses, ocsp_response, i);
|
||||
clear_response(response);
|
||||
}
|
||||
g_array_free(ocsp->responses, TRUE);
|
||||
}
|
||||
memset(ocsp, 0, sizeof(*ocsp));
|
||||
g_slice_free(liGnuTLSOCSP, ocsp);
|
||||
}
|
||||
|
||||
void li_gnutls_ocsp_use(liGnuTLSOCSP *ocsp, gnutls_certificate_credentials_t creds) {
|
||||
gnutls_certificate_set_ocsp_status_request_function(creds, ctx_ocsp_response, ocsp);
|
||||
}
|
||||
|
||||
gboolean li_gnutls_ocsp_add(liServer *srv, liGnuTLSOCSP *ocsp, const char* filename) {
|
||||
int r;
|
||||
gnutls_datum_t file = { NULL, 0 };
|
||||
gnutls_datum_t decoded = { NULL, 0 };
|
||||
gnutls_datum_t* der_data;
|
||||
gboolean result = FALSE;
|
||||
|
||||
if (GNUTLS_E_SUCCESS != (r = gnutls_load_file(filename, &file))) {
|
||||
ERROR(srv, "Failed to load OCSP file '%s' (%s): %s",
|
||||
filename,
|
||||
gnutls_strerror_name(r), gnutls_strerror(r));
|
||||
goto error;
|
||||
}
|
||||
|
||||
/* decode pem "-----BEGIN OCSP RESPONSE-----", otherwise expect DER */
|
||||
if (file.size > 20 && 0 == memcmp(file.data, CONST_STR_LEN("-----BEGIN "))) {
|
||||
r = gnutls_pem_base64_decode2("OCSP RESPONSE", &file, &decoded);
|
||||
|
||||
if (GNUTLS_E_SUCCESS != r) {
|
||||
ERROR(srv, "gnutls_pem_base64_decode2 failed to decode OCSP RESPONSE from '%s' (%s): %s",
|
||||
filename,
|
||||
gnutls_strerror_name(r), gnutls_strerror(r));
|
||||
goto error;
|
||||
}
|
||||
der_data = &decoded;
|
||||
} else {
|
||||
der_data = &file;
|
||||
}
|
||||
|
||||
result = add_response(srv, ocsp, der_data);
|
||||
if (!result) {
|
||||
ERROR(srv, "Failed loading OCSP response from '%s'", filename);
|
||||
}
|
||||
|
||||
error:
|
||||
gnutls_free(file.data);
|
||||
gnutls_free(decoded.data);
|
||||
return result;
|
||||
}
|
||||
|
||||
gboolean li_gnutls_ocsp_search(liServer *srv, liGnuTLSOCSP *ocsp, const char* filename) {
|
||||
int r;
|
||||
gnutls_datum_t file = { NULL, 0 };
|
||||
gnutls_datum_t decoded = { NULL, 0 };
|
||||
gboolean result = FALSE;
|
||||
|
||||
if (GNUTLS_E_SUCCESS != (r = gnutls_load_file(filename, &file))) {
|
||||
ERROR(srv, "Failed to load OCSP file '%s' (%s): %s",
|
||||
filename,
|
||||
gnutls_strerror_name(r), gnutls_strerror(r));
|
||||
goto error;
|
||||
}
|
||||
|
||||
r = gnutls_pem_base64_decode2("OCSP RESPONSE", &file, &decoded);
|
||||
|
||||
if (GNUTLS_E_SUCCESS == r) {
|
||||
result = add_response(srv, ocsp, &decoded);
|
||||
if (!result) {
|
||||
ERROR(srv, "Failed loading OCSP response from '%s'", filename);
|
||||
goto error;
|
||||
}
|
||||
} else if (GNUTLS_E_BASE64_UNEXPECTED_HEADER_ERROR == r) {
|
||||
/* ignore GNUTLS_E_BASE64_UNEXPECTED_HEADER_ERROR */
|
||||
} else {
|
||||
ERROR(srv, "gnutls_pem_base64_decode2 failed to decode OCSP RESPONSE from '%s' (%s): %s",
|
||||
filename,
|
||||
gnutls_strerror_name(r), gnutls_strerror(r));
|
||||
/* continue anyway */
|
||||
}
|
||||
result = TRUE;
|
||||
|
||||
error:
|
||||
gnutls_free(file.data);
|
||||
gnutls_free(decoded.data);
|
||||
return result;
|
||||
}
|
@ -0,0 +1,25 @@
|
||||
#ifndef _LIGHTTPD_GNUTLS_OCSP_H_
|
||||
#define _LIGHTTPD_GNUTLS_OCSP_H_
|
||||
|
||||
#include <lighttpd/base.h>
|
||||
|
||||
#include <gnutls/gnutls.h>
|
||||
|
||||
typedef struct liGnuTLSOCSP liGnuTLSOCSP;
|
||||
|
||||
LI_API liGnuTLSOCSP* li_gnutls_ocsp_new(void);
|
||||
|
||||
/* doesn't call closed_cb; but you can call this from closed_cb */
|
||||
LI_API void li_gnutls_ocsp_free(liGnuTLSOCSP *ocsp);
|
||||
|
||||
LI_API void li_gnutls_ocsp_use(liGnuTLSOCSP *ocsp, gnutls_certificate_credentials_t creds);
|
||||
|
||||
/* load DER or PEM ("OCSP RESPONSE") encoded ocsp response */
|
||||
LI_API gboolean li_gnutls_ocsp_add(liServer *srv, liGnuTLSOCSP *ocsp, const char* filename);
|
||||
|
||||
/* search in PEM file for a OCSP RESPONSE block and add it if there is one;
|
||||
* returns only FALSE if a block was found which COULDN'T be loaded
|
||||
*/
|
||||
LI_API gboolean li_gnutls_ocsp_search(liServer *srv, liGnuTLSOCSP *ocsp, const char* filename);
|
||||
|
||||
#endif
|
Loading…
Reference in new issue