summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorNikos Mavrogiannopoulos <nmav@redhat.com>2016-09-07 14:49:35 +0200
committerNikos Mavrogiannopoulos <nmav@redhat.com>2016-09-12 09:49:23 +0200
commit6b76e0c899b1ff08df9bd9b41588f771f050be89 (patch)
tree6997e6243e6319c07c59c3bcabce11695de02531
parent3026dfb211d75c3d1085ed18304f8dffde855c1b (diff)
downloadgnutls-6b76e0c899b1ff08df9bd9b41588f771f050be89.tar.gz
Added gnutls_certificate_set_ocsp_status_request_function2
That introduces a new function to allow setting an OCSP status request handling function per certificate. Furthermore it repurposes the flag parameters to an index option on gnutls_certificate_set_ocsp_status_request_file. The changes above allow setting a different OCSP status response file per certificate, and a different function. The indexes they rely on to associate with existing certs are the indexes returned by the gnutls_certificate_set_key() and friends functions.
-rw-r--r--lib/auth/cert.c40
-rw-r--r--lib/auth/cert.h12
-rw-r--r--lib/cert.c2
-rw-r--r--lib/ext/status_request.c167
-rw-r--r--lib/gnutls_int.h2
-rw-r--r--lib/includes/gnutls/gnutls.h.in7
-rw-r--r--lib/libgnutls.map1
-rw-r--r--lib/x509.c2
8 files changed, 156 insertions, 77 deletions
diff --git a/lib/auth/cert.c b/lib/auth/cert.c
index 15601725dc..2501ff0bd6 100644
--- a/lib/auth/cert.c
+++ b/lib/auth/cert.c
@@ -65,6 +65,13 @@ static gnutls_privkey_t alloc_and_load_pkcs11_key(gnutls_pkcs11_privkey_t
key, int deinit);
#endif
+static void
+_gnutls_selected_certs_set(gnutls_session_t session,
+ gnutls_pcert_st * certs, int ncerts,
+ gnutls_privkey_t key, int need_free,
+ gnutls_status_request_ocsp_func ocsp_func,
+ void *ocsp_func_ptr);
+
#define MAX_CLIENT_SIGN_ALGOS 3
#define CERTTYPE_SIZE (MAX_CLIENT_SIGN_ALGOS+1)
typedef enum CertificateSigType { RSA_SIGN = 1, DSA_SIGN = 2, ECDSA_SIGN = 64
@@ -450,8 +457,9 @@ call_get_cert_callback(gnutls_session_t session,
pcert = NULL;
local_key = NULL;
}
+
_gnutls_selected_certs_set(session, pcert, pcert_length,
- local_key, 0);
+ local_key, 0, NULL, NULL);
return 0;
@@ -552,7 +560,8 @@ call_get_cert_callback(gnutls_session_t session,
}
_gnutls_selected_certs_set(session, local_certs,
- st2.ncerts, local_key, 1);
+ st2.ncerts, local_key, 1,
+ NULL, NULL);
ret = 0;
@@ -672,9 +681,11 @@ select_client_cert(gnutls_session_t session,
cert_list[0],
cred->certs[indx].
cert_list_length,
- cred->pkey[indx], 0);
+ cred->pkey[indx], 0,
+ NULL, NULL);
} else {
- _gnutls_selected_certs_set(session, NULL, 0, NULL, 0);
+ _gnutls_selected_certs_set(session, NULL, 0, NULL, 0,
+ NULL, NULL);
}
result = 0;
@@ -1882,20 +1893,25 @@ void _gnutls_selected_certs_deinit(gnutls_session_t session)
selected_cert_list[i]);
}
gnutls_free(session->internals.selected_cert_list);
- session->internals.selected_cert_list = NULL;
- session->internals.selected_cert_list_length = 0;
gnutls_privkey_deinit(session->internals.selected_key);
- session->internals.selected_key = NULL;
}
+ session->internals.selected_ocsp_func = NULL;
+
+ session->internals.selected_cert_list = NULL;
+ session->internals.selected_cert_list_length = 0;
+
+ session->internals.selected_key = NULL;
return;
}
-void
+static void
_gnutls_selected_certs_set(gnutls_session_t session,
gnutls_pcert_st * certs, int ncerts,
- gnutls_privkey_t key, int need_free)
+ gnutls_privkey_t key, int need_free,
+ gnutls_status_request_ocsp_func ocsp_func,
+ void *ocsp_func_ptr)
{
_gnutls_selected_certs_deinit(session);
@@ -1904,6 +1920,8 @@ _gnutls_selected_certs_set(gnutls_session_t session,
session->internals.selected_key = key;
session->internals.selected_need_free = need_free;
+ session->internals.selected_ocsp_func = ocsp_func;
+ session->internals.selected_ocsp_func_ptr = ocsp_func_ptr;
}
static void get_server_name(gnutls_session_t session, uint8_t * name,
@@ -2053,7 +2071,9 @@ _gnutls_server_select_cert(gnutls_session_t session,
_gnutls_selected_certs_set(session,
&cred->certs[idx].cert_list[0],
cred->certs[idx].cert_list_length,
- cred->pkey[idx], 0);
+ cred->pkey[idx], 0,
+ cred->certs[idx].ocsp_func,
+ cred->certs[idx].ocsp_func_ptr);
} else {
gnutls_assert();
/* Certificate does not support REQUESTED_ALGO. */
diff --git a/lib/auth/cert.h b/lib/auth/cert.h
index cab6036f0b..fae049c09b 100644
--- a/lib/auth/cert.h
+++ b/lib/auth/cert.h
@@ -34,6 +34,10 @@ typedef struct {
gnutls_pcert_st *cert_list; /* a certificate chain */
unsigned int cert_list_length; /* its length */
gnutls_str_array_t names; /* the names in the first certificate */
+
+ gnutls_status_request_ocsp_func ocsp_func;
+ void *ocsp_func_ptr; /* corresponding OCSP response function + ptr */
+ char *ocsp_response_file; /* corresponding OCSP response file */
} certs_st;
/* This structure may be complex, but it's the only way to
@@ -81,9 +85,8 @@ typedef struct gnutls_certificate_credentials_st {
char pin_tmp[GNUTLS_PKCS11_MAX_PIN_LEN];
/* OCSP */
- gnutls_status_request_ocsp_func ocsp_func;
- void *ocsp_func_ptr;
- char *ocsp_response_file;
+ gnutls_status_request_ocsp_func glob_ocsp_func;
+ void *glob_ocsp_func_ptr; /* corresponding OCSP response function */
} certificate_credentials_st;
typedef struct rsa_info_st {
@@ -131,9 +134,6 @@ int _gnutls_get_selected_cert(gnutls_session_t session,
int _gnutls_server_select_cert(struct gnutls_session_int *,
gnutls_pk_algorithm_t *, size_t);
void _gnutls_selected_certs_deinit(gnutls_session_t session);
-void _gnutls_selected_certs_set(gnutls_session_t session,
- gnutls_pcert_st * certs, int ncerts,
- gnutls_privkey_t key, int need_free);
int _gnutls_get_auth_info_pcert(gnutls_pcert_st * gcert,
gnutls_certificate_type_t type,
diff --git a/lib/cert.c b/lib/cert.c
index e1cb2305a6..d81267e324 100644
--- a/lib/cert.c
+++ b/lib/cert.c
@@ -63,6 +63,7 @@ void gnutls_certificate_free_keys(gnutls_certificate_credentials_t sc)
gnutls_pcert_deinit(&sc->certs[i].cert_list[j]);
}
gnutls_free(sc->certs[i].cert_list);
+ gnutls_free(sc->certs[i].ocsp_response_file);
_gnutls_str_array_clear(&sc->certs[i].names);
}
@@ -200,7 +201,6 @@ gnutls_certificate_free_credentials(gnutls_certificate_credentials_t sc)
{
gnutls_x509_trust_list_deinit(sc->tlist, 1);
gnutls_certificate_free_keys(sc);
- gnutls_free(sc->ocsp_response_file);
memset(sc->pin_tmp, 0, sizeof(sc->pin_tmp));
#ifdef ENABLE_OPENPGP
gnutls_openpgp_keyring_deinit(sc->keyring);
diff --git a/lib/ext/status_request.c b/lib/ext/status_request.c
index 44f0ecfe0e..916da1c24f 100644
--- a/lib/ext/status_request.c
+++ b/lib/ext/status_request.c
@@ -193,40 +193,6 @@ server_recv(gnutls_session_t session,
return 0;
}
-/*
- Servers return a certificate response along with their certificate
- by sending a "CertificateStatus" message immediately after the
- "Certificate" message (and before any "ServerKeyExchange" or
- "CertificateRequest" messages). If a server returns a
- "CertificateStatus" message, then the server MUST have included an
- extension of type "status_request" with empty "extension_data" in
- the extended server hello.
-*/
-
-static int
-server_send(gnutls_session_t session,
- gnutls_buffer_st * extdata, status_request_ext_st * priv)
-{
- int ret;
- gnutls_certificate_credentials_t cred;
-
- cred = (gnutls_certificate_credentials_t)
- _gnutls_get_cred(session, GNUTLS_CRD_CERTIFICATE);
- if (cred == NULL) /* no certificate authentication */
- return gnutls_assert_val(0);
-
- if (cred->ocsp_func == NULL)
- return gnutls_assert_val(GNUTLS_E_SUCCESS);
-
- ret =
- cred->ocsp_func(session, cred->ocsp_func_ptr, &priv->response);
- if (ret == GNUTLS_E_NO_CERTIFICATE_STATUS)
- return 0;
- else if (ret < 0)
- return gnutls_assert_val(ret);
-
- return GNUTLS_E_INT_RET_0;
-}
static int
client_recv(gnutls_session_t session,
@@ -269,7 +235,16 @@ _gnutls_status_request_send_params(gnutls_session_t session,
GNUTLS_EXTENSION_STATUS_REQUEST,
epriv);
- return server_send(session, extdata, priv);
+ /*
+ Servers return a certificate response along with their certificate
+ by sending a "CertificateStatus" message immediately after the
+ "Certificate" message (and before any "ServerKeyExchange" or
+ "CertificateRequest" messages). If a server returns a
+ "CertificateStatus" message, then the server MUST have included an
+ extension of type "status_request" with empty "extension_data" in
+ the extended server hello.
+ */
+ return GNUTLS_E_INT_RET_0;
}
}
@@ -409,8 +384,8 @@ gnutls_ocsp_status_request_get(gnutls_session_t session,
* OCSP response. The response must be a value allocated using gnutls_malloc(),
* and will be deinitialized by the caller.
*
- * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned,
- * otherwise a negative error code is returned.
+ * It is possible to set a specific callback for each provided certificate
+ * using gnutls_certificate_set_ocsp_status_request_function2().
*
* Since: 3.1.3
**/
@@ -420,17 +395,59 @@ gnutls_certificate_set_ocsp_status_request_function
gnutls_status_request_ocsp_func ocsp_func, void *ptr)
{
- sc->ocsp_func = ocsp_func;
- sc->ocsp_func_ptr = ptr;
+ sc->glob_ocsp_func = ocsp_func;
+ sc->glob_ocsp_func_ptr = ptr;
+}
+
+/**
+ * gnutls_certificate_set_ocsp_status_request_function2:
+ * @sc: is a #gnutls_certificate_credentials_t type.
+ * @idx: is a certificate index as returned by gnutls_certificate_set_key() and friends
+ * @ocsp_func: function pointer to OCSP status request callback.
+ * @ptr: opaque pointer passed to callback function
+ *
+ * This function is to be used by server to register a callback to
+ * handle OCSP status requests that correspond to the indexed certificate
+ * from the client. The callback will be invoked if the client supplied a
+ * status-request OCSP extension.
+ *
+ * The callback function prototype is:
+ *
+ * typedef int (*gnutls_status_request_ocsp_func)
+ * (gnutls_session_t session, void *ptr, gnutls_datum_t *ocsp_response);
+ *
+ * The callback will be invoked if the client requests an OCSP certificate
+ * status. The callback may return %GNUTLS_E_NO_CERTIFICATE_STATUS, if
+ * there is no recent OCSP response. If the callback returns %GNUTLS_E_SUCCESS,
+ * it is expected to have the @ocsp_response field set with a valid (DER-encoded)
+ * OCSP response. The response must be a value allocated using gnutls_malloc(),
+ * and will be deinitialized by the caller.
+ *
+ * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned,
+ * otherwise a negative error code is returned.
+ *
+ * Since: 3.5.5
+ **/
+int
+gnutls_certificate_set_ocsp_status_request_function2
+(gnutls_certificate_credentials_t sc, unsigned idx, gnutls_status_request_ocsp_func ocsp_func, void *ptr)
+{
+ if (idx >= sc->ncerts)
+ return gnutls_assert_val(GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE);
+
+ sc->certs[idx].ocsp_func = ocsp_func;
+ sc->certs[idx].ocsp_func_ptr = ptr;
+
+ return 0;
}
static int file_ocsp_func(gnutls_session_t session, void *ptr,
gnutls_datum_t * ocsp_response)
{
int ret;
- gnutls_certificate_credentials_t sc = ptr;
+ const char *file = ptr;
- ret = gnutls_load_file(sc->ocsp_response_file, ocsp_response);
+ ret = gnutls_load_file(file, ocsp_response);
if (ret < 0)
return gnutls_assert_val(GNUTLS_E_NO_CERTIFICATE_STATUS);
@@ -441,13 +458,15 @@ static int file_ocsp_func(gnutls_session_t session, void *ptr,
* gnutls_certificate_set_ocsp_status_request_file:
* @sc: is a credentials structure.
* @response_file: a filename of the OCSP response
- * @flags: should be zero
+ * @idx: is a certificate index as returned by gnutls_certificate_set_key() and friends
*
* This function sets the filename of an OCSP response, that will be
- * sent to the client if requests an OCSP certificate status. This is
- * a convenience function which is inefficient on busy servers since
+ * sent to the client if requests an OCSP certificate status for
+ * the certificate chain specified by @idx.
+ *
+ * This is a convenience function which may be inefficient on busy servers since
* the file is opened on every access. Use
- * gnutls_certificate_set_ocsp_status_request_function() to fine-tune
+ * gnutls_certificate_set_ocsp_status_request_function2() to fine-tune
* file accesses.
*
* Returns: On success, %GNUTLS_E_SUCCESS (0) is returned,
@@ -458,15 +477,18 @@ static int file_ocsp_func(gnutls_session_t session, void *ptr,
int
gnutls_certificate_set_ocsp_status_request_file
(gnutls_certificate_credentials_t sc, const char *response_file,
- unsigned int flags)
+ unsigned idx)
{
- sc->ocsp_func = file_ocsp_func;
- sc->ocsp_func_ptr = sc;
- gnutls_free(sc->ocsp_response_file);
- sc->ocsp_response_file = gnutls_strdup(response_file);
- if (sc->ocsp_response_file == NULL)
+ if (idx >= sc->ncerts)
+ return gnutls_assert_val(GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE);
+
+ gnutls_free(sc->certs[idx].ocsp_response_file);
+ sc->certs[idx].ocsp_response_file = gnutls_strdup(response_file);
+ if (sc->certs[idx].ocsp_response_file == NULL)
return gnutls_assert_val(GNUTLS_E_MEMORY_ERROR);
+ gnutls_certificate_set_ocsp_status_request_function2(sc, idx, file_ocsp_func, sc->certs[idx].ocsp_response_file);
+
return 0;
}
@@ -540,8 +562,12 @@ _gnutls_send_server_certificate_status(gnutls_session_t session, int again)
uint8_t *data;
int data_size = 0;
int ret;
- status_request_ext_st *priv = NULL;
extension_priv_data_t epriv;
+ gnutls_datum_t response = {NULL, 0};
+ gnutls_status_request_ocsp_func func;
+ void *func_ptr;
+ gnutls_certificate_credentials_t cred;
+
if (again == 0) {
ret =
_gnutls_ext_get_session_data(session,
@@ -549,24 +575,47 @@ _gnutls_send_server_certificate_status(gnutls_session_t session, int again)
&epriv);
if (ret < 0)
return 0;
- priv = epriv;
- if (!priv->response.size)
+ cred = (gnutls_certificate_credentials_t)
+ _gnutls_get_cred(session, GNUTLS_CRD_CERTIFICATE);
+ if (cred == NULL)
+ return gnutls_assert_val(0);
+
+ if (session->internals.selected_ocsp_func) {
+ func = session->internals.selected_ocsp_func;
+ func_ptr = session->internals.selected_ocsp_func_ptr;
+ } else if (cred->glob_ocsp_func) {
+ func = cred->glob_ocsp_func;
+ func_ptr = cred->glob_ocsp_func_ptr;
+ } else
+ return 0;
+
+ if (func) {
+ ret = func(session, func_ptr, &response);
+ if (ret == GNUTLS_E_NO_CERTIFICATE_STATUS)
+ return 0;
+ else if (ret < 0)
+ return gnutls_assert_val(ret);
+ }
+
+ if (!response.size)
return 0;
- data_size = priv->response.size + 4;
+ data_size = response.size + 4;
bufel =
_gnutls_handshake_alloc(session, data_size);
- if (!bufel)
+ if (!bufel) {
+ _gnutls_free_datum(&response);
return gnutls_assert_val(GNUTLS_E_MEMORY_ERROR);
+ }
data = _mbuffer_get_udata_ptr(bufel);
data[0] = 0x01;
- _gnutls_write_uint24(priv->response.size, &data[1]);
- memcpy(&data[4], priv->response.data, priv->response.size);
+ _gnutls_write_uint24(response.size, &data[1]);
+ memcpy(&data[4], response.data, response.size);
- _gnutls_free_datum(&priv->response);
+ _gnutls_free_datum(&response);
}
return _gnutls_send_handshake(session, data_size ? bufel : NULL,
GNUTLS_HANDSHAKE_CERTIFICATE_STATUS);
diff --git a/lib/gnutls_int.h b/lib/gnutls_int.h
index 2435c5c1e1..8eb555361f 100644
--- a/lib/gnutls_int.h
+++ b/lib/gnutls_int.h
@@ -889,6 +889,8 @@ typedef struct {
int16_t selected_cert_list_length;
struct gnutls_privkey_st *selected_key;
bool selected_need_free;
+ gnutls_status_request_ocsp_func selected_ocsp_func;
+ void *selected_ocsp_func_ptr;
/* In case of a client holds the extensions we sent to the peer;
* otherwise the extensions we received from the client.
diff --git a/lib/includes/gnutls/gnutls.h.in b/lib/includes/gnutls/gnutls.h.in
index c04e1597d0..c240f791fe 100644
--- a/lib/includes/gnutls/gnutls.h.in
+++ b/lib/includes/gnutls/gnutls.h.in
@@ -1761,9 +1761,14 @@ gnutls_certificate_set_ocsp_status_request_function
gnutls_status_request_ocsp_func ocsp_func, void *ptr);
int
+gnutls_certificate_set_ocsp_status_request_function2
+(gnutls_certificate_credentials_t res, unsigned idx,
+gnutls_status_request_ocsp_func ocsp_func, void *ptr);
+
+int
gnutls_certificate_set_ocsp_status_request_file
(gnutls_certificate_credentials_t res, const char *response_file,
- unsigned int flags);
+ unsigned idx);
int gnutls_ocsp_status_request_enable_client(gnutls_session_t session,
gnutls_datum_t * responder_id,
diff --git a/lib/libgnutls.map b/lib/libgnutls.map
index fffdd1eb21..38805070b3 100644
--- a/lib/libgnutls.map
+++ b/lib/libgnutls.map
@@ -1104,6 +1104,7 @@ GNUTLS_3_4
gnutls_x509_dn_set_str;
gnutls_transport_set_fastopen;
gnutls_oid_to_mac;
+ gnutls_certificate_set_ocsp_status_request_function2;
local:
*;
};
diff --git a/lib/x509.c b/lib/x509.c
index 91082dcca5..47347dfbf1 100644
--- a/lib/x509.c
+++ b/lib/x509.c
@@ -1049,6 +1049,8 @@ certificate_credential_append_crt_list(gnutls_certificate_credentials_t
return GNUTLS_E_MEMORY_ERROR;
}
+ memset(&res->certs[res->ncerts], 0, sizeof(res->certs[0]));
+
res->certs[res->ncerts].cert_list = crt;
res->certs[res->ncerts].cert_list_length = nr;
res->certs[res->ncerts].names = names;